am 66cb11d8: (-s ours) am 0f888787: Import revised translations. DO NOT MERGE
* commit '66cb11d8a71665adee3c30c008d08b5db985d6cc':
Import revised translations. DO NOT MERGE
diff --git a/Android.mk b/Android.mk
index 5a820bd..ff1b7e2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -5,7 +5,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := com.android.phone.common
+LOCAL_STATIC_JAVA_LIBRARIES := com.android.phone.common com.android.vcard android-common
LOCAL_PACKAGE_NAME := Contacts
LOCAL_CERTIFICATE := shared
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 92ac452..52562e0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -34,12 +34,15 @@
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<application
+ android:name="com.android.contacts.ContactsApplication"
android:label="@string/contactsList"
- android:icon="@drawable/ic_launcher_contacts"
+ android:icon="@mipmap/ic_launcher_contacts"
android:process="android.process.acore"
android:taskAffinity="android.task.contacts"
+ android:hardwareAccelerated="true"
>
<!-- A virtual 12 key dialer -->
@@ -64,14 +67,66 @@
</intent-filter>
</activity>
+ <!-- Intercept Dialer Intents for devices without a phone.
+ This activity should have the same intent filters as the DialtactsActivity,
+ so that its capturing the same events. Omit android.intent.category.LAUNCHER, because we
+ don't want this to show up in the Launcher. The priorities of the intent-filters
+ are set lower, so that the user does not see a disambig dialog -->
+ <activity
+ android:name=".activities.NonPhoneActivity"
+ android:theme="@style/NonPhoneActivityTheme"
+ >
+ <intent-filter android:priority="-1">
+ <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 android:priority="-1">
+ <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 android:priority="-1">
+ <action android:name="android.intent.action.DIAL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter android:priority="-1">
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ </intent-filter>
+ <intent-filter android:priority="-1">
+ <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 android:priority="-1">
+ <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 android:priority="-1">
+ <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>
+
<!-- Tab container for all tabs -->
<activity android:name="DialtactsActivity"
android:label="@string/launcherDialer"
android:theme="@style/DialtactsTheme"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
- android:icon="@drawable/ic_launcher_phone"
+ android:icon="@mipmap/ic_launcher_phone"
android:screenOrientation="nosensor"
+ android:enabled="@*android:bool/config_voice_capable"
>
<intent-filter>
<action android:name="android.intent.action.DIAL" />
@@ -116,11 +171,13 @@
</intent-filter>
</activity>
- <!-- Tab container for all tabs -->
- <activity-alias android:name="DialtactsContactsEntryActivity"
- android:targetActivity="DialtactsActivity"
+ <!-- Front door proxy that picks the right UI based on the screen config -->
+ <activity android:name=".activities.ContactsFrontDoor"
android:label="@string/contactsList"
- android:icon="@drawable/ic_launcher_contacts"
+ android:icon="@mipmap/ic_launcher_contacts"
+ android:theme="@style/ContactBrowserTheme"
+ android:clearTaskOnLaunch="true"
+ android:launchMode="singleTop"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -132,25 +189,19 @@
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="vnd.android.cursor.dir/person" android:host="contacts" />
- <data android:mimeType="vnd.android.cursor.dir/contact" android:host="com.android.contacts" />
+ <data android:mimeType="vnd.android.cursor.dir/person" />
+ <data android:mimeType="vnd.android.cursor.dir/contact" />
+ <data android:mimeType="vnd.android.cursor.item/person" />
+ <data android:mimeType="vnd.android.cursor.item/contact" />
+ <data android:mimeType="vnd.android.cursor.item/raw_contact" />
</intent-filter>
+ </activity>
- </activity-alias>
-
- <!-- An empty activity that presents the DialtactActivity's Favorites tab -->
- <activity-alias android:name="DialtactsFavoritesEntryActivity"
- android:targetActivity="DialtactsActivity"
- >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity-alias>
-
- <!-- The actual list of contacts, usually embedded in ContactsActivity -->
- <activity android:name="ContactsListActivity"
+ <!-- The actual list of contacts -->
+ <activity android:name=".activities.ContactBrowserActivity"
android:label="@string/contactsList"
+ android:theme="@style/ContactBrowserTheme"
+ android:launchMode="singleTop"
android:clearTaskOnLaunch="true"
>
<intent-filter>
@@ -196,6 +247,34 @@
</intent-filter>
<intent-filter>
+ <action android:name="android.intent.action.SEARCH" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="vnd.android.cursor.dir/contact" />
+ </intent-filter>
+
+ <intent-filter>
+ <action android:name="android.intent.action.SEARCH" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+
+ <intent-filter>
+ <action android:name="com.android.contacts.action.FILTER_CONTACTS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="vnd.android.cursor.dir/contact" />
+ </intent-filter>
+
+ <meta-data android:name="android.app.searchable"
+ android:resource="@xml/searchable"
+ />
+ </activity>
+
+ <activity android:name=".activities.ContactSelectionActivity"
+ android:label="@string/contactsList"
+ android:theme="@style/ContactPickerTheme"
+ android:launchMode="singleTop"
+ android:clearTaskOnLaunch="true"
+ >
+ <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" />
@@ -224,52 +303,38 @@
<data android:mimeType="vnd.android.cursor.item/postal-address_v2" />
<data android:mimeType="vnd.android.cursor.item/postal-address" />
</intent-filter>
-
</activity>
+ <!-- Backwards compatibility: somebody may have hard coded this activity name -->
+ <activity-alias android:name="ContactsListActivity"
+ android:targetActivity=".activities.ContactBrowserActivity"
+ />
+
<!-- An activity for joining contacts -->
- <activity android:name="ContactsListActivity$JoinContactActivity"
- android:theme="@style/TallTitleBarTheme"
+ <activity android:name=".activities.JoinContactActivity"
+ android:theme="@style/JoinContactActivityTheme"
android:clearTaskOnLaunch="true"
>
<intent-filter>
- <action android:name="com.android.contacts.action.JOIN_AGGREGATE" />
+ <action android:name="com.android.contacts.action.JOIN_CONTACT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <!-- The contacts search/filter UI -->
- <activity android:name="ContactsListActivity$ContactsSearchActivity"
- android:theme="@style/ContactsSearchTheme"
- android:windowSoftInputMode="stateAlwaysVisible|adjustPan"
- >
- <intent-filter>
- <action android:name="com.android.contacts.action.FILTER_CONTACTS" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="vnd.android.cursor.dir/contact" android:host="com.android.contacts" />
- </intent-filter>
- </activity>
-
- <!-- The contacts search/filter UI -->
- <activity android:name="SearchResultsActivity"
- android:theme="@style/TallTitleBarTheme"
- android:label="@string/contactsList"
- >
- <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>
+ <!-- Used to set options -->
+ <activity
+ android:name=".preference.ContactsPreferenceActivity"
+ android:label="@string/activity_title_settings"
+ android:theme="@style/ContactsPreferencesTheme" />
<!-- Used to select display and sync groups -->
- <activity android:name=".ui.ContactsPreferencesActivity" android:label="@string/displayGroups" />
+ <activity
+ android:name=".list.CustomContactListFilterActivity"
+ android:label="@string/custom_list_filter"
+ android:theme="@style/CustomContactListFilterTheme" />
<activity
- android:name=".ui.ShowOrCreateActivity"
+ android:name=".activities.ShowOrCreateActivity"
android:theme="@style/FullyTranslucent">
<intent-filter>
@@ -283,7 +348,7 @@
<!-- Used to show QuickContact window over a translucent activity, which is a
temporary hack until we add better framework support. -->
<activity
- android:name=".ui.QuickContactActivity"
+ android:name=".quickcontact.QuickContactActivity"
android:theme="@style/FullyTranslucent.QuickContact"
android:launchMode="singleTop"
android:excludeFromRecents="true"
@@ -294,15 +359,35 @@
<intent-filter>
<action android:name="com.android.contacts.action.QUICK_CONTACT" />
<category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="vnd.android.cursor.item/contact" android:host="com.android.contacts" />
- <data android:mimeType="vnd.android.cursor.item/person" android:host="contacts" />
+ <data android:mimeType="vnd.android.cursor.item/contact" />
+ <data android:mimeType="vnd.android.cursor.item/person" />
</intent-filter>
</activity>
+ <!-- Flushes the QuickContact IntentCache -->
+ <receiver android:name=".quickcontact.PackageIntentReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.PACKAGE_ADDED" />
+ <data android:scheme="package" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.PACKAGE_REPLACED" />
+ <data android:scheme="package" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.PACKAGE_REMOVED" />
+ <data android:scheme="package" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.PACKAGE_CHANGED" />
+ <data android:scheme="package" />
+ </intent-filter>
+ </receiver>
+
<activity-alias android:name="ContactShortcut"
- android:targetActivity="ContactsListActivity"
+ android:targetActivity=".activities.ContactSelectionActivity"
android:label="@string/shortcutContact"
- android:icon="@drawable/ic_launcher_shortcut_contact">
+ android:icon="@mipmap/ic_launcher_shortcut_contact">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
@@ -312,9 +397,10 @@
</activity-alias>
<activity-alias android:name="alias.DialShortcut"
- android:targetActivity="ContactsListActivity"
+ android:targetActivity=".activities.ContactSelectionActivity"
android:label="@string/shortcutDialContact"
- android:icon="@drawable/ic_launcher_shortcut_directdial">
+ android:icon="@mipmap/ic_launcher_shortcut_directdial"
+ android:enabled="@*android:bool/config_voice_capable">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
@@ -325,9 +411,10 @@
</activity-alias>
<activity-alias android:name="alias.MessageShortcut"
- android:targetActivity="ContactsListActivity"
+ android:targetActivity=".activities.ContactSelectionActivity"
android:label="@string/shortcutMessageContact"
- android:icon="@drawable/ic_launcher_shortcut_directmessage">
+ android:icon="@mipmap/ic_launcher_shortcut_directmessage"
+ android:enabled="@*android:bool/config_voice_capable">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
@@ -338,7 +425,7 @@
<activity android:name="CallDetailActivity"
android:label="@string/callDetailTitle"
- android:theme="@style/TallTitleBarTheme"
+ android:theme="@style/CallDetailActivityTheme"
>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -348,32 +435,29 @@
</activity>
<!-- Views the details of a single contact -->
- <activity android:name="ViewContactActivity"
+ <activity android:name=".activities.ContactDetailActivity"
android:label="@string/viewContactTitle"
- android:theme="@style/TallTitleBarTheme">
+ android:theme="@style/ContactDetailActivityTheme">
<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" android:host="contacts" />
- <data android:mimeType="vnd.android.cursor.item/contact" android:host="com.android.contacts" />
- <data android:mimeType="vnd.android.cursor.item/raw_contact" android:host="com.android.contacts" />
</intent-filter>
</activity>
- <!-- Edit or insert details for a contact -->
+ <!-- Create a new or edit an existing contact -->
<activity
- android:name=".ui.EditContactActivity"
- android:windowSoftInputMode="stateHidden|adjustResize">
+ android:name=".activities.ContactEditorActivity"
+ android:theme="@style/ContactEditorActivityTheme"
+ android:windowSoftInputMode="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" android:host="contacts" />
- <data android:mimeType="vnd.android.cursor.item/contact" android:host="com.android.contacts" />
- <data android:mimeType="vnd.android.cursor.item/raw_contact" android:host="com.android.contacts" />
+ <data android:mimeType="vnd.android.cursor.item/person" />
+ <data android:mimeType="vnd.android.cursor.item/contact" />
+ <data android:mimeType="vnd.android.cursor.item/raw_contact" />
</intent-filter>
-
<intent-filter android:label="@string/insertContactDescription">
<action android:name="android.intent.action.INSERT" />
<category android:name="android.intent.category.DEFAULT" />
@@ -381,7 +465,12 @@
<data android:mimeType="vnd.android.cursor.dir/contact" />
<data android:mimeType="vnd.android.cursor.dir/raw_contact" />
</intent-filter>
+ </activity>
+ <activity android:name=".test.FragmentTestActivity">
+ <intent-filter>
+ <category android:name="android.intent.category.TEST" />
+ </intent-filter>
</activity>
<!-- Stub service used to keep our process alive long enough for
@@ -390,6 +479,11 @@
android:name=".util.EmptyService"
android:exported="false" />
+ <!-- Service to save a contact -->
+ <service
+ android:name=".ContactSaveService"
+ android:exported="false" />
+
<!-- Views the details of a single contact -->
<activity android:name="ContactOptionsActivity"
android:label="@string/contactOptionsTitle"
@@ -401,8 +495,8 @@
</activity>
<!-- Attaches a photo to a contact. Started from external applications -->
- <activity android:name="AttachImage"
- android:label="@string/attachToContact"
+ <activity android:name=".activities.AttachPhotoActivity"
+ android:label="@string/attach_photo_dialog_title"
android:taskAffinity="">
<intent-filter>
<action android:name="android.intent.action.ATTACH_DATA" />
@@ -412,17 +506,22 @@
/>
</activity>
- <!-- Makes .ContactsListActivity the search target for any activity in Contacts -->
+ <!-- Interstitial activity that shows a phone disambig dialog -->
+ <activity android:name="CallContactActivity"
+ android:theme="@android:style/Theme.Translucent">
+ </activity>
+
+ <!-- Makes .ContactBrowserActivity the search target for any activity in Contacts -->
<meta-data
android:name="android.app.default_searchable"
- android:value=".ContactsListActivity" />
+ android:value=".activities.ContactBrowserActivity" />
<!-- LIVE FOLDERS -->
<activity
android:name=".ContactsLiveFolders$AllContacts"
android:label="@string/liveFolderAll"
- android:icon="@drawable/ic_launcher_folder_live_contacts">
+ android:icon="@mipmap/ic_launcher_folder_live_contacts">
<intent-filter>
<action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
<category android:name="android.intent.category.DEFAULT" />
@@ -432,7 +531,7 @@
<activity
android:name=".ContactsLiveFolders$StarredContacts"
android:label="@string/liveFolderFavorites"
- android:icon="@drawable/ic_launcher_folder_live_contacts_starred">
+ android:icon="@mipmap/ic_launcher_folder_live_contacts_starred">
<intent-filter>
<action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
<category android:name="android.intent.category.DEFAULT" />
@@ -442,24 +541,63 @@
<activity
android:name=".ContactsLiveFolders$PhoneContacts"
android:label="@string/liveFolderPhone"
- android:icon="@drawable/ic_launcher_folder_live_contacts_phone">
+ android:icon="@mipmap/ic_launcher_folder_live_contacts_phone">
<intent-filter>
<action android:name="android.intent.action.CREATE_LIVE_FOLDER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <activity android:name=".ImportVCardActivity"
+ <!-- vCard related -->
+ <activity android:name=".vcard.ImportVCardActivity"
+ android:configChanges="orientation"
android:theme="@style/BackgroundOnly">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="text/directory" />
+ <data android:mimeType="text/vcard" />
<data android:mimeType="text/x-vcard" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <activity android:name=".ExportVCardActivity"
+ <activity android:name=".vcard.CancelActivity"
android:theme="@style/BackgroundOnly" />
+
+ <activity android:name=".vcard.SelectAccountActivity"
+ android:theme="@style/BackgroundOnly" />
+
+ <activity android:name=".vcard.ExportVCardActivity"
+ android:theme="@style/BackgroundOnly" />
+
+ <service
+ android:name=".vcard.VCardService"
+ android:exported="false" />
+
+ <!-- Pinned header list demo -->
+ <activity android:name=".widget.PinnedHeaderListDemoActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <!-- The widget that shows details and the social stream of a contact -->
+ <receiver android:name=".socialwidget.SocialWidgetProvider"
+ android:label="@string/social_widget_label" >
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/social_widget_info" />
+ </receiver>
+
+ <activity
+ android:name=".socialwidget.SocialWidgetConfigureActivity"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar" >
+ <intent-filter>
+ <action android:name="android.intent.action.APPWIDGET_PICK" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/proguard.flags b/proguard.flags
index 6cc704c..577144b 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -12,3 +12,8 @@
-keep class com.android.contacts.model.EntityDelta$ValuesDelta {
public android.content.ContentValues getAfter();
}
+
+# Any methods whose name is '*ForTest' are preserved.
+-keep class ** {
+ *** *ForTest(...);
+}
\ No newline at end of file
diff --git a/res/anim/aizy_preview_popup_enter.xml b/res/anim/aizy_preview_popup_enter.xml
new file mode 100644
index 0000000..194a3a3
--- /dev/null
+++ b/res/anim/aizy_preview_popup_enter.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_interpolator">
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="@android:integer/config_shortAnimTime" />
+</set>
diff --git a/res/anim/aizy_preview_popup_exit.xml b/res/anim/aizy_preview_popup_exit.xml
new file mode 100644
index 0000000..ed5cda8
--- /dev/null
+++ b/res/anim/aizy_preview_popup_exit.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_interpolator">
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:duration="@android:integer/config_longAnimTime" />
+</set>
diff --git a/res/anim/footer_appear.xml b/res/anim/footer_appear.xml
new file mode 100644
index 0000000..941454a
--- /dev/null
+++ b/res/anim/footer_appear.xml
@@ -0,0 +1,23 @@
+<?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.
+*/
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromYDelta="+10%p"
+ android:toYDelta="0"
+ android:duration="300" />
\ No newline at end of file
diff --git a/res/anim/quickcontact.xml b/res/anim/quickcontact.xml
deleted file mode 100644
index 5dd5a16..0000000
--- a/res/anim/quickcontact.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<translate
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:fromXDelta="100%p"
- android:toXDelta="0"
- android:duration="325" />
diff --git a/res/anim/quickcontact_above_enter.xml b/res/anim/quickcontact_above_enter.xml
index dc2d053..d86c98c 100644
--- a/res/anim/quickcontact_above_enter.xml
+++ b/res/anim/quickcontact_above_enter.xml
@@ -18,12 +18,12 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:interpolator="@android:anim/decelerate_interpolator"
- android:fromXScale="0.75" android:toXScale="1.0"
- android:fromYScale="0.75" android:toYScale="1.0"
+ <scale android:interpolator="@android:interpolator/decelerate_quint"
+ android:fromXScale="1" android:toXScale="1.0"
+ android:fromYScale="0.8" android:toYScale="1.0"
android:pivotX="50%" android:pivotY="100%"
android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:interpolator="@android:anim/decelerate_interpolator"
+ <alpha android:interpolator="@android:interpolator/decelerate_cubic"
android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="@android:integer/config_shortAnimTime" />
</set>
diff --git a/res/anim/quickcontact_above_exit.xml b/res/anim/quickcontact_above_exit.xml
index dd34f87..e8a16b4 100644
--- a/res/anim/quickcontact_above_exit.xml
+++ b/res/anim/quickcontact_above_exit.xml
@@ -17,12 +17,13 @@
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:anim/accelerate_interpolator">
- <scale android:fromXScale="1.0" android:toXScale=".5"
- android:fromYScale="1.0" android:toYScale=".5"
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <scale android:interpolator="@android:interpolator/accelerate_quint"
+ android:fromXScale="1.0" android:toXScale="1.0"
+ android:fromYScale="1.0" android:toYScale=".8"
android:pivotX="50%" android:pivotY="100%"
android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
+ <alpha android:interpolator="@android:interpolator/accelerate_cubic"
+ android:fromAlpha="1.0" android:toAlpha="0"
android:duration="@android:integer/config_shortAnimTime" />
</set>
diff --git a/res/anim/quickcontact_below_enter.xml b/res/anim/quickcontact_below_enter.xml
index 9a1a577..39aa177 100644
--- a/res/anim/quickcontact_below_enter.xml
+++ b/res/anim/quickcontact_below_enter.xml
@@ -18,12 +18,12 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:interpolator="@android:anim/decelerate_interpolator"
- android:fromXScale="0.75" android:toXScale="1.0"
- android:fromYScale="0.75" android:toYScale="1.0"
+ <scale android:interpolator="@android:interpolator/decelerate_quint"
+ android:fromXScale="1" android:toXScale="1.0"
+ android:fromYScale="0.8" android:toYScale="1.0"
android:pivotX="50%" android:pivotY="0%"
android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:interpolator="@android:anim/decelerate_interpolator"
+ <alpha android:interpolator="@android:interpolator/decelerate_cubic"
android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="@android:integer/config_shortAnimTime" />
</set>
diff --git a/res/anim/quickcontact_below_exit.xml b/res/anim/quickcontact_below_exit.xml
index 7587c7a..8f70751 100644
--- a/res/anim/quickcontact_below_exit.xml
+++ b/res/anim/quickcontact_below_exit.xml
@@ -17,12 +17,13 @@
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@android:anim/accelerate_interpolator">
- <scale android:fromXScale="1.0" android:toXScale=".5"
- android:fromYScale="1.0" android:toYScale=".5"
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <scale android:interpolator="@android:interpolator/accelerate_quint"
+ android:fromXScale="1.0" android:toXScale="1.0"
+ android:fromYScale="1.0" android:toYScale="0.8"
android:pivotX="50%" android:pivotY="0%"
android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:fromAlpha="1.0" android:toAlpha="0"
+ <alpha android:interpolator="@android:interpolator/accelerate_cubic"
+ android:fromAlpha="1.0" android:toAlpha="0"
android:duration="@android:integer/config_shortAnimTime" />
</set>
diff --git a/res/anim/search_bar_enter.xml b/res/anim/search_bar_enter.xml
index f0ee241..19bbbba 100644
--- a/res/anim/search_bar_enter.xml
+++ b/res/anim/search_bar_enter.xml
@@ -19,8 +19,6 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">
- <translate android:fromYDelta="-32" android:toYDelta="0"
- android:duration="@android:integer/config_shortAnimTime"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="@android:integer/config_shortAnimTime" />
</set>
diff --git a/res/color-finger/tab_indicator_text.xml b/res/color-finger/tab_indicator_text.xml
deleted file mode 100644
index 50ca824..0000000
--- a/res/color-finger/tab_indicator_text.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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:color="@*android:color/dim_foreground_dark_inverse"/>
- <item android:color="@*android:color/dim_foreground_dark"/> <!-- not selected -->
-</selector>
diff --git a/res/color-finger/dialer_button_text.xml b/res/color/dialer_button_text.xml
similarity index 100%
rename from res/color-finger/dialer_button_text.xml
rename to res/color/dialer_button_text.xml
diff --git a/res/color-finger/kind_title.xml b/res/color/kind_title.xml
similarity index 100%
rename from res/color-finger/kind_title.xml
rename to res/color/kind_title.xml
diff --git a/res/drawable-finger/btn_circle.xml b/res/drawable-finger/btn_circle.xml
deleted file mode 100644
index 9208010..0000000
--- a/res/drawable-finger/btn_circle.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?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_contact_picture.xml b/res/drawable-finger/btn_contact_picture.xml
deleted file mode 100644
index 643231a..0000000
--- a/res/drawable-finger/btn_contact_picture.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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_textfield.xml b/res/drawable-finger/btn_dial_textfield.xml
deleted file mode 100644
index 109f6ae..0000000
--- a/res/drawable-finger/btn_dial_textfield.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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_normal" />
- <item
- android:drawable="@drawable/btn_dial_textfield_normal" />
-</selector>
-
diff --git a/res/drawable-finger/btn_dial_textfield_active.xml b/res/drawable-finger/btn_dial_textfield_active.xml
deleted file mode 100644
index 246d543..0000000
--- a/res/drawable-finger/btn_dial_textfield_active.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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_activated" />
- <item
- android:drawable="@drawable/btn_dial_textfield_activated" />
-</selector>
-
diff --git a/res/drawable-finger/quickcontact_disambig_checkbox.xml b/res/drawable-finger/quickcontact_disambig_checkbox.xml
deleted file mode 100644
index 4add69c..0000000
--- a/res/drawable-finger/quickcontact_disambig_checkbox.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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"
- android:dither="true">
-
- <item android:state_checked="true"
- android:drawable="@drawable/quickcontact_disambig_checkbox_on" />
- <item
- android:drawable="@drawable/quickcontact_disambig_checkbox_off" />
-
-</selector>
diff --git a/res/drawable-finger/quickcontact_slider_btn.xml b/res/drawable-finger/quickcontact_slider_btn.xml
deleted file mode 100644
index a1be8f4..0000000
--- a/res/drawable-finger/quickcontact_slider_btn.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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"
- android:dither="true">
-
- <item android:state_checked="true"
- android:drawable="@drawable/quickcontact_slider_btn_on" />
- <item android:state_window_focused="false"
- android:drawable="@drawable/quickcontact_slider_btn_normal" />
- <item android:state_pressed="true"
- android:drawable="@drawable/quickcontact_slider_btn_pressed" />
- <item android:state_focused="true"
- android:drawable="@drawable/quickcontact_slider_btn_selected" />
- <item
- android:drawable="@drawable/quickcontact_slider_btn_normal" />
-
-</selector>
diff --git a/res/drawable-hdpi-finger/btn_circle_disable.png b/res/drawable-hdpi-finger/btn_circle_disable.png
deleted file mode 100755
index f04da38..0000000
--- a/res/drawable-hdpi-finger/btn_circle_disable.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_circle_disable_focused.png b/res/drawable-hdpi-finger/btn_circle_disable_focused.png
deleted file mode 100755
index 1c918a5..0000000
--- a/res/drawable-hdpi-finger/btn_circle_disable_focused.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_circle_normal.png b/res/drawable-hdpi-finger/btn_circle_normal.png
deleted file mode 100755
index f5aedb4..0000000
--- a/res/drawable-hdpi-finger/btn_circle_normal.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_circle_pressed.png b/res/drawable-hdpi-finger/btn_circle_pressed.png
deleted file mode 100755
index bada362..0000000
--- a/res/drawable-hdpi-finger/btn_circle_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_circle_selected.png b/res/drawable-hdpi-finger/btn_circle_selected.png
deleted file mode 100755
index 1c918a5..0000000
--- a/res/drawable-hdpi-finger/btn_circle_selected.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_left_disable.9.png b/res/drawable-hdpi-finger/btn_dial_action_left_disable.9.png
deleted file mode 100644
index 2f17d86..0000000
--- a/res/drawable-hdpi-finger/btn_dial_action_left_disable.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_middle_disable.9.png b/res/drawable-hdpi-finger/btn_dial_action_middle_disable.9.png
deleted file mode 100644
index e86afd1..0000000
--- a/res/drawable-hdpi-finger/btn_dial_action_middle_disable.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_right_disable.9.png b/res/drawable-hdpi-finger/btn_dial_action_right_disable.9.png
deleted file mode 100644
index 54896a3..0000000
--- a/res/drawable-hdpi-finger/btn_dial_action_right_disable.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_textfield_normal.9.png b/res/drawable-hdpi-finger/btn_dial_textfield_normal.9.png
deleted file mode 100644
index adacb44..0000000
--- a/res/drawable-hdpi-finger/btn_dial_textfield_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_search_dialog_default.9.png b/res/drawable-hdpi-finger/btn_search_dialog_default.9.png
deleted file mode 100644
index f65fa14..0000000
--- a/res/drawable-hdpi-finger/btn_search_dialog_default.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_search_dialog_pressed.9.png b/res/drawable-hdpi-finger/btn_search_dialog_pressed.9.png
deleted file mode 100644
index 17cc0dd..0000000
--- a/res/drawable-hdpi-finger/btn_search_dialog_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_search_dialog_selected.9.png b/res/drawable-hdpi-finger/btn_search_dialog_selected.9.png
deleted file mode 100644
index 0509e14..0000000
--- a/res/drawable-hdpi-finger/btn_search_dialog_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/contact_picture_border_highlight.9.png b/res/drawable-hdpi-finger/contact_picture_border_highlight.9.png
deleted file mode 100755
index 776d614..0000000
--- a/res/drawable-hdpi-finger/contact_picture_border_highlight.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/contact_picture_border_normal.9.png b/res/drawable-hdpi-finger/contact_picture_border_normal.9.png
deleted file mode 100755
index be67b1a..0000000
--- a/res/drawable-hdpi-finger/contact_picture_border_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/contact_picture_border_pressed.9.png b/res/drawable-hdpi-finger/contact_picture_border_pressed.9.png
deleted file mode 100755
index 32fbaa5..0000000
--- a/res/drawable-hdpi-finger/contact_picture_border_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_0_wht.png b/res/drawable-hdpi-finger/dial_num_0_wht.png
deleted file mode 100644
index 98dfecd..0000000
--- a/res/drawable-hdpi-finger/dial_num_0_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_1_no_vm_wht.png b/res/drawable-hdpi-finger/dial_num_1_no_vm_wht.png
deleted file mode 100644
index 8f65ee1..0000000
--- a/res/drawable-hdpi-finger/dial_num_1_no_vm_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_2_wht.png b/res/drawable-hdpi-finger/dial_num_2_wht.png
deleted file mode 100644
index a7c8993..0000000
--- a/res/drawable-hdpi-finger/dial_num_2_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_3_wht.png b/res/drawable-hdpi-finger/dial_num_3_wht.png
deleted file mode 100644
index d8276c0..0000000
--- a/res/drawable-hdpi-finger/dial_num_3_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_4_wht.png b/res/drawable-hdpi-finger/dial_num_4_wht.png
deleted file mode 100644
index 7de2623..0000000
--- a/res/drawable-hdpi-finger/dial_num_4_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_5_wht.png b/res/drawable-hdpi-finger/dial_num_5_wht.png
deleted file mode 100644
index e1cb279..0000000
--- a/res/drawable-hdpi-finger/dial_num_5_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_6_wht.png b/res/drawable-hdpi-finger/dial_num_6_wht.png
deleted file mode 100644
index 5b33e2f..0000000
--- a/res/drawable-hdpi-finger/dial_num_6_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_7_wht.png b/res/drawable-hdpi-finger/dial_num_7_wht.png
deleted file mode 100644
index 5136e67..0000000
--- a/res/drawable-hdpi-finger/dial_num_7_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_8_wht.png b/res/drawable-hdpi-finger/dial_num_8_wht.png
deleted file mode 100644
index 3386866..0000000
--- a/res/drawable-hdpi-finger/dial_num_8_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_9_wht.png b/res/drawable-hdpi-finger/dial_num_9_wht.png
deleted file mode 100644
index db78a47..0000000
--- a/res/drawable-hdpi-finger/dial_num_9_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_pound_wht.png b/res/drawable-hdpi-finger/dial_num_pound_wht.png
deleted file mode 100644
index b4793fd..0000000
--- a/res/drawable-hdpi-finger/dial_num_pound_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_star_wht.png b/res/drawable-hdpi-finger/dial_num_star_wht.png
deleted file mode 100644
index 612c4ee..0000000
--- a/res/drawable-hdpi-finger/dial_num_star_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_btn_round_less.png b/res/drawable-hdpi-finger/ic_btn_round_less.png
deleted file mode 100644
index 21a8eff..0000000
--- a/res/drawable-hdpi-finger/ic_btn_round_less.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_btn_round_minus.png b/res/drawable-hdpi-finger/ic_btn_round_minus.png
deleted file mode 100755
index 2cd610c..0000000
--- a/res/drawable-hdpi-finger/ic_btn_round_minus.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_btn_round_more.png b/res/drawable-hdpi-finger/ic_btn_round_more.png
deleted file mode 100755
index bf91bef..0000000
--- a/res/drawable-hdpi-finger/ic_btn_round_more.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_btn_round_plus.png b/res/drawable-hdpi-finger/ic_btn_round_plus.png
deleted file mode 100755
index a541efb..0000000
--- a/res/drawable-hdpi-finger/ic_btn_round_plus.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_contact_list_picture.png b/res/drawable-hdpi-finger/ic_contact_list_picture.png
deleted file mode 100644
index b96fbf4..0000000
--- a/res/drawable-hdpi-finger/ic_contact_list_picture.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_contact_picture.png b/res/drawable-hdpi-finger/ic_contact_picture.png
deleted file mode 100755
index 7c34f5c..0000000
--- a/res/drawable-hdpi-finger/ic_contact_picture.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_contact_picture_2.png b/res/drawable-hdpi-finger/ic_contact_picture_2.png
deleted file mode 100755
index 5e65276..0000000
--- a/res/drawable-hdpi-finger/ic_contact_picture_2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_contact_picture_3.png b/res/drawable-hdpi-finger/ic_contact_picture_3.png
deleted file mode 100755
index a8ec1e1..0000000
--- a/res/drawable-hdpi-finger/ic_contact_picture_3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_default_number.png b/res/drawable-hdpi-finger/ic_default_number.png
deleted file mode 100755
index cdc05a8..0000000
--- a/res/drawable-hdpi-finger/ic_default_number.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_dial_number_blk.png b/res/drawable-hdpi-finger/ic_dial_number_blk.png
deleted file mode 100755
index 971d60a..0000000
--- a/res/drawable-hdpi-finger/ic_dial_number_blk.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_dial_number_wht.png b/res/drawable-hdpi-finger/ic_dial_number_wht.png
deleted file mode 100755
index ffc0048..0000000
--- a/res/drawable-hdpi-finger/ic_dial_number_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_join.png b/res/drawable-hdpi-finger/ic_join.png
deleted file mode 100644
index 8f140d4..0000000
--- a/res/drawable-hdpi-finger/ic_join.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_menu_account_list.png b/res/drawable-hdpi-finger/ic_menu_account_list.png
deleted file mode 100755
index a69c642..0000000
--- a/res/drawable-hdpi-finger/ic_menu_account_list.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_menu_add_picture.png b/res/drawable-hdpi-finger/ic_menu_add_picture.png
deleted file mode 100755
index 85faf1c..0000000
--- a/res/drawable-hdpi-finger/ic_menu_add_picture.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_menu_import_export.png b/res/drawable-hdpi-finger/ic_menu_import_export.png
deleted file mode 100644
index 4f1b608..0000000
--- a/res/drawable-hdpi-finger/ic_menu_import_export.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_menu_merge.png b/res/drawable-hdpi-finger/ic_menu_merge.png
deleted file mode 100644
index d3ed8cc..0000000
--- a/res/drawable-hdpi-finger/ic_menu_merge.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_menu_split.png b/res/drawable-hdpi-finger/ic_menu_split.png
deleted file mode 100644
index 33ef601..0000000
--- a/res/drawable-hdpi-finger/ic_menu_split.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_selected_friends_list.png b/res/drawable-hdpi-finger/ic_tab_selected_friends_list.png
deleted file mode 100755
index 201b80f..0000000
--- a/res/drawable-hdpi-finger/ic_tab_selected_friends_list.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_unselected_friends_list.png b/res/drawable-hdpi-finger/ic_tab_unselected_friends_list.png
deleted file mode 100755
index 6a31485..0000000
--- a/res/drawable-hdpi-finger/ic_tab_unselected_friends_list.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_arrow_down.png b/res/drawable-hdpi-finger/quickcontact_arrow_down.png
deleted file mode 100644
index 7eba756..0000000
--- a/res/drawable-hdpi-finger/quickcontact_arrow_down.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_arrow_up.png b/res/drawable-hdpi-finger/quickcontact_arrow_up.png
deleted file mode 100644
index 6daf90a..0000000
--- a/res/drawable-hdpi-finger/quickcontact_arrow_up.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_bottom_frame.9.png b/res/drawable-hdpi-finger/quickcontact_bottom_frame.9.png
deleted file mode 100644
index 9fac225..0000000
--- a/res/drawable-hdpi-finger/quickcontact_bottom_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_disambig_bottom_bg.9.png b/res/drawable-hdpi-finger/quickcontact_disambig_bottom_bg.9.png
deleted file mode 100644
index 4702f16..0000000
--- a/res/drawable-hdpi-finger/quickcontact_disambig_bottom_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_disambig_checkbox_off.png b/res/drawable-hdpi-finger/quickcontact_disambig_checkbox_off.png
deleted file mode 100644
index f87572c..0000000
--- a/res/drawable-hdpi-finger/quickcontact_disambig_checkbox_off.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_disambig_checkbox_on.png b/res/drawable-hdpi-finger/quickcontact_disambig_checkbox_on.png
deleted file mode 100644
index 3ea5360..0000000
--- a/res/drawable-hdpi-finger/quickcontact_disambig_checkbox_on.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_disambig_divider.9.png b/res/drawable-hdpi-finger/quickcontact_disambig_divider.9.png
deleted file mode 100644
index 8c35e8c..0000000
--- a/res/drawable-hdpi-finger/quickcontact_disambig_divider.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_drop_shadow.9.png b/res/drawable-hdpi-finger/quickcontact_drop_shadow.9.png
deleted file mode 100644
index 0dcf076..0000000
--- a/res/drawable-hdpi-finger/quickcontact_drop_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_photo_frame.9.png b/res/drawable-hdpi-finger/quickcontact_photo_frame.9.png
deleted file mode 100644
index 990c75d..0000000
--- a/res/drawable-hdpi-finger/quickcontact_photo_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_slider_background.png b/res/drawable-hdpi-finger/quickcontact_slider_background.png
deleted file mode 100644
index c9c09ee..0000000
--- a/res/drawable-hdpi-finger/quickcontact_slider_background.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_slider_btn_normal.9.png b/res/drawable-hdpi-finger/quickcontact_slider_btn_normal.9.png
deleted file mode 100644
index 9d3d7ad..0000000
--- a/res/drawable-hdpi-finger/quickcontact_slider_btn_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_slider_btn_on.9.png b/res/drawable-hdpi-finger/quickcontact_slider_btn_on.9.png
deleted file mode 100644
index ac2b496..0000000
--- a/res/drawable-hdpi-finger/quickcontact_slider_btn_on.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_slider_btn_pressed.9.png b/res/drawable-hdpi-finger/quickcontact_slider_btn_pressed.9.png
deleted file mode 100644
index d9da598..0000000
--- a/res/drawable-hdpi-finger/quickcontact_slider_btn_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_slider_btn_selected.9.png b/res/drawable-hdpi-finger/quickcontact_slider_btn_selected.9.png
deleted file mode 100644
index 72d053b..0000000
--- a/res/drawable-hdpi-finger/quickcontact_slider_btn_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_slider_grip_left.png b/res/drawable-hdpi-finger/quickcontact_slider_grip_left.png
deleted file mode 100644
index 97f12aa..0000000
--- a/res/drawable-hdpi-finger/quickcontact_slider_grip_left.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_slider_grip_right.png b/res/drawable-hdpi-finger/quickcontact_slider_grip_right.png
deleted file mode 100644
index e410059..0000000
--- a/res/drawable-hdpi-finger/quickcontact_slider_grip_right.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_slider_presence_active.png b/res/drawable-hdpi-finger/quickcontact_slider_presence_active.png
deleted file mode 100644
index f62e681..0000000
--- a/res/drawable-hdpi-finger/quickcontact_slider_presence_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_slider_presence_away.png b/res/drawable-hdpi-finger/quickcontact_slider_presence_away.png
deleted file mode 100644
index 0516b97..0000000
--- a/res/drawable-hdpi-finger/quickcontact_slider_presence_away.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_slider_presence_busy.png b/res/drawable-hdpi-finger/quickcontact_slider_presence_busy.png
deleted file mode 100644
index 26063f4..0000000
--- a/res/drawable-hdpi-finger/quickcontact_slider_presence_busy.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_slider_presence_inactive.png b/res/drawable-hdpi-finger/quickcontact_slider_presence_inactive.png
deleted file mode 100644
index fdcf75e..0000000
--- a/res/drawable-hdpi-finger/quickcontact_slider_presence_inactive.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/quickcontact_top_frame.9.png b/res/drawable-hdpi-finger/quickcontact_top_frame.9.png
deleted file mode 100644
index 4556bb2..0000000
--- a/res/drawable-hdpi-finger/quickcontact_top_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/sym_action_map.png b/res/drawable-hdpi-finger/sym_action_map.png
deleted file mode 100755
index cf00c7b..0000000
--- a/res/drawable-hdpi-finger/sym_action_map.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/sym_action_organization.png b/res/drawable-hdpi-finger/sym_action_organization.png
deleted file mode 100755
index 9db8b44..0000000
--- a/res/drawable-hdpi-finger/sym_action_organization.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/sym_note.png b/res/drawable-hdpi-finger/sym_note.png
deleted file mode 100755
index 5257329..0000000
--- a/res/drawable-hdpi-finger/sym_note.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/aggregation_suggestions_bg.9.png b/res/drawable-hdpi/aggregation_suggestions_bg.9.png
new file mode 100644
index 0000000..687cc08
--- /dev/null
+++ b/res/drawable-hdpi/aggregation_suggestions_bg.9.png
Binary files differ
diff --git a/res/drawable-hdpi/aggregation_suggestions_bg_light_holo.9.png b/res/drawable-hdpi/aggregation_suggestions_bg_light_holo.9.png
new file mode 100644
index 0000000..da1fe94
--- /dev/null
+++ b/res/drawable-hdpi/aggregation_suggestions_bg_light_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/badge_action_call.png b/res/drawable-hdpi/badge_action_call.png
similarity index 100%
rename from res/drawable-hdpi-finger/badge_action_call.png
rename to res/drawable-hdpi/badge_action_call.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/badge_action_sms.png b/res/drawable-hdpi/badge_action_sms.png
similarity index 100%
rename from res/drawable-hdpi-finger/badge_action_sms.png
rename to res/drawable-hdpi/badge_action_sms.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/bg_blk_search_contact.9.png b/res/drawable-hdpi/bg_blk_search_contact.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/bg_blk_search_contact.9.png
rename to res/drawable-hdpi/bg_blk_search_contact.9.png
Binary files differ
diff --git a/res/drawable-hdpi/bg_infobar_new.9.png b/res/drawable-hdpi/bg_infobar_new.9.png
deleted file mode 100644
index 104ced9..0000000
--- a/res/drawable-hdpi/bg_infobar_new.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/bg_status_contact_widget.9.png b/res/drawable-hdpi/bg_status_contact_widget.9.png
new file mode 100644
index 0000000..5615057
--- /dev/null
+++ b/res/drawable-hdpi/bg_status_contact_widget.9.png
Binary files differ
diff --git a/res/drawable-hdpi/btn_dial_action_left_disable.9.png b/res/drawable-hdpi/btn_dial_action_left_disable.9.png
new file mode 100644
index 0000000..f693c41
--- /dev/null
+++ b/res/drawable-hdpi/btn_dial_action_left_disable.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_left_disable_focused.9.png b/res/drawable-hdpi/btn_dial_action_left_disable_focused.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_action_left_disable_focused.9.png
rename to res/drawable-hdpi/btn_dial_action_left_disable_focused.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_left_normal.9.png b/res/drawable-hdpi/btn_dial_action_left_normal.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_action_left_normal.9.png
rename to res/drawable-hdpi/btn_dial_action_left_normal.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_left_pressed.9.png b/res/drawable-hdpi/btn_dial_action_left_pressed.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_action_left_pressed.9.png
rename to res/drawable-hdpi/btn_dial_action_left_pressed.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_left_selected.9.png b/res/drawable-hdpi/btn_dial_action_left_selected.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_action_left_selected.9.png
rename to res/drawable-hdpi/btn_dial_action_left_selected.9.png
Binary files differ
diff --git a/res/drawable-hdpi/btn_dial_action_middle_disable.9.png b/res/drawable-hdpi/btn_dial_action_middle_disable.9.png
new file mode 100644
index 0000000..5159c74
--- /dev/null
+++ b/res/drawable-hdpi/btn_dial_action_middle_disable.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_middle_disable_focused.9.png b/res/drawable-hdpi/btn_dial_action_middle_disable_focused.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_action_middle_disable_focused.9.png
rename to res/drawable-hdpi/btn_dial_action_middle_disable_focused.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_middle_normal.9.png b/res/drawable-hdpi/btn_dial_action_middle_normal.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_action_middle_normal.9.png
rename to res/drawable-hdpi/btn_dial_action_middle_normal.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_middle_pressed.9.png b/res/drawable-hdpi/btn_dial_action_middle_pressed.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_action_middle_pressed.9.png
rename to res/drawable-hdpi/btn_dial_action_middle_pressed.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_middle_selected.9.png b/res/drawable-hdpi/btn_dial_action_middle_selected.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_action_middle_selected.9.png
rename to res/drawable-hdpi/btn_dial_action_middle_selected.9.png
Binary files differ
diff --git a/res/drawable-hdpi/btn_dial_action_right_disable.9.png b/res/drawable-hdpi/btn_dial_action_right_disable.9.png
new file mode 100644
index 0000000..4844ee2
--- /dev/null
+++ b/res/drawable-hdpi/btn_dial_action_right_disable.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_right_disable_focused.9.png b/res/drawable-hdpi/btn_dial_action_right_disable_focused.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_action_right_disable_focused.9.png
rename to res/drawable-hdpi/btn_dial_action_right_disable_focused.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_right_normal.9.png b/res/drawable-hdpi/btn_dial_action_right_normal.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_action_right_normal.9.png
rename to res/drawable-hdpi/btn_dial_action_right_normal.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_right_pressed.9.png b/res/drawable-hdpi/btn_dial_action_right_pressed.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_action_right_pressed.9.png
rename to res/drawable-hdpi/btn_dial_action_right_pressed.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_right_selected.9.png b/res/drawable-hdpi/btn_dial_action_right_selected.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_action_right_selected.9.png
rename to res/drawable-hdpi/btn_dial_action_right_selected.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_normal.9.png b/res/drawable-hdpi/btn_dial_normal.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_normal.9.png
rename to res/drawable-hdpi/btn_dial_normal.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_pressed.9.png b/res/drawable-hdpi/btn_dial_pressed.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_pressed.9.png
rename to res/drawable-hdpi/btn_dial_pressed.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_selected.9.png b/res/drawable-hdpi/btn_dial_selected.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_selected.9.png
rename to res/drawable-hdpi/btn_dial_selected.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_textfield_activated.9.png b/res/drawable-hdpi/btn_dial_textfield_activated.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_textfield_activated.9.png
rename to res/drawable-hdpi/btn_dial_textfield_activated.9.png
Binary files differ
diff --git a/res/drawable-hdpi/btn_dial_textfield_normal.9.png b/res/drawable-hdpi/btn_dial_textfield_normal.9.png
new file mode 100644
index 0000000..1718372
--- /dev/null
+++ b/res/drawable-hdpi/btn_dial_textfield_normal.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_textfield_pressed.9.png b/res/drawable-hdpi/btn_dial_textfield_pressed.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_textfield_pressed.9.png
rename to res/drawable-hdpi/btn_dial_textfield_pressed.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_textfield_selected.9.png b/res/drawable-hdpi/btn_dial_textfield_selected.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/btn_dial_textfield_selected.9.png
rename to res/drawable-hdpi/btn_dial_textfield_selected.9.png
Binary files differ
diff --git a/res/drawable-hdpi/change_photo_box_focused_holo_light.9.png b/res/drawable-hdpi/change_photo_box_focused_holo_light.9.png
new file mode 100644
index 0000000..5254473
--- /dev/null
+++ b/res/drawable-hdpi/change_photo_box_focused_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/change_photo_box_normal_holo_light.9.png b/res/drawable-hdpi/change_photo_box_normal_holo_light.9.png
new file mode 100644
index 0000000..f026cc8
--- /dev/null
+++ b/res/drawable-hdpi/change_photo_box_normal_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/change_photo_box_pressed_holo_light.9.png b/res/drawable-hdpi/change_photo_box_pressed_holo_light.9.png
new file mode 100644
index 0000000..a0770ea
--- /dev/null
+++ b/res/drawable-hdpi/change_photo_box_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/contacts_widget_preview.png b/res/drawable-hdpi/contacts_widget_preview.png
new file mode 100644
index 0000000..89b2f45
--- /dev/null
+++ b/res/drawable-hdpi/contacts_widget_preview.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_0_blk.png b/res/drawable-hdpi/dial_num_0_blk.png
similarity index 100%
rename from res/drawable-hdpi-finger/dial_num_0_blk.png
rename to res/drawable-hdpi/dial_num_0_blk.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_0_blk.png b/res/drawable-hdpi/dial_num_0_wht.png
similarity index 100%
copy from res/drawable-hdpi-finger/dial_num_0_blk.png
copy to res/drawable-hdpi/dial_num_0_wht.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_1_no_vm_blk.png b/res/drawable-hdpi/dial_num_1_no_vm_blk.png
similarity index 100%
rename from res/drawable-hdpi-finger/dial_num_1_no_vm_blk.png
rename to res/drawable-hdpi/dial_num_1_no_vm_blk.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_1_no_vm_blk.png b/res/drawable-hdpi/dial_num_1_no_vm_wht.png
similarity index 100%
copy from res/drawable-hdpi-finger/dial_num_1_no_vm_blk.png
copy to res/drawable-hdpi/dial_num_1_no_vm_wht.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_2_blk.png b/res/drawable-hdpi/dial_num_2_blk.png
similarity index 100%
rename from res/drawable-hdpi-finger/dial_num_2_blk.png
rename to res/drawable-hdpi/dial_num_2_blk.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_2_blk.png b/res/drawable-hdpi/dial_num_2_wht.png
similarity index 100%
copy from res/drawable-hdpi-finger/dial_num_2_blk.png
copy to res/drawable-hdpi/dial_num_2_wht.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_3_blk.png b/res/drawable-hdpi/dial_num_3_blk.png
similarity index 100%
rename from res/drawable-hdpi-finger/dial_num_3_blk.png
rename to res/drawable-hdpi/dial_num_3_blk.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_3_blk.png b/res/drawable-hdpi/dial_num_3_wht.png
similarity index 100%
copy from res/drawable-hdpi-finger/dial_num_3_blk.png
copy to res/drawable-hdpi/dial_num_3_wht.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_4_blk.png b/res/drawable-hdpi/dial_num_4_blk.png
similarity index 100%
rename from res/drawable-hdpi-finger/dial_num_4_blk.png
rename to res/drawable-hdpi/dial_num_4_blk.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_4_blk.png b/res/drawable-hdpi/dial_num_4_wht.png
similarity index 100%
copy from res/drawable-hdpi-finger/dial_num_4_blk.png
copy to res/drawable-hdpi/dial_num_4_wht.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_5_blk.png b/res/drawable-hdpi/dial_num_5_blk.png
similarity index 100%
rename from res/drawable-hdpi-finger/dial_num_5_blk.png
rename to res/drawable-hdpi/dial_num_5_blk.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_5_blk.png b/res/drawable-hdpi/dial_num_5_wht.png
similarity index 100%
copy from res/drawable-hdpi-finger/dial_num_5_blk.png
copy to res/drawable-hdpi/dial_num_5_wht.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_6_blk.png b/res/drawable-hdpi/dial_num_6_blk.png
similarity index 100%
rename from res/drawable-hdpi-finger/dial_num_6_blk.png
rename to res/drawable-hdpi/dial_num_6_blk.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_6_blk.png b/res/drawable-hdpi/dial_num_6_wht.png
similarity index 100%
copy from res/drawable-hdpi-finger/dial_num_6_blk.png
copy to res/drawable-hdpi/dial_num_6_wht.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_7_blk.png b/res/drawable-hdpi/dial_num_7_blk.png
similarity index 100%
rename from res/drawable-hdpi-finger/dial_num_7_blk.png
rename to res/drawable-hdpi/dial_num_7_blk.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_7_blk.png b/res/drawable-hdpi/dial_num_7_wht.png
similarity index 100%
copy from res/drawable-hdpi-finger/dial_num_7_blk.png
copy to res/drawable-hdpi/dial_num_7_wht.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_8_blk.png b/res/drawable-hdpi/dial_num_8_blk.png
similarity index 100%
rename from res/drawable-hdpi-finger/dial_num_8_blk.png
rename to res/drawable-hdpi/dial_num_8_blk.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_8_blk.png b/res/drawable-hdpi/dial_num_8_wht.png
similarity index 100%
copy from res/drawable-hdpi-finger/dial_num_8_blk.png
copy to res/drawable-hdpi/dial_num_8_wht.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_9_blk.png b/res/drawable-hdpi/dial_num_9_blk.png
similarity index 100%
rename from res/drawable-hdpi-finger/dial_num_9_blk.png
rename to res/drawable-hdpi/dial_num_9_blk.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_9_blk.png b/res/drawable-hdpi/dial_num_9_wht.png
similarity index 100%
copy from res/drawable-hdpi-finger/dial_num_9_blk.png
copy to res/drawable-hdpi/dial_num_9_wht.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_pound_blk.png b/res/drawable-hdpi/dial_num_pound_blk.png
similarity index 100%
rename from res/drawable-hdpi-finger/dial_num_pound_blk.png
rename to res/drawable-hdpi/dial_num_pound_blk.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_pound_blk.png b/res/drawable-hdpi/dial_num_pound_wht.png
similarity index 100%
copy from res/drawable-hdpi-finger/dial_num_pound_blk.png
copy to res/drawable-hdpi/dial_num_pound_wht.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_star_blk.png b/res/drawable-hdpi/dial_num_star_blk.png
similarity index 100%
rename from res/drawable-hdpi-finger/dial_num_star_blk.png
rename to res/drawable-hdpi/dial_num_star_blk.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_star_blk.png b/res/drawable-hdpi/dial_num_star_wht.png
similarity index 100%
copy from res/drawable-hdpi-finger/dial_num_star_blk.png
copy to res/drawable-hdpi/dial_num_star_wht.png
Binary files differ
diff --git a/res/drawable-hdpi/directory_bg.9.png b/res/drawable-hdpi/directory_bg.9.png
new file mode 100644
index 0000000..f0a92d4
--- /dev/null
+++ b/res/drawable-hdpi/directory_bg.9.png
Binary files differ
diff --git a/res/drawable-hdpi/directory_bg_holo.9.png b/res/drawable-hdpi/directory_bg_holo.9.png
new file mode 100644
index 0000000..7f7209d
--- /dev/null
+++ b/res/drawable-hdpi/directory_bg_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/divider_vertical_dark.9.png b/res/drawable-hdpi/divider_vertical_dark.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/divider_vertical_dark.9.png
rename to res/drawable-hdpi/divider_vertical_dark.9.png
Binary files differ
diff --git a/res/drawable-hdpi/frame_thumbnail_contact_widget_focused_holo.png b/res/drawable-hdpi/frame_thumbnail_contact_widget_focused_holo.png
new file mode 100644
index 0000000..e2a9fe0
--- /dev/null
+++ b/res/drawable-hdpi/frame_thumbnail_contact_widget_focused_holo.png
Binary files differ
diff --git a/res/drawable-hdpi/frame_thumbnail_contact_widget_normal_holo.png b/res/drawable-hdpi/frame_thumbnail_contact_widget_normal_holo.png
new file mode 100644
index 0000000..21566bc
--- /dev/null
+++ b/res/drawable-hdpi/frame_thumbnail_contact_widget_normal_holo.png
Binary files differ
diff --git a/res/drawable-hdpi/frame_thumbnail_contact_widget_pressed_holo.png b/res/drawable-hdpi/frame_thumbnail_contact_widget_pressed_holo.png
new file mode 100644
index 0000000..1f5b5f9
--- /dev/null
+++ b/res/drawable-hdpi/frame_thumbnail_contact_widget_pressed_holo.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_call_log_header_incoming_call.png b/res/drawable-hdpi/ic_call_log_header_incoming_call.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_call_log_header_incoming_call.png
rename to res/drawable-hdpi/ic_call_log_header_incoming_call.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_call_log_header_missed_call.png b/res/drawable-hdpi/ic_call_log_header_missed_call.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_call_log_header_missed_call.png
rename to res/drawable-hdpi/ic_call_log_header_missed_call.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_call_log_header_outgoing_call.png b/res/drawable-hdpi/ic_call_log_header_outgoing_call.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_call_log_header_outgoing_call.png
rename to res/drawable-hdpi/ic_call_log_header_outgoing_call.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_call_log_list_incoming_call.png b/res/drawable-hdpi/ic_call_log_list_incoming_call.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_call_log_list_incoming_call.png
rename to res/drawable-hdpi/ic_call_log_list_incoming_call.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_call_log_list_missed_call.png b/res/drawable-hdpi/ic_call_log_list_missed_call.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_call_log_list_missed_call.png
rename to res/drawable-hdpi/ic_call_log_list_missed_call.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_call_log_list_outgoing_call.png b/res/drawable-hdpi/ic_call_log_list_outgoing_call.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_call_log_list_outgoing_call.png
rename to res/drawable-hdpi/ic_call_log_list_outgoing_call.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_contact_picture.png b/res/drawable-hdpi/ic_contact_picture.png
new file mode 100755
index 0000000..e29e63a
--- /dev/null
+++ b/res/drawable-hdpi/ic_contact_picture.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_dial_action_call.png b/res/drawable-hdpi/ic_dial_action_call.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_dial_action_call.png
rename to res/drawable-hdpi/ic_dial_action_call.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_dial_action_delete.png b/res/drawable-hdpi/ic_dial_action_delete.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_dial_action_delete.png
rename to res/drawable-hdpi/ic_dial_action_delete.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_dial_action_voice_mail.png b/res/drawable-hdpi/ic_dial_action_voice_mail.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_dial_action_voice_mail.png
rename to res/drawable-hdpi/ic_dial_action_voice_mail.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_dialer_fork_add_call.png b/res/drawable-hdpi/ic_dialer_fork_add_call.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_dialer_fork_add_call.png
rename to res/drawable-hdpi/ic_dialer_fork_add_call.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_dialer_fork_current_call.png b/res/drawable-hdpi/ic_dialer_fork_current_call.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_dialer_fork_current_call.png
rename to res/drawable-hdpi/ic_dialer_fork_current_call.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_dialer_fork_tt_keypad.png b/res/drawable-hdpi/ic_dialer_fork_tt_keypad.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_dialer_fork_tt_keypad.png
rename to res/drawable-hdpi/ic_dialer_fork_tt_keypad.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_contacts.png b/res/drawable-hdpi/ic_launcher_contacts.png
deleted file mode 100644
index 69a72c4..0000000
--- a/res/drawable-hdpi/ic_launcher_contacts.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_phone.png b/res/drawable-hdpi/ic_launcher_phone.png
deleted file mode 100644
index 7739546..0000000
--- a/res/drawable-hdpi/ic_launcher_phone.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_2sec_pause.png b/res/drawable-hdpi/ic_menu_2sec_pause.png
new file mode 100644
index 0000000..3951948
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_2sec_pause.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_accounts_holo_light.png b/res/drawable-hdpi/ic_menu_accounts_holo_light.png
new file mode 100644
index 0000000..59496c9
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_accounts_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_add_contact_holo_light.png b/res/drawable-hdpi/ic_menu_add_contact_holo_light.png
new file mode 100644
index 0000000..6335d40
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_add_contact_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_add_field_holo_light.png b/res/drawable-hdpi/ic_menu_add_field_holo_light.png
new file mode 100644
index 0000000..4ef604a
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_add_field_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_compose_holo_light.png b/res/drawable-hdpi/ic_menu_compose_holo_light.png
new file mode 100644
index 0000000..07ab787
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_compose_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_contacts_holo_light.png b/res/drawable-hdpi/ic_menu_contacts_holo_light.png
new file mode 100644
index 0000000..8a7358c
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_contacts_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_display_all.png b/res/drawable-hdpi/ic_menu_display_all.png
new file mode 100755
index 0000000..563083c
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_display_all.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_display_all_holo_light.png b/res/drawable-hdpi/ic_menu_display_all_holo_light.png
new file mode 100644
index 0000000..59631f0
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_display_all_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_display_selected.png b/res/drawable-hdpi/ic_menu_display_selected.png
new file mode 100644
index 0000000..76b2e22
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_display_selected.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_done_holo_light.png b/res/drawable-hdpi/ic_menu_done_holo_light.png
new file mode 100644
index 0000000..89c6e04
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_done_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_expander_maximized_holo_light.png b/res/drawable-hdpi/ic_menu_expander_maximized_holo_light.png
new file mode 100644
index 0000000..3c47e9e
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_expander_maximized_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_expander_minimized_holo_light.png b/res/drawable-hdpi/ic_menu_expander_minimized_holo_light.png
new file mode 100644
index 0000000..6b8c6b0
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_expander_minimized_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_import_export_holo_light.png b/res/drawable-hdpi/ic_menu_import_export_holo_light.png
new file mode 100644
index 0000000..a67386f
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_import_export_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_merge_holo_light.png b/res/drawable-hdpi/ic_menu_merge_holo_light.png
new file mode 100644
index 0000000..eaf32d2
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_merge_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_remove_field_holo_light.png b/res/drawable-hdpi/ic_menu_remove_field_holo_light.png
new file mode 100644
index 0000000..d8172ee
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_remove_field_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_select.png b/res/drawable-hdpi/ic_menu_select.png
new file mode 100644
index 0000000..c5bb503
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_select.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_settings_holo_light.png b/res/drawable-hdpi/ic_menu_settings_holo_light.png
new file mode 100644
index 0000000..b7bb5c4
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_settings_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_share_holo_light.png b/res/drawable-hdpi/ic_menu_share_holo_light.png
new file mode 100644
index 0000000..2ba6fb7
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_share_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_split_holo_light.png b/res/drawable-hdpi/ic_menu_split_holo_light.png
new file mode 100644
index 0000000..3784544
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_split_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_star_holo_light.png b/res/drawable-hdpi/ic_menu_star_holo_light.png
new file mode 100644
index 0000000..4513796
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_star_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_trash_holo_light.png b/res/drawable-hdpi/ic_menu_trash_holo_light.png
new file mode 100644
index 0000000..481eb65
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_trash_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_unselect.png b/res/drawable-hdpi/ic_menu_unselect.png
new file mode 100644
index 0000000..178f314
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_unselect.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_wait.png b/res/drawable-hdpi/ic_menu_wait.png
new file mode 100644
index 0000000..6886e5d
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_wait.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_selected_contacts.png b/res/drawable-hdpi/ic_tab_selected_contacts.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_tab_selected_contacts.png
rename to res/drawable-hdpi/ic_tab_selected_contacts.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_selected_dialer.png b/res/drawable-hdpi/ic_tab_selected_dialer.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_tab_selected_dialer.png
rename to res/drawable-hdpi/ic_tab_selected_dialer.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_selected_recent.png b/res/drawable-hdpi/ic_tab_selected_recent.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_tab_selected_recent.png
rename to res/drawable-hdpi/ic_tab_selected_recent.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_selected_starred.png b/res/drawable-hdpi/ic_tab_selected_starred.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_tab_selected_starred.png
rename to res/drawable-hdpi/ic_tab_selected_starred.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_unselected_contacts.png b/res/drawable-hdpi/ic_tab_unselected_contacts.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_tab_unselected_contacts.png
rename to res/drawable-hdpi/ic_tab_unselected_contacts.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_unselected_dialer.png b/res/drawable-hdpi/ic_tab_unselected_dialer.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_tab_unselected_dialer.png
rename to res/drawable-hdpi/ic_tab_unselected_dialer.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_unselected_recent.png b/res/drawable-hdpi/ic_tab_unselected_recent.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_tab_unselected_recent.png
rename to res/drawable-hdpi/ic_tab_unselected_recent.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_unselected_starred.png b/res/drawable-hdpi/ic_tab_unselected_starred.png
similarity index 100%
rename from res/drawable-hdpi-finger/ic_tab_unselected_starred.png
rename to res/drawable-hdpi/ic_tab_unselected_starred.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/infobar_dark.9.png b/res/drawable-hdpi/infobar_dark.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/infobar_dark.9.png
rename to res/drawable-hdpi/infobar_dark.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_activated_holo.9.png b/res/drawable-hdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..36ccb79
--- /dev/null
+++ b/res/drawable-hdpi/list_activated_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_background_holo.9.png b/res/drawable-hdpi/list_background_holo.9.png
new file mode 100644
index 0000000..e382b2d
--- /dev/null
+++ b/res/drawable-hdpi/list_background_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_focused_holo.9.png b/res/drawable-hdpi/list_focused_holo.9.png
new file mode 100644
index 0000000..54c5c2d
--- /dev/null
+++ b/res/drawable-hdpi/list_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_item_divider.9.png b/res/drawable-hdpi/list_item_divider.9.png
new file mode 100644
index 0000000..60e2cb2
--- /dev/null
+++ b/res/drawable-hdpi/list_item_divider.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_item_divider_holo.9.png b/res/drawable-hdpi/list_item_divider_holo.9.png
new file mode 100644
index 0000000..f5130d5
--- /dev/null
+++ b/res/drawable-hdpi/list_item_divider_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_pressed_holo.9.png b/res/drawable-hdpi/list_pressed_holo.9.png
new file mode 100644
index 0000000..423fa4b
--- /dev/null
+++ b/res/drawable-hdpi/list_pressed_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/list_title_holo.9.png b/res/drawable-hdpi/list_title_holo.9.png
new file mode 100644
index 0000000..ebdd4e3
--- /dev/null
+++ b/res/drawable-hdpi/list_title_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/panel_content.9.png b/res/drawable-hdpi/panel_content.9.png
new file mode 100644
index 0000000..4a9b90a
--- /dev/null
+++ b/res/drawable-hdpi/panel_content.9.png
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_arrowdown_left_holo_light.9.png b/res/drawable-hdpi/quickactions_arrowdown_left_holo_light.9.png
new file mode 100644
index 0000000..b09016b
--- /dev/null
+++ b/res/drawable-hdpi/quickactions_arrowdown_left_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_arrowdown_middle_holo_light.9.png b/res/drawable-hdpi/quickactions_arrowdown_middle_holo_light.9.png
new file mode 100644
index 0000000..670d89f
--- /dev/null
+++ b/res/drawable-hdpi/quickactions_arrowdown_middle_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_arrowdown_right_holo_light.9.png b/res/drawable-hdpi/quickactions_arrowdown_right_holo_light.9.png
new file mode 100644
index 0000000..81f4859
--- /dev/null
+++ b/res/drawable-hdpi/quickactions_arrowdown_right_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_arrowup_left_holo_light.9.png b/res/drawable-hdpi/quickactions_arrowup_left_holo_light.9.png
new file mode 100644
index 0000000..99ad9e2
--- /dev/null
+++ b/res/drawable-hdpi/quickactions_arrowup_left_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_arrowup_middle_holo_light.9.png b/res/drawable-hdpi/quickactions_arrowup_middle_holo_light.9.png
new file mode 100644
index 0000000..500d820
--- /dev/null
+++ b/res/drawable-hdpi/quickactions_arrowup_middle_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_arrowup_right_holo_light.9.png b/res/drawable-hdpi/quickactions_arrowup_right_holo_light.9.png
new file mode 100644
index 0000000..d99058b
--- /dev/null
+++ b/res/drawable-hdpi/quickactions_arrowup_right_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_icon_activated.9.png b/res/drawable-hdpi/quickactions_icon_activated.9.png
new file mode 100644
index 0000000..b9ccc80
--- /dev/null
+++ b/res/drawable-hdpi/quickactions_icon_activated.9.png
Binary files differ
diff --git a/res/drawable-hdpi/section_header.9.png b/res/drawable-hdpi/section_header.9.png
new file mode 100644
index 0000000..8cd231b
--- /dev/null
+++ b/res/drawable-hdpi/section_header.9.png
Binary files differ
diff --git a/res/drawable-hdpi/section_header_holo.9.png b/res/drawable-hdpi/section_header_holo.9.png
new file mode 100644
index 0000000..de12d0e
--- /dev/null
+++ b/res/drawable-hdpi/section_header_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/statusbox_attribute_holo.9.png b/res/drawable-hdpi/statusbox_attribute_holo.9.png
new file mode 100644
index 0000000..13ffd7a
--- /dev/null
+++ b/res/drawable-hdpi/statusbox_attribute_holo.9.png
Binary files differ
diff --git a/res/drawable-hdpi/statusbox_landscape_holo_light.9.png b/res/drawable-hdpi/statusbox_landscape_holo_light.9.png
new file mode 100644
index 0000000..9fa578c
--- /dev/null
+++ b/res/drawable-hdpi/statusbox_landscape_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/statusbox_portrait_holo_light.9.png b/res/drawable-hdpi/statusbox_portrait_holo_light.9.png
new file mode 100644
index 0000000..614bc59
--- /dev/null
+++ b/res/drawable-hdpi/statusbox_portrait_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/sym_action_add.png b/res/drawable-hdpi/sym_action_add.png
similarity index 100%
rename from res/drawable-hdpi-finger/sym_action_add.png
rename to res/drawable-hdpi/sym_action_add.png
Binary files differ
diff --git a/res/drawable-hdpi/sym_action_audiochat_holo_light.png b/res/drawable-hdpi/sym_action_audiochat_holo_light.png
new file mode 100644
index 0000000..8bea3ec
--- /dev/null
+++ b/res/drawable-hdpi/sym_action_audiochat_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/sym_action_email_holo_light.png b/res/drawable-hdpi/sym_action_email_holo_light.png
new file mode 100644
index 0000000..e1ff65f
--- /dev/null
+++ b/res/drawable-hdpi/sym_action_email_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/sym_action_goto_website_holo_light.png b/res/drawable-hdpi/sym_action_goto_website_holo_light.png
new file mode 100644
index 0000000..4c5c614
--- /dev/null
+++ b/res/drawable-hdpi/sym_action_goto_website_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/sym_action_show_map_holo_light.png b/res/drawable-hdpi/sym_action_show_map_holo_light.png
new file mode 100644
index 0000000..4c6ba98
--- /dev/null
+++ b/res/drawable-hdpi/sym_action_show_map_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/sym_action_talk_holo_light.png b/res/drawable-hdpi/sym_action_talk_holo_light.png
new file mode 100644
index 0000000..62ad687
--- /dev/null
+++ b/res/drawable-hdpi/sym_action_talk_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi/sym_action_videochat_holo_light.png b/res/drawable-hdpi/sym_action_videochat_holo_light.png
new file mode 100644
index 0000000..b86c435
--- /dev/null
+++ b/res/drawable-hdpi/sym_action_videochat_holo_light.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/sym_action_view_contact.png b/res/drawable-hdpi/sym_action_view_contact.png
similarity index 100%
rename from res/drawable-hdpi-finger/sym_action_view_contact.png
rename to res/drawable-hdpi/sym_action_view_contact.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/tab_focused.9.png b/res/drawable-hdpi/tab_focused.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/tab_focused.9.png
rename to res/drawable-hdpi/tab_focused.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/tab_focused_bottom.9.png b/res/drawable-hdpi/tab_focused_bottom.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/tab_focused_bottom.9.png
rename to res/drawable-hdpi/tab_focused_bottom.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/tab_left_arrow.png b/res/drawable-hdpi/tab_left_arrow.png
similarity index 100%
rename from res/drawable-hdpi-finger/tab_left_arrow.png
rename to res/drawable-hdpi/tab_left_arrow.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/tab_pressed.9.png b/res/drawable-hdpi/tab_pressed.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/tab_pressed.9.png
rename to res/drawable-hdpi/tab_pressed.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/tab_pressed_bottom.9.png b/res/drawable-hdpi/tab_pressed_bottom.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/tab_pressed_bottom.9.png
rename to res/drawable-hdpi/tab_pressed_bottom.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/tab_right_arrow.png b/res/drawable-hdpi/tab_right_arrow.png
similarity index 100%
rename from res/drawable-hdpi-finger/tab_right_arrow.png
rename to res/drawable-hdpi/tab_right_arrow.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/tab_selected.9.png b/res/drawable-hdpi/tab_selected.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/tab_selected.9.png
rename to res/drawable-hdpi/tab_selected.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/tab_selected_bottom.9.png b/res/drawable-hdpi/tab_selected_bottom.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/tab_selected_bottom.9.png
rename to res/drawable-hdpi/tab_selected_bottom.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/tab_unselected.9.png b/res/drawable-hdpi/tab_unselected.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/tab_unselected.9.png
rename to res/drawable-hdpi/tab_unselected.9.png
Binary files differ
diff --git a/res/drawable-hdpi/title_bar_medium.9.png b/res/drawable-hdpi/title_bar_medium.9.png
new file mode 100644
index 0000000..311a54a
--- /dev/null
+++ b/res/drawable-hdpi/title_bar_medium.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/title_bar_shadow.9.png b/res/drawable-hdpi/title_bar_shadow.9.png
similarity index 100%
rename from res/drawable-hdpi-finger/title_bar_shadow.9.png
rename to res/drawable-hdpi/title_bar_shadow.9.png
Binary files differ
diff --git a/res/drawable-hdpi/unknown_source.png b/res/drawable-hdpi/unknown_source.png
new file mode 100644
index 0000000..0a8f37d
--- /dev/null
+++ b/res/drawable-hdpi/unknown_source.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_circle_disable.png b/res/drawable-mdpi-finger/btn_circle_disable.png
deleted file mode 100644
index 33b74a6..0000000
--- a/res/drawable-mdpi-finger/btn_circle_disable.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_circle_disable_focused.png b/res/drawable-mdpi-finger/btn_circle_disable_focused.png
deleted file mode 100644
index 005ad8d..0000000
--- a/res/drawable-mdpi-finger/btn_circle_disable_focused.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_circle_normal.png b/res/drawable-mdpi-finger/btn_circle_normal.png
deleted file mode 100644
index fc5af1c..0000000
--- a/res/drawable-mdpi-finger/btn_circle_normal.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_circle_pressed.png b/res/drawable-mdpi-finger/btn_circle_pressed.png
deleted file mode 100644
index 8f40afd..0000000
--- a/res/drawable-mdpi-finger/btn_circle_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_circle_selected.png b/res/drawable-mdpi-finger/btn_circle_selected.png
deleted file mode 100644
index c74fac2..0000000
--- a/res/drawable-mdpi-finger/btn_circle_selected.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_left_disable.9.png b/res/drawable-mdpi-finger/btn_dial_action_left_disable.9.png
deleted file mode 100644
index 5f091f2..0000000
--- a/res/drawable-mdpi-finger/btn_dial_action_left_disable.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_middle_disable.9.png b/res/drawable-mdpi-finger/btn_dial_action_middle_disable.9.png
deleted file mode 100644
index 575793c..0000000
--- a/res/drawable-mdpi-finger/btn_dial_action_middle_disable.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_right_disable.9.png b/res/drawable-mdpi-finger/btn_dial_action_right_disable.9.png
deleted file mode 100644
index 515e4f4..0000000
--- a/res/drawable-mdpi-finger/btn_dial_action_right_disable.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_textfield_normal.9.png b/res/drawable-mdpi-finger/btn_dial_textfield_normal.9.png
deleted file mode 100644
index 3def8c0..0000000
--- a/res/drawable-mdpi-finger/btn_dial_textfield_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_search_dialog_default.9.png b/res/drawable-mdpi-finger/btn_search_dialog_default.9.png
deleted file mode 100644
index 7275231..0000000
--- a/res/drawable-mdpi-finger/btn_search_dialog_default.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_search_dialog_pressed.9.png b/res/drawable-mdpi-finger/btn_search_dialog_pressed.9.png
deleted file mode 100644
index 50a9209..0000000
--- a/res/drawable-mdpi-finger/btn_search_dialog_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_search_dialog_selected.9.png b/res/drawable-mdpi-finger/btn_search_dialog_selected.9.png
deleted file mode 100644
index 14b774a..0000000
--- a/res/drawable-mdpi-finger/btn_search_dialog_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/contact_picture_border_highlight.9.png b/res/drawable-mdpi-finger/contact_picture_border_highlight.9.png
deleted file mode 100644
index c48b83f..0000000
--- a/res/drawable-mdpi-finger/contact_picture_border_highlight.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/contact_picture_border_normal.9.png b/res/drawable-mdpi-finger/contact_picture_border_normal.9.png
deleted file mode 100644
index 84bfad5..0000000
--- a/res/drawable-mdpi-finger/contact_picture_border_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/contact_picture_border_pressed.9.png b/res/drawable-mdpi-finger/contact_picture_border_pressed.9.png
deleted file mode 100644
index d5328f3..0000000
--- a/res/drawable-mdpi-finger/contact_picture_border_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_0_wht.png b/res/drawable-mdpi-finger/dial_num_0_wht.png
deleted file mode 100644
index 44c38d2..0000000
--- a/res/drawable-mdpi-finger/dial_num_0_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_1_no_vm_wht.png b/res/drawable-mdpi-finger/dial_num_1_no_vm_wht.png
deleted file mode 100644
index 267bed5..0000000
--- a/res/drawable-mdpi-finger/dial_num_1_no_vm_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_2_wht.png b/res/drawable-mdpi-finger/dial_num_2_wht.png
deleted file mode 100644
index f1f27a3..0000000
--- a/res/drawable-mdpi-finger/dial_num_2_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_3_wht.png b/res/drawable-mdpi-finger/dial_num_3_wht.png
deleted file mode 100644
index b229202..0000000
--- a/res/drawable-mdpi-finger/dial_num_3_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_4_wht.png b/res/drawable-mdpi-finger/dial_num_4_wht.png
deleted file mode 100644
index 8c9af62..0000000
--- a/res/drawable-mdpi-finger/dial_num_4_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_5_wht.png b/res/drawable-mdpi-finger/dial_num_5_wht.png
deleted file mode 100644
index bac9b8f..0000000
--- a/res/drawable-mdpi-finger/dial_num_5_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_6_wht.png b/res/drawable-mdpi-finger/dial_num_6_wht.png
deleted file mode 100644
index c76a988..0000000
--- a/res/drawable-mdpi-finger/dial_num_6_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_7_wht.png b/res/drawable-mdpi-finger/dial_num_7_wht.png
deleted file mode 100644
index 4fe6992..0000000
--- a/res/drawable-mdpi-finger/dial_num_7_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_8_wht.png b/res/drawable-mdpi-finger/dial_num_8_wht.png
deleted file mode 100644
index 0481eb4..0000000
--- a/res/drawable-mdpi-finger/dial_num_8_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_9_wht.png b/res/drawable-mdpi-finger/dial_num_9_wht.png
deleted file mode 100644
index b54c6c2..0000000
--- a/res/drawable-mdpi-finger/dial_num_9_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_pound_wht.png b/res/drawable-mdpi-finger/dial_num_pound_wht.png
deleted file mode 100644
index 2186b31..0000000
--- a/res/drawable-mdpi-finger/dial_num_pound_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_star_wht.png b/res/drawable-mdpi-finger/dial_num_star_wht.png
deleted file mode 100644
index 3c75031..0000000
--- a/res/drawable-mdpi-finger/dial_num_star_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_btn_round_less.png b/res/drawable-mdpi-finger/ic_btn_round_less.png
deleted file mode 100644
index 96fc42e..0000000
--- a/res/drawable-mdpi-finger/ic_btn_round_less.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_btn_round_minus.png b/res/drawable-mdpi-finger/ic_btn_round_minus.png
deleted file mode 100644
index 96dbb17..0000000
--- a/res/drawable-mdpi-finger/ic_btn_round_minus.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_btn_round_more.png b/res/drawable-mdpi-finger/ic_btn_round_more.png
deleted file mode 100644
index e3355cc..0000000
--- a/res/drawable-mdpi-finger/ic_btn_round_more.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_btn_round_plus.png b/res/drawable-mdpi-finger/ic_btn_round_plus.png
deleted file mode 100644
index 1ec8a95..0000000
--- a/res/drawable-mdpi-finger/ic_btn_round_plus.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_contact_list_picture.png b/res/drawable-mdpi-finger/ic_contact_list_picture.png
deleted file mode 100644
index f8aa4ba..0000000
--- a/res/drawable-mdpi-finger/ic_contact_list_picture.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_contact_picture_2.png b/res/drawable-mdpi-finger/ic_contact_picture_2.png
deleted file mode 100644
index 8b184af..0000000
--- a/res/drawable-mdpi-finger/ic_contact_picture_2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_contact_picture_3.png b/res/drawable-mdpi-finger/ic_contact_picture_3.png
deleted file mode 100644
index a2d08b5..0000000
--- a/res/drawable-mdpi-finger/ic_contact_picture_3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_default_number.png b/res/drawable-mdpi-finger/ic_default_number.png
deleted file mode 100644
index 30ead23..0000000
--- a/res/drawable-mdpi-finger/ic_default_number.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_dial_number_blk.png b/res/drawable-mdpi-finger/ic_dial_number_blk.png
deleted file mode 100644
index c1f572d..0000000
--- a/res/drawable-mdpi-finger/ic_dial_number_blk.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_dial_number_wht.png b/res/drawable-mdpi-finger/ic_dial_number_wht.png
deleted file mode 100644
index d303b2b..0000000
--- a/res/drawable-mdpi-finger/ic_dial_number_wht.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_join.png b/res/drawable-mdpi-finger/ic_join.png
deleted file mode 100644
index 177a582..0000000
--- a/res/drawable-mdpi-finger/ic_join.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_menu_account_list.png b/res/drawable-mdpi-finger/ic_menu_account_list.png
deleted file mode 100644
index f0945b2..0000000
--- a/res/drawable-mdpi-finger/ic_menu_account_list.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_menu_add_picture.png b/res/drawable-mdpi-finger/ic_menu_add_picture.png
deleted file mode 100644
index 35f10b8..0000000
--- a/res/drawable-mdpi-finger/ic_menu_add_picture.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_menu_import_export.png b/res/drawable-mdpi-finger/ic_menu_import_export.png
deleted file mode 100644
index 1cefb7c..0000000
--- a/res/drawable-mdpi-finger/ic_menu_import_export.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_menu_merge.png b/res/drawable-mdpi-finger/ic_menu_merge.png
deleted file mode 100644
index b448c27..0000000
--- a/res/drawable-mdpi-finger/ic_menu_merge.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_menu_split.png b/res/drawable-mdpi-finger/ic_menu_split.png
deleted file mode 100644
index 9d69e4c..0000000
--- a/res/drawable-mdpi-finger/ic_menu_split.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_selected_friends_list.png b/res/drawable-mdpi-finger/ic_tab_selected_friends_list.png
deleted file mode 100644
index de2c4cc..0000000
--- a/res/drawable-mdpi-finger/ic_tab_selected_friends_list.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_unselected_friends_list.png b/res/drawable-mdpi-finger/ic_tab_unselected_friends_list.png
deleted file mode 100644
index 80736e9..0000000
--- a/res/drawable-mdpi-finger/ic_tab_unselected_friends_list.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_arrow_down.png b/res/drawable-mdpi-finger/quickcontact_arrow_down.png
deleted file mode 100644
index 3ba6c8c..0000000
--- a/res/drawable-mdpi-finger/quickcontact_arrow_down.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_arrow_up.png b/res/drawable-mdpi-finger/quickcontact_arrow_up.png
deleted file mode 100644
index 1a6fa2e..0000000
--- a/res/drawable-mdpi-finger/quickcontact_arrow_up.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_bottom_frame.9.png b/res/drawable-mdpi-finger/quickcontact_bottom_frame.9.png
deleted file mode 100644
index 6f84306..0000000
--- a/res/drawable-mdpi-finger/quickcontact_bottom_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_disambig_bottom_bg.9.png b/res/drawable-mdpi-finger/quickcontact_disambig_bottom_bg.9.png
deleted file mode 100644
index bbc2075..0000000
--- a/res/drawable-mdpi-finger/quickcontact_disambig_bottom_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_disambig_checkbox_off.png b/res/drawable-mdpi-finger/quickcontact_disambig_checkbox_off.png
deleted file mode 100644
index 7e51863..0000000
--- a/res/drawable-mdpi-finger/quickcontact_disambig_checkbox_off.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_disambig_checkbox_on.png b/res/drawable-mdpi-finger/quickcontact_disambig_checkbox_on.png
deleted file mode 100644
index 93c06aa..0000000
--- a/res/drawable-mdpi-finger/quickcontact_disambig_checkbox_on.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_disambig_divider.9.png b/res/drawable-mdpi-finger/quickcontact_disambig_divider.9.png
deleted file mode 100644
index 8c35e8c..0000000
--- a/res/drawable-mdpi-finger/quickcontact_disambig_divider.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_drop_shadow.9.png b/res/drawable-mdpi-finger/quickcontact_drop_shadow.9.png
deleted file mode 100644
index 2d20076..0000000
--- a/res/drawable-mdpi-finger/quickcontact_drop_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_photo_frame.9.png b/res/drawable-mdpi-finger/quickcontact_photo_frame.9.png
deleted file mode 100644
index a509c30..0000000
--- a/res/drawable-mdpi-finger/quickcontact_photo_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_slider_background.png b/res/drawable-mdpi-finger/quickcontact_slider_background.png
deleted file mode 100644
index b6a9f91..0000000
--- a/res/drawable-mdpi-finger/quickcontact_slider_background.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_slider_btn_normal.9.png b/res/drawable-mdpi-finger/quickcontact_slider_btn_normal.9.png
deleted file mode 100644
index cf4f1e4..0000000
--- a/res/drawable-mdpi-finger/quickcontact_slider_btn_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_slider_btn_on.9.png b/res/drawable-mdpi-finger/quickcontact_slider_btn_on.9.png
deleted file mode 100644
index 330f49b..0000000
--- a/res/drawable-mdpi-finger/quickcontact_slider_btn_on.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_slider_btn_pressed.9.png b/res/drawable-mdpi-finger/quickcontact_slider_btn_pressed.9.png
deleted file mode 100644
index d4916f5..0000000
--- a/res/drawable-mdpi-finger/quickcontact_slider_btn_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_slider_btn_selected.9.png b/res/drawable-mdpi-finger/quickcontact_slider_btn_selected.9.png
deleted file mode 100644
index b910028..0000000
--- a/res/drawable-mdpi-finger/quickcontact_slider_btn_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_slider_grip_left.png b/res/drawable-mdpi-finger/quickcontact_slider_grip_left.png
deleted file mode 100644
index 337b2fc..0000000
--- a/res/drawable-mdpi-finger/quickcontact_slider_grip_left.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_slider_grip_right.png b/res/drawable-mdpi-finger/quickcontact_slider_grip_right.png
deleted file mode 100644
index 1e222c3..0000000
--- a/res/drawable-mdpi-finger/quickcontact_slider_grip_right.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_slider_presence_active.png b/res/drawable-mdpi-finger/quickcontact_slider_presence_active.png
deleted file mode 100644
index 2d57813..0000000
--- a/res/drawable-mdpi-finger/quickcontact_slider_presence_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_slider_presence_away.png b/res/drawable-mdpi-finger/quickcontact_slider_presence_away.png
deleted file mode 100644
index 22d014b..0000000
--- a/res/drawable-mdpi-finger/quickcontact_slider_presence_away.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_slider_presence_busy.png b/res/drawable-mdpi-finger/quickcontact_slider_presence_busy.png
deleted file mode 100644
index 5734e00..0000000
--- a/res/drawable-mdpi-finger/quickcontact_slider_presence_busy.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_slider_presence_inactive.png b/res/drawable-mdpi-finger/quickcontact_slider_presence_inactive.png
deleted file mode 100644
index 81731a8..0000000
--- a/res/drawable-mdpi-finger/quickcontact_slider_presence_inactive.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/quickcontact_top_frame.9.png b/res/drawable-mdpi-finger/quickcontact_top_frame.9.png
deleted file mode 100644
index 6d43305..0000000
--- a/res/drawable-mdpi-finger/quickcontact_top_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/sym_action_map.png b/res/drawable-mdpi-finger/sym_action_map.png
deleted file mode 100644
index 12719cc..0000000
--- a/res/drawable-mdpi-finger/sym_action_map.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/sym_action_organization.png b/res/drawable-mdpi-finger/sym_action_organization.png
deleted file mode 100644
index 994d3f5..0000000
--- a/res/drawable-mdpi-finger/sym_action_organization.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-finger/sym_note.png b/res/drawable-mdpi-finger/sym_note.png
deleted file mode 100644
index e4dbdf5..0000000
--- a/res/drawable-mdpi-finger/sym_note.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/aggregation_suggestions_bg.9.png b/res/drawable-mdpi/aggregation_suggestions_bg.9.png
new file mode 100644
index 0000000..687cc08
--- /dev/null
+++ b/res/drawable-mdpi/aggregation_suggestions_bg.9.png
Binary files differ
diff --git a/res/drawable-mdpi/aggregation_suggestions_bg_light_holo.9.png b/res/drawable-mdpi/aggregation_suggestions_bg_light_holo.9.png
new file mode 100644
index 0000000..03e24c6
--- /dev/null
+++ b/res/drawable-mdpi/aggregation_suggestions_bg_light_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/badge_action_call.png b/res/drawable-mdpi/badge_action_call.png
similarity index 100%
rename from res/drawable-mdpi-finger/badge_action_call.png
rename to res/drawable-mdpi/badge_action_call.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/badge_action_sms.png b/res/drawable-mdpi/badge_action_sms.png
similarity index 100%
rename from res/drawable-mdpi-finger/badge_action_sms.png
rename to res/drawable-mdpi/badge_action_sms.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/bg_blk_search_contact.9.png b/res/drawable-mdpi/bg_blk_search_contact.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/bg_blk_search_contact.9.png
rename to res/drawable-mdpi/bg_blk_search_contact.9.png
Binary files differ
diff --git a/res/drawable-mdpi/bg_infobar_new.9.png b/res/drawable-mdpi/bg_infobar_new.9.png
deleted file mode 100644
index f3a83d4..0000000
--- a/res/drawable-mdpi/bg_infobar_new.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/bg_status_contact_widget.9.png b/res/drawable-mdpi/bg_status_contact_widget.9.png
new file mode 100644
index 0000000..e3eccc2
--- /dev/null
+++ b/res/drawable-mdpi/bg_status_contact_widget.9.png
Binary files differ
diff --git a/res/drawable-mdpi/btn_dial_action_left_disable.9.png b/res/drawable-mdpi/btn_dial_action_left_disable.9.png
new file mode 100644
index 0000000..8eb45c8
--- /dev/null
+++ b/res/drawable-mdpi/btn_dial_action_left_disable.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_left_disable_focused.9.png b/res/drawable-mdpi/btn_dial_action_left_disable_focused.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_action_left_disable_focused.9.png
rename to res/drawable-mdpi/btn_dial_action_left_disable_focused.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_left_normal.9.png b/res/drawable-mdpi/btn_dial_action_left_normal.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_action_left_normal.9.png
rename to res/drawable-mdpi/btn_dial_action_left_normal.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_left_pressed.9.png b/res/drawable-mdpi/btn_dial_action_left_pressed.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_action_left_pressed.9.png
rename to res/drawable-mdpi/btn_dial_action_left_pressed.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_left_selected.9.png b/res/drawable-mdpi/btn_dial_action_left_selected.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_action_left_selected.9.png
rename to res/drawable-mdpi/btn_dial_action_left_selected.9.png
Binary files differ
diff --git a/res/drawable-mdpi/btn_dial_action_middle_disable.9.png b/res/drawable-mdpi/btn_dial_action_middle_disable.9.png
new file mode 100644
index 0000000..e13b958
--- /dev/null
+++ b/res/drawable-mdpi/btn_dial_action_middle_disable.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_middle_disable_focused.9.png b/res/drawable-mdpi/btn_dial_action_middle_disable_focused.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_action_middle_disable_focused.9.png
rename to res/drawable-mdpi/btn_dial_action_middle_disable_focused.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_middle_normal.9.png b/res/drawable-mdpi/btn_dial_action_middle_normal.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_action_middle_normal.9.png
rename to res/drawable-mdpi/btn_dial_action_middle_normal.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_middle_pressed.9.png b/res/drawable-mdpi/btn_dial_action_middle_pressed.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_action_middle_pressed.9.png
rename to res/drawable-mdpi/btn_dial_action_middle_pressed.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_middle_selected.9.png b/res/drawable-mdpi/btn_dial_action_middle_selected.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_action_middle_selected.9.png
rename to res/drawable-mdpi/btn_dial_action_middle_selected.9.png
Binary files differ
diff --git a/res/drawable-mdpi/btn_dial_action_right_disable.9.png b/res/drawable-mdpi/btn_dial_action_right_disable.9.png
new file mode 100644
index 0000000..de5c271
--- /dev/null
+++ b/res/drawable-mdpi/btn_dial_action_right_disable.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_right_disable_focused.9.png b/res/drawable-mdpi/btn_dial_action_right_disable_focused.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_action_right_disable_focused.9.png
rename to res/drawable-mdpi/btn_dial_action_right_disable_focused.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_right_normal.9.png b/res/drawable-mdpi/btn_dial_action_right_normal.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_action_right_normal.9.png
rename to res/drawable-mdpi/btn_dial_action_right_normal.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_right_pressed.9.png b/res/drawable-mdpi/btn_dial_action_right_pressed.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_action_right_pressed.9.png
rename to res/drawable-mdpi/btn_dial_action_right_pressed.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_right_selected.9.png b/res/drawable-mdpi/btn_dial_action_right_selected.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_action_right_selected.9.png
rename to res/drawable-mdpi/btn_dial_action_right_selected.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_normal.9.png b/res/drawable-mdpi/btn_dial_normal.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_normal.9.png
rename to res/drawable-mdpi/btn_dial_normal.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_pressed.9.png b/res/drawable-mdpi/btn_dial_pressed.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_pressed.9.png
rename to res/drawable-mdpi/btn_dial_pressed.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_selected.9.png b/res/drawable-mdpi/btn_dial_selected.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_selected.9.png
rename to res/drawable-mdpi/btn_dial_selected.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_textfield_activated.9.png b/res/drawable-mdpi/btn_dial_textfield_activated.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_textfield_activated.9.png
rename to res/drawable-mdpi/btn_dial_textfield_activated.9.png
Binary files differ
diff --git a/res/drawable-mdpi/btn_dial_textfield_normal.9.png b/res/drawable-mdpi/btn_dial_textfield_normal.9.png
new file mode 100644
index 0000000..277400b
--- /dev/null
+++ b/res/drawable-mdpi/btn_dial_textfield_normal.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_textfield_pressed.9.png b/res/drawable-mdpi/btn_dial_textfield_pressed.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_textfield_pressed.9.png
rename to res/drawable-mdpi/btn_dial_textfield_pressed.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_textfield_selected.9.png b/res/drawable-mdpi/btn_dial_textfield_selected.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/btn_dial_textfield_selected.9.png
rename to res/drawable-mdpi/btn_dial_textfield_selected.9.png
Binary files differ
diff --git a/res/drawable-mdpi/change_photo_box_focused_holo_light.9.png b/res/drawable-mdpi/change_photo_box_focused_holo_light.9.png
new file mode 100644
index 0000000..36c8b87
--- /dev/null
+++ b/res/drawable-mdpi/change_photo_box_focused_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/change_photo_box_normal_holo_light.9.png b/res/drawable-mdpi/change_photo_box_normal_holo_light.9.png
new file mode 100644
index 0000000..d380b40
--- /dev/null
+++ b/res/drawable-mdpi/change_photo_box_normal_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/change_photo_box_pressed_holo_light.9.png b/res/drawable-mdpi/change_photo_box_pressed_holo_light.9.png
new file mode 100644
index 0000000..21d8a96
--- /dev/null
+++ b/res/drawable-mdpi/change_photo_box_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/contacts_widget_preview.png b/res/drawable-mdpi/contacts_widget_preview.png
new file mode 100644
index 0000000..e0bd83e
--- /dev/null
+++ b/res/drawable-mdpi/contacts_widget_preview.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_0_blk.png b/res/drawable-mdpi/dial_num_0_blk.png
similarity index 100%
rename from res/drawable-mdpi-finger/dial_num_0_blk.png
rename to res/drawable-mdpi/dial_num_0_blk.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_0_wht.png b/res/drawable-mdpi/dial_num_0_wht.png
new file mode 100644
index 0000000..c3b3f2c
--- /dev/null
+++ b/res/drawable-mdpi/dial_num_0_wht.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_1_no_vm_blk.png b/res/drawable-mdpi/dial_num_1_no_vm_blk.png
similarity index 100%
rename from res/drawable-mdpi-finger/dial_num_1_no_vm_blk.png
rename to res/drawable-mdpi/dial_num_1_no_vm_blk.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_1_no_vm_wht.png b/res/drawable-mdpi/dial_num_1_no_vm_wht.png
new file mode 100644
index 0000000..a5bdb41
--- /dev/null
+++ b/res/drawable-mdpi/dial_num_1_no_vm_wht.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_2_blk.png b/res/drawable-mdpi/dial_num_2_blk.png
similarity index 100%
rename from res/drawable-mdpi-finger/dial_num_2_blk.png
rename to res/drawable-mdpi/dial_num_2_blk.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_2_wht.png b/res/drawable-mdpi/dial_num_2_wht.png
new file mode 100644
index 0000000..ac99cec
--- /dev/null
+++ b/res/drawable-mdpi/dial_num_2_wht.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_3_blk.png b/res/drawable-mdpi/dial_num_3_blk.png
similarity index 100%
rename from res/drawable-mdpi-finger/dial_num_3_blk.png
rename to res/drawable-mdpi/dial_num_3_blk.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_3_wht.png b/res/drawable-mdpi/dial_num_3_wht.png
new file mode 100644
index 0000000..69170b9
--- /dev/null
+++ b/res/drawable-mdpi/dial_num_3_wht.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_4_blk.png b/res/drawable-mdpi/dial_num_4_blk.png
similarity index 100%
rename from res/drawable-mdpi-finger/dial_num_4_blk.png
rename to res/drawable-mdpi/dial_num_4_blk.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_4_wht.png b/res/drawable-mdpi/dial_num_4_wht.png
new file mode 100644
index 0000000..48a02a5
--- /dev/null
+++ b/res/drawable-mdpi/dial_num_4_wht.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_5_blk.png b/res/drawable-mdpi/dial_num_5_blk.png
similarity index 100%
rename from res/drawable-mdpi-finger/dial_num_5_blk.png
rename to res/drawable-mdpi/dial_num_5_blk.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_5_wht.png b/res/drawable-mdpi/dial_num_5_wht.png
new file mode 100644
index 0000000..e3c9940
--- /dev/null
+++ b/res/drawable-mdpi/dial_num_5_wht.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_6_blk.png b/res/drawable-mdpi/dial_num_6_blk.png
similarity index 100%
rename from res/drawable-mdpi-finger/dial_num_6_blk.png
rename to res/drawable-mdpi/dial_num_6_blk.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_6_wht.png b/res/drawable-mdpi/dial_num_6_wht.png
new file mode 100644
index 0000000..ab12781
--- /dev/null
+++ b/res/drawable-mdpi/dial_num_6_wht.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_7_blk.png b/res/drawable-mdpi/dial_num_7_blk.png
similarity index 100%
rename from res/drawable-mdpi-finger/dial_num_7_blk.png
rename to res/drawable-mdpi/dial_num_7_blk.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_7_wht.png b/res/drawable-mdpi/dial_num_7_wht.png
new file mode 100644
index 0000000..9e66205
--- /dev/null
+++ b/res/drawable-mdpi/dial_num_7_wht.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_8_blk.png b/res/drawable-mdpi/dial_num_8_blk.png
similarity index 100%
rename from res/drawable-mdpi-finger/dial_num_8_blk.png
rename to res/drawable-mdpi/dial_num_8_blk.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_8_wht.png b/res/drawable-mdpi/dial_num_8_wht.png
new file mode 100644
index 0000000..2af30fa
--- /dev/null
+++ b/res/drawable-mdpi/dial_num_8_wht.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_9_blk.png b/res/drawable-mdpi/dial_num_9_blk.png
similarity index 100%
rename from res/drawable-mdpi-finger/dial_num_9_blk.png
rename to res/drawable-mdpi/dial_num_9_blk.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_9_wht.png b/res/drawable-mdpi/dial_num_9_wht.png
new file mode 100644
index 0000000..1c99b61
--- /dev/null
+++ b/res/drawable-mdpi/dial_num_9_wht.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_pound_blk.png b/res/drawable-mdpi/dial_num_pound_blk.png
similarity index 100%
rename from res/drawable-mdpi-finger/dial_num_pound_blk.png
rename to res/drawable-mdpi/dial_num_pound_blk.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_pound_wht.png b/res/drawable-mdpi/dial_num_pound_wht.png
new file mode 100644
index 0000000..e17f2bf
--- /dev/null
+++ b/res/drawable-mdpi/dial_num_pound_wht.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_star_blk.png b/res/drawable-mdpi/dial_num_star_blk.png
similarity index 100%
rename from res/drawable-mdpi-finger/dial_num_star_blk.png
rename to res/drawable-mdpi/dial_num_star_blk.png
Binary files differ
diff --git a/res/drawable-mdpi/dial_num_star_wht.png b/res/drawable-mdpi/dial_num_star_wht.png
new file mode 100644
index 0000000..86113ed
--- /dev/null
+++ b/res/drawable-mdpi/dial_num_star_wht.png
Binary files differ
diff --git a/res/drawable-mdpi/directory_bg.9.png b/res/drawable-mdpi/directory_bg.9.png
new file mode 100644
index 0000000..80578cd
--- /dev/null
+++ b/res/drawable-mdpi/directory_bg.9.png
Binary files differ
diff --git a/res/drawable-mdpi/directory_bg_holo.9.png b/res/drawable-mdpi/directory_bg_holo.9.png
new file mode 100644
index 0000000..7f7209d
--- /dev/null
+++ b/res/drawable-mdpi/directory_bg_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/divider_vertical_dark.9.png b/res/drawable-mdpi/divider_vertical_dark.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/divider_vertical_dark.9.png
rename to res/drawable-mdpi/divider_vertical_dark.9.png
Binary files differ
diff --git a/res/drawable-mdpi/frame_thumbnail_contact_widget_focused_holo.png b/res/drawable-mdpi/frame_thumbnail_contact_widget_focused_holo.png
new file mode 100644
index 0000000..ce60a60
--- /dev/null
+++ b/res/drawable-mdpi/frame_thumbnail_contact_widget_focused_holo.png
Binary files differ
diff --git a/res/drawable-mdpi/frame_thumbnail_contact_widget_normal_holo.png b/res/drawable-mdpi/frame_thumbnail_contact_widget_normal_holo.png
new file mode 100644
index 0000000..a62834c
--- /dev/null
+++ b/res/drawable-mdpi/frame_thumbnail_contact_widget_normal_holo.png
Binary files differ
diff --git a/res/drawable-mdpi/frame_thumbnail_contact_widget_pressed_holo.png b/res/drawable-mdpi/frame_thumbnail_contact_widget_pressed_holo.png
new file mode 100644
index 0000000..bba0d2c
--- /dev/null
+++ b/res/drawable-mdpi/frame_thumbnail_contact_widget_pressed_holo.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_call_log_header_incoming_call.png b/res/drawable-mdpi/ic_call_log_header_incoming_call.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_call_log_header_incoming_call.png
rename to res/drawable-mdpi/ic_call_log_header_incoming_call.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_call_log_header_missed_call.png b/res/drawable-mdpi/ic_call_log_header_missed_call.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_call_log_header_missed_call.png
rename to res/drawable-mdpi/ic_call_log_header_missed_call.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_call_log_header_outgoing_call.png b/res/drawable-mdpi/ic_call_log_header_outgoing_call.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_call_log_header_outgoing_call.png
rename to res/drawable-mdpi/ic_call_log_header_outgoing_call.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_call_log_list_incoming_call.png b/res/drawable-mdpi/ic_call_log_list_incoming_call.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_call_log_list_incoming_call.png
rename to res/drawable-mdpi/ic_call_log_list_incoming_call.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_call_log_list_missed_call.png b/res/drawable-mdpi/ic_call_log_list_missed_call.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_call_log_list_missed_call.png
rename to res/drawable-mdpi/ic_call_log_list_missed_call.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_call_log_list_outgoing_call.png b/res/drawable-mdpi/ic_call_log_list_outgoing_call.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_call_log_list_outgoing_call.png
rename to res/drawable-mdpi/ic_call_log_list_outgoing_call.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_picture.png b/res/drawable-mdpi/ic_contact_picture.png
new file mode 100644
index 0000000..faa3dc0
--- /dev/null
+++ b/res/drawable-mdpi/ic_contact_picture.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_dial_action_call.png b/res/drawable-mdpi/ic_dial_action_call.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_dial_action_call.png
rename to res/drawable-mdpi/ic_dial_action_call.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_dial_action_delete.png b/res/drawable-mdpi/ic_dial_action_delete.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_dial_action_delete.png
rename to res/drawable-mdpi/ic_dial_action_delete.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_dial_action_voice_mail.png b/res/drawable-mdpi/ic_dial_action_voice_mail.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_dial_action_voice_mail.png
rename to res/drawable-mdpi/ic_dial_action_voice_mail.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_dialer_fork_add_call.png b/res/drawable-mdpi/ic_dialer_fork_add_call.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_dialer_fork_add_call.png
rename to res/drawable-mdpi/ic_dialer_fork_add_call.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_dialer_fork_current_call.png b/res/drawable-mdpi/ic_dialer_fork_current_call.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_dialer_fork_current_call.png
rename to res/drawable-mdpi/ic_dialer_fork_current_call.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_dialer_fork_tt_keypad.png b/res/drawable-mdpi/ic_dialer_fork_tt_keypad.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_dialer_fork_tt_keypad.png
rename to res/drawable-mdpi/ic_dialer_fork_tt_keypad.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_contacts.png b/res/drawable-mdpi/ic_launcher_contacts.png
deleted file mode 100644
index 08468fd..0000000
--- a/res/drawable-mdpi/ic_launcher_contacts.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_phone.png b/res/drawable-mdpi/ic_launcher_phone.png
deleted file mode 100644
index aa4f3c5..0000000
--- a/res/drawable-mdpi/ic_launcher_phone.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/ic_menu_2sec_pause.png b/res/drawable-mdpi/ic_menu_2sec_pause.png
similarity index 100%
rename from res/drawable/ic_menu_2sec_pause.png
rename to res/drawable-mdpi/ic_menu_2sec_pause.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_accounts_holo_light.png b/res/drawable-mdpi/ic_menu_accounts_holo_light.png
new file mode 100644
index 0000000..ae291d8
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_accounts_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_add_contact_holo_light.png b/res/drawable-mdpi/ic_menu_add_contact_holo_light.png
new file mode 100644
index 0000000..0a82abd
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_add_contact_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_add_field_holo_light.png b/res/drawable-mdpi/ic_menu_add_field_holo_light.png
new file mode 100644
index 0000000..fbb1a7a
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_add_field_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_compose_holo_light.png b/res/drawable-mdpi/ic_menu_compose_holo_light.png
new file mode 100644
index 0000000..5236c1c
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_compose_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_contacts_holo_light.png b/res/drawable-mdpi/ic_menu_contacts_holo_light.png
new file mode 100644
index 0000000..5ef1184
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_contacts_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_display_all.png b/res/drawable-mdpi/ic_menu_display_all.png
new file mode 100644
index 0000000..61a9e35
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_display_all.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_display_all_holo_light.png b/res/drawable-mdpi/ic_menu_display_all_holo_light.png
new file mode 100644
index 0000000..9781312
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_display_all_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_display_selected.png b/res/drawable-mdpi/ic_menu_display_selected.png
new file mode 100644
index 0000000..b4ec7a8
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_display_selected.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_done_holo_light.png b/res/drawable-mdpi/ic_menu_done_holo_light.png
new file mode 100644
index 0000000..3468bbd
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_done_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_expander_maximized_holo_light.png b/res/drawable-mdpi/ic_menu_expander_maximized_holo_light.png
new file mode 100644
index 0000000..3dee74d
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_expander_maximized_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_expander_minimized_holo_light.png b/res/drawable-mdpi/ic_menu_expander_minimized_holo_light.png
new file mode 100644
index 0000000..6dba137
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_expander_minimized_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_import_export_holo_light.png b/res/drawable-mdpi/ic_menu_import_export_holo_light.png
new file mode 100644
index 0000000..e31ec6b
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_import_export_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_merge_holo_light.png b/res/drawable-mdpi/ic_menu_merge_holo_light.png
new file mode 100644
index 0000000..a650c4d
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_merge_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_remove_field_holo_light.png b/res/drawable-mdpi/ic_menu_remove_field_holo_light.png
new file mode 100644
index 0000000..cb993fd
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_remove_field_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_select.png b/res/drawable-mdpi/ic_menu_select.png
new file mode 100644
index 0000000..29e3d7e
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_select.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_settings_holo_light.png b/res/drawable-mdpi/ic_menu_settings_holo_light.png
new file mode 100644
index 0000000..1ebc112
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_settings_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_share_holo_light.png b/res/drawable-mdpi/ic_menu_share_holo_light.png
new file mode 100644
index 0000000..6b42585
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_share_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_split_holo_light.png b/res/drawable-mdpi/ic_menu_split_holo_light.png
new file mode 100644
index 0000000..56afbc9
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_split_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_star_holo_light.png b/res/drawable-mdpi/ic_menu_star_holo_light.png
new file mode 100644
index 0000000..8263b27
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_star_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_trash_holo_light.png b/res/drawable-mdpi/ic_menu_trash_holo_light.png
new file mode 100644
index 0000000..29801b4
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_trash_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_unselect.png b/res/drawable-mdpi/ic_menu_unselect.png
new file mode 100644
index 0000000..2b69bc8
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_unselect.png
Binary files differ
diff --git a/res/drawable/ic_menu_wait.png b/res/drawable-mdpi/ic_menu_wait.png
similarity index 100%
rename from res/drawable/ic_menu_wait.png
rename to res/drawable-mdpi/ic_menu_wait.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_selected_contacts.png b/res/drawable-mdpi/ic_tab_selected_contacts.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_tab_selected_contacts.png
rename to res/drawable-mdpi/ic_tab_selected_contacts.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_selected_dialer.png b/res/drawable-mdpi/ic_tab_selected_dialer.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_tab_selected_dialer.png
rename to res/drawable-mdpi/ic_tab_selected_dialer.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_selected_recent.png b/res/drawable-mdpi/ic_tab_selected_recent.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_tab_selected_recent.png
rename to res/drawable-mdpi/ic_tab_selected_recent.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_selected_starred.png b/res/drawable-mdpi/ic_tab_selected_starred.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_tab_selected_starred.png
rename to res/drawable-mdpi/ic_tab_selected_starred.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_unselected_contacts.png b/res/drawable-mdpi/ic_tab_unselected_contacts.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_tab_unselected_contacts.png
rename to res/drawable-mdpi/ic_tab_unselected_contacts.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_unselected_dialer.png b/res/drawable-mdpi/ic_tab_unselected_dialer.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_tab_unselected_dialer.png
rename to res/drawable-mdpi/ic_tab_unselected_dialer.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_unselected_recent.png b/res/drawable-mdpi/ic_tab_unselected_recent.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_tab_unselected_recent.png
rename to res/drawable-mdpi/ic_tab_unselected_recent.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_unselected_starred.png b/res/drawable-mdpi/ic_tab_unselected_starred.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_tab_unselected_starred.png
rename to res/drawable-mdpi/ic_tab_unselected_starred.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/infobar_dark.9.png b/res/drawable-mdpi/infobar_dark.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/infobar_dark.9.png
rename to res/drawable-mdpi/infobar_dark.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_activated_holo.9.png b/res/drawable-mdpi/list_activated_holo.9.png
new file mode 100644
index 0000000..f7cd24e
--- /dev/null
+++ b/res/drawable-mdpi/list_activated_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_background_holo.9.png b/res/drawable-mdpi/list_background_holo.9.png
new file mode 100644
index 0000000..ca2f750
--- /dev/null
+++ b/res/drawable-mdpi/list_background_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_focused_holo.9.png b/res/drawable-mdpi/list_focused_holo.9.png
new file mode 100644
index 0000000..32cb628
--- /dev/null
+++ b/res/drawable-mdpi/list_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_item_divider.9.png b/res/drawable-mdpi/list_item_divider.9.png
new file mode 100644
index 0000000..60e2cb2
--- /dev/null
+++ b/res/drawable-mdpi/list_item_divider.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_item_divider_holo.9.png b/res/drawable-mdpi/list_item_divider_holo.9.png
new file mode 100644
index 0000000..f5130d5
--- /dev/null
+++ b/res/drawable-mdpi/list_item_divider_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_pressed_holo.9.png b/res/drawable-mdpi/list_pressed_holo.9.png
new file mode 100644
index 0000000..7ea8666
--- /dev/null
+++ b/res/drawable-mdpi/list_pressed_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_title_holo.9.png b/res/drawable-mdpi/list_title_holo.9.png
new file mode 100644
index 0000000..e866452
--- /dev/null
+++ b/res/drawable-mdpi/list_title_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/panel_content.9.png b/res/drawable-mdpi/panel_content.9.png
new file mode 100644
index 0000000..1d30d5f
--- /dev/null
+++ b/res/drawable-mdpi/panel_content.9.png
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_arrowdown_left_holo_light.9.png b/res/drawable-mdpi/quickactions_arrowdown_left_holo_light.9.png
new file mode 100644
index 0000000..bd43850
--- /dev/null
+++ b/res/drawable-mdpi/quickactions_arrowdown_left_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_arrowdown_middle_holo_light.9.png b/res/drawable-mdpi/quickactions_arrowdown_middle_holo_light.9.png
new file mode 100644
index 0000000..c284dbb
--- /dev/null
+++ b/res/drawable-mdpi/quickactions_arrowdown_middle_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_arrowdown_right_holo_light.9.png b/res/drawable-mdpi/quickactions_arrowdown_right_holo_light.9.png
new file mode 100644
index 0000000..c057f71
--- /dev/null
+++ b/res/drawable-mdpi/quickactions_arrowdown_right_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_arrowup_left_holo_light.9.png b/res/drawable-mdpi/quickactions_arrowup_left_holo_light.9.png
new file mode 100644
index 0000000..85d092f
--- /dev/null
+++ b/res/drawable-mdpi/quickactions_arrowup_left_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_arrowup_middle_holo_light.9.png b/res/drawable-mdpi/quickactions_arrowup_middle_holo_light.9.png
new file mode 100644
index 0000000..828b718
--- /dev/null
+++ b/res/drawable-mdpi/quickactions_arrowup_middle_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_arrowup_right_holo_light.9.png b/res/drawable-mdpi/quickactions_arrowup_right_holo_light.9.png
new file mode 100644
index 0000000..e4994b4
--- /dev/null
+++ b/res/drawable-mdpi/quickactions_arrowup_right_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_icon_activated.9.png b/res/drawable-mdpi/quickactions_icon_activated.9.png
new file mode 100644
index 0000000..777c988
--- /dev/null
+++ b/res/drawable-mdpi/quickactions_icon_activated.9.png
Binary files differ
diff --git a/res/drawable-mdpi/section_header.9.png b/res/drawable-mdpi/section_header.9.png
new file mode 100644
index 0000000..ac906cd
--- /dev/null
+++ b/res/drawable-mdpi/section_header.9.png
Binary files differ
diff --git a/res/drawable-mdpi/section_header_holo.9.png b/res/drawable-mdpi/section_header_holo.9.png
new file mode 100644
index 0000000..de12d0e
--- /dev/null
+++ b/res/drawable-mdpi/section_header_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/statusbox_attribute_holo.9.png b/res/drawable-mdpi/statusbox_attribute_holo.9.png
new file mode 100644
index 0000000..b124392
--- /dev/null
+++ b/res/drawable-mdpi/statusbox_attribute_holo.9.png
Binary files differ
diff --git a/res/drawable-mdpi/statusbox_landscape_holo_light.9.png b/res/drawable-mdpi/statusbox_landscape_holo_light.9.png
new file mode 100644
index 0000000..49546e5
--- /dev/null
+++ b/res/drawable-mdpi/statusbox_landscape_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/statusbox_portrait_holo_light.9.png b/res/drawable-mdpi/statusbox_portrait_holo_light.9.png
new file mode 100644
index 0000000..902e559
--- /dev/null
+++ b/res/drawable-mdpi/statusbox_portrait_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/sym_action_add.png b/res/drawable-mdpi/sym_action_add.png
similarity index 100%
rename from res/drawable-mdpi-finger/sym_action_add.png
rename to res/drawable-mdpi/sym_action_add.png
Binary files differ
diff --git a/res/drawable-mdpi/sym_action_audiochat_holo_light.png b/res/drawable-mdpi/sym_action_audiochat_holo_light.png
new file mode 100644
index 0000000..9e6e328
--- /dev/null
+++ b/res/drawable-mdpi/sym_action_audiochat_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/sym_action_email_holo_light.png b/res/drawable-mdpi/sym_action_email_holo_light.png
new file mode 100644
index 0000000..3160adb
--- /dev/null
+++ b/res/drawable-mdpi/sym_action_email_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/sym_action_goto_website_holo_light.png b/res/drawable-mdpi/sym_action_goto_website_holo_light.png
new file mode 100644
index 0000000..5b17bd2
--- /dev/null
+++ b/res/drawable-mdpi/sym_action_goto_website_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/sym_action_show_map_holo_light.png b/res/drawable-mdpi/sym_action_show_map_holo_light.png
new file mode 100644
index 0000000..059e821
--- /dev/null
+++ b/res/drawable-mdpi/sym_action_show_map_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/sym_action_talk_holo_light.png b/res/drawable-mdpi/sym_action_talk_holo_light.png
new file mode 100644
index 0000000..ff444d8
--- /dev/null
+++ b/res/drawable-mdpi/sym_action_talk_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi/sym_action_videochat_holo_light.png b/res/drawable-mdpi/sym_action_videochat_holo_light.png
new file mode 100644
index 0000000..0ef40f8
--- /dev/null
+++ b/res/drawable-mdpi/sym_action_videochat_holo_light.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/sym_action_view_contact.png b/res/drawable-mdpi/sym_action_view_contact.png
similarity index 100%
rename from res/drawable-mdpi-finger/sym_action_view_contact.png
rename to res/drawable-mdpi/sym_action_view_contact.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/tab_focused.9.png b/res/drawable-mdpi/tab_focused.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/tab_focused.9.png
rename to res/drawable-mdpi/tab_focused.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/tab_focused_bottom.9.png b/res/drawable-mdpi/tab_focused_bottom.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/tab_focused_bottom.9.png
rename to res/drawable-mdpi/tab_focused_bottom.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/tab_left_arrow.png b/res/drawable-mdpi/tab_left_arrow.png
similarity index 100%
rename from res/drawable-mdpi-finger/tab_left_arrow.png
rename to res/drawable-mdpi/tab_left_arrow.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/tab_pressed.9.png b/res/drawable-mdpi/tab_pressed.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/tab_pressed.9.png
rename to res/drawable-mdpi/tab_pressed.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/tab_pressed_bottom.9.png b/res/drawable-mdpi/tab_pressed_bottom.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/tab_pressed_bottom.9.png
rename to res/drawable-mdpi/tab_pressed_bottom.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/tab_right_arrow.png b/res/drawable-mdpi/tab_right_arrow.png
similarity index 100%
rename from res/drawable-mdpi-finger/tab_right_arrow.png
rename to res/drawable-mdpi/tab_right_arrow.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/tab_selected.9.png b/res/drawable-mdpi/tab_selected.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/tab_selected.9.png
rename to res/drawable-mdpi/tab_selected.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/tab_selected_bottom.9.png b/res/drawable-mdpi/tab_selected_bottom.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/tab_selected_bottom.9.png
rename to res/drawable-mdpi/tab_selected_bottom.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/tab_unselected.9.png b/res/drawable-mdpi/tab_unselected.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/tab_unselected.9.png
rename to res/drawable-mdpi/tab_unselected.9.png
Binary files differ
diff --git a/res/drawable-mdpi/title_bar_medium.9.png b/res/drawable-mdpi/title_bar_medium.9.png
new file mode 100644
index 0000000..2d41d02
--- /dev/null
+++ b/res/drawable-mdpi/title_bar_medium.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/title_bar_shadow.9.png b/res/drawable-mdpi/title_bar_shadow.9.png
similarity index 100%
rename from res/drawable-mdpi-finger/title_bar_shadow.9.png
rename to res/drawable-mdpi/title_bar_shadow.9.png
Binary files differ
diff --git a/res/drawable/unknown_source.png b/res/drawable-mdpi/unknown_source.png
similarity index 100%
rename from res/drawable/unknown_source.png
rename to res/drawable-mdpi/unknown_source.png
Binary files differ
diff --git a/res/drawable-xlarge-hdpi/panel_message.9.png b/res/drawable-xlarge-hdpi/panel_message.9.png
new file mode 100644
index 0000000..796a8fa
--- /dev/null
+++ b/res/drawable-xlarge-hdpi/panel_message.9.png
Binary files differ
diff --git a/res/drawable-xlarge-mdpi/panel_message.9.png b/res/drawable-xlarge-mdpi/panel_message.9.png
new file mode 100644
index 0000000..529c61d
--- /dev/null
+++ b/res/drawable-xlarge-mdpi/panel_message.9.png
Binary files differ
diff --git a/res/drawable-xlarge-nodpi/divider_vertical_dark.9.png b/res/drawable-xlarge-nodpi/divider_vertical_dark.9.png
new file mode 100644
index 0000000..38b794b
--- /dev/null
+++ b/res/drawable-xlarge-nodpi/divider_vertical_dark.9.png
Binary files differ
diff --git a/res/drawable-finger/btn_dial.xml b/res/drawable/btn_dial.xml
similarity index 100%
rename from res/drawable-finger/btn_dial.xml
rename to res/drawable/btn_dial.xml
diff --git a/res/drawable-finger/btn_dial_action.xml b/res/drawable/btn_dial_action.xml
similarity index 100%
rename from res/drawable-finger/btn_dial_action.xml
rename to res/drawable/btn_dial_action.xml
diff --git a/res/drawable-finger/btn_dial_delete.xml b/res/drawable/btn_dial_delete.xml
similarity index 100%
rename from res/drawable-finger/btn_dial_delete.xml
rename to res/drawable/btn_dial_delete.xml
diff --git a/res/drawable-finger/btn_dial_voicemail.xml b/res/drawable/btn_dial_voicemail.xml
similarity index 100%
rename from res/drawable-finger/btn_dial_voicemail.xml
rename to res/drawable/btn_dial_voicemail.xml
diff --git a/res/drawable/change_photo_box_holo_light.xml b/res/drawable/change_photo_box_holo_light.xml
new file mode 100644
index 0000000..f08beb0
--- /dev/null
+++ b/res/drawable/change_photo_box_holo_light.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/change_photo_box_normal_holo_light" />
+ <item android:state_pressed="true"
+ android:drawable="@drawable/change_photo_box_pressed_holo_light" />
+ <item android:state_focused="true"
+ android:drawable="@drawable/change_photo_box_focused_holo_light" />
+ <item
+ android:drawable="@drawable/change_photo_box_normal_holo_light" />
+</selector>
diff --git a/res/drawable/contacts_widget_preview.png b/res/drawable/contacts_widget_preview.png
new file mode 100755
index 0000000..86c3042
--- /dev/null
+++ b/res/drawable/contacts_widget_preview.png
Binary files differ
diff --git a/res/drawable-finger/dial_num_0.xml b/res/drawable/dial_num_0.xml
similarity index 100%
rename from res/drawable-finger/dial_num_0.xml
rename to res/drawable/dial_num_0.xml
diff --git a/res/drawable-finger/dial_num_1_no_vm.xml b/res/drawable/dial_num_1_no_vm.xml
similarity index 100%
rename from res/drawable-finger/dial_num_1_no_vm.xml
rename to res/drawable/dial_num_1_no_vm.xml
diff --git a/res/drawable-finger/dial_num_2.xml b/res/drawable/dial_num_2.xml
similarity index 100%
rename from res/drawable-finger/dial_num_2.xml
rename to res/drawable/dial_num_2.xml
diff --git a/res/drawable-finger/dial_num_3.xml b/res/drawable/dial_num_3.xml
similarity index 100%
rename from res/drawable-finger/dial_num_3.xml
rename to res/drawable/dial_num_3.xml
diff --git a/res/drawable-finger/dial_num_4.xml b/res/drawable/dial_num_4.xml
similarity index 100%
rename from res/drawable-finger/dial_num_4.xml
rename to res/drawable/dial_num_4.xml
diff --git a/res/drawable-finger/dial_num_5.xml b/res/drawable/dial_num_5.xml
similarity index 100%
rename from res/drawable-finger/dial_num_5.xml
rename to res/drawable/dial_num_5.xml
diff --git a/res/drawable-finger/dial_num_6.xml b/res/drawable/dial_num_6.xml
similarity index 100%
rename from res/drawable-finger/dial_num_6.xml
rename to res/drawable/dial_num_6.xml
diff --git a/res/drawable-finger/dial_num_7.xml b/res/drawable/dial_num_7.xml
similarity index 100%
rename from res/drawable-finger/dial_num_7.xml
rename to res/drawable/dial_num_7.xml
diff --git a/res/drawable-finger/dial_num_8.xml b/res/drawable/dial_num_8.xml
similarity index 100%
rename from res/drawable-finger/dial_num_8.xml
rename to res/drawable/dial_num_8.xml
diff --git a/res/drawable-finger/dial_num_9.xml b/res/drawable/dial_num_9.xml
similarity index 100%
rename from res/drawable-finger/dial_num_9.xml
rename to res/drawable/dial_num_9.xml
diff --git a/res/drawable-finger/dial_num_pound.xml b/res/drawable/dial_num_pound.xml
similarity index 100%
rename from res/drawable-finger/dial_num_pound.xml
rename to res/drawable/dial_num_pound.xml
diff --git a/res/drawable-finger/dial_num_star.xml b/res/drawable/dial_num_star.xml
similarity index 100%
rename from res/drawable-finger/dial_num_star.xml
rename to res/drawable/dial_num_star.xml
diff --git a/res/drawable/filter_selector_background.xml b/res/drawable/filter_selector_background.xml
new file mode 100644
index 0000000..16464f6
--- /dev/null
+++ b/res/drawable/filter_selector_background.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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="@color/filter_selector_selected_background" />
+ <item android:drawable="@android:color/transparent"/>
+</selector>
diff --git a/res/drawable/frame_thumbnail_contact_widget_holo.xml b/res/drawable/frame_thumbnail_contact_widget_holo.xml
new file mode 100644
index 0000000..1858f9d
--- /dev/null
+++ b/res/drawable/frame_thumbnail_contact_widget_holo.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true"
+ android:drawable="@drawable/frame_thumbnail_contact_widget_pressed_holo" />
+ <item android:state_focused="true"
+ android:drawable="@drawable/frame_thumbnail_contact_widget_focused_holo" />
+ <item
+ android:drawable="@drawable/frame_thumbnail_contact_widget_normal_holo" />
+</selector>
diff --git a/res/drawable-finger/ic_tab_contacts.xml b/res/drawable/ic_tab_contacts.xml
similarity index 100%
rename from res/drawable-finger/ic_tab_contacts.xml
rename to res/drawable/ic_tab_contacts.xml
diff --git a/res/drawable-finger/ic_tab_dialer.xml b/res/drawable/ic_tab_dialer.xml
similarity index 100%
rename from res/drawable-finger/ic_tab_dialer.xml
rename to res/drawable/ic_tab_dialer.xml
diff --git a/res/drawable-finger/ic_tab_recent.xml b/res/drawable/ic_tab_recent.xml
similarity index 100%
rename from res/drawable-finger/ic_tab_recent.xml
rename to res/drawable/ic_tab_recent.xml
diff --git a/res/drawable-finger/ic_tab_starred.xml b/res/drawable/ic_tab_starred.xml
similarity index 100%
rename from res/drawable-finger/ic_tab_starred.xml
rename to res/drawable/ic_tab_starred.xml
diff --git a/res/drawable/list_item_activated_background.xml b/res/drawable/list_item_activated_background.xml
new file mode 100644
index 0000000..334e8ec
--- /dev/null
+++ b/res/drawable/list_item_activated_background.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"
+ android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+ <item android:state_activated="true" android:drawable="@drawable/list_activated_holo" />
+ <item android:drawable="@android:color/transparent" />
+</selector>
diff --git a/res/drawable/list_selector.xml b/res/drawable/list_selector.xml
new file mode 100644
index 0000000..b43852c
--- /dev/null
+++ b/res/drawable/list_selector.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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"
+ android:exitFadeDuration="@android:integer/config_mediumAnimTime">
+ <item
+ android:state_window_focused="false"
+ android:drawable="@android:color/transparent" />
+ <item
+ android:state_focused="true"
+ android:state_pressed="true"
+ android:drawable="@drawable/list_pressed_holo" />
+ <item
+ android:state_focused="false"
+ android:state_pressed="true"
+ android:drawable="@drawable/list_pressed_holo" />
+ <item
+ android:state_focused="true"
+ android:drawable="@drawable/list_focused_holo" />
+ <item
+ android:drawable="@android:color/transparent" />
+</selector>
+
diff --git a/res/drawable/quickcontact_slider_btn.xml b/res/drawable/quickcontact_slider_btn.xml
new file mode 100644
index 0000000..b3d0ec7
--- /dev/null
+++ b/res/drawable/quickcontact_slider_btn.xml
@@ -0,0 +1,30 @@
+<?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"
+ android:dither="true">
+ <!-- TODO Still a hack. This needs a default from the framework instead for the checked state -->
+ <item android:state_checked="true"
+ android:drawable="@drawable/quickactions_icon_activated" />
+ <item android:state_window_focused="false"
+ android:drawable="@android:color/transparent" />
+ <item android:state_pressed="true"
+ android:drawable="@drawable/quickactions_icon_activated" />
+ <item android:state_focused="true"
+ android:drawable="@drawable/quickactions_icon_activated" />
+ <item
+ android:drawable="@android:color/transparent" />
+</selector>
diff --git a/res/drawable-finger/tab_bottom.xml b/res/drawable/tab_bottom.xml
similarity index 100%
rename from res/drawable-finger/tab_bottom.xml
rename to res/drawable/tab_bottom.xml
diff --git a/res/drawable-finger/tab_indicator_bg.xml b/res/drawable/tab_indicator_bg.xml
similarity index 100%
rename from res/drawable-finger/tab_indicator_bg.xml
rename to res/drawable/tab_indicator_bg.xml
diff --git a/res/layout-finger/contact_card_layout.xml b/res/layout-finger/contact_card_layout.xml
deleted file mode 100644
index 38ce17e..0000000
--- a/res/layout-finger/contact_card_layout.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?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/card_root_view"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <com.android.internal.widget.ContactHeaderWidget
- android:id="@+id/contact_header_widget"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
-
- <ListView android:id="@+id/contact_data"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/title_bar_shadow"
- />
-
- <ScrollView android:id="@android:id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fillViewport="true"
- >
- <TextView android:id="@+id/emptyText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/no_contact_details"
- 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_content.xml b/res/layout-finger/contacts_list_content.xml
deleted file mode 100644
index 36c03ce..0000000
--- a/res/layout-finger/contacts_list_content.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?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/pinned_header_list_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- >
-
- <view
- class="com.android.contacts.PinnedHeaderListView"
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fastScrollEnabled="true"
- />
-
- <include layout="@layout/contacts_list_empty"/>
-
-</LinearLayout>
diff --git a/res/layout-finger/contacts_list_content_join.xml b/res/layout-finger/contacts_list_content_join.xml
deleted file mode 100644
index b50713b..0000000
--- a/res/layout-finger/contacts_list_content_join.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?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="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:background="@*android:drawable/title_bar_medium"
- android:paddingLeft="5dip"
- android:paddingRight="5dip"
- android:gravity="center_vertical"
- >
-
- <ImageView
- android:layout_width="48dip"
- android:layout_height="48dip"
- android:src="@drawable/ic_join"
- android:gravity="center"
- android:scaleType="fitCenter"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="10dip">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/titleJoinContactDataWith"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:shadowColor="#BB000000"
- android:shadowRadius="2.75"
- />
- <TextView
- android:id="@+id/join_contact_blurb"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="-2dip"
- android:maxLines="2"
- android:textAppearance="?android:attr/textAppearanceSmall"
- />
- </LinearLayout>
- </LinearLayout>
-
- <ListView android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fastScrollEnabled="true"
- />
-</LinearLayout>
-
diff --git a/res/layout-finger/contacts_list_empty.xml b/res/layout-finger/contacts_list_empty.xml
deleted file mode 100644
index 195da1e..0000000
--- a/res/layout-finger/contacts_list_empty.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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:id="@android:id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fillViewport="true"
->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView android:id="@+id/emptyText"
- android:layout_width="match_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"
- />
-
- <LinearLayout android:id="@+id/import_failure"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="fill_horizontal"
- android:padding="20dip"
- android:visibility="gone">
-
- <Button
- android:id="@+id/import_failure_uninstall_apps"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/upgrade_out_of_memory_uninstall"/>
-
- <Button
- android:id="@+id/import_failure_retry_upgrade"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dip"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/upgrade_out_of_memory_retry"/>
- </LinearLayout>
- </LinearLayout>
-</ScrollView>
diff --git a/res/layout-finger/contacts_list_search_all_item.xml b/res/layout-finger/contacts_list_search_all_item.xml
deleted file mode 100644
index da2a3d2..0000000
--- a/res/layout-finger/contacts_list_search_all_item.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2010, 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="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
->
- <TextView
- android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical|left"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:text="@string/search_for_all_contacts"
- android:paddingLeft="14dip"
- />
-
- <View android:id="@+id/list_divider"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@*android:drawable/divider_horizontal_dark_opaque"
- />
-</LinearLayout>
diff --git a/res/layout-finger/contacts_list_search_results.xml b/res/layout-finger/contacts_list_search_results.xml
deleted file mode 100644
index 244ca80..0000000
--- a/res/layout-finger/contacts_list_search_results.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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/pinned_header_list_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@*android:drawable/title_bar_medium"
- android:paddingLeft="10dip"
- android:paddingRight="10dip"
- android:gravity="center_vertical"
- >
-
- <TextView
- android:id="@+id/search_results_for"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/titleJoinContactDataWith"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:shadowColor="#BB000000"
- android:shadowRadius="2.75"
- />
-
- <TextView
- android:id="@+id/search_results_found"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="-2dip"
- android:textAppearance="?android:attr/textAppearanceSmall"
- />
-
- </LinearLayout>
-
- <view
- class="com.android.contacts.PinnedHeaderListView"
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fastScrollEnabled="true"
- />
-
- <include layout="@layout/contacts_list_empty"/>
-
-</LinearLayout>
diff --git a/res/layout-finger/contacts_list_show_all_item.xml b/res/layout-finger/contacts_list_show_all_item.xml
deleted file mode 100644
index 4111a8f..0000000
--- a/res/layout-finger/contacts_list_show_all_item.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?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="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
->
- <TextView
- android:layout_width="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical|left"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:text="@string/showAllContactsJoinItem"
- android:paddingLeft="14dip"
- />
-
- <View android:id="@+id/list_divider"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@*android:drawable/divider_horizontal_dark_opaque"
- />
-</LinearLayout>
diff --git a/res/layout-finger/contacts_search_content.xml b/res/layout-finger/contacts_search_content.xml
deleted file mode 100644
index ae72376..0000000
--- a/res/layout-finger/contacts_search_content.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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/pinned_header_list_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:background="@color/translucent_search_background"
- >
-
- <include android:id="@+id/searchView"
- layout="@layout/search_bar"/>
-
- <ListView
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fastScrollEnabled="true"
- android:background="@android:color/background_dark"
- />
-
- <!-- Transparent filler -->
- <View android:id="@android:id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
-</LinearLayout>
diff --git a/res/layout-finger/edit_phonetic_name.xml b/res/layout-finger/edit_phonetic_name.xml
deleted file mode 100644
index 418f8fa..0000000
--- a/res/layout-finger/edit_phonetic_name.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?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="match_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="match_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/horizontal_divider.xml b/res/layout-finger/horizontal_divider.xml
deleted file mode 100644
index 202e663..0000000
--- a/res/layout-finger/horizontal_divider.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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="match_parent"
- android:layout_height="wrap_content"
- android:background="@*android:drawable/divider_horizontal_dark_opaque"
-/>
diff --git a/res/layout-finger/list_item_text_icons.xml b/res/layout-finger/list_item_text_icons.xml
deleted file mode 100644
index 69e064e..0000000
--- a/res/layout-finger/list_item_text_icons.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-<?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="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:orientation="horizontal"
- android:paddingLeft="9dip"
- android:gravity="center_vertical"
->
-
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginLeft="5dip"
- android:orientation="vertical"
- android:paddingTop="5dip"
- android:paddingBottom="7dip"
- 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:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- >
-
- <TextView android:id="@android:id/text2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:textAppearance="?android:attr/textAppearanceSmall"
- />
-
- <ImageView android:id="@+id/primary_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingLeft="3dip"
- android:src="@drawable/ic_default_number"
- />
-
- </LinearLayout>
-
- <TextView
- android:id="@+id/footer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:visibility="gone" />
-
- </LinearLayout>
-
- <ImageView android:id="@+id/presence_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="5dip"
- android:gravity="center"
- android:scaleType="centerInside"
- />
-
- <ImageView android:id="@+id/action_icon"
- android:layout_width="30dip"
- android:layout_height="30dip"
- android:layout_marginLeft="14dip"
- android:layout_marginRight="14dip"
- android:gravity="center"
- android:scaleType="centerInside"
- />
-
- <View android:id="@+id/divider"
- android:layout_width="1px"
- android:layout_height="match_parent"
- android:layout_marginTop="5dip"
- android:layout_marginBottom="5dip"
- android:background="@drawable/divider_vertical_dark"
- />
-
- <ImageView android:id="@+id/secondary_action_button"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_centerVertical="true"
- android:paddingLeft="14dip"
- android:paddingRight="14dip"
- android:gravity="center"
- android:scaleType="center"
- android:background="@android:drawable/list_selector_background"
- />
-
-</LinearLayout>
diff --git a/res/layout-finger/list_section.xml b/res/layout-finger/list_section.xml
deleted file mode 100644
index f7ba693..0000000
--- a/res/layout-finger/list_section.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?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.
--->
-
-<!-- Layout used for list section separators. -->
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="25dip"
- android:background="@*android:drawable/dark_header"
- >
- <TextView
- android:id="@+id/header_text"
- android:layout_width="56dip"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_alignParentLeft="true"
- android:textStyle="bold"
- android:textColor="@*android:color/dim_foreground_dark"
- android:textSize="14sp"
- android:gravity="center"
- />
-</RelativeLayout>
diff --git a/res/layout-finger/preference_with_more_button.xml b/res/layout-finger/preference_with_more_button.xml
deleted file mode 100644
index 93b8d5b..0000000
--- a/res/layout-finger/preference_with_more_button.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?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/preference"
- android:layout_width="match_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"
- >
-
- <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/quickcontact.xml b/res/layout-finger/quickcontact.xml
deleted file mode 100644
index 340ca3f..0000000
--- a/res/layout-finger/quickcontact.xml
+++ /dev/null
@@ -1,150 +0,0 @@
-<?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.
--->
-
-<view
- xmlns:android="http://schemas.android.com/apk/res/android"
- class="com.android.contacts.ui.QuickContactWindow$RootLayout"
- android:id="@+id/root"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="@dimen/quickcontact_shadow_horiz"
- android:paddingRight="@dimen/quickcontact_shadow_horiz"
- android:background="@drawable/quickcontact_drop_shadow">
-
- <FrameLayout
- android:id="@+id/header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dip">
-
- <ViewStub
- android:id="@+id/header_small"
- android:inflatedId="@+id/header_small"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout="@layout/quickcontact_header_small" />
-
- <ViewStub
- android:id="@+id/header_medium"
- android:inflatedId="@+id/header_medium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout="@layout/quickcontact_header_med" />
-
- <ViewStub
- android:id="@+id/header_large"
- android:inflatedId="@+id/header_large"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout="@layout/quickcontact_header_large" />
-
- </FrameLayout>
-
- <HorizontalScrollView
- android:id="@+id/scroll"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/header"
- android:fadingEdgeLength="0dip"
- android:background="@drawable/quickcontact_slider_background"
- android:scrollbars="none">
-
- <LinearLayout
- android:id="@+id/quickcontact"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="4dip"
- android:paddingBottom="4dip"
- android:orientation="horizontal">
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/quickcontact_slider_grip_left" />
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/quickcontact_slider_grip_right" />
-
- </LinearLayout>
-
- </HorizontalScrollView>
-
- <FrameLayout
- android:id="@+id/footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/scroll"
- android:background="@drawable/quickcontact_bottom_frame" />
-
- <LinearLayout
- android:id="@+id/footer_disambig"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/scroll"
- android:background="@drawable/quickcontact_disambig_bottom_bg"
- android:orientation="vertical"
- android:visibility="gone">
-
- <ListView
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:background="@color/quickcontact_disambig"
- android:divider="@drawable/quickcontact_disambig_divider"
- android:cacheColorHint="@null" />
-
- <CheckBox
- android:id="@android:id/checkbox"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="19dip"
- android:layout_marginRight="19dip"
- android:minHeight="60dip"
- android:textColor="#f000"
- android:textStyle="bold"
- android:text="@string/quickcontact_remember_choice"
- android:textAppearance="?android:attr/textAppearanceSmallInverse"
- android:button="@drawable/quickcontact_disambig_checkbox" />
-
- </LinearLayout>
-
- <ImageView
- android:id="@+id/arrow_up"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/quickcontact_arrow_up" />
-
- <ImageView
- android:id="@+id/arrow_down"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="-1dip"
- android:layout_below="@id/footer"
- android:src="@drawable/quickcontact_arrow_down" />
-
- <ImageView
- android:id="@+id/arrow_down_stub"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="-1dip"
- android:layout_below="@id/footer_disambig"
- android:visibility="invisible"
- android:src="@drawable/quickcontact_arrow_down" />
-
-</view>
diff --git a/res/layout-finger/quickcontact_header_large.xml b/res/layout-finger/quickcontact_header_large.xml
deleted file mode 100644
index d9d4875..0000000
--- a/res/layout-finger/quickcontact_header_large.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?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/header_large"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="87dip"
- android:background="@drawable/quickcontact_top_frame"
- android:gravity="center_vertical"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/photo"
- android:layout_width="54dip"
- android:layout_height="57dip"
- android:layout_marginLeft="15dip"
- android:background="@drawable/quickcontact_photo_frame" />
-
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginLeft="15dip"
- android:paddingRight="8dip"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@*android:color/primary_text_light"
- android:textStyle="bold"
- android:textSize="18dip" />
-
- <TextView
- android:id="@+id/status"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@*android:color/secondary_text_light"
- android:textSize="15dip"
- android:layout_marginTop="-3dip" />
-
- <TextView
- android:id="@+id/timestamp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@*android:color/secondary_text_light"
- android:textSize="12dip"
- android:layout_marginTop="-2dip" />
-
- </LinearLayout>
-
- <ImageView
- android:id="@+id/presence"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="15dip"
- android:src="@drawable/quickcontact_slider_presence_active"
- android:scaleType="centerInside" />
-
-</LinearLayout>
diff --git a/res/layout-finger/quickcontact_header_med.xml b/res/layout-finger/quickcontact_header_med.xml
deleted file mode 100644
index bed886d..0000000
--- a/res/layout-finger/quickcontact_header_med.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?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/header_medium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="51dip"
- android:background="@drawable/quickcontact_top_frame"
- android:gravity="center_vertical"
- android:orientation="horizontal">
-
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginLeft="15dip"
- android:layout_marginRight="15dip"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/status"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@*android:color/primary_text_light"
- android:textSize="15sp" />
-
- <TextView
- android:id="@+id/timestamp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@*android:color/secondary_text_light"
- android:textSize="12sp"
- android:layout_marginTop="-2dip" />
-
- </LinearLayout>
-
-</LinearLayout>
diff --git a/res/layout-finger/quickcontact_header_small.xml b/res/layout-finger/quickcontact_header_small.xml
deleted file mode 100644
index 9dfe0a1..0000000
--- a/res/layout-finger/quickcontact_header_small.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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/header_small"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/quickcontact_top_frame"
- android:orientation="horizontal" />
diff --git a/res/layout-finger/quickcontact_item.xml b/res/layout-finger/quickcontact_item.xml
deleted file mode 100644
index 8580ac5..0000000
--- a/res/layout-finger/quickcontact_item.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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.
--->
-
-<com.android.contacts.ui.widget.CheckableImageView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="59dip"
- android:layout_height="51dip"
- android:paddingLeft="12dip"
- android:paddingRight="12dip"
- android:paddingTop="8dip"
- android:paddingBottom="8dip"
- android:scaleType="centerInside"
- android:focusable="true"
- android:clickable="true"
- android:background="@drawable/quickcontact_slider_btn" />
diff --git a/res/layout-finger/quickcontact_item_nodata.xml b/res/layout-finger/quickcontact_item_nodata.xml
index 5215e30..5f951e2 100644
--- a/res/layout-finger/quickcontact_item_nodata.xml
+++ b/res/layout-finger/quickcontact_item_nodata.xml
@@ -24,6 +24,5 @@
android:focusable="false"
android:clickable="false"
android:gravity="center_vertical"
- android:background="@drawable/quickcontact_slider_btn_normal"
android:textColor="@android:color/black"
android:text="@string/quickcontact_no_data" />
diff --git a/res/layout-finger/quickcontact_resolve_item.xml b/res/layout-finger/quickcontact_resolve_item.xml
deleted file mode 100755
index db683a0..0000000
--- a/res/layout-finger/quickcontact_resolve_item.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?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="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="30dip"
- android:paddingRight="30dip"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical">
-
- <TextView
- android:id="@android:id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textStyle="bold"
- android:textAppearance="?android:attr/textAppearanceMediumInverse" />
-
- <TextView
- android:id="@android:id/text2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="-4dip"
- android:textAppearance="?android:attr/textAppearanceSmallInverse" />
-
-</LinearLayout>
diff --git a/res/layout-finger/recent_calls_list_child_item.xml b/res/layout-finger/recent_calls_list_child_item.xml
deleted file mode 100644
index 14eb24d..0000000
--- a/res/layout-finger/recent_calls_list_child_item.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?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="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:paddingLeft="7dip"
- android:background="@drawable/list_item_background_secondary"
->
-
- <com.android.contacts.ui.widget.DontPressWithParentImageView android:id="@+id/call_icon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingLeft="14dip"
- android:paddingRight="14dip"
- android:layout_alignParentRight="true"
-
- android:gravity="center_vertical"
- android:src="@android:drawable/sym_action_call"
- android:background="@drawable/call_background_secondary"
- />
-
- <include layout="@layout/recent_calls_list_item_layout"/>
-
-</RelativeLayout>
diff --git a/res/layout-finger/recent_calls_list_group_item.xml b/res/layout-finger/recent_calls_list_group_item.xml
deleted file mode 100644
index 79e7bb9..0000000
--- a/res/layout-finger/recent_calls_list_group_item.xml
+++ /dev/null
@@ -1,125 +0,0 @@
-<?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="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:paddingLeft="7dip"
->
-
- <com.android.contacts.ui.widget.DontPressWithParentImageView android:id="@+id/call_icon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingLeft="14dip"
- android:paddingRight="14dip"
- android:layout_alignParentRight="true"
-
- android:gravity="center_vertical"
- android:src="@android:drawable/sym_action_call"
- android:background="@drawable/call_background"
- />
-
- <View android:id="@+id/divider"
- android:layout_width="1px"
- android:layout_height="match_parent"
- android:layout_marginTop="5dip"
- android:layout_marginBottom="5dip"
- android:layout_toLeftOf="@id/call_icon"
- android:layout_marginLeft="11dip"
- android:background="@drawable/divider_vertical_dark"
- />
-
- <TextView android:id="@+id/date"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toLeftOf="@id/divider"
- android:layout_alignParentBottom="true"
- android:layout_marginBottom="8dip"
- android:layout_marginLeft="10dip"
-
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:singleLine="true"
- />
-
- <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_marginLeft="36dip"
- android:layout_marginRight="5dip"
- android:layout_alignBaseline="@id/date"
-
- 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_toRightOf="@id/label"
- android:layout_toLeftOf="@id/date"
- android:layout_alignBaseline="@id/label"
- android:layout_alignWithParentIfMissing="true"
-
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceSmall"
- />
-
- <TextView android:id="@+id/groupSize"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_toLeftOf="@id/divider"
- android:layout_above="@id/date"
- android:layout_alignWithParentIfMissing="true"
- android:layout_marginBottom="-10dip"
-
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:singleLine="true"
- android:gravity="center_vertical"
- />
-
- <TextView android:id="@+id/line1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:layout_toLeftOf="@+id/groupSize"
- android:layout_above="@id/date"
- android:layout_alignWithParentIfMissing="true"
- android:layout_marginLeft="36dip"
- android:layout_marginBottom="-10dip"
-
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:gravity="center_vertical"
- />
-
- <ImageView
- android:id="@+id/groupIndicator"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_centerVertical="true"
- android:src="@*android:drawable/expander_ic_minimized"
- android:gravity="center_vertical"
- />
-</RelativeLayout>
diff --git a/res/layout-finger/recent_calls_list_item.xml b/res/layout-finger/recent_calls_list_item.xml
deleted file mode 100644
index 8efa23c..0000000
--- a/res/layout-finger/recent_calls_list_item.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?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="match_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:paddingLeft="7dip"
->
-
- <com.android.contacts.ui.widget.DontPressWithParentImageView android:id="@+id/call_icon"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingLeft="14dip"
- android:paddingRight="14dip"
- android:layout_alignParentRight="true"
-
- android:gravity="center_vertical"
- android:src="@android:drawable/sym_action_call"
- android:background="@drawable/call_background"
- />
-
- <include layout="@layout/recent_calls_list_item_layout"/>
-
-</RelativeLayout>
diff --git a/res/layout-finger/search_bar.xml b/res/layout-finger/search_bar.xml
deleted file mode 100644
index d322948..0000000
--- a/res/layout-finger/search_bar.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 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/search_bar"
- android:layout_width="match_parent"
- android:layout_height="64dip"
- android:orientation="vertical"
- android:focusable="true"
- android:descendantFocusability="afterDescendants"
- android:background="@drawable/bg_blk_search_contact">
-
- <!-- Outer layout defines the entire search bar at the top of the screen -->
- <LinearLayout
- android:id="@+id/search_plate"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="4dip"
- android:paddingRight="10dip"
- android:paddingTop="6dip"
- android:paddingBottom="0dip"
- >
-
- <!-- Inner layout contains the app icon, button(s) and EditText -->
- <LinearLayout
- android:id="@+id/search_edit_frame"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_launcher_contacts"
- android:layout_marginRight="7dip"
- android:layout_gravity="center_vertical"
- android:scaleType="centerInside" />
-
- <view
- class="com.android.contacts.SearchEditText"
- android:id="@+id/search_src_text"
- android:layout_height="wrap_content"
- android:layout_width="0dip"
- android:layout_weight="1.0"
- android:layout_marginLeft="4dip"
- android:layout_marginBottom="0dip"
- android:singleLine="true"
- android:ellipsize="end"
- android:inputType="textNoSuggestions"
- android:imeOptions="flagNoExtractUi"
- android:hint="@string/search_bar_hint"
- android:drawableRight="@drawable/magnifying_glass"
- android:freezesText="true"
- />
- </LinearLayout>
-
- </LinearLayout>
-
-</LinearLayout>
diff --git a/res/layout-finger/tab_account_name.xml b/res/layout-finger/tab_account_name.xml
deleted file mode 100644
index 8707d98..0000000
--- a/res/layout-finger/tab_account_name.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
--->
-
-<!-- looks like Widget.TextView.ListSeparator -->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/account_name"
- android:background="@drawable/bg_infobar_new"
- android:layout_width="match_parent"
- android:layout_height="@dimen/account_name_height"
- android:layout_below="@+id/tab_scroll_view"
- android:textStyle="normal"
- android:textColor="@*android:color/dim_foreground_dark"
- android:textSize="12sp"
- android:gravity="left|center_vertical"
- android:paddingLeft="7dip"
-/>
diff --git a/res/layout-finger/tab_layout.xml b/res/layout-finger/tab_layout.xml
deleted file mode 100644
index 42461f6..0000000
--- a/res/layout-finger/tab_layout.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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.
--->
-
-<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/tab_scroll_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:scrollbars="none"
- android:fadingEdgeLength="0dip">
-
- <com.android.contacts.TabStripView
- android:id="@android:id/tabs"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
-</HorizontalScrollView>
\ No newline at end of file
diff --git a/res/layout-land-finger/twelve_key_dialer.xml b/res/layout-land/twelve_key_dialer.xml
similarity index 100%
rename from res/layout-land-finger/twelve_key_dialer.xml
rename to res/layout-land/twelve_key_dialer.xml
diff --git a/res/layout-large/contact_detail_header_view.xml b/res/layout-large/contact_detail_header_view.xml
new file mode 100644
index 0000000..19132dd
--- /dev/null
+++ b/res/layout-large/contact_detail_header_view.xml
@@ -0,0 +1,149 @@
+<?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/banner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingBottom="28dip">
+
+ <TextView
+ android:id="@+id/attribution"
+ android:layout_width="match_parent"
+ android:layout_height="56dip"
+ android:paddingRight="24dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorTertiary"
+ android:gravity="right|center_vertical"
+ android:singleLine="true" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/photo"
+ android:layout_marginLeft="16dip"
+ android:layout_width="96dip"
+ android:layout_height="96dip" />
+
+ <LinearLayout
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:layout_gravity="top">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="@color/contact_detail_header_divider_color" />
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="16dip"
+ android:layout_marginLeft="16dip">
+
+ <!-- Star -->
+ <CheckBox
+ android:id="@+id/star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dip"
+ android:layout_gravity="top"
+ android:contentDescription="@string/description_star"
+ android:visibility="invisible"
+ style="?android:attr/starStyle" />
+
+ <!-- Name, PhoneticName, Directory -->
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dip"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textSize="@dimen/contact_name_text_size" />
+
+ <TextView
+ android:id="@+id/phonetic_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_marginTop="-2dip"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/organization"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-2dip"
+ android:visibility="gone"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorTertiary" />
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+ <!-- Status info -->
+ <LinearLayout
+ android:id="@+id/status_container"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:background="@drawable/statusbox_portrait_holo_light"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="24dip"
+ android:layout_marginTop="10dip"
+ android:paddingLeft="12dip"
+ android:paddingTop="36dip"
+ android:paddingRight="16dip"
+ android:paddingBottom="12dip"
+ android:visibility="gone">
+
+ <TextView
+ android:id="@+id/status"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="3"
+ android:ellipsize="end"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/status_date"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary"
+ android:gravity="right"
+ android:visibility="gone" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/layout-long-land-finger/twelve_key_dialer.xml b/res/layout-long-land/twelve_key_dialer.xml
similarity index 100%
rename from res/layout-long-land-finger/twelve_key_dialer.xml
rename to res/layout-long-land/twelve_key_dialer.xml
diff --git a/res/layout-long-finger/dialpad.xml b/res/layout-long/dialpad.xml
similarity index 100%
rename from res/layout-long-finger/dialpad.xml
rename to res/layout-long/dialpad.xml
diff --git a/res/layout-long-finger/twelve_key_dialer.xml b/res/layout-long/twelve_key_dialer.xml
similarity index 100%
rename from res/layout-long-finger/twelve_key_dialer.xml
rename to res/layout-long/twelve_key_dialer.xml
diff --git a/res/layout-long-finger/voicemail_dial_delete.xml b/res/layout-long/voicemail_dial_delete.xml
similarity index 100%
rename from res/layout-long-finger/voicemail_dial_delete.xml
rename to res/layout-long/voicemail_dial_delete.xml
diff --git a/res/layout-xlarge-land/contact_detail_header_view.xml b/res/layout-xlarge-land/contact_detail_header_view.xml
new file mode 100644
index 0000000..fb57202
--- /dev/null
+++ b/res/layout-xlarge-land/contact_detail_header_view.xml
@@ -0,0 +1,158 @@
+<?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/banner"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingBottom="28dip">
+
+ <TextView
+ android:id="@+id/attribution"
+ android:layout_width="match_parent"
+ android:layout_height="56dip"
+ android:paddingRight="16dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorTertiary"
+ android:gravity="right|center_vertical"
+ android:singleLine="true" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/photo"
+ android:layout_marginLeft="-1dip"
+ android:layout_width="96dip"
+ android:layout_height="96dip" />
+
+ <LinearLayout
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="top"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="@color/contact_detail_header_divider_color" />
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="16dip"
+ android:layout_marginLeft="16dip">
+
+ <!-- Star -->
+ <CheckBox
+ android:id="@+id/star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dip"
+ android:layout_gravity="top"
+ android:contentDescription="@string/description_star"
+ android:visibility="invisible"
+ style="?android:attr/starStyle" />
+
+ <!-- Name, PhoneticName, Directory -->
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dip"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textSize="@dimen/contact_name_text_size" />
+
+ <TextView
+ android:id="@+id/phonetic_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_marginTop="-2dip"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/organization"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_marginTop="-2dip"
+ android:visibility="gone"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorTertiary" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <!-- Status info -->
+ <LinearLayout
+ android:id="@+id/status_container"
+ android:layout_width="0px"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:background="@drawable/statusbox_landscape_holo_light"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="30dip"
+ android:paddingLeft="52dip"
+ android:paddingTop="12dip"
+ android:paddingRight="16dip"
+ android:paddingBottom="12dip"
+ android:visibility="gone">
+
+ <TextView
+ android:id="@+id/status"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="3"
+ android:ellipsize="end"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/status_date"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary"
+ android:gravity="right"
+ android:visibility="gone" />
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/layout-xlarge-land/contact_detail_list_item.xml b/res/layout-xlarge-land/contact_detail_list_item.xml
new file mode 100644
index 0000000..529059a
--- /dev/null
+++ b/res/layout-xlarge-land/contact_detail_list_item.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2010, 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="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- Longer separating line (between kinds) -->
+ <View
+ android:id="@+id/kind_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="@drawable/list_item_divider_holo" />
+
+ <!-- Shorter separating line if there was no kind-separator -->
+ <View
+ android:id="@+id/in_kind_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:layout_marginLeft="164dip"
+ android:background="@drawable/list_item_divider_holo" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:minHeight="@dimen/detail_min_line_item_height">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_gravity="center_vertical">
+ <TextView
+ android:id="@+id/kind"
+ android:layout_width="164dip"
+ android:layout_height="@dimen/detail_min_line_item_height"
+ android:paddingLeft="19dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:gravity="center_vertical" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:paddingLeft="12dip"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/data"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ <TextView
+ android:id="@+id/footer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:visibility="gone" />
+ </LinearLayout>
+ <TextView
+ android:id="@+id/type"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/detail_min_line_item_height"
+ android:maxWidth="150dip"
+ android:gravity="right|center_vertical"
+ android:textColor="@color/detail_item_type_color"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <ImageView
+ android:id="@+id/presence_icon"
+ android:layout_width="32dip"
+ android:layout_height="@dimen/detail_min_line_item_height"
+ android:layout_marginLeft="5dip"
+ android:gravity="center"
+ android:scaleType="centerInside" />
+
+ <ImageView
+ android:id="@+id/action_icon"
+ android:layout_width="32dip"
+ android:layout_height="@dimen/detail_min_line_item_height"
+ android:layout_marginLeft="16dip"
+ android:layout_gravity="center_vertical"
+ android:scaleType="centerInside" />
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="1px"
+ android:layout_height="match_parent"
+ android:layout_marginTop="5dip"
+ android:layout_marginBottom="5dip"
+ android:layout_marginLeft="14dip"
+ android:layout_marginRight="14dip"
+ android:background="?android:attr/dividerVertical" />
+
+ <ImageView
+ android:id="@+id/secondary_action_button"
+ android:layout_width="32dip"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ android:scaleType="center"
+ android:background="?android:attr/selectableItemBackground" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <!-- Longer separating line (only for the last row) -->
+ <View
+ android:id="@+id/line_below_last"
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="@drawable/list_item_divider_holo" />
+</LinearLayout>
diff --git a/res/layout-xlarge/aggregation_suggestions.xml b/res/layout-xlarge/aggregation_suggestions.xml
new file mode 100644
index 0000000..3a314b6
--- /dev/null
+++ b/res/layout-xlarge/aggregation_suggestions.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2010, 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="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:background="@drawable/aggregation_suggestions_bg_light_holo"
+ android:paddingBottom="10dip">
+ <LinearLayout
+ android:id="@+id/aggregation_suggestions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+</LinearLayout>
diff --git a/res/layout-xlarge/aggregation_suggestions_item.xml b/res/layout-xlarge/aggregation_suggestions_item.xml
new file mode 100644
index 0000000..f12c608
--- /dev/null
+++ b/res/layout-xlarge/aggregation_suggestions_item.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2010, 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"
+ class="com.android.contacts.editor.AggregationSuggestionView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingLeft="5dip"
+ android:paddingRight="15dip"
+>
+ <ImageView
+ android:id="@+id/aggregation_suggestion_photo"
+ android:layout_width="@dimen/aggregation_suggestion_icon_size"
+ android:layout_height="@dimen/aggregation_suggestion_icon_size"
+ android:layout_alignParentLeft="true"
+ android:layout_centerInParent="true"
+ android:layout_marginTop="4dip"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView
+ android:id="@+id/aggregation_suggestion_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/aggregation_suggestion_photo"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="4dip"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorSecondary"
+ />
+
+ <TextView
+ android:id="@+id/aggregation_suggestion_data"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/aggregation_suggestion_photo"
+ android:layout_below="@id/aggregation_suggestion_name"
+ android:layout_marginLeft="10dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorSecondary"
+ />
+</view>
diff --git a/res/layout-xlarge/contact_browser.xml b/res/layout-xlarge/contact_browser.xml
new file mode 100644
index 0000000..997ed21
--- /dev/null
+++ b/res/layout-xlarge/contact_browser.xml
@@ -0,0 +1,99 @@
+<?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.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.android.contacts.widget.InterpolatingLayout
+ android:id="@+id/main_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="8dip"
+ android:splitMotionEvents="true">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:minWidth="100dip"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowWidth="310dip"
+ ex:layout_wideParentWidth="1280dip"
+ ex:layout_wideWidth="430dip"
+ android:background="@drawable/list_background_holo">
+
+ <View
+ style="@style/SectionDivider"
+ android:layout_marginLeft="40dip"
+ android:layout_marginTop="24dip" />
+
+ <fragment
+ android:id="@+id/list_fragment"
+ class="com.android.contacts.list.DefaultContactBrowseListFragment"
+ android:layout_height="0dip"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ />
+ </LinearLayout>
+
+ <view
+ class="com.android.contacts.widget.TransitionAnimationView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowMarginRight="0dip"
+ ex:layout_wideParentWidth="1280dip"
+ ex:layout_wideMarginRight="48dip"
+ ex:clipMarginLeft="0dip"
+ ex:clipMarginTop="3dip"
+ ex:clipMarginRight="3dip"
+ ex:clipMarginBottom="9dip"
+ ex:enterAnimation="@android:animator/fade_in"
+ ex:exitAnimation="@android:animator/fade_out"
+ ex:animationDuration="200">
+ <fragment
+ android:id="@+id/detail_fragment"
+ class="com.android.contacts.detail.ContactDetailFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+ </view>
+
+ </com.android.contacts.widget.InterpolatingLayout>
+
+ <com.android.contacts.widget.InterpolatingLayout
+ android:id="@+id/contacts_unavailable_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone">
+
+ <FrameLayout
+ android:id="@+id/contacts_unavailable_container"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowMarginLeft="80dip"
+ ex:layout_narrowMarginRight="80dip"
+ ex:layout_wideParentWidth="1280dip"
+ ex:layout_wideMarginLeft="200dip"
+ ex:layout_wideMarginRight="200dip"
+ android:paddingBottom="20dip" />
+
+ </com.android.contacts.widget.InterpolatingLayout>
+</FrameLayout>
diff --git a/res/layout-xlarge/contact_detail_fragment.xml b/res/layout-xlarge/contact_detail_fragment.xml
new file mode 100644
index 0000000..60d3cd5
--- /dev/null
+++ b/res/layout-xlarge/contact_detail_fragment.xml
@@ -0,0 +1,96 @@
+<?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"
+ xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+ android:id="@+id/contact_detail"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/panel_content">
+
+ <!-- Header View (including social status) -->
+ <com.android.contacts.widget.InterpolatingLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
+ <com.android.contacts.detail.ContactDetailHeaderView
+ android:id="@+id/contact_header_widget"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="28dip"
+ ex:layout_wideParentWidth="800dip"
+ ex:layout_wideMarginLeft="80dip"
+ ex:layout_wideMarginRight="48dip"
+ ex:layout_narrowParentWidth="500dip"
+ ex:layout_narrowMarginLeft="15dip"
+ ex:layout_narrowMarginRight="16dip"
+ />
+ </com.android.contacts.widget.InterpolatingLayout>
+
+ <!-- Placeholder for empty list -->
+ <com.android.contacts.widget.InterpolatingLayout
+ android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <TextView android:id="@+id/emptyText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/no_contact_details"
+ android:textSize="20sp"
+ android:textColor="?android:attr/textColorSecondary"
+ ex:layout_wideParentWidth="800dip"
+ ex:layout_wideMarginLeft="64dip"
+ ex:layout_narrowParentWidth="500dip"
+ ex:layout_narrowMarginLeft="42dip"
+ android:paddingTop="10dip"
+ android:lineSpacingMultiplier="0.92"
+ />
+ </com.android.contacts.widget.InterpolatingLayout>
+
+ <!-- Real list -->
+ <com.android.contacts.widget.InterpolatingLayout
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1">
+ <ListView android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ ex:layout_wideParentWidth="800dip"
+ ex:layout_wideMarginLeft="64dip"
+ ex:layout_widePaddingRight="48dip"
+ ex:layout_narrowParentWidth="500dip"
+ ex:layout_narrowMarginLeft="32dip"
+ ex:layout_narrowPaddingRight="16dip"
+ android:cacheColorHint="#00000000"
+ android:divider="@android:color/transparent"
+ />
+ </com.android.contacts.widget.InterpolatingLayout>
+
+ <!-- "Copy to my contacts"- button -->
+ <Button
+ android:id="@+id/copyLocal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/menu_copyContact"
+ android:visibility="gone"
+ android:layout_gravity="right"
+ android:layout_marginRight="40dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginBottom="10dip" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-xlarge/contact_editor_activity.xml b/res/layout-xlarge/contact_editor_activity.xml
new file mode 100644
index 0000000..b9f98a3
--- /dev/null
+++ b/res/layout-xlarge/contact_editor_activity.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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"
+ xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+ <com.android.contacts.widget.InterpolatingLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <fragment class="com.android.contacts.editor.ContactEditorFragment"
+ android:id="@+id/contact_editor_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ ex:layout_wideParentWidth="1280dip"
+ ex:layout_wideMarginLeft="160dip"
+ ex:layout_wideMarginRight="160dip"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowMarginLeft="0dip"
+ ex:layout_narrowMarginRight="0dip"
+ />
+ </com.android.contacts.widget.InterpolatingLayout>
+</ScrollView>
diff --git a/res/layout-xlarge/contact_editor_fragment.xml b/res/layout-xlarge/contact_editor_fragment.xml
new file mode 100644
index 0000000..0d01214
--- /dev/null
+++ b/res/layout-xlarge/contact_editor_fragment.xml
@@ -0,0 +1,25 @@
+<?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"
+ xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+ android:id="@+id/editors"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/panel_message"
+ android:orientation="vertical"
+/>
diff --git a/res/layout-xlarge/contact_picker_content.xml b/res/layout-xlarge/contact_picker_content.xml
new file mode 100644
index 0000000..225b725
--- /dev/null
+++ b/res/layout-xlarge/contact_picker_content.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="2dip"
+ android:layout_marginTop="1dip"
+ android:layout_marginBottom="1dip"
+ android:background="#7e7e87" />
+
+ <view
+ class="com.android.contacts.list.ContactEntryListView"
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:fastScrollEnabled="true"
+ android:layout_weight="1"/>
+
+ <ViewStub
+ android:id="@+id/footer_stub"
+ android:layout="@layout/footer_panel"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/res/layout-xlarge/external_raw_contact_editor_view.xml b/res/layout-xlarge/external_raw_contact_editor_view.xml
new file mode 100644
index 0000000..0304c48
--- /dev/null
+++ b/res/layout-xlarge/external_raw_contact_editor_view.xml
@@ -0,0 +1,142 @@
+<?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.
+-->
+
+<com.android.contacts.editor.ExternalRawContactEditorView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include
+ layout="@layout/raw_contact_editor_header"
+ android:id="@+id/header" />
+
+ <LinearLayout
+ android:id="@+id/body"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="10dip">
+
+ <com.android.contacts.widget.InterpolatingLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <FrameLayout
+ android:id="@+id/stub_photo"
+ android:layout_width="96dip"
+ android:layout_height="96dip"
+ android:layout_gravity="top|left"
+ ex:layout_wideParentWidth="960dip"
+ ex:layout_wideMarginLeft="96dip"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowMarginLeft="15dip">
+
+ <include
+ android:id="@+id/edit_photo"
+ layout="@layout/item_photo_editor" />
+
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ ex:layout_wideParentWidth="960dip"
+ ex:layout_wideMarginRight="48dip"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowMarginRight="15dip"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="@color/contact_detail_header_divider_color" />
+
+ <TextView
+ android:id="@+id/read_only_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="64dip"
+ android:layout_marginRight="48dip"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="4dip"
+ android:paddingRight="?android:attr/scrollbarSize"
+ android:textSize="@dimen/contact_name_text_size" />
+ </LinearLayout>
+ </com.android.contacts.widget.InterpolatingLayout>
+
+ <com.android.contacts.widget.InterpolatingLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ ex:layout_wideParentWidth="960dip"
+ ex:layout_wideMarginLeft="96dip"
+ ex:layout_wideMarginRight="96dip"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowMarginLeft="15dip"
+ ex:layout_narrowMarginRight="15dip">
+
+ <TextView
+ android:id="@+id/read_only_warning"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="13dip"
+ android:layout_marginBottom="13dip"
+ android:layout_marginLeft="13dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimary"
+ android:drawableLeft="?android:attr/alertDialogIcon"
+ android:drawablePadding="10dip"
+ android:gravity="center_vertical" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="?android:attr/listDivider" />
+
+ <LinearLayout
+ android:id="@+id/sect_general"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" />
+ </LinearLayout>
+ </com.android.contacts.widget.InterpolatingLayout>
+
+ <com.android.contacts.widget.InterpolatingLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:layout_marginBottom="42dip">
+ <Button
+ android:id="@+id/button_edit_externally"
+ android:text="@string/edit_contact"
+ ex:layout_wideParentWidth="960dip"
+ ex:layout_wideMarginLeft="246dip"
+ ex:layout_wideMarginRight="156dip"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowMarginLeft="165dip"
+ ex:layout_narrowMarginRight="121dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </com.android.contacts.widget.InterpolatingLayout>
+ </LinearLayout>
+</com.android.contacts.editor.ExternalRawContactEditorView>
diff --git a/res/layout-xlarge/item_group_membership.xml b/res/layout-xlarge/item_group_membership.xml
new file mode 100644
index 0000000..41d77b7
--- /dev/null
+++ b/res/layout-xlarge/item_group_membership.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<!-- the body surrounding all editors for a specific kind -->
+
+<com.android.contacts.editor.GroupMembershipView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/editor_min_line_item_height"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/kind_title"
+ android:layout_width="150dip"
+ android:layout_height="@dimen/editor_min_line_item_height"
+ android:gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ android:textColor="@color/editor_label_text_color"
+ android:ellipsize="marquee" />
+
+ <Button
+ style="?android:attr/spinnerStyle"
+ android:id="@+id/group_list"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="left|center_vertical"
+ android:ellipsize="end"
+ android:focusable="true"
+ android:paddingLeft="18dip"
+ />
+
+ <!-- Plus/Minus button only for layout. This makes the editor lay out nicely with the other fields -->
+ <ImageButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/editor_round_button_padding_left"
+ android:paddingRight="@dimen/editor_round_button_padding_right"
+ android:paddingTop="@dimen/editor_round_button_padding_top"
+ android:paddingBottom="@dimen/editor_round_button_padding_bottom"
+ android:src="@drawable/ic_menu_add_field_holo_light"
+ android:background="@null"
+ android:visibility="invisible" />
+ <ImageButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/editor_round_button_padding_left"
+ android:paddingRight="@dimen/editor_round_button_padding_right"
+ android:paddingTop="@dimen/editor_round_button_padding_top"
+ android:paddingBottom="@dimen/editor_round_button_padding_bottom"
+ android:src="@drawable/ic_menu_remove_field_holo_light"
+ android:background="@null"
+ android:visibility="invisible" />
+</com.android.contacts.editor.GroupMembershipView>
diff --git a/res/layout-xlarge/item_kind_section.xml b/res/layout-xlarge/item_kind_section.xml
new file mode 100644
index 0000000..57de26d
--- /dev/null
+++ b/res/layout-xlarge/item_kind_section.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<!-- the body surrounding all editors for a specific kind -->
+
+<com.android.contacts.editor.KindSectionView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/kind_title"
+ android:layout_width="150dip"
+ android:layout_height="@dimen/editor_min_line_item_height"
+ android:gravity="center_vertical"
+ android:textColor="#7F7F7F"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ android:ellipsize="marquee" />
+
+ <LinearLayout
+ android:id="@+id/kind_editors"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="bottom"
+ android:orientation="vertical" />
+
+ <FrameLayout
+ android:id="@+id/kind_plus_container"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/editor_min_line_item_height">
+ <ImageButton
+ android:id="@+id/kind_plus"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingLeft="@dimen/editor_round_button_padding_left"
+ android:paddingRight="@dimen/editor_round_button_padding_right"
+ android:paddingTop="@dimen/editor_round_button_padding_top"
+ android:paddingBottom="@dimen/editor_round_button_padding_bottom"
+ android:background="?android:attr/selectableItemBackground"
+ android:src="@drawable/ic_menu_add_field_holo_light"
+ android:contentDescription="@string/description_plus_button" />
+ </FrameLayout>
+</com.android.contacts.editor.KindSectionView>
diff --git a/res/layout-xlarge/list_section.xml b/res/layout-xlarge/list_section.xml
new file mode 100644
index 0000000..c684a0f
--- /dev/null
+++ b/res/layout-xlarge/list_section.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Layout used for list section separators. -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="37dip"
+ android:background="@drawable/section_header_holo"
+ >
+ <TextView
+ android:id="@+id/header_text"
+ android:layout_width="56dip"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true"
+ android:textStyle="bold"
+ android:textColor="@color/section_header_text_color"
+ android:textSize="14sp"
+ android:gravity="center"
+ />
+</RelativeLayout>
diff --git a/res/layout-xlarge/raw_contact_editor_header.xml b/res/layout-xlarge/raw_contact_editor_header.xml
new file mode 100644
index 0000000..a973464
--- /dev/null
+++ b/res/layout-xlarge/raw_contact_editor_header.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+
+<!-- Account info header -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="64dip"
+ android:layout_width="match_parent"
+ android:background="?android:attr/selectableItemBackground">
+
+ <ImageView
+ android:id="@+id/header_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/header_color_bar" />
+
+ <TextView
+ android:id="@+id/header_account_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toLeftOf="@+id/header_icon"
+ android:layout_alignTop="@id/header_icon"
+ android:layout_marginTop="-4dip"
+
+ android:textSize="24sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true" />
+
+ <TextView
+ android:id="@+id/header_account_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toLeftOf="@+id/header_icon"
+ android:layout_alignBottom="@+id/header_icon"
+ android:layout_marginBottom="2dip"
+
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true" />
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:layout_alignParentBottom="true"
+ android:background="?android:attr/listDivider"
+ android:visibility="gone" />
+
+</RelativeLayout>
diff --git a/res/layout-xlarge/raw_contact_editor_view.xml b/res/layout-xlarge/raw_contact_editor_view.xml
new file mode 100644
index 0000000..044a5ea
--- /dev/null
+++ b/res/layout-xlarge/raw_contact_editor_view.xml
@@ -0,0 +1,137 @@
+<?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.
+-->
+
+<com.android.contacts.editor.RawContactEditorView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include
+ layout="@layout/raw_contact_editor_header"
+ android:id="@+id/header" />
+
+ <LinearLayout
+ android:id="@+id/body"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="10dip">
+
+ <com.android.contacts.widget.InterpolatingLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <FrameLayout
+ android:id="@+id/stub_photo"
+ android:layout_width="96dip"
+ android:layout_height="96dip"
+ android:layout_gravity="top|left"
+ ex:layout_wideParentWidth="960dip"
+ ex:layout_wideMarginLeft="96dip"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowMarginLeft="15dip">
+
+ <include
+ android:id="@+id/edit_photo"
+ layout="@layout/item_photo_editor" />
+
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ ex:layout_wideParentWidth="960dip"
+ ex:layout_wideMarginRight="48dip"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowMarginRight="15dip"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="@color/contact_detail_header_divider_color" />
+
+ <com.android.contacts.editor.TextFieldsEditorView
+ android:id="@+id/edit_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingRight="?android:attr/scrollbarSize"
+ android:layout_marginLeft="52dip"
+ android:layout_marginRight="48dip"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="4dip" />
+
+ <ViewStub
+ android:id="@+id/aggregation_suggestion_stub"
+ android:inflatedId="@+id/aggregation_suggestion"
+ android:layout="@layout/aggregation_suggestions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="visible" />
+ </LinearLayout>
+ </com.android.contacts.widget.InterpolatingLayout>
+
+ <com.android.contacts.widget.InterpolatingLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ ex:layout_wideParentWidth="960dip"
+ ex:layout_wideMarginLeft="96dip"
+ ex:layout_wideMarginRight="48dip"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowMarginLeft="15dip"
+ ex:layout_narrowMarginRight="15dip">
+
+ <LinearLayout
+ android:id="@+id/sect_fields"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingRight="?android:attr/scrollbarSize" />
+ </LinearLayout>
+ </com.android.contacts.widget.InterpolatingLayout>
+
+ <com.android.contacts.widget.InterpolatingLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:layout_marginBottom="42dip">
+ <Button
+ android:id="@+id/button_add_field"
+ android:text="@string/add_field"
+ ex:layout_wideParentWidth="960dip"
+ ex:layout_wideMarginLeft="246dip"
+ ex:layout_wideMarginRight="156dip"
+ ex:layout_narrowParentWidth="800dip"
+ ex:layout_narrowMarginLeft="165dip"
+ ex:layout_narrowMarginRight="121dip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </com.android.contacts.widget.InterpolatingLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:layout_alignParentBottom="true"
+ android:background="?android:attr/listDivider" />
+ </LinearLayout>
+</com.android.contacts.editor.RawContactEditorView>
diff --git a/res/layout-xlarge/search_header.xml b/res/layout-xlarge/search_header.xml
new file mode 100644
index 0000000..e543883
--- /dev/null
+++ b/res/layout-xlarge/search_header.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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"
+ style="DirectoryHeader"
+ android:layout_width="match_parent"
+ android:layout_height="56dip">
+ <TextView
+ android:id="@+id/totalContactsText"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true"
+ android:layout_marginLeft="8dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textStyle="bold" />
+
+ <ProgressBar
+ android:id="@+id/progress"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/progressBarStyleSmall"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:layout_marginRight="10dip" />
+
+</RelativeLayout>
diff --git a/res/layout-xlarge/total_contacts.xml b/res/layout-xlarge/total_contacts.xml
new file mode 100644
index 0000000..8c05119
--- /dev/null
+++ b/res/layout-xlarge/total_contacts.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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="match_parent"
+ android:layout_height="47dip"
+ android:paddingLeft="5dp"
+ android:paddingRight="7dp"
+ >
+
+ <TextView
+ android:id="@+id/totalContactsText"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:textColor="#ff666666"
+ android:textSize="14sp"
+ android:textStyle="normal"
+ android:layout_alignParentLeft="true"
+ android:gravity="center_vertical"
+ />
+
+</RelativeLayout>
diff --git a/res/layout/account_selector_list_item.xml b/res/layout/account_selector_list_item.xml
new file mode 100644
index 0000000..38acfc5
--- /dev/null
+++ b/res/layout/account_selector_list_item.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+>
+ <ImageView android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:layout_centerVertical="true"
+ />
+
+ <TextView android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="6dip"
+ android:layout_marginTop="6dip"
+ android:layout_toRightOf="@android:id/icon"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ />
+
+ <TextView android:id="@android:id/text2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/text1"
+ android:layout_alignLeft="@android:id/text1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+</RelativeLayout>
diff --git a/res/layout/act_edit.xml b/res/layout/act_edit.xml
deleted file mode 100644
index 2a71717..0000000
--- a/res/layout/act_edit.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?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="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
->
-
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="1px"
- android:layout_weight="1"
- android:fillViewport="true"
- >
-
- <LinearLayout android:id="@+id/editors"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- />
-
- </ScrollView>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- style="@android:style/ButtonBar"
- >
-
- <Button android:id="@+id/btn_done"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/menu_done"
- />
-
- <Button android:id="@+id/btn_discard"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/menu_doNotSave"
- />
-
- </LinearLayout>
-
-</LinearLayout>
diff --git a/res/layout/add_contact_menu_item.xml b/res/layout/add_contact_menu_item.xml
new file mode 100644
index 0000000..1bdbcac
--- /dev/null
+++ b/res/layout/add_contact_menu_item.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:divider="?android:attr/dividerVertical"
+ android:showDividers="end"
+ android:dividerPadding="12dip"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ style="?android:attr/actionButtonStyle"
+ android:id="@+id/menu_item"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="8dip"
+ android:src="@drawable/ic_menu_add_contact_holo_light"
+ android:description="@string/menu_newContact" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/menu_new_contact_action_bar" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/aggregation_suggestions.xml b/res/layout/aggregation_suggestions.xml
new file mode 100644
index 0000000..3b9d278
--- /dev/null
+++ b/res/layout/aggregation_suggestions.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2010, 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="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:background="@drawable/aggregation_suggestions_bg">
+ <LinearLayout
+ android:id="@+id/aggregation_suggestions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ />
+</LinearLayout>
diff --git a/res/layout/aggregation_suggestions_item.xml b/res/layout/aggregation_suggestions_item.xml
new file mode 100644
index 0000000..f2477cb
--- /dev/null
+++ b/res/layout/aggregation_suggestions_item.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2010, 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"
+ class="com.android.contacts.editor.AggregationSuggestionView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingLeft="5dip"
+ android:paddingRight="15dip"
+>
+
+ <ImageView
+ android:id="@+id/aggregation_suggestion_photo"
+ android:layout_width="@dimen/aggregation_suggestion_icon_size"
+ android:layout_height="@dimen/aggregation_suggestion_icon_size"
+ android:layout_alignParentLeft="true"
+ android:layout_centerInParent="true"
+ android:layout_marginTop="4dip"
+ android:scaleType="fitCenter"
+ />
+
+ <TextView
+ android:id="@+id/aggregation_suggestion_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/aggregation_suggestion_photo"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="4dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ />
+
+ <TextView
+ android:id="@+id/aggregation_suggestion_data"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/aggregation_suggestion_photo"
+ android:layout_below="@id/aggregation_suggestion_name"
+ android:layout_marginLeft="10dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ />
+</view>
diff --git a/res/layout-finger/call_detail.xml b/res/layout/call_detail.xml
similarity index 100%
rename from res/layout-finger/call_detail.xml
rename to res/layout/call_detail.xml
diff --git a/res/layout-finger/call_detail_list_item.xml b/res/layout/call_detail_list_item.xml
similarity index 100%
rename from res/layout-finger/call_detail_list_item.xml
rename to res/layout/call_detail_list_item.xml
diff --git a/res/layout/contact_browser.xml b/res/layout/contact_browser.xml
new file mode 100644
index 0000000..0b4bb63
--- /dev/null
+++ b/res/layout/contact_browser.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+</FrameLayout>
diff --git a/res/layout/contact_detail_activity.xml b/res/layout/contact_detail_activity.xml
new file mode 100644
index 0000000..9408f89
--- /dev/null
+++ b/res/layout/contact_detail_activity.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <fragment class="com.android.contacts.detail.ContactDetailFragment"
+ android:id="@+id/contact_detail_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</FrameLayout>
diff --git a/res/layout/contact_detail_fragment.xml b/res/layout/contact_detail_fragment.xml
new file mode 100644
index 0000000..90a075a
--- /dev/null
+++ b/res/layout/contact_detail_fragment.xml
@@ -0,0 +1,66 @@
+<?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/contact_detail"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.android.contacts.detail.ContactDetailHeaderView
+ android:id="@+id/contact_header_widget"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <ListView android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1"
+ android:background="@drawable/title_bar_shadow"
+ />
+
+ <ScrollView android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1"
+ android:visibility="gone"
+ >
+ <TextView android:id="@+id/emptyText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/no_contact_details"
+ android:textSize="20sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ android:paddingTop="10dip"
+ android:lineSpacingMultiplier="0.92"
+ />
+ </ScrollView>
+
+ <!-- "Copy to my contacts"- button -->
+ <Button
+ android:id="@+id/copyLocal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/menu_copyContact"
+ android:visibility="gone"
+ android:layout_gravity="right"
+ android:layout_marginRight="40dip"
+ android:layout_marginTop="20dip"
+ android:layout_marginBottom="20dip" />
+</LinearLayout>
+
diff --git a/res/layout/contact_detail_header_view.xml b/res/layout/contact_detail_header_view.xml
new file mode 100644
index 0000000..3248920
--- /dev/null
+++ b/res/layout/contact_detail_header_view.xml
@@ -0,0 +1,114 @@
+<?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/banner"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:background="@drawable/title_bar_medium"
+ android:paddingRight="5dip">
+
+ <ImageView android:id="@+id/photo"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="-1dip"
+ android:layout_marginTop="4dip"
+ android:layout_marginBottom="4dip"
+ android:layout_marginRight="8dip"
+ android:layout_width="64dip"
+ android:layout_height="64dip"
+ />
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:layout_gravity="center_vertical" >
+
+ <TextView android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:shadowColor="#BB000000"
+ android:shadowRadius="2.75"
+ />
+
+ <TextView android:id="@+id/phonetic_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_marginTop="-2dip"
+ android:visibility="gone"
+ />
+
+ <TextView android:id="@+id/organization"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_marginTop="-2dip"
+ android:visibility="gone"
+ />
+
+ <TextView android:id="@+id/attribution"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_marginTop="-2dip"
+ android:visibility="gone"
+ />
+
+ <TextView android:id="@+id/status"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_marginTop="-2dip"
+ android:visibility="gone"
+ />
+
+ <TextView android:id="@+id/status_date"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textSize="12sp"
+ android:layout_marginTop="-2dip"
+ android:visibility="gone"
+ />
+ </LinearLayout>
+
+ <CheckBox
+ android:id="@+id/star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:contentDescription="@string/description_star"
+ android:visibility="invisible"
+ style="?android:attr/starStyle"
+ />
+</LinearLayout>
diff --git a/res/layout/contact_detail_list_item.xml b/res/layout/contact_detail_list_item.xml
new file mode 100644
index 0000000..13b07bb
--- /dev/null
+++ b/res/layout/contact_detail_list_item.xml
@@ -0,0 +1,134 @@
+<?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="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- Seperator that shows the kind -->
+ <LinearLayout
+ android:id="@+id/kind_divider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="@drawable/list_item_divider_holo" />
+ <TextView
+ android:id="@+id/kind"
+ android:gravity="center_vertical"
+ android:layout_width="match_parent"
+ android:layout_height="31dip"
+ android:paddingLeft="16dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary" />
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="@drawable/list_item_divider_holo" />
+ </LinearLayout>
+
+ <!-- Shorter seperating line if there was no kind-seperator -->
+ <ImageView
+ android:id="@+id/in_kind_divider"
+ android:layout_marginLeft="31dip"
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="@drawable/list_item_divider_holo" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/detail_min_line_item_height"
+ android:orientation="horizontal"
+ android:paddingLeft="31dip"
+ android:gravity="center_vertical">
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="5dip"
+ android:orientation="vertical"
+ android:paddingTop="5dip"
+ android:paddingBottom="7dip"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/data"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/detail_item_type_color" />
+
+ <TextView
+ android:id="@+id/footer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/presence_icon"
+ android:layout_width="32dip"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="5dip"
+ android:gravity="center"
+ android:scaleType="centerInside" />
+
+ <ImageView
+ android:id="@+id/action_icon"
+ android:layout_width="32dip"
+ android:layout_height="32dip"
+ android:layout_marginLeft="16dip"
+ android:gravity="center"
+ android:scaleType="centerInside" />
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="1px"
+ android:layout_height="match_parent"
+ android:layout_marginTop="5dip"
+ android:layout_marginBottom="5dip"
+ android:layout_marginLeft="14dip"
+ android:layout_marginRight="14dip"
+ android:background="?android:attr/dividerVertical" />
+
+ <ImageView
+ android:id="@+id/secondary_action_button"
+ android:layout_width="32dip"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ android:scaleType="center"
+ android:background="?android:attr/selectableItemBackground" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/contact_editor_activity.xml b/res/layout/contact_editor_activity.xml
new file mode 100644
index 0000000..630e82c
--- /dev/null
+++ b/res/layout/contact_editor_activity.xml
@@ -0,0 +1,42 @@
+<?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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <fragment class="com.android.contacts.editor.ContactEditorFragment"
+ android:id="@+id/contact_editor_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="50dip"
+ android:orientation="horizontal"
+ android:gravity="center_horizontal">
+
+ <Button
+ android:id="@+id/done"
+ android:layout_width="150dip"
+ android:layout_height="match_parent"
+ android:text="@string/menu_done" />
+ <Button
+ android:id="@+id/revert"
+ android:layout_width="150dip"
+ android:layout_height="match_parent"
+ android:text="@string/menu_doNotSave" />
+ </LinearLayout>
+</FrameLayout>
diff --git a/res/layout/contact_editor_fragment.xml b/res/layout/contact_editor_fragment.xml
new file mode 100644
index 0000000..602f7f9
--- /dev/null
+++ b/res/layout/contact_editor_fragment.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+>
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ >
+
+ <LinearLayout android:id="@+id/editors"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ />
+
+ </ScrollView>
+</FrameLayout>
diff --git a/res/layout/contact_list_filter_custom.xml b/res/layout/contact_list_filter_custom.xml
new file mode 100644
index 0000000..98925be
--- /dev/null
+++ b/res/layout/contact_list_filter_custom.xml
@@ -0,0 +1,61 @@
+<?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"
+ style="@style/CustomContactListFilterView"
+ android:orientation="vertical"
+ android:fillViewport="true">
+
+ <ExpandableListView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:background="?android:attr/dividerHorizontal" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ style="?android:attr/buttonBarStyle">
+
+ <Button
+ android:id="@+id/btn_done"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@android:string/ok" />
+
+ <Button
+ android:id="@+id/btn_discard"
+ style="?android:attr/buttonBarButtonStyle"
+ 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/display_options_phones_only.xml b/res/layout/contact_list_filter_phones_only.xml
similarity index 100%
rename from res/layout-finger/display_options_phones_only.xml
rename to res/layout/contact_list_filter_phones_only.xml
diff --git a/res/layout-finger/contact_options.xml b/res/layout/contact_options.xml
similarity index 100%
rename from res/layout-finger/contact_options.xml
rename to res/layout/contact_options.xml
diff --git a/res/layout/contact_picker.xml b/res/layout/contact_picker.xml
new file mode 100644
index 0000000..6b03501
--- /dev/null
+++ b/res/layout/contact_picker.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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"
+ class="com.android.contacts.widget.FullHeightLinearLayout"
+ style="@style/ContactPickerLayout"
+ android:paddingLeft="32dip"
+ android:paddingRight="32dip"
+ android:orientation="vertical"
+ android:layout_height="match_parent">
+ <view
+ class="android.widget.SearchView"
+ android:id="@+id/search_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ android:iconifiedByDefault="false" />
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:id="@+id/list_container">
+ </FrameLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:background="?android:attr/dividerHorizontal" />
+
+ <LinearLayout
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <Button
+ style="?android:attr/buttonBarButtonStyle"
+ android:id="@+id/cancel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@android:string/cancel" />
+ </LinearLayout>
+</view>
diff --git a/res/layout/contact_picker_content.xml b/res/layout/contact_picker_content.xml
new file mode 100644
index 0000000..6792f9c
--- /dev/null
+++ b/res/layout/contact_picker_content.xml
@@ -0,0 +1,37 @@
+<?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/pinned_header_list_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <view
+ class="com.android.contacts.list.ContactEntryListView"
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:fastScrollEnabled="true"
+ android:layout_weight="1" />
+
+ <ViewStub
+ android:id="@+id/footer_stub"
+ android:layout="@layout/footer_panel"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/res/layout/contacts_list_content.xml b/res/layout/contacts_list_content.xml
new file mode 100644
index 0000000..6792f9c
--- /dev/null
+++ b/res/layout/contacts_list_content.xml
@@ -0,0 +1,37 @@
+<?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/pinned_header_list_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <view
+ class="com.android.contacts.list.ContactEntryListView"
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:fastScrollEnabled="true"
+ android:layout_weight="1" />
+
+ <ViewStub
+ android:id="@+id/footer_stub"
+ android:layout="@layout/footer_panel"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/res/layout/contacts_preferences.xml b/res/layout/contacts_preferences.xml
deleted file mode 100644
index c8a1bcf..0000000
--- a/res/layout/contacts_preferences.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?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="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:fillViewport="true">
-
- <ExpandableListView
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- style="@android:style/ButtonBar">
-
- <Button
- android:id="@+id/btn_done"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/menu_done" />
-
- <Button
- android:id="@+id/btn_discard"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/menu_doNotSave" />
-
- </LinearLayout>
-
-</LinearLayout>
diff --git a/res/layout/contacts_search_content.xml b/res/layout/contacts_search_content.xml
new file mode 100644
index 0000000..d36ad00
--- /dev/null
+++ b/res/layout/contacts_search_content.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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/pinned_header_list_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@color/translucent_search_background"
+ >
+
+ <include android:id="@+id/searchView"
+ layout="@layout/search_bar"/>
+
+ <FrameLayout
+ android:id="@+id/list_container"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:fastScrollEnabled="true"
+ android:background="@android:color/background_dark"
+ />
+
+ <!-- Transparent filler -->
+ <View android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:visibility="gone"
+ />
+ <ViewStub android:id="@+id/footer_stub"
+ android:layout="@layout/footer_panel"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ />
+</LinearLayout>
diff --git a/res/layout/contacts_unavailable_fragment.xml b/res/layout/contacts_unavailable_fragment.xml
new file mode 100644
index 0000000..44c51b5
--- /dev/null
+++ b/res/layout/contacts_unavailable_fragment.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.widget.InterpolatingLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/panel_message">
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="vertical"
+ ex:layout_narrowParentWidth="600dip"
+ ex:layout_narrowWidth="400dip"
+ ex:layout_wideParentWidth="880dip"
+ ex:layout_wideWidth="600dip">
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:layout_marginBottom="20dip" />
+
+ <Button
+ android:id="@+id/create_contact_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="15dip"
+ android:text="@string/contacts_unavailable_create_contact" />
+
+ <Button
+ android:id="@+id/add_account_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="15dip"
+ android:text="@string/contacts_unavailable_add_account" />
+
+ <Button
+ android:id="@+id/import_contacts_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="15dip"
+ android:text="@string/contacts_unavailable_import_contacts" />
+
+ <Button
+ android:id="@+id/import_failure_uninstall_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="15dip"
+ android:text="@string/upgrade_out_of_memory_uninstall" />
+
+ <Button
+ android:id="@+id/import_failure_retry_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="15dip"
+ android:text="@string/upgrade_out_of_memory_retry" />
+
+ <ProgressBar
+ android:id="@+id/progress"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="15dip" />
+ </LinearLayout>
+</com.android.contacts.widget.InterpolatingLayout>
diff --git a/res/layout-finger/create_new_contact.xml b/res/layout/create_new_contact.xml
similarity index 100%
rename from res/layout-finger/create_new_contact.xml
rename to res/layout/create_new_contact.xml
diff --git a/res/layout-finger/display_group.xml b/res/layout/custom_contact_list_filter_account.xml
similarity index 100%
rename from res/layout-finger/display_group.xml
rename to res/layout/custom_contact_list_filter_account.xml
diff --git a/res/layout-finger/display_child.xml b/res/layout/custom_contact_list_filter_group.xml
similarity index 100%
rename from res/layout-finger/display_child.xml
rename to res/layout/custom_contact_list_filter_group.xml
diff --git a/res/layout/date_picker.xml b/res/layout/date_picker.xml
new file mode 100644
index 0000000..4fb2318
--- /dev/null
+++ b/res/layout/date_picker.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- Layout of date picker-->
+
+<!-- Warning: everything within the parent is removed and re-ordered depending
+ on the date format selected by the user. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <CheckBox
+ android:id="@+id/yearToggle"
+ android:text="@string/date_year_toggle"
+ android:paddingTop="5dip"
+ android:paddingBottom="5dip"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <LinearLayout
+ android:id="@+id/parent"
+ android:orientation="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <!-- Month -->
+ <NumberPicker
+ android:id="@+id/month"
+ android:layout_width="80dip"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="1dip"
+ android:layout_marginRight="1dip"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ />
+
+ <!-- Day -->
+ <NumberPicker
+ android:id="@+id/day"
+ android:layout_width="80dip"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="1dip"
+ android:layout_marginRight="1dip"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ />
+
+ <!-- Year -->
+ <NumberPicker
+ android:id="@+id/year"
+ android:layout_width="95dip"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="1dip"
+ android:layout_marginRight="1dip"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ />
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/date_picker_dialog.xml b/res/layout/date_picker_dialog.xml
new file mode 100644
index 0000000..edaba4c
--- /dev/null
+++ b/res/layout/date_picker_dialog.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2010, 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"
+ class="com.android.contacts.datepicker.DatePicker"
+ android:id="@+id/datePicker"
+ android:padding="5dip"
+ android:layout_gravity="center_horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
diff --git a/res/layout-finger/dialer_activity.xml b/res/layout/dialer_activity.xml
similarity index 100%
rename from res/layout-finger/dialer_activity.xml
rename to res/layout/dialer_activity.xml
diff --git a/res/layout-finger/dialpad.xml b/res/layout/dialpad.xml
similarity index 100%
rename from res/layout-finger/dialpad.xml
rename to res/layout/dialpad.xml
diff --git a/res/layout-finger/dialpad_chooser_list_item.xml b/res/layout/dialpad_chooser_list_item.xml
similarity index 100%
rename from res/layout-finger/dialpad_chooser_list_item.xml
rename to res/layout/dialpad_chooser_list_item.xml
diff --git a/res/layout/directory_header.xml b/res/layout/directory_header.xml
new file mode 100644
index 0000000..63d8297
--- /dev/null
+++ b/res/layout/directory_header.xml
@@ -0,0 +1,60 @@
+<?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.
+-->
+
+<!-- Layout used for list section separators. -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/DirectoryHeader"
+ android:layout_width="match_parent"
+ android:layout_height="56dip"
+ >
+ <TextView
+ android:id="@+id/count"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_alignParentRight="true"
+ android:layout_marginRight="8dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textStyle="bold"
+ />
+ <TextView
+ android:id="@+id/label"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignBaseline="@id/count"
+ android:layout_marginLeft="8dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textStyle="bold"
+ />
+ <TextView
+ android:id="@+id/display_name"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_toRightOf="@id/label"
+ android:layout_toLeftOf="@id/count"
+ android:layout_alignBaseline="@id/count"
+ android:layout_marginLeft="6dip"
+ android:layout_marginRight="6dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary"
+ android:singleLine="true"
+ android:ellipsize="end"
+ />
+</RelativeLayout>
diff --git a/res/layout-finger/edit_contact_entry_voicemail.xml b/res/layout/edit_contact_entry_voicemail.xml
similarity index 100%
rename from res/layout-finger/edit_contact_entry_voicemail.xml
rename to res/layout/edit_contact_entry_voicemail.xml
diff --git a/res/layout-finger/edit_divider.xml b/res/layout/edit_divider.xml
similarity index 100%
rename from res/layout-finger/edit_divider.xml
rename to res/layout/edit_divider.xml
diff --git a/res/layout-finger/empty.xml b/res/layout/empty.xml
similarity index 100%
rename from res/layout-finger/empty.xml
rename to res/layout/empty.xml
diff --git a/res/layout/external_raw_contact_editor_view.xml b/res/layout/external_raw_contact_editor_view.xml
new file mode 100644
index 0000000..a964ce4
--- /dev/null
+++ b/res/layout/external_raw_contact_editor_view.xml
@@ -0,0 +1,163 @@
+<?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.
+-->
+
+<!-- placed inside act_edit as tabcontent -->
+<com.android.contacts.editor.ExternalRawContactEditorView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+>
+
+ <!-- Left side color bar -->
+ <ImageView
+ android:id="@+id/color_bar"
+ android:layout_width="4dip"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ />
+
+ <!-- The content -->
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ >
+
+ <!-- Account info header -->
+ <RelativeLayout android:id="@+id/header"
+ android:layout_height="64dip"
+ android:layout_width="match_parent"
+ >
+
+ <ImageView android:id="@+id/header_color_bar"
+ android:layout_width="match_parent"
+ android:layout_height="4dip"
+ android:layout_marginBottom="5dip"
+ android:background="@color/edit_divider"
+ />
+
+ <ImageView android:id="@+id/header_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:layout_centerVertical="true"
+ android:layout_below="@id/header_color_bar"
+ />
+
+ <TextView android:id="@+id/header_account_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@+id/header_icon"
+ android:layout_alignTop="@id/header_icon"
+ android:layout_marginTop="-4dip"
+
+ android:textSize="24sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true"
+ />
+
+ <TextView android:id="@+id/header_account_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@+id/header_icon"
+ android:layout_alignBottom="@+id/header_icon"
+ android:layout_marginBottom="2dip"
+
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true"
+ />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:layout_alignParentBottom="true"
+
+ android:background="?android:attr/listDivider"
+ />
+
+ </RelativeLayout>
+
+ <FrameLayout
+ android:id="@+id/stub_photo"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="12dip"
+ android:paddingTop="10dip">
+
+ <include
+ android:id="@+id/edit_photo"
+ layout="@layout/item_photo_editor" />
+
+ </FrameLayout>
+
+ <TextView android:id="@+id/read_only_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="6dip"
+ android:layout_marginLeft="10dip"
+
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="?android:attr/listDivider"
+ />
+
+ <TextView
+ android:id="@+id/read_only_warning"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="13dip"
+ android:layout_marginBottom="13dip"
+ android:layout_marginLeft="13dip"
+
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimary"
+ android:drawableLeft="?android:attr/alertDialogIcon"
+ android:drawablePadding="10dip"
+ />
+
+ <Button
+ android:id="@+id/button_edit_externally"
+ android:text="@string/edit_contact"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="13dip"
+ android:layout_marginBottom="13dip"
+ android:layout_marginLeft="13dip"
+ />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="?android:attr/listDivider"
+ />
+
+ <LinearLayout android:id="@+id/sect_general"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ />
+ </LinearLayout>
+
+</com.android.contacts.editor.ExternalRawContactEditorView>
diff --git a/res/layout/filter_spinner_item.xml b/res/layout/filter_spinner_item.xml
new file mode 100644
index 0000000..c2d5d68
--- /dev/null
+++ b/res/layout/filter_spinner_item.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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"
+ class="com.android.contacts.list.ContactListFilterView"
+ android:layout_height="52dip"
+ android:layout_width="fill_parent"
+ android:paddingLeft="7dip"
+ android:paddingRight="7dip"
+ android:paddingTop="2dip"
+ android:paddingBottom="2dip"
+ android:gravity="left">
+
+ <View
+ android:id="@+id/indent"
+ android:layout_width="40dip"
+ android:layout_height="fill_parent" />
+
+ <ImageView
+ android:id="@+id/icon"
+ android:scaleType="fitCenter"
+ android:layout_width="24dip"
+ android:layout_height="24dip"
+ android:layout_marginLeft="8dip"
+ android:layout_marginRight="7dip"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center_vertical"
+ android:ellipsize="end" />
+</view>
+
diff --git a/res/layout/footer_panel.xml b/res/layout/footer_panel.xml
new file mode 100644
index 0000000..2625a43
--- /dev/null
+++ b/res/layout/footer_panel.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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/footer"
+ android:orientation="horizontal"
+ android:visibility="gone"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ style="@android:style/ButtonBar"
+>
+
+ <Button android:id="@+id/done"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/menu_done"
+ />
+
+ <Button android:id="@+id/revert"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/menu_doNotSave"
+ />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/group_membership_list_item.xml b/res/layout/group_membership_list_item.xml
new file mode 100644
index 0000000..1e4f049
--- /dev/null
+++ b/res/layout/group_membership_list_item.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="48dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center_vertical"
+ android:checkMark="?android:attr/listChoiceIndicatorMultiple"
+ android:paddingLeft="6dip"
+ android:paddingRight="6dip"
+/>
diff --git a/res/layout/group_name_dialog.xml b/res/layout/group_name_dialog.xml
new file mode 100644
index 0000000..b22c8e0
--- /dev/null
+++ b/res/layout/group_name_dialog.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="25dip"
+ android:paddingRight="25dip"
+ android:paddingBottom="25dip"
+ android:paddingLeft="25dip">
+ <EditText
+ android:id="@+id/group_label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+</FrameLayout>
diff --git a/res/layout/item_contact_editor.xml b/res/layout/item_contact_editor.xml
deleted file mode 100644
index f5e7bd3..0000000
--- a/res/layout/item_contact_editor.xml
+++ /dev/null
@@ -1,180 +0,0 @@
-<?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.
--->
-
-<!-- placed inside act_edit as tabcontent -->
-<com.android.contacts.ui.widget.ContactEditorView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
->
-
- <!-- Left side color bar -->
- <ImageView
- android:id="@+id/color_bar"
- android:layout_width="4dip"
- android:layout_height="match_parent"
- android:visibility="gone"
- />
-
- <!-- The content -->
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- >
-
- <!-- Account info header -->
- <RelativeLayout android:id="@+id/header"
- android:layout_height="64dip"
- android:layout_width="match_parent"
- >
-
- <ImageView android:id="@+id/header_color_bar"
- android:layout_width="match_parent"
- android:layout_height="4dip"
- android:layout_marginBottom="5dip"
- android:background="@color/edit_divider"
- />
-
- <ImageView android:id="@+id/header_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="7dip"
- android:layout_marginRight="7dip"
- android:layout_centerVertical="true"
- android:layout_below="@id/header_color_bar"
- />
-
- <TextView android:id="@+id/header_account_type"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@+id/header_icon"
- android:layout_alignTop="@id/header_icon"
- android:layout_marginTop="-4dip"
-
- android:textSize="24sp"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="true"
- />
-
- <TextView android:id="@+id/header_account_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@+id/header_icon"
- android:layout_alignBottom="@+id/header_icon"
- android:layout_marginBottom="2dip"
-
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="true"
- />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1px"
- android:layout_alignParentBottom="true"
-
- android:background="?android:attr/listDivider"
- />
-
- </RelativeLayout>
-
- <FrameLayout
- android:id="@+id/stub_photo"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="12dip"
- android:paddingTop="10dip">
-
- <include
- android:id="@+id/edit_photo"
- layout="@layout/item_photo_editor" />
-
- </FrameLayout>
-
- <include
- android:id="@+id/edit_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/stub_photo"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="4dip"
- layout="@layout/item_generic_editor" />
-
- <TextView android:id="@+id/read_only_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="6dip"
- android:layout_marginLeft="10dip"
-
- android:textAppearance="?android:attr/textAppearanceLarge"
- />
-
- <LinearLayout
- android:id="@+id/sect_general"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- />
-
- <View android:id="@+id/head_secondary_divider"
- android:layout_width="match_parent"
- android:layout_height="1px"
- android:background="?android:attr/listDivider" />
-
- <TextView
- android:id="@+id/head_secondary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
-
- android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:text="@string/edit_secondary_collapse"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="@color/kind_title"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:focusable="true"
- android:clickable="true"
- android:paddingLeft="10dip"
- android:drawablePadding="10dip" />
-
- <LinearLayout
- android:id="@+id/sect_secondary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" />
-
- <TextView
- android:id="@+id/edit_read_only"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dip"
- android:layout_marginBottom="10dip"
- android:layout_marginLeft="10dip"
-
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary"
- android:drawableLeft="@android:drawable/ic_dialog_alert"
- android:drawablePadding="10dip"
- />
-
- </LinearLayout>
-
-</com.android.contacts.ui.widget.ContactEditorView>
diff --git a/res/layout/item_editor_field.xml b/res/layout/item_editor_field.xml
deleted file mode 100644
index 9ad8689..0000000
--- a/res/layout/item_editor_field.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-
-<EditText
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
diff --git a/res/layout/item_generic_editor.xml b/res/layout/item_generic_editor.xml
deleted file mode 100644
index e672eba..0000000
--- a/res/layout/item_generic_editor.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?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.
--->
-
-<com.android.contacts.ui.widget.GenericEditorView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:baselineAligned="false"
- android:paddingRight="?android:attr/scrollbarSize">
-
- <Button
- android:id="@+id/edit_label"
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:gravity="left|center_vertical" />
-
- <ImageButton
- android:id="@+id/edit_delete"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- style="@style/MinusButton" />
-
- <LinearLayout
- android:id="@+id/edit_fields"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_marginLeft="4dip"
- android:layout_alignWithParentIfMissing="true"
- android:layout_toRightOf="@id/edit_label"
- android:layout_toLeftOf="@id/edit_delete"
- android:orientation="vertical"
- android:baselineAligned="false"
- android:gravity="center_vertical" />
-
- <ImageButton
- android:id="@+id/edit_more"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_alignBottom="@id/edit_fields"
- android:visibility="gone"
- style="@style/MoreButton" />
-
- <ImageButton
- android:id="@+id/edit_less"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_alignBottom="@id/edit_fields"
- android:visibility="gone"
- style="@style/LessButton" />
-
-</com.android.contacts.ui.widget.GenericEditorView>
diff --git a/res/layout/item_group_membership.xml b/res/layout/item_group_membership.xml
new file mode 100644
index 0000000..65730d4
--- /dev/null
+++ b/res/layout/item_group_membership.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.editor.GroupMembershipView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:background="?android:attr/listDivider" />
+
+ <LinearLayout
+ android:id="@+id/kind_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="14dip"
+ android:layout_marginTop="2dip"
+ android:layout_marginBottom="2dip"
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:focusable="true"
+ android:clickable="true">
+
+ <TextView
+ android:id="@+id/kind_title"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/kind_title"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:focusable="true" />
+
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/group_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:ellipsize="end"
+ android:gravity="left"
+ />
+
+</com.android.contacts.editor.GroupMembershipView>
diff --git a/res/layout/item_kind_section.xml b/res/layout/item_kind_section.xml
index d1dec5e..fdb55c9 100644
--- a/res/layout/item_kind_section.xml
+++ b/res/layout/item_kind_section.xml
@@ -16,7 +16,7 @@
<!-- the body surrounding all editors for a specific kind -->
-<com.android.contacts.ui.widget.KindSectionView
+<com.android.contacts.editor.KindSectionView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -25,7 +25,7 @@
<View
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="1px"
android:background="?android:attr/listDivider" />
<LinearLayout
@@ -53,12 +53,18 @@
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
- <ImageView
+ <ImageButton
android:id="@+id/kind_plus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="true"
- style="@style/PlusButton" />
+ android:background="?android:attr/selectableItemBackground"
+ android:src="@drawable/ic_menu_add_field_holo_light"
+ android:paddingLeft="@dimen/editor_round_button_padding_left"
+ android:paddingRight="@dimen/editor_round_button_padding_right"
+ android:paddingTop="@dimen/editor_round_button_padding_top"
+ android:paddingBottom="@dimen/editor_round_button_padding_bottom"
+ android:contentDescription="@string/description_plus_button" />
</LinearLayout>
@@ -69,4 +75,4 @@
android:paddingBottom="6dip"
android:orientation="vertical" />
-</com.android.contacts.ui.widget.KindSectionView>
+</com.android.contacts.editor.KindSectionView>
diff --git a/res/layout/item_photo_editor.xml b/res/layout/item_photo_editor.xml
index b981131..642908e 100644
--- a/res/layout/item_photo_editor.xml
+++ b/res/layout/item_photo_editor.xml
@@ -14,15 +14,28 @@
limitations under the License.
-->
-<com.android.contacts.ui.widget.PhotoEditorView
+<view
+ class="com.android.contacts.editor.PhotoEditorView"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/edit_photo_size"
android:layout_height="@dimen/edit_photo_size"
- android:clickable="true"
- android:focusable="true"
- android:src="@drawable/ic_menu_add_picture"
- android:cropToPadding="true"
- android:scaleType="center"
- android:background="@drawable/btn_contact_picture"
- android:contentDescription="@string/description_contact_photo"
- android:gravity="center" />
+ >
+ <ImageView
+ android:id="@+id/photo"
+ android:layout_width="@dimen/edit_photo_size"
+ android:layout_height="@dimen/edit_photo_size"
+ android:src="@drawable/ic_contact_picture"
+ android:cropToPadding="true"
+ android:scaleType="centerCrop"
+ android:gravity="center"
+ />
+ <View
+ android:id="@+id/frame"
+ android:layout_width="@dimen/edit_photo_size"
+ android:layout_height="@dimen/edit_photo_size"
+ android:clickable="true"
+ android:focusable="true"
+ android:contentDescription="@string/description_contact_photo"
+ android:background="@drawable/change_photo_box_holo_light"
+ />
+</view>
diff --git a/res/layout/item_read_only_contact_editor.xml b/res/layout/item_read_only_contact_editor.xml
deleted file mode 100644
index df442c8..0000000
--- a/res/layout/item_read_only_contact_editor.xml
+++ /dev/null
@@ -1,153 +0,0 @@
-<?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.
--->
-
-<!-- placed inside act_edit as tabcontent -->
-<com.android.contacts.ui.widget.ReadOnlyContactEditorView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
->
-
- <!-- Left side color bar -->
- <ImageView
- android:id="@+id/color_bar"
- android:layout_width="4dip"
- android:layout_height="match_parent"
- android:visibility="gone"
- />
-
- <!-- The content -->
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- >
-
- <!-- Account info header -->
- <RelativeLayout android:id="@+id/header"
- android:layout_height="64dip"
- android:layout_width="match_parent"
- >
-
- <ImageView android:id="@+id/header_color_bar"
- android:layout_width="match_parent"
- android:layout_height="4dip"
- android:layout_marginBottom="5dip"
- android:background="@color/edit_divider"
- />
-
- <ImageView android:id="@+id/header_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="7dip"
- android:layout_marginRight="7dip"
- android:layout_centerVertical="true"
- android:layout_below="@id/header_color_bar"
- />
-
- <TextView android:id="@+id/header_account_type"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@+id/header_icon"
- android:layout_alignTop="@id/header_icon"
- android:layout_marginTop="-4dip"
-
- android:textSize="24sp"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="true"
- />
-
- <TextView android:id="@+id/header_account_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@+id/header_icon"
- android:layout_alignBottom="@+id/header_icon"
- android:layout_marginBottom="2dip"
-
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary"
- android:singleLine="true"
- />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1px"
- android:layout_alignParentBottom="true"
-
- android:background="?android:attr/listDivider"
- />
-
- </RelativeLayout>
-
- <FrameLayout
- android:id="@+id/stub_photo"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="12dip"
- android:paddingTop="10dip">
-
- <include
- android:id="@+id/edit_photo"
- layout="@layout/item_photo_editor" />
-
- </FrameLayout>
-
- <TextView android:id="@+id/read_only_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="6dip"
- android:layout_marginLeft="10dip"
-
- android:textAppearance="?android:attr/textAppearanceLarge"
- />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1px"
- android:background="?android:attr/listDivider"
- />
-
- <TextView
- android:id="@+id/read_only_warning"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="13dip"
- android:layout_marginBottom="13dip"
- android:layout_marginLeft="13dip"
-
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorPrimary"
- android:drawableLeft="@android:drawable/ic_dialog_alert"
- android:drawablePadding="10dip"
- />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1px"
- android:background="?android:attr/listDivider"
- />
-
- <LinearLayout android:id="@+id/sect_general"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- />
- </LinearLayout>
-
-</com.android.contacts.ui.widget.ReadOnlyContactEditorView>
diff --git a/res/layout/join_contact_picker.xml b/res/layout/join_contact_picker.xml
new file mode 100644
index 0000000..20a7740
--- /dev/null
+++ b/res/layout/join_contact_picker.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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"
+ class="com.android.contacts.widget.FullHeightLinearLayout"
+ style="@style/ContactPickerLayout"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:id="@+id/list_container">
+ </FrameLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:background="?android:attr/dividerHorizontal" />
+
+ <LinearLayout
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <Button
+ style="?android:attr/buttonBarButtonStyle"
+ android:id="@+id/cancel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@android:string/cancel"/>
+ </LinearLayout>
+</view>
diff --git a/res/layout/join_contact_picker_list_content.xml b/res/layout/join_contact_picker_list_content.xml
new file mode 100644
index 0000000..0d2a089
--- /dev/null
+++ b/res/layout/join_contact_picker_list_content.xml
@@ -0,0 +1,51 @@
+<?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="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/join_contact_blurb"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="10dip"
+ android:paddingTop="12dip"
+ android:paddingBottom="12dip"
+ android:layout_marginLeft="12dip"
+ android:layout_marginRight="12dip"
+ android:maxLines="2"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ <FrameLayout
+ android:id="@+id/pinned_header_list_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginLeft="48dip"
+ android:layout_marginRight="48dip">
+
+ <view
+ class="com.android.contacts.list.ContactEntryListView"
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fastScrollEnabled="true" />
+ </FrameLayout>
+</LinearLayout>
diff --git a/res/layout/join_contact_picker_section.xml b/res/layout/join_contact_picker_section.xml
new file mode 100644
index 0000000..95ec107
--- /dev/null
+++ b/res/layout/join_contact_picker_section.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_height="32dip"
+ android:layout_width="match_parent"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:gravity="center_vertical" />
+
+ <View
+ style="@style/SectionDivider" />
+</LinearLayout>
diff --git a/res/layout/join_contact_picker_show_all.xml b/res/layout/join_contact_picker_show_all.xml
new file mode 100644
index 0000000..d332649
--- /dev/null
+++ b/res/layout/join_contact_picker_show_all.xml
@@ -0,0 +1,44 @@
+<?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="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/dividerHorizontal" />
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_height="48dip"
+ android:layout_width="match_parent"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:gravity="center_vertical"
+ android:text="@string/showAllContactsJoinItem" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/dividerHorizontal" />
+</LinearLayout>
diff --git a/res/layout/list_section.xml b/res/layout/list_section.xml
new file mode 100644
index 0000000..e920673
--- /dev/null
+++ b/res/layout/list_section.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Layout used for list section separators. -->
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="25dip"
+ android:background="@drawable/section_header"
+ >
+ <TextView
+ android:id="@+id/header_text"
+ android:layout_width="56dip"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true"
+ android:textStyle="bold"
+ android:textColor="@color/section_header_text_color"
+ android:textSize="14sp"
+ android:gravity="center"
+ />
+</RelativeLayout>
diff --git a/res/layout-finger/list_separator.xml b/res/layout/list_separator.xml
similarity index 100%
rename from res/layout-finger/list_separator.xml
rename to res/layout/list_separator.xml
diff --git a/res/layout/navigation_bar.xml b/res/layout/navigation_bar.xml
new file mode 100644
index 0000000..803705c
--- /dev/null
+++ b/res/layout/navigation_bar.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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"
+ style="?android:attr/actionBarTabBarStyle"
+ android:id="@+id/navigation_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/search_label"
+ style="?android:attr/actionButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/search_label"
+ android:minWidth="@dimen/action_bar_filter_min_width" />
+
+ <view
+ class="com.android.contacts.list.ContactListFilterView"
+ style="?android:attr/actionDropDownStyle"
+ android:id="@+id/filter_view"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="left|center_vertical"
+ android:focusable="true"
+ android:minWidth="@dimen/action_bar_filter_min_width"
+ >
+ <ImageView
+ android:id="@+id/icon"
+ android:scaleType="fitCenter"
+ android:layout_width="24dip"
+ android:layout_height="24dip"
+ android:layout_marginRight="7dip"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/label"
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:maxWidth="@dimen/action_bar_filter_max_width" />
+ </view>
+
+ <SearchView
+ android:id="@+id/search_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="@dimen/action_bar_search_spacing"
+ android:maxWidth="@dimen/action_bar_search_max_width"
+ android:iconifiedByDefault="false" />
+
+</LinearLayout>
diff --git a/res/layout-finger/phone_disambig_item.xml b/res/layout/phone_disambig_item.xml
similarity index 100%
rename from res/layout-finger/phone_disambig_item.xml
rename to res/layout/phone_disambig_item.xml
diff --git a/res/layout/pinned_header_list_demo.xml b/res/layout/pinned_header_list_demo.xml
new file mode 100644
index 0000000..9a26e98
--- /dev/null
+++ b/res/layout/pinned_header_list_demo.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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/pinned_header_list_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ >
+
+ <view
+ class="com.android.contacts.widget.PinnedHeaderListView"
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:fastScrollEnabled="true"
+ android:layout_weight="1"
+ />
+</LinearLayout>
diff --git a/res/layout/preference_with_more_button.xml b/res/layout/preference_with_more_button.xml
new file mode 100644
index 0000000..e61f891
--- /dev/null
+++ b/res/layout/preference_with_more_button.xml
@@ -0,0 +1,66 @@
+<?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/preference"
+ android:layout_width="match_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"
+ >
+
+ <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
+ android:src="@drawable/ic_menu_expander_minimized_holo_light"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+</LinearLayout>
diff --git a/res/layout/quickcontact.xml b/res/layout/quickcontact.xml
new file mode 100644
index 0000000..a74424c
--- /dev/null
+++ b/res/layout/quickcontact.xml
@@ -0,0 +1,136 @@
+<!-- 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.
+-->
+
+<view
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.contacts.quickcontact.QuickContactRootLayout"
+ android:id="@+id/root"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dip">
+
+ <ViewStub
+ android:id="@+id/header_small"
+ android:inflatedId="@+id/header_small"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout="@layout/quickcontact_header_small" />
+
+ <ViewStub
+ android:id="@+id/header_medium"
+ android:inflatedId="@+id/header_medium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout="@layout/quickcontact_header_med" />
+
+ <ViewStub
+ android:id="@+id/header_large"
+ android:inflatedId="@+id/header_large"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout="@layout/quickcontact_header_large" />
+
+ </FrameLayout>
+
+ <HorizontalScrollView
+ android:id="@+id/scroll"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dip"
+ android:layout_marginLeft="15dip"
+ android:layout_marginRight="15dip"
+ android:layout_marginBottom="10dip"
+ android:fadingEdgeLength="0dip"
+ android:scrollbars="none">
+
+ <LinearLayout
+ android:id="@+id/quickcontact"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" />
+ </HorizontalScrollView>
+
+ <FrameLayout
+ android:id="@+id/footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <LinearLayout
+ android:id="@+id/footer_disambig"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+
+ <ListView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:layout_marginLeft="5dip"
+ android:layout_marginRight="5dip"
+ android:cacheColorHint="@null" />
+
+ <CheckBox
+ android:id="@android:id/checkbox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="19dip"
+ android:layout_marginRight="19dip"
+ android:minHeight="60dip"
+ android:textColor="#f000"
+ android:textStyle="bold"
+ android:text="@string/quickcontact_remember_choice"
+ android:textAppearance="?android:attr/textAppearanceSmallInverse" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/footer_clear_defaults"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="30dip"
+ android:layout_marginRight="5dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/quickcontact_clear_defaults_caption" />
+ <ListView
+ android:id="@+id/defaults_list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:layout_marginLeft="5dip"
+ android:layout_marginRight="5dip"
+ android:cacheColorHint="@null" />
+ <Button
+ android:id="@+id/clear_defaults_button"
+ android:layout_marginLeft="20dip"
+ android:layout_marginBottom="20dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/quickcontact_clear_defaults_button" />
+ </LinearLayout>
+ </FrameLayout>
+</view>
diff --git a/res/layout/quickcontact_default_item.xml b/res/layout/quickcontact_default_item.xml
new file mode 100755
index 0000000..25b6910
--- /dev/null
+++ b/res/layout/quickcontact_default_item.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="25dip"
+ android:paddingRight="25dip"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:textAppearance="?android:attr/textAppearanceMediumInverse" />
+
+ <TextView
+ android:id="@android:id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-4dip"
+ android:textAppearance="?android:attr/textAppearanceSmallInverse" />
+
+</LinearLayout>
diff --git a/res/layout/quickcontact_header_large.xml b/res/layout/quickcontact_header_large.xml
new file mode 100644
index 0000000..b8a19cf
--- /dev/null
+++ b/res/layout/quickcontact_header_large.xml
@@ -0,0 +1,79 @@
+<?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/header_large"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="87dip"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/photo"
+ android:layout_width="64dip"
+ android:layout_height="64dip"
+ android:layout_marginLeft="15dip" />
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="15dip"
+ android:paddingRight="8dip"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textColor="@*android:color/primary_text_light"
+ android:textStyle="bold"
+ android:textSize="18dip" />
+
+ <TextView
+ android:id="@+id/status"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textColor="@*android:color/secondary_text_light"
+ android:textSize="15dip"
+ android:layout_marginTop="-3dip" />
+
+ <TextView
+ android:id="@+id/timestamp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textColor="@*android:color/secondary_text_light"
+ android:textSize="12dip"
+ android:layout_marginTop="-2dip" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/presence"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="15dip"
+ android:scaleType="centerInside" />
+
+</LinearLayout>
diff --git a/res/layout/quickcontact_header_med.xml b/res/layout/quickcontact_header_med.xml
new file mode 100644
index 0000000..77cb1a5
--- /dev/null
+++ b/res/layout/quickcontact_header_med.xml
@@ -0,0 +1,55 @@
+<?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/header_medium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="51dip"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="15dip"
+ android:layout_marginRight="15dip"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/status"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textColor="@*android:color/primary_text_light"
+ android:textSize="15sp" />
+
+ <TextView
+ android:id="@+id/timestamp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textColor="@*android:color/secondary_text_light"
+ android:textSize="12sp"
+ android:layout_marginTop="-2dip" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/quickcontact_header_small.xml b/res/layout/quickcontact_header_small.xml
new file mode 100644
index 0000000..f3a46d5
--- /dev/null
+++ b/res/layout/quickcontact_header_small.xml
@@ -0,0 +1,22 @@
+<?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/header_small"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" />
diff --git a/res/layout/quickcontact_item.xml b/res/layout/quickcontact_item.xml
new file mode 100644
index 0000000..ca57d66
--- /dev/null
+++ b/res/layout/quickcontact_item.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.
+-->
+
+<com.android.contacts.quickcontact.CheckableImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="59dip"
+ android:layout_height="51dip"
+ android:paddingLeft="12dip"
+ android:paddingRight="12dip"
+ android:paddingTop="8dip"
+ android:paddingBottom="8dip"
+ android:scaleType="centerInside"
+ android:focusable="true"
+ android:clickable="true"
+ android:background="@drawable/quickcontact_slider_btn" />
diff --git a/res/layout/quickcontact_resolve_item.xml b/res/layout/quickcontact_resolve_item.xml
new file mode 100755
index 0000000..55de80e
--- /dev/null
+++ b/res/layout/quickcontact_resolve_item.xml
@@ -0,0 +1,40 @@
+<?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="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="25dip"
+ android:paddingRight="25dip"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textStyle="bold"
+ android:textAppearance="?android:attr/textAppearanceMediumInverse" />
+
+ <TextView
+ android:id="@android:id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-4dip"
+ android:textAppearance="?android:attr/textAppearanceSmallInverse" />
+
+</LinearLayout>
diff --git a/res/layout/raw_contact_editor_view.xml b/res/layout/raw_contact_editor_view.xml
new file mode 100644
index 0000000..62d904f
--- /dev/null
+++ b/res/layout/raw_contact_editor_view.xml
@@ -0,0 +1,138 @@
+<?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.
+-->
+
+<com.android.contacts.editor.RawContactEditorView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+>
+
+ <!-- Account info header -->
+ <RelativeLayout android:id="@+id/header"
+ android:layout_height="64dip"
+ android:layout_width="match_parent"
+ android:background="@android:drawable/list_selector_background"
+ >
+
+ <ImageView android:id="@+id/header_color_bar"
+ android:layout_width="match_parent"
+ android:layout_height="4dip"
+ android:layout_marginBottom="5dip"
+ android:background="@color/edit_divider"
+ />
+
+ <ImageView android:id="@+id/header_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:layout_centerVertical="true"
+ android:layout_below="@id/header_color_bar"
+ />
+
+ <TextView android:id="@+id/header_account_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@+id/header_icon"
+ android:layout_alignTop="@id/header_icon"
+ android:layout_marginTop="-4dip"
+
+ android:textSize="24sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true"
+ />
+
+ <TextView android:id="@+id/header_account_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@+id/header_icon"
+ android:layout_alignBottom="@+id/header_icon"
+ android:layout_marginBottom="2dip"
+
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true"
+ />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:layout_alignParentBottom="true"
+
+ android:background="?android:attr/listDivider"
+ />
+ </RelativeLayout>
+
+ <LinearLayout
+ android:id="@+id/body"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/stub_photo"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="12dip"
+ android:paddingTop="10dip">
+
+ <include
+ android:id="@+id/edit_photo"
+ layout="@layout/item_photo_editor" />
+
+ </FrameLayout>
+
+ <com.android.contacts.editor.TextFieldsEditorView
+ android:id="@+id/edit_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingRight="?android:attr/scrollbarSize"
+ android:layout_below="@id/stub_photo"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="4dip" />
+
+ <ViewStub android:id="@+id/aggregation_suggestion_stub"
+ android:inflatedId="@+id/aggregation_suggestion"
+ android:layout="@layout/aggregation_suggestions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="visible"/>
+
+ <LinearLayout
+ android:id="@+id/sect_fields"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1px"
+ android:layout_alignParentBottom="true"
+ android:background="?android:attr/listDivider"
+ />
+
+ <Button
+ android:id="@+id/button_add_field"
+ android:text="@string/add_field"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:layout_marginTop="10dip"
+ />
+ </LinearLayout>
+</com.android.contacts.editor.RawContactEditorView>
diff --git a/res/layout-finger/recent_calls.xml b/res/layout/recent_calls.xml
similarity index 100%
rename from res/layout-finger/recent_calls.xml
rename to res/layout/recent_calls.xml
diff --git a/res/layout/recent_calls_list_child_item.xml b/res/layout/recent_calls_list_child_item.xml
new file mode 100644
index 0000000..527e259
--- /dev/null
+++ b/res/layout/recent_calls_list_child_item.xml
@@ -0,0 +1,38 @@
+<?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="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:paddingLeft="7dip"
+ android:background="@drawable/list_item_background_secondary"
+>
+
+ <ImageView android:id="@+id/call_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingLeft="14dip"
+ android:paddingRight="14dip"
+ android:layout_alignParentRight="true"
+
+ android:gravity="center_vertical"
+ android:src="@android:drawable/sym_action_call"
+ android:background="@drawable/call_background_secondary"
+ />
+
+ <include layout="@layout/recent_calls_list_item_layout"/>
+
+</RelativeLayout>
diff --git a/res/layout/recent_calls_list_group_item.xml b/res/layout/recent_calls_list_group_item.xml
new file mode 100644
index 0000000..79423fa
--- /dev/null
+++ b/res/layout/recent_calls_list_group_item.xml
@@ -0,0 +1,125 @@
+<?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="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:paddingLeft="7dip"
+>
+
+ <ImageView android:id="@+id/call_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingLeft="14dip"
+ android:paddingRight="14dip"
+ android:layout_alignParentRight="true"
+
+ android:gravity="center_vertical"
+ android:src="@android:drawable/sym_action_call"
+ android:background="@drawable/call_background"
+ />
+
+ <View android:id="@+id/divider"
+ android:layout_width="1px"
+ android:layout_height="match_parent"
+ android:layout_marginTop="5dip"
+ android:layout_marginBottom="5dip"
+ android:layout_toLeftOf="@id/call_icon"
+ android:layout_marginLeft="11dip"
+ android:background="@drawable/divider_vertical_dark"
+ />
+
+ <TextView android:id="@+id/date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toLeftOf="@id/divider"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="8dip"
+ android:layout_marginLeft="10dip"
+
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ />
+
+ <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_marginLeft="36dip"
+ android:layout_marginRight="5dip"
+ android:layout_alignBaseline="@id/date"
+
+ 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_toRightOf="@id/label"
+ android:layout_toLeftOf="@id/date"
+ android:layout_alignBaseline="@id/label"
+ android:layout_alignWithParentIfMissing="true"
+
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+ <TextView android:id="@+id/groupSize"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_toLeftOf="@id/divider"
+ android:layout_above="@id/date"
+ android:layout_alignWithParentIfMissing="true"
+ android:layout_marginBottom="-10dip"
+
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:singleLine="true"
+ android:gravity="center_vertical"
+ />
+
+ <TextView android:id="@+id/line1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_toLeftOf="@+id/groupSize"
+ android:layout_above="@id/date"
+ android:layout_alignWithParentIfMissing="true"
+ android:layout_marginLeft="36dip"
+ android:layout_marginBottom="-10dip"
+
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:gravity="center_vertical"
+ />
+
+ <ImageView
+ android:id="@+id/groupIndicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:src="@*android:drawable/expander_ic_minimized"
+ android:gravity="center_vertical"
+ />
+</RelativeLayout>
diff --git a/res/layout/recent_calls_list_item.xml b/res/layout/recent_calls_list_item.xml
new file mode 100644
index 0000000..2c519d6
--- /dev/null
+++ b/res/layout/recent_calls_list_item.xml
@@ -0,0 +1,37 @@
+<?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="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:paddingLeft="7dip"
+>
+
+ <ImageView android:id="@+id/call_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingLeft="14dip"
+ android:paddingRight="14dip"
+ android:layout_alignParentRight="true"
+
+ android:gravity="center_vertical"
+ android:src="@android:drawable/sym_action_call"
+ android:background="@drawable/call_background"
+ />
+
+ <include layout="@layout/recent_calls_list_item_layout"/>
+
+</RelativeLayout>
diff --git a/res/layout-finger/recent_calls_list_item_layout.xml b/res/layout/recent_calls_list_item_layout.xml
similarity index 100%
rename from res/layout-finger/recent_calls_list_item_layout.xml
rename to res/layout/recent_calls_list_item_layout.xml
diff --git a/res/layout/search_bar.xml b/res/layout/search_bar.xml
new file mode 100644
index 0000000..7dfd8ec
--- /dev/null
+++ b/res/layout/search_bar.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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/search_bar"
+ android:layout_width="match_parent"
+ android:layout_height="64dip"
+ android:orientation="vertical"
+ android:focusable="true"
+ android:descendantFocusability="afterDescendants"
+ android:background="@drawable/bg_blk_search_contact">
+
+ <!-- Outer layout defines the entire search bar at the top of the screen -->
+ <LinearLayout
+ android:id="@+id/search_plate"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="4dip"
+ android:paddingRight="10dip"
+ android:paddingTop="6dip"
+ android:paddingBottom="0dip"
+ >
+
+ <!-- Inner layout contains the app icon, button(s) and EditText -->
+ <LinearLayout
+ android:id="@+id/search_edit_frame"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@mipmap/ic_launcher_contacts"
+ android:layout_marginRight="7dip"
+ android:layout_gravity="center_vertical"
+ android:scaleType="centerInside" />
+
+ <view
+ class="com.android.contacts.widget.SearchEditText"
+ android:id="@+id/search_src_text"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1.0"
+ android:layout_marginLeft="4dip"
+ android:layout_marginBottom="0dip"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:inputType="textNoSuggestions"
+ android:imeOptions="flagNoExtractUi"
+ android:hint="@string/search_bar_hint"
+ android:drawableRight="@drawable/magnifying_glass"
+ android:freezesText="true"
+ />
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/search_header.xml b/res/layout/search_header.xml
new file mode 100644
index 0000000..ab8ec53
--- /dev/null
+++ b/res/layout/search_header.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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="match_parent"
+ android:layout_height="28dip"
+ android:background="@drawable/infobar_dark">
+ <TextView
+ android:id="@+id/totalContactsText"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_alignParentLeft="true"
+ android:layout_marginLeft="8dip"
+ android:textColor="#ffbfbfbf"
+ android:textSize="14sp"
+ android:textStyle="normal" />
+
+ <ProgressBar
+ android:id="@+id/progress"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/totalContactsText"
+ style="?android:attr/progressBarStyleSmall"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:layout_marginRight="10dip" />
+
+</RelativeLayout>
diff --git a/res/layout-finger/set_primary_checkbox.xml b/res/layout/set_primary_checkbox.xml
similarity index 100%
rename from res/layout-finger/set_primary_checkbox.xml
rename to res/layout/set_primary_checkbox.xml
diff --git a/res/layout/social_widget.xml b/res/layout/social_widget.xml
new file mode 100644
index 0000000..fb02bb7
--- /dev/null
+++ b/res/layout/social_widget.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="96dip"
+ android:background="#00000000"
+ android:paddingLeft="12dip"
+ android:paddingTop="1dip"
+ android:paddingRight="12dip"
+ android:paddingBottom="20dip"
+>
+ <FrameLayout
+ android:layout_width="70dip"
+ android:layout_height="70dip"
+ android:layout_marginTop="1dip"
+ android:layout_marginLeft="1dip"
+ >
+ <ImageView
+ android:id="@+id/image"
+ android:padding="4dip"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+ <ImageButton
+ android:id="@+id/border"
+ android:background="@drawable/frame_thumbnail_contact_widget_holo"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </FrameLayout>
+ <RelativeLayout
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="@drawable/bg_status_contact_widget"
+ android:layout_marginTop="4dip"
+ android:layout_marginRight="4dip"
+ android:layout_marginBottom="7dip"
+ android:layout_marginLeft="0dip"
+ android:paddingLeft="47dip"
+ android:paddingRight="8dip"
+ android:paddingTop="3dip"
+ android:paddingBottom="6dip">
+
+ <TextView
+ android:id="@+id/name_and_snippet"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:maxLines="3"
+ android:lineSpacingExtra="2sp"
+ android:textColor="#FFFFFFFF"
+ android:textSize="@dimen/widget_text_size_snippet" />
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:textColor="#FFFFFFFF"
+ android:textSize="@dimen/widget_text_size_name" />
+
+ <TextView
+ android:id="@+id/status_date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentBottom="true"
+ android:background="@drawable/statusbox_attribute_holo"
+ android:textSize="13sp"
+ android:textColor="#FF444444"
+ android:visibility="gone" />
+ </RelativeLayout>
+</LinearLayout>
diff --git a/res/layout-finger/split_aggregate_list_item.xml b/res/layout/split_aggregate_list_item.xml
similarity index 100%
rename from res/layout-finger/split_aggregate_list_item.xml
rename to res/layout/split_aggregate_list_item.xml
diff --git a/res/layout/status_bar_ongoing_event_progress_bar.xml b/res/layout/status_bar_ongoing_event_progress_bar.xml
new file mode 100644
index 0000000..e3edf95
--- /dev/null
+++ b/res/layout/status_bar_ongoing_event_progress_bar.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright 2010, 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: This is copied from DownloadProvider, and looks similar to Bluetooth's.
+ It might be better to have this in framework.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:drawable/status_bar_item_app_background"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ >
+ <LinearLayout
+ android:layout_width="40dp"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingTop="8dp"
+ android:focusable="true"
+ >
+ <ImageView
+ android:id="@+id/status_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:src="@android:drawable/sym_def_app_icon"
+ />
+ <TextView android:id="@+id/status_progress_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="#ff000000"
+ android:singleLine="true"
+ android:textSize="14sp"
+ android:layout_gravity="center_horizontal" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:focusable="true">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:focusable="true"
+ android:layout_alignParentTop="true"
+ android:paddingTop="10dp">
+ <TextView android:id="@+id/status_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true"
+ android:textSize="18sp"
+ android:paddingLeft="5dp" />
+ </LinearLayout>
+ <ProgressBar
+ android:id="@+id/status_progress_bar"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:paddingBottom="8dp"
+ android:paddingRight="25dp" />
+ </RelativeLayout>
+ </LinearLayout>
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:src="@android:drawable/divider_horizontal_bright"
+ />
+</LinearLayout>
+
diff --git a/res/layout-finger/tab_indicator.xml b/res/layout/tab_indicator.xml
similarity index 100%
rename from res/layout-finger/tab_indicator.xml
rename to res/layout/tab_indicator.xml
diff --git a/res/layout-finger/tab_left_arrow.xml b/res/layout/tab_left_arrow.xml
similarity index 100%
rename from res/layout-finger/tab_left_arrow.xml
rename to res/layout/tab_left_arrow.xml
diff --git a/res/layout-finger/tab_right_arrow.xml b/res/layout/tab_right_arrow.xml
similarity index 100%
rename from res/layout-finger/tab_right_arrow.xml
rename to res/layout/tab_right_arrow.xml
diff --git a/res/layout-finger/total_contacts.xml b/res/layout/total_contacts.xml
similarity index 100%
rename from res/layout-finger/total_contacts.xml
rename to res/layout/total_contacts.xml
diff --git a/res/layout-finger/twelve_key_dialer.xml b/res/layout/twelve_key_dialer.xml
similarity index 100%
rename from res/layout-finger/twelve_key_dialer.xml
rename to res/layout/twelve_key_dialer.xml
diff --git a/res/layout/vcardshare_main.xml b/res/layout/vcardshare_main.xml
deleted file mode 100644
index 4361cfe..0000000
--- a/res/layout/vcardshare_main.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
-</LinearLayout>
diff --git a/res/layout-finger/voicemail_dial_delete.xml b/res/layout/voicemail_dial_delete.xml
similarity index 100%
rename from res/layout-finger/voicemail_dial_delete.xml
rename to res/layout/voicemail_dial_delete.xml
diff --git a/res/menu/actions.xml b/res/menu/actions.xml
new file mode 100644
index 0000000..a53967f
--- /dev/null
+++ b/res/menu/actions.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/menu_add"
+ android:showAsAction="always" />
+
+ <item
+ android:id="@+id/menu_settings"
+ android:icon="@drawable/ic_menu_settings_holo_light"
+ android:title="@string/menu_settings" />
+
+ <item
+ android:id="@+id/menu_accounts"
+ android:icon="@drawable/ic_menu_accounts_holo_light"
+ android:title="@string/menu_accounts" />
+
+ <item
+ android:id="@+id/menu_import_export"
+ android:icon="@drawable/ic_menu_import_export_holo_light"
+ android:title="@string/menu_import_export" />
+
+ <item
+ android:id="@+id/menu_rename_group"
+ android:icon="@drawable/ic_menu_settings_holo_light"
+ android:title="@string/menu_renameGroup" />
+
+ <item
+ android:id="@+id/menu_delete_group"
+ android:icon="@drawable/ic_menu_trash_holo_light"
+ android:title="@string/menu_deleteGroup" />
+</menu>
diff --git a/res/menu/edit.xml b/res/menu/edit.xml
index 6fafbcf..857c9c5 100644
--- a/res/menu/edit.xml
+++ b/res/menu/edit.xml
@@ -18,32 +18,28 @@
<item
android:id="@+id/menu_done"
android:alphabeticShortcut="\n"
- android:icon="@android:drawable/ic_menu_save"
- android:title="@string/menu_done" />
+ android:icon="@drawable/ic_menu_done_holo_light"
+ android:title="@string/menu_done"
+ android:showAsAction="always|withText" />
<item
android:id="@+id/menu_discard"
android:alphabeticShortcut="q"
- android:icon="@android:drawable/ic_menu_close_clear_cancel"
- android:title="@string/menu_doNotSave" />
-
- <item
- android:id="@+id/menu_add"
- android:icon="@android:drawable/ic_menu_add"
- android:title="@string/menu_newContact" />
+ android:title="@string/menu_doNotSave"
+ android:showAsAction="always|withText" />
<item
android:id="@+id/menu_delete"
- android:icon="@android:drawable/ic_menu_delete"
+ android:icon="@drawable/ic_menu_trash_holo_light"
android:title="@string/menu_deleteContact" />
<item
android:id="@+id/menu_split"
- android:icon="@drawable/ic_menu_split"
+ android:icon="@drawable/ic_menu_split_holo_light"
android:title="@string/menu_splitAggregate" />
<item
android:id="@+id/menu_join"
- android:icon="@drawable/ic_menu_merge"
+ android:icon="@drawable/ic_menu_merge_holo_light"
android:title="@string/menu_joinAggregate" />
</menu>
diff --git a/res/menu/list.xml b/res/menu/list.xml
index b8f9b76..2b3f453 100644
--- a/res/menu/list.xml
+++ b/res/menu/list.xml
@@ -22,7 +22,7 @@
<item
android:id="@+id/menu_add"
- android:icon="@android:drawable/ic_menu_add"
+ android:icon="@drawable/ic_menu_add_contact_holo_light"
android:title="@string/menu_newContact"
android:alphabeticShortcut="n" />
@@ -33,12 +33,12 @@
<item
android:id="@+id/menu_accounts"
- android:icon="@drawable/ic_menu_account_list"
+ android:icon="@drawable/ic_menu_accounts_holo_light"
android:title="@string/menu_accounts" />
<item
android:id="@+id/menu_import_export"
- android:icon="@drawable/ic_menu_import_export"
+ android:icon="@drawable/ic_menu_import_export_holo_light"
android:title="@string/menu_import_export" />
</menu>
diff --git a/res/menu/pick.xml b/res/menu/pick.xml
new file mode 100644
index 0000000..5302dd9
--- /dev/null
+++ b/res/menu/pick.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/menu_display_selected"
+ android:icon="@drawable/ic_menu_display_selected"
+ android:title="@string/menu_display_selected" />
+
+ <item
+ android:id="@+id/menu_display_all"
+ android:icon="@drawable/ic_menu_display_all"
+ android:title="@string/menu_display_all" />
+
+ <item
+ android:id="@+id/menu_select_all"
+ android:icon="@drawable/ic_menu_select"
+ android:title="@string/menu_select_all" />
+
+ <item
+ android:id="@+id/menu_select_none"
+ android:icon="@drawable/ic_menu_unselect"
+ android:title="@string/menu_select_none" />
+
+</menu>
diff --git a/res/menu/search.xml b/res/menu/search.xml
new file mode 100644
index 0000000..4e2e8e3
--- /dev/null
+++ b/res/menu/search.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/menu_search"
+ android:icon="@android:drawable/ic_menu_search"
+ android:title="@string/menu_search" />
+
+</menu>
diff --git a/res/menu/view.xml b/res/menu/view.xml
index 428434d..733ab25 100644
--- a/res/menu/view.xml
+++ b/res/menu/view.xml
@@ -17,13 +17,14 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/menu_edit"
- android:icon="@android:drawable/ic_menu_edit"
+ android:icon="@drawable/ic_menu_compose_holo_light"
android:title="@string/menu_editContact"
- android:alphabeticShortcut="e" />
+ android:alphabeticShortcut="e"
+ android:showAsAction="always" />
<item
android:id="@+id/menu_share"
- android:icon="@android:drawable/ic_menu_share"
+ android:icon="@drawable/ic_menu_share_holo_light"
android:title="@string/menu_share"
android:alphabeticShortcut="s" />
@@ -34,7 +35,6 @@
<item
android:id="@+id/menu_delete"
- android:icon="@android:drawable/ic_menu_delete"
+ android:icon="@drawable/ic_menu_trash_holo_light"
android:title="@string/menu_deleteContact" />
-
</menu>
diff --git a/res/mipmap-hdpi/ic_launcher_contacts.png b/res/mipmap-hdpi/ic_launcher_contacts.png
new file mode 100644
index 0000000..e935f52
--- /dev/null
+++ b/res/mipmap-hdpi/ic_launcher_contacts.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_folder_live_contacts.png b/res/mipmap-hdpi/ic_launcher_folder_live_contacts.png
similarity index 100%
rename from res/drawable-hdpi/ic_launcher_folder_live_contacts.png
rename to res/mipmap-hdpi/ic_launcher_folder_live_contacts.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_folder_live_contacts_phone.png b/res/mipmap-hdpi/ic_launcher_folder_live_contacts_phone.png
similarity index 100%
rename from res/drawable-hdpi/ic_launcher_folder_live_contacts_phone.png
rename to res/mipmap-hdpi/ic_launcher_folder_live_contacts_phone.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_folder_live_contacts_starred.png b/res/mipmap-hdpi/ic_launcher_folder_live_contacts_starred.png
similarity index 100%
rename from res/drawable-hdpi/ic_launcher_folder_live_contacts_starred.png
rename to res/mipmap-hdpi/ic_launcher_folder_live_contacts_starred.png
Binary files differ
diff --git a/res/mipmap-hdpi/ic_launcher_phone.png b/res/mipmap-hdpi/ic_launcher_phone.png
new file mode 100644
index 0000000..0943ce5
--- /dev/null
+++ b/res/mipmap-hdpi/ic_launcher_phone.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_shortcut_contact.png b/res/mipmap-hdpi/ic_launcher_shortcut_contact.png
similarity index 100%
rename from res/drawable-hdpi/ic_launcher_shortcut_contact.png
rename to res/mipmap-hdpi/ic_launcher_shortcut_contact.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_shortcut_directdial.png b/res/mipmap-hdpi/ic_launcher_shortcut_directdial.png
similarity index 100%
rename from res/drawable-hdpi/ic_launcher_shortcut_directdial.png
rename to res/mipmap-hdpi/ic_launcher_shortcut_directdial.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_shortcut_directmessage.png b/res/mipmap-hdpi/ic_launcher_shortcut_directmessage.png
similarity index 100%
rename from res/drawable-hdpi/ic_launcher_shortcut_directmessage.png
rename to res/mipmap-hdpi/ic_launcher_shortcut_directmessage.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_contacts.png b/res/mipmap-mdpi/ic_launcher_contacts.png
new file mode 100644
index 0000000..155ba5a
--- /dev/null
+++ b/res/mipmap-mdpi/ic_launcher_contacts.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_folder_live_contacts.png b/res/mipmap-mdpi/ic_launcher_folder_live_contacts.png
similarity index 100%
rename from res/drawable-mdpi/ic_launcher_folder_live_contacts.png
rename to res/mipmap-mdpi/ic_launcher_folder_live_contacts.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_folder_live_contacts_phone.png b/res/mipmap-mdpi/ic_launcher_folder_live_contacts_phone.png
similarity index 100%
rename from res/drawable-mdpi/ic_launcher_folder_live_contacts_phone.png
rename to res/mipmap-mdpi/ic_launcher_folder_live_contacts_phone.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_folder_live_contacts_starred.png b/res/mipmap-mdpi/ic_launcher_folder_live_contacts_starred.png
similarity index 100%
rename from res/drawable-mdpi/ic_launcher_folder_live_contacts_starred.png
rename to res/mipmap-mdpi/ic_launcher_folder_live_contacts_starred.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_phone.png b/res/mipmap-mdpi/ic_launcher_phone.png
new file mode 100644
index 0000000..724f94a
--- /dev/null
+++ b/res/mipmap-mdpi/ic_launcher_phone.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_shortcut_contact.png b/res/mipmap-mdpi/ic_launcher_shortcut_contact.png
similarity index 100%
rename from res/drawable-mdpi/ic_launcher_shortcut_contact.png
rename to res/mipmap-mdpi/ic_launcher_shortcut_contact.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_shortcut_directdial.png b/res/mipmap-mdpi/ic_launcher_shortcut_directdial.png
similarity index 100%
rename from res/drawable-mdpi/ic_launcher_shortcut_directdial.png
rename to res/mipmap-mdpi/ic_launcher_shortcut_directdial.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_shortcut_directmessage.png b/res/mipmap-mdpi/ic_launcher_shortcut_directmessage.png
similarity index 100%
rename from res/drawable-mdpi/ic_launcher_shortcut_directmessage.png
rename to res/mipmap-mdpi/ic_launcher_shortcut_directmessage.png
Binary files differ
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index df83ace..0519562 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"اختيار اختصار لجهة الاتصال"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"اختيار رقم للاتصال به"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"اختيار رقم لإرسال رسالة له"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"تحديد جهة اتصال"</string>
<string name="starredList" msgid="4817256136413959463">"مميّزة بنجمة"</string>
<string name="frequentList" msgid="7154768136473953056">"متكررة"</string>
<string name="strequentList" msgid="5640192862059373511">"المفضلة"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"حذف جهة الاتصال"</string>
<string name="menu_call" msgid="3992595586042260618">"الاتصال بجهة الاتصال"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"إرسال رسالة لجهة الاتصال"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"إرسال رسالة إلكترونية"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"عنوان الخريطة"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"جعله رقمًا افتراضيًا"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"تحديد كبريد إلكتروني افتراضي"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"فصل"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"تم فصل جهات الاتصال"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"إعادة تسمية المجموعة"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"حذف المجموعة"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"جديد"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"فصل جهة الاتصال"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"هل أنت متأكد من أنك تريد فصل جهة الاتصال الفردية هذه إلى عدة جهات اتصال: جهة اتصال لكل مجموعة من معلومات الاتصال التي تم تجميعها بها؟"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"ضم"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"سيؤدي حذف جهة الاتصال هذه إلى حذف المعلومات من عدة حسابات."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"سيتم حذف جهة الاتصال هذه."</string>
<string name="menu_done" msgid="796017761764190697">"تم"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"تراجع"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"إلغاء"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"تعديل جهة الاتصال"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"جهة اتصال جديدة"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"صوتيًا"</string>
<string name="label_notes" msgid="8337354953278341042">"ملاحظات"</string>
<string name="label_sip_address" msgid="124073911714324974">"مكالمة عبر الإنترنت"</string>
<string name="label_ringtone" msgid="8833166825330686244">"نغمة الرنين"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"الاسم الأول والأخير"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"الاسم"</string>
<string name="ghostData_company" msgid="5414421120553765775">"شركة"</string>
<string name="ghostData_title" msgid="7496735200318496110">"المسمى الوظيفي"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"جهة الاتصال غير موجودة."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"إنشاء جهة اتصال جديدة"</string>
- <string name="selectLabel" msgid="4255424123394910733">"تحديد التصنيف"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"الهاتف"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"إرسال رسالة إلكترونية"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"المراسلة الفورية"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"العنوان البريدي"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"العنوان"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"المؤسسة"</item>
<item msgid="7196592230748086755">"ملاحظة"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"لا تتوفر أية صور على الهاتف."</string>
- <string name="attachToContact" msgid="8820530304406066714">"رمز جهة الاتصال"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"ليس هناك صور متوفرة على الجهاز اللوحي."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"لا تتوفر أية صور على الهاتف."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"صورة جهة الاتصال"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"اسم تصنيف مخصص"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"خيارات العرض"</string>
- <string name="displayGroups" msgid="2278964020773993336">"خيارات العرض"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"إرسال المكالمات إلى البريد الصوتي مباشرة"</string>
<string name="default_ringtone" msgid="9099988849649827972">"افتراضي"</string>
- <string name="changePicture" msgid="2943329047610967714">"تغيير الرمز"</string>
- <string name="removePicture" msgid="3041230993155966350">"إزالة الرمز"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"إزالة الصور"</string>
<string name="noContacts" msgid="8579310973261953559">"لا توجد جهات اتصال."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"لم يتم العثور على أية جهات اتصال متطابقة."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"ليس هناك جهات اتصال تشتمل على أرقام هواتف."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"تم حفظ جهة الاتصال."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"خطأ، غير قادر على حفظ تغييرات جهة الاتصال."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"عرض جهة اتصال واحدة تشتمل على رقم هاتف"</item>
- <item quantity="other" msgid="6133262880804110289">"عرض عدد <xliff:g id="COUNT">%d</xliff:g> من جهات الاتصال التي تشتمل على أرقام هواتف"</item>
+ <item quantity="one" msgid="3015357862286673986">"جهة اتصال واحدة بها رقم هاتف"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> من جهات الاتصال التي تشتمل على أرقام هواتف"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"ليس هناك جهات اتصال مرئية تشتمل على أرقام هواتف"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"ليس هناك جهات اتصال تشتمل على أرقام هواتف"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"عرض جهة اتصال واحدة"</item>
- <item quantity="other" msgid="2865867557378939630">"عرض <xliff:g id="COUNT">%d</xliff:g> من جهات الاتصال"</item>
+ <item quantity="one" msgid="3405747744700823280">"جهة اتصال واحدة"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> من جهات الاتصال"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"ليس هناك جهات اتصال مرئية"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"لا توجد جهات اتصال"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"ليس هناك جهات اتصال مرئية"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"ليس هناك أية جهات اتصال مميزة بنجمة"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"ليس هناك جهات اتصال في <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"تم العثور على جهة اتصال واحدة"</item>
- <item quantity="other" msgid="7752927996850263152">"تم العثور على <xliff:g id="COUNT">%d</xliff:g> من جهات الاتصال"</item>
+ <item quantity="one" msgid="5517063038754171134">"تم العثور على جهة اتصال واحدة"</item>
+ <item quantity="other" msgid="3852668542926965042">"تم العثور على <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"لم يتم العثور على جهة الاتصال"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"تم العثور على أكثر من <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"لم يتم العثور على أيٍّ منها"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"عنوان واحد"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> من جهات الاتصال"</item>
+ <item quantity="one" msgid="4826918429708286628">"تم العثور على جهة اتصال واحدة"</item>
+ <item quantity="other" msgid="7988132539476575389">"تم العثور على <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"جهات الاتصال"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"المفضلة"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"بطاقة SIM وجهات الاتصال"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"ليس لديك أي جهات اتصال لعرضها. (إذا كنت قد أضفت حسابًا توًا، فيمكن أن تستغرق مزامنة جهات الاتصال دقائق معدودة.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"ليس لديك أية جهات اتصال لعرضها."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"ليس لديك أي جهات اتصال لعرضها."\n\n"لإضافة جهات اتصال، اضغط على "<font fgcolor="#ffffffff"><b>"القائمة"</b></font>" والمس:"\n" "\n<li><font fgcolor="#ffffffff"><b>"الحسابات"</b></font>" لإضافة حساب أو تهيئته مع جهات الاتصال التي يمكنك مزامنتها بالهاتف"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"جهة اتصال جديدة"</b></font>" لإنشاء جهة اتصال جديدة من البداية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"استيراد/تصدير"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"ليس لديك أي جهات اتصال لعرضها. (إذا كنت قد أضفت حسابًا توًا، فيمكن أن تستغرق مزامنة جهات الاتصال دقائق معدودة.)"\n\n"لإضافة جهات الاتصال، اضغط على "<font fgcolor="#ffffffff"><b>"القائمة"</b></font>" والمس:"\n" "\n<li><font fgcolor="#ffffffff"><b>"الحسابات"</b></font>" لإضافة حساب أو تهيئته مع جهات الاتصال التي يمكنك مزامنتها إلى الهاتف"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"خيارات العرض"</b></font>" لتغيير جهات الاتصال التي تكون مرئية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"جهة اتصال جديدة"</b></font>" لإنشاء جهة اتصال جديدة من البداية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"استيراد/تصدير"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"ليس لديك أي جهات اتصال لعرضها."\n\n"لإضافة جهات اتصال، اضغط على "<font fgcolor="#ffffffff"><b>"القائمة"</b></font>" والمس:"\n" "\n<li><font fgcolor="#ffffffff"><b>"الحسابات"</b></font>" لإضافة حساب أو تهيئته مع جهات الاتصال التي يمكنك مزامنتها بالهاتف"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"جهة اتصال جديدة"</b></font>" لإنشاء جهة اتصال جديدة من البداية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"استيراد/تصدير"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"ليس لديك أي جهات اتصال لعرضها. (إذا كنت قد أضفت حسابًا توًا، فيمكن أن تستغرق مزامنة جهات الاتصال دقائق معدودة.)"\n\n"لإضافة جهات الاتصال، اضغط على "<font fgcolor="#ffffffff"><b>"القائمة"</b></font>" والمس:"\n" "\n<li><font fgcolor="#ffffffff"><b>"الحسابات"</b></font>" لإضافة حساب أو تهيئته مع جهات الاتصال التي يمكنك مزامنتها إلى الهاتف"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"خيارات العرض"</b></font>" لتغيير جهات الاتصال التي تكون مرئية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"جهة اتصال جديدة"</b></font>" لإنشاء جهة اتصال جديدة من البداية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"استيراد/تصدير"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"ليس لديك أية جهات اتصال لعرضها."\n\n"لإضافة جهات اتصال، اضغط على "<font fgcolor="#ffffffff"><b>"القائمة"</b></font>" والمس:"\n" "\n<li><font fgcolor="#ffffffff"><b>"الحسابات"</b></font>" لإضافة أو تهيئة حساب به جهات الاتصال التي يمكنك مزامنتها إلى الجهاز اللوحي"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"جهة اتصال جديدة"</b></font>" لإنشاء جهة اتصال جديدة من البداية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"استيراد/تصدير"</b></font>" لاستيراد جهات الاتصال من بطاقة SIM أو بطاقة SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"ليس لديك أية جهات اتصال لعرضها."\n\n"لإضافة جهات اتصال، اضغط على "<font fgcolor="#ffffffff"><b>"القائمة"</b></font>" والمس:"\n" "\n<li><font fgcolor="#ffffffff"><b>"الحسابات"</b></font>" لإضافة أو تهيئة حساب به جهات الاتصال التي يمكنك مزامنتها إلى الهاتف"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"جهة اتصال جديدة"</b></font>" لإنشاء جهة اتصال جديدة من البداية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"استيراد/تصدير"</b></font>" لاستيراد جهات الاتصال من بطاقة SIM أو بطاقة SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"ليست لديك أية جهات اتصال لعرضها. (إذا أضفت حسابًا لتوك، فيمكن أن تستغرق مزامنة جهات الاتصال بضع دقائق.)"\n\n"لإضافة جهات اتصال، اضغط على "<font fgcolor="#ffffffff"><b>"القائمة"</b></font>" والمس:"\n" "\n<li><font fgcolor="#ffffffff"><b>"الحسابات"</b></font>" لإضافة أو تهيئة حساب ما به جهات الاتصال التي يمكنك مزامنتها إلى الجهاز اللوحي"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"خيارات العرض"</b></font>" لتغيير جهات الاتصال التي يمكن رؤيتها"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"جهة اتصال جديدة"</b></font>" لإنشاء جهة اتصال جديدة من البداية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"استيراد/تصدير"</b></font>" لاستيراد جهات اتصال من بطاقةSIM أو بطاقة SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"ليس لديك أي جهات اتصال لعرضها. (إذا أضفت حسابًا لتوك، فيمكن أن تستغرق مزامنة جهات الاتصال بضع دقائق.)"\n\n"لإضافة جهات اتصال، اضغط على "<font fgcolor="#ffffffff"><b>"القائمة"</b></font>" والمس:"\n" "\n<li><font fgcolor="#ffffffff"><b>"الحسابات"</b></font>" لإضافة أو تهيئة حساب ما به جهات الاتصال التي يمكنك مزامنتها إلى الهاتف"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"خيارات العرض"</b></font>" لتغيير جهات الاتصال التي يمكن رؤيتها"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"جهة اتصال جديدة"</b></font>" لإنشاء جهة اتصال جديدة من البداية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"استيراد/تصدير"</b></font>" لاستيراد جهات اتصال من بطاقةSIM أو بطاقة SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"ليس لديك أية جهات اتصال لعرضها."\n\n"لإضافة جهات اتصال، اضغط على "<font fgcolor="#ffffffff"><b>"القائمة"</b></font>" والمس:"\n" "\n<li><font fgcolor="#ffffffff"><b>"الحسابات"</b></font>" لإضافة أو تهيئة حساب ما به جهات الاتصال التي يمكنك مزامنتها إلى الجهاز اللوحي"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"جهة اتصال جديدة"</b></font>" لإنشاء جهة اتصال جديدة من البداية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"استيراد/تصدير"</b></font>" لاستيراد جهات الاتصال من بطاقة SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"ليس لديك أية جهات اتصال لعرضها."\n\n"لإضافة جهات اتصال، اضغط على "<font fgcolor="#ffffffff"><b>"القائمة"</b></font>" والمس:"\n" "\n<li><font fgcolor="#ffffffff"><b>"الحسابات"</b></font>" لإضافة أو تهيئة حساب به جهات الاتصال التي يمكنك مزامنتها إلى الهاتف"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"جهة اتصال جديدة"</b></font>" لإنشاء جهة اتصال جديدة من البداية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"استيراد/تصدير"</b></font>" لاستيراد جهات الاتصال من بطاقة SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"ليست لديك أية جهات اتصال لعرضها. (إذا أضفت حسابًا لتوك، فيمكن أن تستغرق مزامنة جهات الاتصال بضع دقائق.)"\n\n"لإضافة جهات اتصال، اضغط على "<font fgcolor="#ffffffff"><b>"القائمة"</b></font>" والمس:"\n" "\n<li><font fgcolor="#ffffffff"><b>"الحسابات"</b></font>" لإضافة أو تهيئة حساب ما به جهات الاتصال التي يمكنك مزامنتها إلى الجهاز اللوحي"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"خيارات العرض"</b></font>" لتغيير جهات الاتصال التي يمكن رؤيتها"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"جهة اتصال جديدة"</b></font>" لإنشاء جهة اتصال جديدة من البداية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"استيراد/تصدير"</b></font>" لاستيراد جهات اتصال من بطاقة SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"ليست لديك أية جهات اتصال لعرضها. (إذا أضفت حسابًا لتوك، فيمكن أن تستغرق مزامنة جهات الاتصال بضع دقائق.)"\n\n"لإضافة جهات اتصال، اضغط على "<font fgcolor="#ffffffff"><b>"القائمة"</b></font>" والمس:"\n" "\n<li><font fgcolor="#ffffffff"><b>"الحسابات"</b></font>" لإضافة أو تهيئة حساب ما به جهات الاتصال التي يمكنك مزامنتها إلى الهاتف"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"خيارات العرض"</b></font>" لتغيير جهات الاتصال التي يمكن رؤيتها"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"جهة اتصال جديدة"</b></font>" لإنشاء جهة اتصال جديدة من البداية"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"استيراد/تصدير"</b></font>" لاستيراد جهات اتصال من بطاقة SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"ليس لديك أي عناصر في المفضلة."\n\n"لإضافة جهة اتصال إلى قائمة المفضلة:"\n\n" "<li>"المس علامة التبويب "<b>"جهات الاتصال"</b>\n</li>" "\n<li>"المس جهة الاتصال التي تريد إضافتها إلى المفضلة"\n</li>" "\n<li>"المس علامة النجمة إلى جانب اسم جهة الاتصال"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"جميع جهات الاتصال"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"مميّزة بنجمة"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"معاودة الاتصال"</string>
<string name="callAgain" msgid="3197312117049874778">"الاتصال مرة أخرى"</string>
<string name="returnCall" msgid="8171961914203617813">"معاودة اتصال بمكالمة فائتة"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"عدد الدقائق:<xliff:g id="MINUTES">%1$s</xliff:g>، عددالثواني: <xliff:g id="SECONDS">%2$s</xliff:g>"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"عدد الدقائق:<xliff:g id="MINUTES">%s</xliff:g>، عددالثواني: <xliff:g id="SECONDS">%s</xliff:g>"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"يتم الاتصال بها بشكل متكرر"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"إضافة جهة اتصال"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"هل ترغب في إضافة \"<xliff:g id="EMAIL">%s</xliff:g>\" إلى جهات الاتصال؟"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"الكل"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"واحد"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"اثنان"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"ثلاثة"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"أخفق فحص وحدة تخزين USB (السبب: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"أخفق المسح الضوئي لبطاقة SD (السبب: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"خطأ I/O"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"الذاكرة لا تكفي (ربما يكون الملف كبير جدًا)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"أخفق تحليل vCard بسبب غير متوقع"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"أخفق تحليل vCard على الرغم من أنها تبدو بتنسيق صالح، نظرًا لأنها غير معتمدة من قِبل التنفيذ الحالي"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"لم يتم العثور على أي ملف vCard في وحدة تخزين USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"لم يتم العثور على ملف vCard على بطاقة SD"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"التنسيق غير معتمد."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"أخفق استيراد vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"لم يتم العثور على ملف vCard على وحدة تخزين USB"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"لم يتم العثور على ملف vCard على بطاقة SD"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"أخفق جمع معلومات وصفية عن ملفات vCard المحددة"</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"أخفق استيراد ملف أو أكثر (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"خطأ غير معروف"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"تحديد ملف vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"قراءة vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"قراءة ملفات vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"لقد أخفقت قراءة بيانات vCard"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> من <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> من جهات الاتصال"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> من إجمالي <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> من الملفات"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"تخزين ملفات vCard مؤقتًا إلى وحدة التخزين المؤقتة المحلية"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"تخزن حاليًا برامج الاستيراد ملفات vCard مؤقتًا إلى وحدة تخزين مؤقتة محلية. سيبدأ الاستيراد الفعلي قريبًا."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"استيراد <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"جارٍ استيراد <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"أخفقت قراءة بيانات vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"تم إلغاء قراءة بيانات vCard"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"تم الانتهاء من استيراد ملف vCard <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"تم إلغاء استيراد <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"سيتم استيراد <xliff:g id="FILENAME">%s</xliff:g> بعد قليل."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"سيتم استيراد الملف قريبًا."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"تم رفض طلب استيراد vCard. الرجاء المحاولة لاحقًا."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"سيتم تصدير <xliff:g id="FILENAME">%s</xliff:g> بعد قليل."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"تم رفض طلب تصدير vCard. الرجاء المحاولة لاحقًا."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"جهة اتصال"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"تأكيد التصدير"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"هل أنت متأكد من أنك تريد تصدير قائمة جهات الاتصال إلى \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"؟"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"أخفق تصدير بيانات جهة الاتصال"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"هناك ملفات vCard كثيرة جدًا في وحدة تخزين USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"هناك ملفات vCard كثيرة جدًا على بطاقة SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"اسم الملف المطلوب طويل جدًا (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"تم الانتهاء من تصدير <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"تم إلغاء تصدير <xliff:g id="FILENAME">%s</xliff:g>"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"تصدير بيانات جهة الاتصال"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"تصدير بيانات جهة الاتصال إلى: \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"تعذرت تهيئة المُصدر: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"حدث خطأ أثناء التصدير: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"أخفق الحصول على معلومات قاعدة البيانات"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"ليس هناك جهات اتصال قابلة للتصدير. إذا كان لديك جهات اتصال على الهاتف فعلاً، فربما يتم منع تصدير جميع جهات الاتصال إلى خارج الهاتف بواسطة أحد موفري البيانات."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"ليس هناك جهات اتصال قابلة للتصدير. إذا كان لديك فعلاً جهات اتصال على الجهاز اللوحي، فقد يحظر بعض موفري البيانات تصدير جميع جهات الاتصال خارج الجهاز اللوحي."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"ليس هناك جهات اتصال قابلة للتصدير. إذا كان لديك فعلاً جهات اتصال على الهاتف، فقد يحظر بعض موفري البيانات تصدير جميع جهات الاتصال خارج الهاتف."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"لم تتم تهيئة مؤلف vCard بشكل صحيح"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"تعذر فتح \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> من <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> من جهات الاتصال"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"تعذر فتح \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> من <xliff:g id="TOTAL_NUMBER">%s</xliff:g> من جهات الاتصال"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"إلغاء استيراد ملف vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"هل تريد بالتأكيد إلغاء استيراد <xliff:g id="FILENAME">%s</xliff:g>؟"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"إلغاء تصدير ملف vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"هل تريد بالتأكيد إلغاء تصدير <xliff:g id="FILENAME">%s</xliff:g>؟"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"أخفق إلغاء استيراد/تصدير ملف vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"أسماء جهات الاتصال"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"إضافة فترة إيقاف مؤقت مدتها ثانيتان"</string>
<string name="add_wait" msgid="3360818652790319634">"إضافة انتظار"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"لم يتم العثور على تطبيق يمكنه مباشرة هذا الإجراء."</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"تذكر هذا الاختيار"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"غير معروف"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"ليس هناك أي بيانات"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"محو الإعدادات الافتراضية"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"الإعدادات الافتراضية لجهة الاتصال هذه:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"محو"</string>
<string name="menu_accounts" msgid="8499114602017077970">"الحسابات"</string>
<string name="menu_import_export" msgid="3765725645491577190">"استيراد/تصدير"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"استيراد/تصدير جهات الاتصال"</string>
- <string name="menu_share" msgid="943789700636542260">"مشاركة"</string>
+ <string name="menu_share" msgid="8746849630474240344">"مشاركة جهة الاتصال"</string>
<string name="share_via" msgid="563121028023030093">"مشاركة جهة الاتصال عبر"</string>
<string name="share_error" msgid="4374508848981697170">"لا يمكن مشاركة جهة الاتصال هذه."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"الاسم"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"المؤسسة"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"موقع ويب"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"حدث"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"العلاقة"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"المجموعات"</string>
<string name="type_short_home" msgid="7770424864090605384">"د"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"جوال"</string>
<string name="type_short_work" msgid="4925330752504537861">"أربعاء"</string>
<string name="type_short_pager" msgid="2613818970827594238">"ط"</string>
<string name="type_short_other" msgid="5669407180177236769">"ض"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"جهة الاتصال هذه للقراءة فقط"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"المزيد"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"الاسم الأساسي"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"إنشاء جهة اتصال ضمن حساب"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"إزالة مجموعة متزامنة"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"إضافة مجموعة متزامنة"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"جميع جهات الاتصال الأخرى"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"جميع جهات الاتصال"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"ستؤدي إزالة \'<xliff:g id="GROUP">%s</xliff:g>\' من المزامنة أيضًا إلى إزالة أية جهات اتصال غير مجمعة من المزامنة."</string>
- <string name="account_phone" msgid="3682950835276226870">"الهاتف فقط، غير متزامنة"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"الجهاز اللوحي فقط، غير متزامنة"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"الهاتف فقط، غير متزامنة"</string>
<string name="call_custom" msgid="7756571794763171802">"الاتصال بـ <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"الاتصال بهاتف منزلي"</string>
<string name="call_mobile" msgid="7502236805487609178">"الاتصال بالجوال"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"الدردشة باستخدام ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"الدردشة باستخدام Jabber"</string>
<string name="chat" msgid="9025361898797412245">"دردشة"</string>
+ <string name="postal_address" msgid="8765560217149624536">"العنوان"</string>
<string name="postal_street" msgid="8133143961580058972">"الشارع"</string>
<string name="postal_pobox" msgid="4431938829180269821">"صندوق البريد"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"منطقة مجاورة"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"المحافظة"</string>
<string name="postal_postcode" msgid="572136414136673751">"الرمز البريدي"</string>
<string name="postal_country" msgid="7638264508416368690">"البلد"</string>
+ <string name="full_name" msgid="6602579550613988977">"الاسم"</string>
<string name="name_given" msgid="1687286314106019813">"الاسم الممنوح"</string>
<string name="name_family" msgid="3416695586119999058">"اسم العائلة"</string>
<string name="name_prefix" msgid="59756378548779822">"بادئة الاسم"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"البحث في جهات الاتصال"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"البحث عن كل جهات الاتصال"</string>
<string name="take_photo" msgid="7496128293167402354">"التقاط صورة"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"التقاط صورة جديدة"</string>
<string name="pick_photo" msgid="448886509158039462">"تحديد صورة من المعرض"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"يتم تحديث قائمة جهات الاتصال لتعكس التغيير الذي حدث في اللغة."\n\n"الرجاء الانتظار..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"يتم حاليًا تحديث قائمة جهات الاتصال."\n\n"الرجاء الانتظار..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"جهات الاتصال قيد التحديث. "\n\n"تتطلب عملية الترقية مساحة تبلغ حوالي <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> ميغابايت من سعة تخزين الهاتف الداخلية."\n\n"اختر أحد الخيارات التالية:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"تحديد صورة جديدة من المعرض"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"يجري تحديث قائمة جهات الاتصال لتعكس التغير في اللغة."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"يجري تحديث قائمة جهات الاتصال."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"جهات الاتصال قيد الترقية. "\n\n"تتطلب عملية الترقية ما يقرب من <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> ميغابايت من مساحة التخزين الداخلية."\n\n"حدد أحد الخيارات التالية:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"إزالة بعض التطبيقات"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"إعادة محاولة الترقية"</string>
- <string name="search_results_for" msgid="8705490885073188513">"نتائج البحث عن: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"جارِ البحث..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"عرض العناصر المحددة"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"عرض الكل"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"تحديد الكل"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"إلغاء تحديد الكل"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"لم يتم تحديد جهات اتصال."</string>
+ <string name="add_field" msgid="2384260056674995230">"إضافة حقل آخر"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"عبر <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> عبر <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"مفضل"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"تعديل جهة الاتصال"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"لم يتم دمجها"</item>
+ <item quantity="other" msgid="425683718017380845">"تم دمجها من <xliff:g id="COUNT">%0$d</xliff:g> من المصادر"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"غير ذلك"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"ضم جهات الاتصال"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"هل تريد ضم جهة الاتصال الحالية إلى جهة الاتصال المحددة؟"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"تعديل جهات الاتصال المحددة"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"هل تريد التبديل إلى تعديل جهة الاتصال المحددة؟ سيتم نسخ المعلومات التي أدخلتها حتى الآن."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"نسخ إلى جهات الاتصال الخاصة بي"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"الدليل <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"البحث في كل جهات الاتصال"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"الدليل"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"جهات الاتصال"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"جارٍ إنشاء نسخة شخصية"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"اختيار قائمة جهات اتصال"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"جميع جهات الاتصال"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"مميّزة بنجمة"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"مخصص"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"تخصيص..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"جهات الاتصال التي تشتمل على أرقام هواتف"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"جهة الاتصال"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"تحديد عرض مخصص"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"الإعدادات"</string>
+ <string name="menu_settings" msgid="377929915873428211">"الإعدادات"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"خيارات العرض"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>، <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"البحث عن جهات اتصال"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"رقم الهاتف"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"إضافة إلى جهات الاتصال"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"إغلاق"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"عرض السنة"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"جهة الاتصال"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"جارٍ التحميل…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"إنشاء جهة اتصال جديدة"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"تسجيل الدخول إلى حساب"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"استيراد جهات الاتصال من ملف"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"إنشاء مجموعة جديدة"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[إنشاء مجموعة جديدة]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"إعادة تسمية المجموعة"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"حذف المجموعة"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"هل تريد بالتأكيد حذف المجموعة \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\"؟ (لن يتم حذف جهات الاتصال نفسها)."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"الرجاء إدخال اسم جهة الاتصال قبل الانضمام إلى جهة اتصال أخرى."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"جهة الاتصال منضمة"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"تم نسخ النص"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"إلغاء التغييرات"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"هل تريد إلغاء تغييراتك؟"</string>
+ <string name="discard" msgid="1234315037371251414">"إلغاء"</string>
</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index c4d1a9a..0efab1a 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Избор на пряк път до контакт"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Избор на номер за обаждане"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Избор на номер за изпращане на съобщение"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Избор на контакт"</string>
<string name="starredList" msgid="4817256136413959463">"Със звезда"</string>
<string name="frequentList" msgid="7154768136473953056">"Често"</string>
<string name="strequentList" msgid="5640192862059373511">"Любими"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Изтриване на контакта"</string>
<string name="menu_call" msgid="3992595586042260618">"Обаждане на контакт"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Изпращaне на SMS на контакт"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Изпращане на имейл"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Адрес на карта"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Да бъде основен номер"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Да бъде основен имейл"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Разделяне"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Контактите са разделени"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Преименуване на групата"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Изтриване на групата"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Нов"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Разделяне на контакт"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Наистина ли искате да разделите този контакт на няколко – по един за всяка група данни за контакта, слети в него?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Сливане"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Изтриването на този контакт ще премахна информация от няколко профила."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Този контакт ще бъде изтрит."</string>
<string name="menu_done" msgid="796017761764190697">"Готово"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Възстановяване"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Отказ"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Редактиране на контакт"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Нов контакт"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Както се произнася"</string>
<string name="label_notes" msgid="8337354953278341042">"Бележки"</string>
<string name="label_sip_address" msgid="124073911714324974">"Интернет обаждане"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Мелодия"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Първо и последно"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Име, както се произнася"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Фирма"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Заглавие"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Контактът не съществува."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Създаване на нов контакт"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Избор на етикет"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Телефон"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Имейл"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Незабавни съобщения"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Пощенски адрес"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Адрес"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Организация"</item>
<item msgid="7196592230748086755">"Бележка"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"В телефона няма снимки."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Икона на контакта"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"В таблета няма снимки."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"В телефона няма снимки."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Снимка на контакта"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Персонализирано име на етикет"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Опции за показване"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Опции за показване"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Обажданията да се изпращат директно в гл. поща"</string>
<string name="default_ringtone" msgid="9099988849649827972">"По подразбиране"</string>
- <string name="changePicture" msgid="2943329047610967714">"Промяна на икона"</string>
- <string name="removePicture" msgid="3041230993155966350">"Премахване на икона"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Премахване на снимката"</string>
<string name="noContacts" msgid="8579310973261953559">"Няма контакти."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Не са намерени съответстващи контакти."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Няма контакти с тел. номера."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Контактът е запазен."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Грешка, промените в контакта не могат да бъдат запазени."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Показва се 1 контакт с тел. номер"</item>
- <item quantity="other" msgid="6133262880804110289">"Показват се <xliff:g id="COUNT">%d</xliff:g> контакта с тел. номера"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 контакт с телефонен номер"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> контакта с телефонни номера"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Няма видими контакти с тел. номера"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Няма контакти с телефонни номера"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Показва се 1 контакт"</item>
- <item quantity="other" msgid="2865867557378939630">"Показват се <xliff:g id="COUNT">%d</xliff:g> контакта"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 контакт"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> контакта"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Няма видими контакти"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Няма контакти"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Няма видими контакти"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Няма контакти със звезда"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Няма контакти в: <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"1 намерен контакт"</item>
- <item quantity="other" msgid="7752927996850263152">"<xliff:g id="COUNT">%d</xliff:g> намерени контакта"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 намерен"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> намерени"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Контактът не е намерен"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"намерени са повече от <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Няма намерени"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 контакт"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> контакта"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 намерен"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> намерени"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Контакти"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Любими"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Контакти от SIM карта"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Нямате контакти за показване. (Ако току-що сте добавили профил, синхронизирането може да отнеме няколко минути.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Нямате контакти за показване."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Нямате контакти за показване."\n\n"За добавяне на контакти натиснете "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и докоснете:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Профили"</b></font>" – за да добавите или конфигурирате профил с контакти, които можете да синхронизирате с телефона"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>" – за да създадете съвсем нов контакт"\n</li>\n<li><font fgcolor="#ffffffff"><b>"Импортиране/Експортиране"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Нямате контакти за показване. (Ако току-що сте добавили профил, синхронизирането може да отнеме няколко минути.)"\n\n"За добавяне на контакти натиснете "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и докоснете:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Профили"</b></font>" – за да добавите или конфигурирате профил с контакти, които можете да синхронизирате с телефона"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Опции за показване"</b></font>" – за да промените кои контакти са видими"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>" – за да създадете съвсем нов контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импортиране/Експортиране"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Нямате контакти за показване."\n\n"За добавяне на контакти натиснете "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и докоснете:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Профили"</b></font>" – за да добавите или конфигурирате профил с контакти, които можете да синхронизирате с телефона"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>" – за да създадете съвсем нов контакт"\n</li>\n<li><font fgcolor="#ffffffff"><b>"Импортиране/Експортиране"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Нямате контакти за показване. (Ако току-що сте добавили профил, синхронизирането може да отнеме няколко минути.)"\n\n"За добавяне на контакти натиснете "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и докоснете:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Профили"</b></font>" – за да добавите или конфигурирате профил с контакти, които можете да синхронизирате с телефона"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Опции за показване"</b></font>" – за да промените кои контакти са видими"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>" – за да създадете съвсем нов контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импортиране/Експортиране"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Нямате контакти за показване."\n\n"За добавяне на контакти натиснете "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и докоснете:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Профили"</b></font>", за да добавите или конфигурирате профил с контакти, които можете да синхронизирате с таблета;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>", за да създадете съвсем нов контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импортиране/Експортиране"</b></font>", за да импортирате контакти от своята SIM или SD карта."\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Нямате контакти за показване."\n\n"За добавяне на контакти натиснете "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и докоснете:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Профили"</b></font>", за да добавите или конфигурирате профил с контакти, които можете да синхронизирате с телефона;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>", за да създадете съвсем нов контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импортиране/Експортиране"</b></font>", за да импортирате контакти от своята SIM или SD карта."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Нямате контакти за показване. (Ако току-що сте добавили профил, синхронизирането им може да отнеме няколко минути.)"\n\n"За добавяне на контакти натиснете "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и докоснете:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Профили"</b></font>", за да добавите или конфигурирате профил с контакти, които можете да синхронизирате с таблета;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Опции за показване"</b></font>", за да промените кои контакти са видими;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>", за да създадете съвсем нов контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импортиране/Експортиране"</b></font>", за да импортирате контакти от своята SIM или SD карта."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Нямате контакти за показване. (Ако току-що сте добавили профил, синхронизирането им може да отнеме няколко минути.)"\n\n"За добавяне на контакти натиснете "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и докоснете:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Профили"</b></font>", за да добавите или конфигурирате профил с контакти, които можете да синхронизирате с телефона;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Опции за показване"</b></font>", за да промените кои контакти са видими;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>", за да създадете съвсем нов контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импортиране/Експортиране"</b></font>", за да импортирате контакти от своята SIM или SD карта."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Нямате контакти за показване."\n\n"За добавяне на контакти натиснете "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и докоснете:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Профили"</b></font>", за да добавите или конфигурирате профил с контакти, които можете да синхронизирате с таблета;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>", за да създадете съвсем нов контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импортиране/Експортиране"</b></font>", за да импортирате контакти от своята SD карта."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Нямате контакти за показване."\n\n"За добавяне на контакти натиснете "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и докоснете:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Профили"</b></font>", за да добавите или конфигурирате профил с контакти, които можете да синхронизирате с телефона;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>", за да създадете съвсем нов контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импортиране/Експортиране"</b></font>", за да импортирате контакти от своята SD карта."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Нямате контакти за показване. (Ако току-що сте добавили профил, синхронизирането им може да отнеме няколко минути.)"\n\n"За добавяне на контакти натиснете "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и докоснете:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Профили"</b></font>", за да добавите или конфигурирате профил с контакти, които можете да синхронизирате с таблета;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Опции за показване"</b></font>", за да промените кои контакти са видими;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>", за да създадете съвсем нов контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импортиране/Експортиране"</b></font>", за да импортирате контакти от своята SD карта."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Нямате контакти за показване. (Ако току-що сте добавили профил, синхронизирането им може да отнеме няколко минути.)"\n\n"За добавяне на контакти натиснете "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и докоснете:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Профили"</b></font>", за да добавите или конфигурирате профил с контакти, които можете да синхронизирате с телефона;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Опции за показване"</b></font>", за да промените кои контакти са видими;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>", за да създадете съвсем нов контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импортиране/Експортиране"</b></font>", за да импортирате контакти от своята SD карта."\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Нямате любими контакти."\n\n"За добавяне на контакт към любими:"\n\n" "<li>"Докоснете раздела "<b>"Контакти"</b>\n</li>" "\n<li>"Докоснете желания контакт"\n</li>" "\n<li>"Докоснете звездата до името му"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Всички контакти"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Със звезда"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Обратно обаждане"</string>
<string name="callAgain" msgid="3197312117049874778">"Повторно обаждане"</string>
<string name="returnCall" msgid="8171961914203617813">"Отговаряне на обаждане"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> мин <xliff:g id="SECONDS">%2$s</xliff:g> сек"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> мин <xliff:g id="SECONDS">%s</xliff:g> сек"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Често търсени"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Добавяне на контакт"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Да се добави ли „<xliff:g id="EMAIL">%s</xliff:g>“ към контакти?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Всички"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"едно"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"две"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"три"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Сканирането на USB хранилището не бе успешно (Причина: „<xliff:g id="FAIL_REASON">%s</xliff:g>“)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Неуспешно сканиране на SD карта ( Причина: „<xliff:g id="FAIL_REASON">%s</xliff:g>“)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"I/O грешка"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Няма достатъчно памет (файлът може да е твърде голям)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Неуспешен анализ на vCard по неизвестна причина"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Неуспешен анализ на vCard, въпреки че изглежда във валиден формат, защото текущото внедряване не го поддържа"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"В USB хранилището не е намерен vCard файл"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"На SD картата не е намерен vCard файл"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Форматът не се поддържа."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Импортирането на vCard не бе успешно"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"В USB устройството за съхранение не бе намерен vCard файл"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"На SD картата не бе намерен vCard файл"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Събирането на метаинформация от дадените vCard файлове не бе успешно."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Неуспешно импортиране на един или повече файлове (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Неизвестна грешка"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Избор на vCard файл"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"vCard се чете"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Четат се файлове от vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Неуспешно четене на данни от vCard"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> от <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> контакта"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> от <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> файла"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"vCard се кешира/т във временно локално хранилище"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Инструментът за импортиране кешира vCard във временно локално хранилище. Самото импортиране ще започне скоро."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Импортира се <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"<xliff:g id="NAME">%s</xliff:g> се импортира"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Четенето на данни от vCard не бе успешно"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Четенето на данни от vCard бе анулирано"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Импортирането на vCard <xliff:g id="FILENAME">%s</xliff:g> завърши"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Импортирането на <xliff:g id="FILENAME">%s</xliff:g> бе анулирано"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> ще се импортира скоро."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Файлът ще се импортира скоро."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Заявката за импортиране на vCard е отхвърлена. Моля, опитайте по-късно."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> ще се експортира скоро."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Заявката за експортиране на vCard е отхвърлена. Моля, опитайте по-късно."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"контакт"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Потвърждение на експортиране"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Наистина ли искате да експортирате списъка с контакти в „<xliff:g id="VCARD_FILENAME">%s</xliff:g>“?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Неуспешно експортиране на данни за контакти"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"В USB хранилището има твърде много vCard файлове"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Твърде много vCard файлове на SD картата"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Изисканото име на файла е твърде дълго („<xliff:g id="FILENAME">%s</xliff:g>“)"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Експортирането на <xliff:g id="FILENAME">%s</xliff:g> завърши"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Експортирането на <xliff:g id="FILENAME">%s</xliff:g> бе анулирано"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Експортиране на данни за контакти"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Експортиране на данни за контакти в „<xliff:g id="FILE_NAME">%s</xliff:g>“"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Експортирането не можа да започне: „<xliff:g id="EXACT_REASON">%s</xliff:g>“"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Възникна грешка при експортирането: „<xliff:g id="EXACT_REASON">%s</xliff:g>“"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Неуспешно извличане на информация за базата от данни"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Този контакт не може да бъде експортиран. Ако наистина имате контакти в телефона си, някой доставчик на данни може да забранява експортирането им извън телефона."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Няма контакти, позволяващи експортиране. Ако в телефона си имате контакти, възможно е експортирането на всички тях извън таблета да е забранено от някои доставчици на данни."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Няма контакти, позволяващи експортиране. Ако в телефона си имате контакти, възможно е експортирането на всички тях извън телефона да е забранено от някои доставчици на данни."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Създателят на vCard не се стартира правилно"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"„<xliff:g id="FILE_NAME">%1$s</xliff:g>“ не можа да се отвори: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> от <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> контакта"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"„<xliff:g id="FILE_NAME">%s</xliff:g>“ не можа да се отвори: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> от <xliff:g id="TOTAL_NUMBER">%s</xliff:g> контакта"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Анулиране на импортирането на vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Наистина ли искате да анулирате импортирането на <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Анулиране на експортирането на vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Наистина ли искате да анулирате експортирането на <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Неуспешно импорт./експорт. на vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Имена на контактите ви"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Добавяне на 2-сек пауза"</string>
<string name="add_wait" msgid="3360818652790319634">"Добавяне на изчакване"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Няма намерено приложение за извършване на това действие"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Запомняне на този избор"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Неизвестно"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Няма данни"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Изчистване на данните по подразбиране"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Данни, зададени за този контакт:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Изчистване"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Профили"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Импортиране/Експортиране"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Импортиране/Експортиране на контакти"</string>
- <string name="menu_share" msgid="943789700636542260">"Споделяне"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Споделяне на контакта"</string>
<string name="share_via" msgid="563121028023030093">"Споделяне на контакт чрез"</string>
<string name="share_error" msgid="4374508848981697170">"Този контакт не може да бъде споделен."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Име"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Организация"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Уебсайт"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Събитие"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Отношение"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Групи"</string>
<string name="type_short_home" msgid="7770424864090605384">"Д"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"М"</string>
<string name="type_short_work" msgid="4925330752504537861">"О"</string>
<string name="type_short_pager" msgid="2613818970827594238">"П"</string>
<string name="type_short_other" msgid="5669407180177236769">"Д"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Този контакт е само за четене"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Още"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Основно име"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Създаване на контакт в профил"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Премахване на група за синхронизиране"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Добавяне на група за синхронизиране"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Всички други контакти"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Всички контакти"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Ако синхронизирането на „<xliff:g id="GROUP">%s</xliff:g>“ спре, то ще спре и за негрупираните контакти."</string>
- <string name="account_phone" msgid="3682950835276226870">"Само в телефона, несинхронизиран"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Само в таблета, несинхронизиран"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Само в телефона, несинхронизиран"</string>
<string name="call_custom" msgid="7756571794763171802">"Обаждане на <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Обаждане на домашен"</string>
<string name="call_mobile" msgid="7502236805487609178">"Обаждане на мобилен"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Чат по ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Чат по Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Чат"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Адрес"</string>
<string name="postal_street" msgid="8133143961580058972">"Улица"</string>
<string name="postal_pobox" msgid="4431938829180269821">"П.К."</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Квартал"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Щат"</string>
<string name="postal_postcode" msgid="572136414136673751">"Пощенски код"</string>
<string name="postal_country" msgid="7638264508416368690">"Държава"</string>
+ <string name="full_name" msgid="6602579550613988977">"Име"</string>
<string name="name_given" msgid="1687286314106019813">"Собствено име"</string>
<string name="name_family" msgid="3416695586119999058">"Фамилия"</string>
<string name="name_prefix" msgid="59756378548779822">"Обръщение"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Търсене в контактите"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Търсене на всички контакти"</string>
<string name="take_photo" msgid="7496128293167402354">"Снимане"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Заснемане на нова снимка"</string>
<string name="pick_photo" msgid="448886509158039462">"Избор на снимка от галерията"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Списъкът с контакти се актуализира, за да отрази промяната на езика."\n\n"Моля, изчакайте..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Списъкът с контакти се актуализира."\n\n"Моля, изчакайте..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Контактите се надстройват. "\n\n"Процесът се нуждае от прибл. <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> Мб вътрешна телефонна памет."\n\n"Изберете една от следните опции:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Избор на нова снимка от галерията"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Списъкът с контакти се актуализира, за да отрази промяната на езика."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Списъкът с контакти се актуализира."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Контактите се надстройват. "\n\n"Процесът се нуждае от приблизително <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Мб вътрешна памет."\n\n"Изберете една от следните опции:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Деинсталиране на някои приложения"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Повторен опит за надстройка"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Резултати от търсенето на: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Търси се..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Показване на избраните"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Показване на всички"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Избиране на всичко"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Премахване на избора от всички"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Няма избрани контакти."</string>
+ <string name="add_field" msgid="2384260056674995230">"Добавяне на друго поле"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"чрез <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> чрез <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"любимо"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Редактиране на контакта"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"без обединяване"</item>
+ <item quantity="other" msgid="425683718017380845">"обединено от <xliff:g id="COUNT">%0$d</xliff:g> източник/а"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Други"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Сливане на контакти"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Да се слее ли текущият контакт с избрания?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Редактиране на избраните контакти"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Да се превключи ли към редактиране на избрания контакт? Въведената досега информация ще бъде копирана."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Копиране в моите контакти"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Директория „<xliff:g id="TYPE">%1$s</xliff:g>“"</string>
+ <string name="search_label" msgid="6789295859496641042">"Търси се във всички контакти"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Директория"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Контакти"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Създаване на лично копие"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Изберете списък с контакти"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Всички контакти"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Със звезда"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"По избор"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Персонализиране..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Контакти с телефонни номера"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Контакт"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Определяне на персонализиран изглед"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Настройки"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Настройки"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Опции за показване"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Намиране на контакти"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Телефонен номер"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Добавяне към контактите"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Затваряне"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Въвеждане на година"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Контакт"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Зарежда се…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Създаване на нов контакт"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Вход в профил"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Импортиране на контакти от файл"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Създаване на нова група"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Създаване на нова група]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Преименуване на групата"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Изтриване на групата"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Наистина ли искате да изтриете групата „<xliff:g id="GROUP_LABEL">%1$s</xliff:g>“? (Самите контакти няма да бъдат изтрити.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Моля, въведете името на контакта преди сливането му с друг."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Слят контакт"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Текстът бе копиран"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Отхвърляне на промените"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Искате ли да отхвърлите направените от вас промени?"</string>
+ <string name="discard" msgid="1234315037371251414">"Отхвърляне"</string>
</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 04fdabe..7c0d0f7 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Trieu una drecera de contacte"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Trieu un número per trucar-hi"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Trieu un número per enviar-hi un missatge"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Selecciona un contacte"</string>
<string name="starredList" msgid="4817256136413959463">"Destacats"</string>
<string name="frequentList" msgid="7154768136473953056">"Freqüent"</string>
<string name="strequentList" msgid="5640192862059373511">"Preferits"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Suprimeix el contacte"</string>
<string name="menu_call" msgid="3992595586042260618">"Truca al contacte"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Envia un SMS al contacte"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Envia un correu electrònic"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Mostra l\'adreça al mapa"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Converteix-lo en número predeterminat"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Converteix-lo en correu electrònic predeterminat"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Separa"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Contactes separats"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Canvia el nom del grup"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Suprimeix el grup"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Nou"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Separació del contacte"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Esteu segur que voleu dividir aquest contacte únic en diversos contactes, un per a cada conjunt d\'informació de contacte que s\'hi ha unit?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Uneix"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Si suprimiu aquest contacte se suprimirà informació de diversos comptes."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Aquest contacte se suprimirà."</string>
<string name="menu_done" msgid="796017761764190697">"Fet"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Retrocedeix"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Cancel·la"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Edita el contacte"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Contacte nou"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonètic"</string>
<string name="label_notes" msgid="8337354953278341042">"Notes"</string>
<string name="label_sip_address" msgid="124073911714324974">"Trucada per Internet"</string>
<string name="label_ringtone" msgid="8833166825330686244">"So de trucada"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Nom i cognoms"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Nom fonètic"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Empresa"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Títol"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"El contacte no existeix."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Crea un contacte nou"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Selecció d\'una etiqueta"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telèfon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Correu electrònic"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"MI"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Adreça postal"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adreça"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organització"</item>
<item msgid="7196592230748086755">"Nota"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"No hi ha cap imatge disponible al telèfon."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Icona de contacte"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"No hi ha imatges disponibles a la tauleta."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"No hi ha cap imatge disponible al telèfon."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Foto de contacte"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Nom d\'etiqueta personalitzada"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Opcions de visualització"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Opcions de visualització"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Envia les trucades directament al correu de veu"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Predeterminat"</string>
- <string name="changePicture" msgid="2943329047610967714">"Canvia la icona"</string>
- <string name="removePicture" msgid="3041230993155966350">"Elimina la icona"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Elimina la foto"</string>
<string name="noContacts" msgid="8579310973261953559">"No hi ha contactes."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"No s\'ha trobat cap contacte coincident."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"No hi ha cap contacte amb número de telèfon."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"S\'ha desat el contacte."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Error. No s\'han pogut desar els canvis al contacte."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Es mostra 1 contacte amb número de telèfon"</item>
- <item quantity="other" msgid="6133262880804110289">"Es mostren <xliff:g id="COUNT">%d</xliff:g> contactes amb números de telèfon"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 contacte amb número de telèfon"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> contactes amb números de telèfon"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"No hi ha cap contacte visible amb número de telèfon"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"No hi ha cap contacte amb número de telèfon"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Es mostra 1 contacte"</item>
- <item quantity="other" msgid="2865867557378939630">"Es mostren <xliff:g id="COUNT">%d</xliff:g> contactes"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 contacte"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> contactes"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"No hi ha cap contacte visible"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"No hi ha cap contacte"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"No hi ha cap contacte visible"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"No hi ha cap contacte destacat"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"No hi ha cap contacte a <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"S\'ha trobat 1 contacte"</item>
- <item quantity="other" msgid="7752927996850263152">"S\'han trobat <xliff:g id="COUNT">%d</xliff:g> contactes"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 contacte"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> contactes"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"No s\'ha trobat el contacte"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"S\'han trobat més de <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"No trobat"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 contacte"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> contactes"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 contacte"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> contactes"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Contactes"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Preferits"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Contactes de la targeta SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"No teniu cap contacte per mostrar. (Si acabeu d\'afegir un compte, la sincronització dels contactes pot trigar una estona.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"No teniu cap contacte per mostrar."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"No teniu cap contacte per mostrar."\n\n"Per afegir contactes, premeu "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" i toqueu:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" per afegir o configurar un compte amb contactes que podeu sincronitzar amb el telèfon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacte nou"</b></font>" per crear un contacte nou des de zero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/Exporta"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"No teniu cap contacte per mostrar. (Si acabeu d\'afegir un compte, la sincronització dels contactes pot trigar una estona.)"\n\n"Per afegir contactes, premeu "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" i toqueu:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" per afegir o configurar un compte amb contactes que podeu sincronitzar amb el telèfon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcions de visualització"</b></font>" per canviar els contactes visibles"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacte nou"</b></font>" per crear un contacte nou des de zero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/Exporta"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"No teniu cap contacte per mostrar."\n\n"Per afegir contactes, premeu "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" i toqueu:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" per afegir o configurar un compte amb contactes que podeu sincronitzar amb el telèfon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacte nou"</b></font>" per crear un contacte nou des de zero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/Exporta"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"No teniu cap contacte per mostrar. (Si acabeu d\'afegir un compte, la sincronització dels contactes pot trigar una estona.)"\n\n"Per afegir contactes, premeu "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" i toqueu:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" per afegir o configurar un compte amb contactes que podeu sincronitzar amb el telèfon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcions de visualització"</b></font>" per canviar els contactes visibles"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacte nou"</b></font>" per crear un contacte nou des de zero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/Exporta"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"No tens contactes per mostrar."\n\n"Per afegir contactes, prem "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" i toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" per afegir o configurar un compte amb contactes que puguis sincronitzar a la tauleta"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nou contacte"</b></font>" per crear un contacte nou des de zero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/Exporta"</b></font>" per importar contactes de la teva targeta SIM o SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"No tens contactes per mostrar."\n\n"Per afegir contactes, prem "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" i toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" per afegir o configurar un compte amb contactes que puguis sincronitzar amb el telèfon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nou contacte"</b></font>" per crear un contacte nou des de zero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/Exporta"</b></font>" per importar contactes de la teva targeta SIM o SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"No tens contactes per mostrar. (Si acabes d\'afegir un compte, és possible que la sincronització dels contactes trigui una mica.)"\n\n"Per afegir contactes, prem "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" i toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" per afegir o configurar un compte amb contactes que pots sincronitzar a la tauleta"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcions de visualització"</b></font>" per modificar quins contactes són visibles"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nou contacte"</b></font>" per crear un contacte nou des de zero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/Exporta"</b></font>" per importar contactes de la teva targeta SIM o SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"No tens contactes per mostrar. (Si acabes d\'afegir un compte, és possible que la sincronització dels contactes trigui una mica.)"\n\n"Per afegir contactes, prem "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" i toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" per afegir o configurar un compte amb contactes que pots sincronitzar amb el telèfon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcions de visualització"</b></font>" per modificar quins contactes són visibles"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nou contacte"</b></font>" per crear un contacte nou des de zero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/Exporta"</b></font>" per importar contactes de la teva targeta SIM o SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"No tens contactes per mostrar."\n\n"Per afegir contactes, prem "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" i toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" per afegir o configurar un compte amb contactes que puguis sincronitzar a la tauleta"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nou contacte"</b></font>" per crear un contacte nou des de zero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/Exporta"</b></font>" per importar contactes de la teva targeta SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"No tens contactes per mostrar."\n\n"Per afegir contactes, prem "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" i toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" per afegir o configurar un compte amb contactes que puguis sincronitzar amb el telèfon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nou contacte"</b></font>" per crear un contacte nou des de zero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/Exporta"</b></font>" per importar contactes de la teva targeta SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"No tens contactes per mostrar. (Si acabes d\'afegir un compte, és possible que la sincronització dels contactes trigui una mica.)"\n\n"Per afegir contactes, prem "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" i toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" per afegir o configurar un compte amb contactes que pots sincronitzar a la tauleta"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcions de visualització"</b></font>" per modificar quins contactes són visibles"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nou contacte"</b></font>" per crear un contacte nou des de zero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/Exporta"</b></font>" per importar contactes de la teva targeta SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"No tens contactes per mostrar. (Si acabes d\'afegir un compte, és possible que la sincronització dels contactes trigui una mica.)"\n\n"Per afegir contactes, prem "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" i toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" per afegir o configurar un compte amb contactes que pots sincronitzar amb el telèfon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcions de visualització"</b></font>" per modificar quins contactes són visibles"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nou contacte"</b></font>" per crear un contacte nou des de zero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/Exporta"</b></font>" per importar contactes de la teva targeta SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"No teniu preferits."\n\n"Per afegir un contacte a la llista de preferits:"\n\n" "<li>"Toqueu la pestanya"<b>"Contactes"</b>" "\n</li>" "\n<li>"Toqueu el contacte que voleu afegir als preferits"\n</li>" "\n<li>"Toqueu l\'estrella que hi ha al costat del nom del contacte"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Tots els contactes"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Destacats"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Torna la trucada"</string>
<string name="callAgain" msgid="3197312117049874778">"Torna a trucar"</string>
<string name="returnCall" msgid="8171961914203617813">"Retorna la trucada"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min <xliff:g id="SECONDS">%2$s</xliff:g> s"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Contactats sovint"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Addició d\'un contacte"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Voleu afegir \"<xliff:g id="EMAIL">%s</xliff:g>\" als contactes?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Totes"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"un"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dos"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tres"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"S\'ha produït un error en escanejar l\'emmagatzematge USB (motiu: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"No s\'ha pogut explorar la targeta SD (motiu: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Error d\'E/S"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"La memòria és insuficient (és possible que el fitxer sigui massa gran)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"No s\'ha pogut analitzar la vCard per un motiu inesperat"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"No s\'ha pogut analitzar la vCard, tot i que sembla tenir un format vàlid, perquè no és compatible amb la implementació actual."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"No s\'ha trobat cap fitxer vCard a l\'emmagatzematge USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"No s\'ha trobat cap fitxer vCard a la targeta SD"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"No s\'admet aquest format."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"No s\'ha pogut importar la vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"No s\'ha trobat cap fitxer vCard a l\'emmagatzematge USB"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"No s\'ha trobat cap fitxer vCard a la targeta SD"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"S\'ha produït un error en aplegar metainformació dels fitxers vCard especificats."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"No s\'ha pogut importar un o més fitxers (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Error desconegut"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Selecció del fitxer vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"S\'està llegint la vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"S\'estan llegint els fitxers vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"S\'ha produït un error en llegir les dades de la vCard"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contactes"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> fitxers"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"S\'estan desant les vCard(s) a l\'emmagatzematge temporal local"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"L\'importador està desant els vCard a l\'emmagatzematge temporal local. La importació real començarà aviat."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"S\'està important <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"S\'està important <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Error en llegir les dades de vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"S\'ha cancel·lat la lectura de dades de vCard"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Importació de vCard <xliff:g id="FILENAME">%s</xliff:g> finalitzada"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"La importació de <xliff:g id="FILENAME">%s</xliff:g> s\'ha cancel·lat"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> s\'importarà d\'aquí a una estona."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"D\'aquí a poc s\'importarà el fitxer."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"S\'ha rebutjat la sol·licitud per importar vCard. Torna-ho a provar més tard."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> s\'exportarà en breu."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"S\'ha rebutjat la sol·licitud per exportar vCard. Torna-ho a provar més tard."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"contacte"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Confirmació de l\'exportació"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Esteu segur que voleu exportar la llista de contactes a \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"No s\'han pogut exportar les dades de contacte"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Massa fitxers vCard a l\'emmagatzematge USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Hi ha massa fitxers vCard a la targeta SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"El nom de fitxer obligatori és massa llarg (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Exportació de <xliff:g id="FILENAME">%s</xliff:g> finalitzada"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"L\'exportació de <xliff:g id="FILENAME">%s</xliff:g> s\'ha cancel·lat"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"S\'estan exportant les dades de contacte"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"S\'estan exportant les dades de contacte a \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"No s\'ha pogut inicialitzar l\'exportador: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"S\'ha produït un error durant l\'exportació: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"No s\'ha pogut obtenir la informació de la base de dades"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"No hi ha cap contacte que es pugui exportar. Si teniu contactes al telèfon, pot ser que l\'exportació a fora del telèfon estigui prohibida per un proveïdor de dades."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"No hi ha contactes que es puguin exportar. Si realment tens contactes a la tauleta, és possible que alguns proveïdors de dades impedeixin exportar tots els contactes fora de la tauleta."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"No hi ha contactes que es puguin exportar. Si realment tens contactes al teu telèfon, és possible que alguns proveïdors de dades impedeixin exportar tots els contactes fora del telèfon."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"El creador de vCard no s\'ha inicialitzat correctament"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"No s\'ha pogut obrir \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contactes"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"No s\'ha pogut obrir \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%s</xliff:g> contactes"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Cancel·lació de la importació de la vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Estàs segur que vols cancel·lar la importació de <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Cancel·lació de l\'exportació de la vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Estàs segur que vols cancel·lar l\'exportació de <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Error en cancel·lar impor./expor. vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Noms dels contactes"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Afegeix una pausa de 2 segons"</string>
<string name="add_wait" msgid="3360818652790319634">"Afegeix espera"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"No s\'ha trobat cap aplicació per processar aquesta acció"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Recorda aquesta selecció"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Desconegut"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Sense dades"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Esborra els valors predeterminats"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Valors predeterminats per a aquest contacte:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Esborra"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Comptes"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importa/Exporta"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importació/Exportació de contactes"</string>
- <string name="menu_share" msgid="943789700636542260">"Comparteix"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Comparteix el contacte"</string>
<string name="share_via" msgid="563121028023030093">"Comparteix el contacte mitjançant"</string>
<string name="share_error" msgid="4374508848981697170">"Aquest contacte no es pot compartir."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Nom"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organització"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Lloc web"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Esdeveniment"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relació"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grups"</string>
<string name="type_short_home" msgid="7770424864090605384">"C"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"F"</string>
<string name="type_short_pager" msgid="2613818970827594238">"C"</string>
<string name="type_short_other" msgid="5669407180177236769">"A"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Aquest contacte és només de lectura"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Més"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Nom principal"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Crea el contacte en un compte"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Elimina el grup de sincronització"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Afegeix un grup de sincronització"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Tots els altres contactes"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Tots els contactes"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Si s\'elimina \"<xliff:g id="GROUP">%s</xliff:g>\" de la sincronització, també s\'eliminaran els contactes no agrupats de la sincronització."</string>
- <string name="account_phone" msgid="3682950835276226870">"Només telèfon, sense sincronitzar"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Només tauleta, sense sincronitzar"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Només telèfon, sense sincronitzar"</string>
<string name="call_custom" msgid="7756571794763171802">"Truca a <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Truca a casa"</string>
<string name="call_mobile" msgid="7502236805487609178">"Truca al mòbil"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Xateja amb ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Xateja amb Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Fes un xat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adreça"</string>
<string name="postal_street" msgid="8133143961580058972">"Carrer"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Apartat postal"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Barri"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Estat"</string>
<string name="postal_postcode" msgid="572136414136673751">"Codi postal"</string>
<string name="postal_country" msgid="7638264508416368690">"País"</string>
+ <string name="full_name" msgid="6602579550613988977">"Nom"</string>
<string name="name_given" msgid="1687286314106019813">"Nom"</string>
<string name="name_family" msgid="3416695586119999058">"Cognom"</string>
<string name="name_prefix" msgid="59756378548779822">"Prefix del nom"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Cerca als contactes"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Cerca tots els contactes"</string>
<string name="take_photo" msgid="7496128293167402354">"Fes una foto"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Fes una foto nova"</string>
<string name="pick_photo" msgid="448886509158039462">"Seleccioneu una foto de la Galeria"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"La llista de contactes s\'està actualitzant per reflectir el canvi d\'idioma."\n\n"Espereu-vos..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"El contacte s\'està actualitzant."\n\n"Espereu-vos..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"S\'estan actualitzant els contactes. "\n\n"El procés d\'actualització requereix aproximadament <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB d\'emmagatzematge intern del telèfon."\n\n"Trieu una de les opcions següents:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Selecciona una foto nova de la galeria"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"S\'actualitza la llista de contactes per reflectir el canvi d\'idioma."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"S\'actualitza la llista de contactes."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Els contactes s\'estan actualitzant. "\n\n"Aquest procés requereix aproximadament <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mb d’espai de memòria interna. "\n\n"Tria una d\'aquetes opcions:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Desinstal·leu aplicacions"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Torna a provar d\'actualitzar"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Resultats de la cerca per a: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"S\'està cercant..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Mostra la selecció"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Mostra-ho tot"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Selecciona-ho tot"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Anul·la tota la selecció"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"No s\'ha seleccionat cap contacte."</string>
+ <string name="add_field" msgid="2384260056674995230">"Afegeix un altre camp"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"mitjançant <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> mitjançant <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"preferit"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Edita el contacte"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"no combinat"</item>
+ <item quantity="other" msgid="425683718017380845">"combinat de <xliff:g id="COUNT">%0$d</xliff:g> fonts"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Altres"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Ajunta els contactes"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Vols ajuntar el contacte actual amb el contacte seleccionat?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Edita els contactes seleccionats"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Vols canviar per editar el contacte seleccionat? Es copiarà la informació que hagis introduït fins ara."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Copia als meus contactes"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Directori <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"S\'estan cercant tots els contactes"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">" Directori"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Contactes"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Creació d\'una còpia personal"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Selecciona la llista de contactes"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Tots els contactes"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Destacats"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Personalitzada"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Personalitza..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Contactes amb números de telèfon"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Contacte"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Defineix la visualització personalitzada"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Configuració"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Configuració"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Opcions de visualització"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Cerca contactes"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Número de telèfon"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Afegeix-ho als contactes"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Tanca"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Indica un any"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Contacte"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"S\'està carregant…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Crea un contacte nou"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Inicia la sessió a un compte"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importa contactes d\'un fitxer"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Creació d\'un grup nou"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Crea un grup nou]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Canvi del nom del grup"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Supressió d\'un grup"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Estàs segur que vols suprimir el grup \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\"? (Els contactes en si no se suprimiran.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Introdueix el nom del contacte abans d\'unir-lo a un altre contacte."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"S\'ha unit el contacte"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Text copiat"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Descarta els canvis"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Vols descartar els canvis?"</string>
+ <string name="discard" msgid="1234315037371251414">"Descarta"</string>
</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 15258e1..18ebfd5 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Výběr zkratky kontaktu"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Výběr čísla pro hovor"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Výběr čísla pro zprávu"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Výběr kontaktu"</string>
<string name="starredList" msgid="4817256136413959463">"Označené hvězdičkou"</string>
<string name="frequentList" msgid="7154768136473953056">"Časté"</string>
<string name="strequentList" msgid="5640192862059373511">"Oblíbené"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Smazat kontakt"</string>
<string name="menu_call" msgid="3992595586042260618">"Volat kontakt"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Odeslat zprávu kontaktu"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Odeslat e-mail"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Adresa na mapě"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Nastavit jako výchozí číslo"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Nastavit jako výchozí e-mail"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Oddělit"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Kontakty byly odděleny"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Přejmenovat skupinu"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Smazat skupinu"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Nový"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Oddělit kontakt"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Opravdu chcete tento kontakt rozdělit do více kontaktů? Pro každou připojenou kontaktní informaci bude vytvořen samostatný kontakt."</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Spojit"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Smazáním tohoto kontaktu smažete informace z více účtů."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Tento kontakt bude smazán."</string>
<string name="menu_done" msgid="796017761764190697">"Hotovo"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Vrátit zpět"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Zrušit"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Upravit kontakt"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Nový kontakt"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Foneticky"</string>
<string name="label_notes" msgid="8337354953278341042">"Poznámky"</string>
<string name="label_sip_address" msgid="124073911714324974">"Internetový hovor"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Vyzváněcí tón"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Jméno a příjmení"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Jméno (foneticky)"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Společnost"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Název"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Kontakt neexistuje."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Vytvořit nový kontakt"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Vyberte štítek"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-mail"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Chat"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Poštovní adresa"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adresa"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organizace"</item>
<item msgid="7196592230748086755">"Poznámka"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"V telefonu nejsou žádné fotografie."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Ikona kontaktu"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"V tabletu nejsou žádné fotografie."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"V telefonu nejsou žádné fotografie."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Fotografie kontaktu"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Vlastní název štítku"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Možnosti zobrazení"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Možnosti zobrazení"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Přesměrovat hovory přímo do hlasové schránky"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Výchozí nastavení"</string>
- <string name="changePicture" msgid="2943329047610967714">"Změnit ikonu"</string>
- <string name="removePicture" msgid="3041230993155966350">"Odstranit ikonu"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Odebrat fotografii"</string>
<string name="noContacts" msgid="8579310973261953559">"Žádné kontakty."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Nebyly nalezeny žádné odpovídající kontakty."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Ke kontaktům nejsou přiřazena žádná telefonní čísla."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Kontakt byl uložen."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Chyba, změny kontaktu nelze uložit."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Zobrazuje se 1 kontakt s telefonním číslem"</item>
- <item quantity="other" msgid="6133262880804110289">"Počet zobrazených kontaktů s telefonními čísly: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 kontakt s telefonním číslem"</item>
+ <item quantity="other" msgid="3299954047880968205">"Počet kontaktů s telefonními čísly: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Žádné kontakty s telefonními čísly nejsou viditelné"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Ke kontaktům nejsou přiřazena žádná telefonní čísla"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Zobrazuje se 1 kontakt"</item>
- <item quantity="other" msgid="2865867557378939630">"Počet zobrazených kontaktů: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 kontakt"</item>
+ <item quantity="other" msgid="3578469907265375314">"Počet kontaktů: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Žádné kontakty nejsou viditelné"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Žádné kontakty"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Žádné kontakty nejsou viditelné"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Žádné kontakty označené hvězdičkou"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Žádné kontakty v položce <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Nalezen 1 kontakt"</item>
- <item quantity="other" msgid="7752927996850263152">"Počet nalezených kontaktů: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="5517063038754171134">"Počet nalezených položek: 1"</item>
+ <item quantity="other" msgid="3852668542926965042">"Počet nalezených položek: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Kontakt nebyl nalezen"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"nalezeno více položek než <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Nenalezeno"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 kontakt"</item>
- <item quantity="other" msgid="5660384247071761844">"Počet kontaktů: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="4826918429708286628">"Počet nalezených položek: 1"</item>
+ <item quantity="other" msgid="7988132539476575389">"Počet nalezených položek: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Kontakty"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Oblíbené"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Kontakty na kartě SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Nemáte uloženy žádné kontakty, které by bylo možno zobrazit. (Pokud jste účet přidali právě teď, může synchronizace kontaktů trvat několik minut.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Nemáte uloženy žádné kontakty, které by bylo možno zobrazit."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Nemáte žádné kontakty, které by bylo možné zobrazit."\n\n"Chcete-li přidat kontakty, stiskněte tlačítko "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotkněte se položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", pokud chcete přidat nebo nakonfigurovat účet a kontakty v něm synchronizovat s telefonem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", pokud chcete vytvořit úplně nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovat nebo exportovat"</b></font>"."\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Nemáte žádné kontakty, které by bylo možné zobrazit. (Pokud jste právě přidali účet, může synchronizace kontaktů trvat několik minut.)"\n\n"Chcete-li přidat kontakty, stiskněte tlačítko "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotkněte se položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", pokud chcete přidat nebo nakonfigurovat účet a kontakty v něm synchronizovat s telefonem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti zobrazení"</b></font>", pokud chcete změnit, které kontakty budou zobrazeny;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", pokud chcete vytvořit úplně nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovat nebo exportovat"</b></font>"."\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Nemáte žádné kontakty, které by bylo možné zobrazit."\n\n"Chcete-li přidat kontakty, stiskněte tlačítko "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotkněte se položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", pokud chcete přidat nebo nakonfigurovat účet a kontakty v něm synchronizovat s telefonem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", pokud chcete vytvořit úplně nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovat nebo exportovat"</b></font>"."\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Nemáte žádné kontakty, které by bylo možné zobrazit. (Pokud jste právě přidali účet, může synchronizace kontaktů trvat několik minut.)"\n\n"Chcete-li přidat kontakty, stiskněte tlačítko "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotkněte se položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", pokud chcete přidat nebo nakonfigurovat účet a kontakty v něm synchronizovat s telefonem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti zobrazení"</b></font>", pokud chcete změnit, které kontakty budou zobrazeny;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", pokud chcete vytvořit úplně nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovat nebo exportovat"</b></font>"."\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Nemáte žádné kontakty, které by bylo možné zobrazit."\n\n"Chcete-li přidat kontakty, stiskněte tlačítko "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotkněte se položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", pokud chcete přidat nebo nakonfigurovat účet a kontakty v něm synchronizovat s tabletem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", pokud chcete vytvořit úplně nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovat nebo exportovat"</b></font>", pokud chcete importovat kontakty z karty SIM nebo SD."\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Nemáte žádné kontakty, které by bylo možné zobrazit."\n\n"Chcete-li přidat kontakty, stiskněte tlačítko "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotkněte se položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", pokud chcete přidat nebo nakonfigurovat účet a kontakty v něm synchronizovat s telefonem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", pokud chcete vytvořit úplně nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovat nebo exportovat"</b></font>", pokud chcete importovat kontakty z karty SIM nebo SD."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Nemáte žádné kontakty, které by bylo možné zobrazit. (Pokud jste právě přidali účet, může synchronizace kontaktů trvat několik minut.)"\n\n"Chcete-li přidat kontakty, stiskněte tlačítko "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotkněte se položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", pokud chcete přidat nebo nakonfigurovat účet a kontakty v něm synchronizovat s tabletem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti zobrazení"</b></font>", pokud chcete změnit, které kontakty budou zobrazeny;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", pokud chcete vytvořit úplně nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovat nebo exportovat"</b></font>", pokud chcete importovat kontakty z karty SIM nebo SD."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Nemáte žádné kontakty, které by bylo možné zobrazit. (Pokud jste právě přidali účet, může synchronizace kontaktů trvat několik minut.)"\n\n"Chcete-li přidat kontakty, stiskněte tlačítko "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotkněte se položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", pokud chcete přidat nebo nakonfigurovat účet a kontakty v něm synchronizovat s telefonem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti zobrazení"</b></font>", pokud chcete změnit, které kontakty budou zobrazeny;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", pokud chcete vytvořit úplně nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovat nebo exportovat"</b></font>", pokud chcete importovat kontakty z karty SIM nebo SD."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Nemáte žádné kontakty, které by bylo možné zobrazit."\n\n"Chcete-li přidat kontakty, stiskněte tlačítko "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotkněte se položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", pokud chcete přidat nebo nakonfigurovat účet a kontakty v něm synchronizovat s tabletem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", pokud chcete vytvořit úplně nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovat nebo exportovat"</b></font>", pokud chcete importovat kontakty z karty SD."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Nemáte žádné kontakty, které by bylo možné zobrazit."\n\n"Chcete-li přidat kontakty, stiskněte tlačítko "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotkněte se položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", pokud chcete přidat nebo nakonfigurovat účet a kontakty v něm synchronizovat s telefonem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", pokud chcete vytvořit úplně nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovat nebo exportovat"</b></font>", pokud chcete importovat kontakty z karty SD."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Nemáte žádné kontakty, které by bylo možné zobrazit. (Pokud jste právě přidali účet, může synchronizace kontaktů trvat několik minut.)"\n\n"Chcete-li přidat kontakty, stiskněte tlačítko "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotkněte se položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", pokud chcete přidat nebo nakonfigurovat účet a kontakty v něm synchronizovat s tabletem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti zobrazení"</b></font>", pokud chcete změnit, které kontakty budou zobrazeny;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", pokud chcete vytvořit úplně nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovat nebo exportovat"</b></font>", pokud chcete importovat kontakty z karty SD."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Nemáte žádné kontakty, které by bylo možné zobrazit. (Pokud jste právě přidali účet, může synchronizace kontaktů trvat několik minut.)"\n\n"Chcete-li přidat kontakty, stiskněte tlačítko "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotkněte se položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", pokud chcete přidat nebo nakonfigurovat účet a kontakty v něm synchronizovat s telefonem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti zobrazení"</b></font>", pokud chcete změnit, které kontakty budou zobrazeny;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", pokud chcete vytvořit úplně nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovat nebo exportovat"</b></font>", pokud chcete importovat kontakty z karty SD."\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Nemáte žádné oblíbené kontakty."\n\n"Přidání kontaktu do seznamu oblíbených:"\n\n" "<li>"Dotkněte se karty "<b>"Kontakty"</b>"."\n</li>" "\n<li>"Dotkněte se kontaktu, který chcete přidat mezi oblíbené."\n</li>" "\n<li>"Dotkněte se hvězdičky vedle jména kontaktu."\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Všechny kontakty"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Označené hvězdičkou"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Zavolat zpět"</string>
<string name="callAgain" msgid="3197312117049874778">"Zavolat znovu"</string>
<string name="returnCall" msgid="8171961914203617813">"Zpětné volání"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min <xliff:g id="SECONDS">%2$s</xliff:g> s"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Nejčastěji používané kontakty"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Přidat kontakt"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Chcete přidat „<xliff:g id="EMAIL">%s</xliff:g>“ do kontaktů?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Všechny"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"jedna"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dvě"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tři"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Vyhledávání v úložišti USB se nezdařilo (Důvod: „<xliff:g id="FAIL_REASON">%s</xliff:g>“)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Vyhledávání na kartě SD se nezdařilo. (Důvod: <xliff:g id="FAIL_REASON">%s</xliff:g>)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Chyba V/V"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Není k dispozici dostatek paměti (soubor může být příliš velký)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Analýza karty vCard se z neznámého důvodu nezdařila."</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Analýza karty vCard se nezdařila. Přestože se zřejmě jedná o správný formát, aktuální implementace jej nepodporuje."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"V úložišti USB nebyl nalezen žádný soubor vCard"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Na kartě SD nebyl nalezen žádný soubor vCard"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Formát není podporován."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Import karty vCard se nezdařil"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"V úložišti USB nebyl nalezen žádný soubor vCard"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Na kartě SD nebyl nalezen žádný soubor vCard"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Shromáždění metainformací zadaných souborů vCard se nezdařilo."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Import jednoho nebo více souborů se nezdařil (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Neznámá chyba"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Výběr souboru vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Čtení karty vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Čtení souborů vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Čtení dat karty vCard se nezdařilo"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> z <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontaktů"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> z <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> souborů"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Načítání vizitek vCard do mezipaměti místního dočasného úložiště"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Nástroj pro import načítá vizitky vCard do mezipaměti místního dočasného úložiště. Vlastní import bude zahájen v krátké době."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Probíhá import: <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Probíhá import: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Čtení dat vizitky vCard se nezdařilo."</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Čtení dat vizitky vCard bylo zrušeno"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Import souboru vCard byl dokončen <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Import souboru <xliff:g id="FILENAME">%s</xliff:g> byl zrušen."</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"Soubor <xliff:g id="FILENAME">%s</xliff:g> bude za okamžik importován."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Soubor bude zakrátko importován."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Požadavek na import vizitek vCard byl zamítnut. Zkuste to prosím později."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"Soubor <xliff:g id="FILENAME">%s</xliff:g> bude za okamžik exportován."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Požadavek na export vizitek vCard byl zamítnut. Zkuste to prosím později."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"kontakt"</string>
+ <string name="percentage" msgid="34897865327092209">"%s %%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Potvrdit export"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Opravdu chcete exportovat seznam kontaktů do souboru <xliff:g id="VCARD_FILENAME">%s</xliff:g>?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Export dat kontaktů se nezdařil"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"V úložišti USB je příliš mnoho souborů vCard"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Na kartě SD je příliš mnoho souborů vCard"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Požadovaný název souboru (<xliff:g id="FILENAME">%s</xliff:g>) je příliš dlouhý"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Export souboru <xliff:g id="FILENAME">%s</xliff:g> byl dokončen."</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Export souboru <xliff:g id="FILENAME">%s</xliff:g> byl zrušen"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Export dat kontaktů"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Probíhá export dat kontaktů do souboru <xliff:g id="FILE_NAME">%s</xliff:g>"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Nelze spustit exportní program: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Při exportu došlo k chybě: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Načtení informací z databáze se nezdařilo"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Nelze exportovat žádné kontakty. Pokud máte v telefonu skutečně uložené kontakty, je možné, že některý poskytovatel datových služeb zakázal jejich export mimo telefon."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Nelze exportovat žádné kontakty. Pokud máte v tabletu skutečně uložené kontakty, je možné, že některý poskytovatel datových služeb zakázal jejich export mimo tablet."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Nelze exportovat žádné kontakty. Pokud máte v telefonu skutečně uložené kontakty, je možné, že některý poskytovatel datových služeb zakázal jejich export mimo telefon."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Editor karty vCard není správně inicializován"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Soubor <xliff:g id="FILE_NAME">%1$s</xliff:g> nelze otevřít: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> z <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontaktů"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Soubor <xliff:g id="FILE_NAME">%s</xliff:g> nelze otevřít: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> z <xliff:g id="TOTAL_NUMBER">%s</xliff:g> kontaktů"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Rušení importu souboru vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Opravdu chcete import souboru <xliff:g id="FILENAME">%s</xliff:g> zrušit?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Rušení exportu souboru vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Opravdu chcete zrušit export souboru <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Import/export souboru vCard nelze zrušit"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Jména vašich kontaktů"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Přidat 2s pauzu"</string>
<string name="add_wait" msgid="3360818652790319634">"Přidat čekání"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Aplikace potřebná k provedení této akce nebyla nalezena"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Zapamatovat tuto volbu"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Neznámé"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Žádná data"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Vymazat výchozí nastavení"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Výchozí nastavení kontaktu:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Vymazat"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Účty"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importovat/Exportovat"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importovat nebo exportovat kontakty"</string>
- <string name="menu_share" msgid="943789700636542260">"Sdílet"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Sdílet kontakt"</string>
<string name="share_via" msgid="563121028023030093">"Sdílet kontakt pomocí"</string>
<string name="share_error" msgid="4374508848981697170">"Tento kontakt nelze sdílet."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Jméno"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organizace"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Webové stránky"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Událost"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Vztah"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Skupiny"</string>
<string name="type_short_home" msgid="7770424864090605384">"D"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"P"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Tento kontakt je pouze pro čtení"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Více"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Primární jméno"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Vytvořit kontakt na základě účtu"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Odstranit synchronizovanou skupinu"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Přidat synchronizovanou skupinu"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Všechny ostatní kontakty"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Všechny kontakty"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Odebráním skupiny <xliff:g id="GROUP">%s</xliff:g> ze synchronizace odeberete ze synchronizace také všechny kontakty mimo skupinu."</string>
- <string name="account_phone" msgid="3682950835276226870">"Pouze v telefonu, nesynchronizováno"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Pouze v tabletu, nesynchronizováno"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Pouze v telefonu, nesynchronizováno"</string>
<string name="call_custom" msgid="7756571794763171802">"Volat kontakt <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Volat domů"</string>
<string name="call_mobile" msgid="7502236805487609178">"Volat mobil"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chatovat pomocí ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chatovat pomocí Jabberu"</string>
<string name="chat" msgid="9025361898797412245">"Chat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adresa"</string>
<string name="postal_street" msgid="8133143961580058972">"Ulice"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Číslo poštovní schránky"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Čtvrť"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Stát"</string>
<string name="postal_postcode" msgid="572136414136673751">"PSČ"</string>
<string name="postal_country" msgid="7638264508416368690">"Země"</string>
+ <string name="full_name" msgid="6602579550613988977">"Jméno"</string>
<string name="name_given" msgid="1687286314106019813">"Křestní jméno"</string>
<string name="name_family" msgid="3416695586119999058">"Příjmení"</string>
<string name="name_prefix" msgid="59756378548779822">"Titul před jménem"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Vyhledat kontakty"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Vyhledat všechny kontakty"</string>
<string name="take_photo" msgid="7496128293167402354">"Vyfotit"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Pořídit novou fotografii"</string>
<string name="pick_photo" msgid="448886509158039462">"Vybrat fotografii z galerie"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"V souvislosti se změnou jazyka probíhá aktualizace seznamu kontaktů."\n\n"Čekejte prosím..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Aktualizace seznamu kontaktů."\n\n"Čekejte prosím..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Probíhá upgrade kontaktů. "\n\n"Upgrade vyžaduje přibližně <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB vnitřní paměti telefonu. "\n\n"Zvolte jednu z následujících možností:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Vybrat novou fotografii z Galerie"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Probíhá aktualizace seznamu kontaktů související se změnou jazyka."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Probíhá aktualizace seznamu kontaktů."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Probíhá upgrade seznamu kontaktů. "\n\n"Tento proces vyžaduje přibližně <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB volného místa v interním úložišti."\n\n"Vyberte jednu z následujících možností:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Odinstalovat některé aplikace"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Zkusit upgradovat znovu"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Výsledky vyhledávání pro dotaz: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Vyhledávání..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Zobrazit vybrané"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Zobrazit vše"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Vybrat vše"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Zrušit výběr všech"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"Počet vybraných příjemců: 1"</item>
+ <item quantity="other" msgid="4608837420986126229">"Počet vybraných příjemců: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Nevybrali jste žádné kontakty."</string>
+ <string name="add_field" msgid="2384260056674995230">"Přidat další pole"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"pomocí služby <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> prostřednictvím služby <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"oblíbené"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Upravit kontakt"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"sloučení se nezdařilo"</item>
+ <item quantity="other" msgid="425683718017380845">"sloučeno ze <xliff:g id="COUNT">%0$d</xliff:g> zdrojů"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Ostatní"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Spojit kontakty"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Spojit aktuální kontakt s vybraným kontaktem?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Upravit vybrané kontakty"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Přepnout do režimu úpravy vybraného kontaktu? Doposud zadané informace budou zkopírovány."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Zkopírovat do mých kontaktů"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Adresář <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Vyhledávání ve všech kontaktech"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Adresář"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Kontakty"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Vytváření osobní kopie"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Vyberte seznam kontaktů"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Všechny kontakty"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Označené hvězdičkou"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Vlastní"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Přizpůsobit..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Kontakty s telefonním číslem"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kontakt"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Definice vlastního zobrazení"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Nastavení"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Nastavení"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Možnosti zobrazení"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Najít kontakty"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Telefonní číslo"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Přidat do kontaktů"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Zavřít"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Uvést rok"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Kontakt"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Načítání…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Vytvořit nový kontakt"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Přihlásit se do účtu"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importovat kontakty ze souboru"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Vytvořit novou skupinu"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Vytvořit novou skupinu]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Přejmenovat skupinu"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Smazat skupinu"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Opravdu chcete smazat skupinu <xliff:g id="GROUP_LABEL">%1$s</xliff:g>? (Samotné kontakty smazány nebudou.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Před spojením s jiným kontaktem je třeba zadat jméno kontaktu."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Připojený kontakt"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Text zkopírován"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Zrušit změny"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Opravdu chcete změny zrušit?"</string>
+ <string name="discard" msgid="1234315037371251414">"Zrušit"</string>
</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 0e743df..7cc317c 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Vælg en kontaktgenvej"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Vælg et nummer at ringe til"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Vælg et nummer, som beskeden skal sendes til"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Vælg en kontaktperson"</string>
<string name="starredList" msgid="4817256136413959463">"Stjernemarkerede"</string>
<string name="frequentList" msgid="7154768136473953056">"Ofte"</string>
<string name="strequentList" msgid="5640192862059373511">"Favorit"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Slet kontakt"</string>
<string name="menu_call" msgid="3992595586042260618">"Ring til kontakt"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Send sms til kontakt"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Send e-mail"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Kortadresse"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Gør til standardnummer"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Benyt e-mail som standard"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Opdel"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Kontakterne blev opdelt"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Omdøb gruppe"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Slet gruppe"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Ny"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Opdel kontakt"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Er du sikker på, at du vil opdele denne ene kontakt i flere kontakter: én for hver sæt af de indeholdte kontaktoplysninger?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Føj til"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Sletning af denne kontakt sletter oplysninger fra flere konti."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Denne kontakt slettes."</string>
<string name="menu_done" msgid="796017761764190697">"Udfør"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Annuller"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Annuller"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Rediger kontakt"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Ny kontakt"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetisk"</string>
<string name="label_notes" msgid="8337354953278341042">"Noter"</string>
<string name="label_sip_address" msgid="124073911714324974">"Internetopkald"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Ringetone"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Fornavn og efternavn"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Fonetisk navn"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Virksomhed"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Titel"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Kontakten eksisterer ikke."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Opret ny kontakt"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Vælg etiket"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-mail"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"IM"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Postadresse"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adresse"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organisation"</item>
<item msgid="7196592230748086755">"Note"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Der er ingen tilgængelige billeder på telefonen."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Kontaktikon"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Der er ingen tilgængelige billeder på tabletcomputeren."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Der er ingen tilgængelige billeder på telefonen."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Billede af kontaktperson"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Navn på tilpasset etiket"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Indstillinger for visning"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Indstillinger for visning"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Send opkald direkte til voicemail"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Standard"</string>
- <string name="changePicture" msgid="2943329047610967714">"Skift ikon"</string>
- <string name="removePicture" msgid="3041230993155966350">"Fjern ikon"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Fjern billede"</string>
<string name="noContacts" msgid="8579310973261953559">"Der er ingen kontakter."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Der blev ikke fundet nogen matchende kontakter."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Der er ingen kontakter med telefonnumre."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Kontakten er gemt."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Fejl, kunne ikke gemme ændringer af kontakter"</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Viser 1 kontakt med telefonnummer"</item>
- <item quantity="other" msgid="6133262880804110289">"Viser <xliff:g id="COUNT">%d</xliff:g> kontaktpersoner med telefonnumre"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 kontakt med telefonnummer"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> kontaktpersoner med telefonnumre"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Der er ingen synlige kontakter med telefonnumre"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Der er ingen kontakter med telefonnumre"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Viser 1 kontakt"</item>
- <item quantity="other" msgid="2865867557378939630">"Viser <xliff:g id="COUNT">%d</xliff:g> kontaktpersoner"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 kontaktperson"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> kontaktpersoner"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Ingen synlige kontakter"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Ingen kontakter"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Ingen synlige kontakter"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Ingen stjernemarkerede kontakter"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Ingen kontakter i <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Fandt 1 kontakt"</item>
- <item quantity="other" msgid="7752927996850263152">"Fandt <xliff:g id="COUNT">%d</xliff:g> kontaktpersoner"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 fundet"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> fundet"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Kontakten blev ikke fundet"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"mere end <xliff:g id="COUNT">%d</xliff:g> fundet"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Ikke fundet"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 kontaktperson"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> kontaktpersoner"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 fundet"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> fundet"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Kontakter"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favorit"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Kontakter på SIM-kort"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Du har ingen kontaktpersoner. (Hvis du lige har tilføjet en konto, kan det tage et par minutter at synkronisere kontaktpersoner)."</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Du har ingen kontaktpersoner."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Du har ingen kontaktpersoner, der kan vises."\n\n"Hvis du vil tilføje kontaktpersoner, skal du trykke på "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" og på"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>" for at tilføje eller konfigurere en konto med kontaktpersoner, som du kan synkronisere med telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontaktperson"</b></font>" for at oprette en ny kontaktperson helt fra bunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Du har ingen kontaktpersoner, der kan vises. (Hvis du lige har tilføjet en konto, kan det tage et par minutter at synkronisere kontaktpersoner)."\n\n"Hvis du vil tilføje kontaktpersoner, skal du trykke på "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" og på"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>" for at tilføje eller konfigurere en konto med kontaktpersoner, du kan synkronisere til telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsindstillinger"</b></font>" for at ændre, hvilke kontaktpersoner der er synlige"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontaktperson"</b></font>" for at oprette en ny kontaktperson fra bunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Du har ingen kontaktpersoner, der kan vises."\n\n"Hvis du vil tilføje kontaktpersoner, skal du trykke på "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" og på"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>" for at tilføje eller konfigurere en konto med kontaktpersoner, du kan synkronisere med telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontaktperson"</b></font>" for at oprette en ny kontaktperson fra bunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Du har ingen kontaktpersoner, der kan vises. (Hvis du lige har tilføjet en konto, kan det tage et par minutter at synkronisere kontaktpersonerne)."\n\n"Hvis du vil tilføje kontaktpersoner, skal du trykke på "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" og på"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>" for at tilføje eller konfigurere en konto med kontaktpersoner, du kan synkronisere til telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsindstillinger"</b></font>" for at ændre, hvilke kontaktpersoner der er synlige"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontaktperson"</b></font>" for at oprette en ny kontaktperson fra bunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Du har ingen kontaktpersoner."\n\n"Hvis du vil tilføje kontaktpersoner, skal du trykke på "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" og "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>" for at tilføje eller konfigurere en konto med kontaktpersoner, som du kan synkronisere med tabletcomputeren"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontaktperson"</b></font>" for at oprette en helt ny kontaktperson"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>" for at importere kontaktpersoner fra dit SIM-kort eller SD-kort"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Du har ingen kontaktpersoner."\n\n"Hvis du vil tilføje kontaktpersoner, skal du trykke på "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" og "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>" for at tilføje eller konfigurere en konto med kontaktpersoner, som du kan synkronisere med telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontaktperson"</b></font>" for at oprette en helt ny kontaktperson"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>" for at importere kontaktpersoner fra dit SIM-kort eller SD-kort"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Du har ingen kontaktpersoner. (Hvis du lige har tilføjet en konto, kan det tage et par minutter at synkronisere kontaktpersoner)."\n\n"Hvis du vil tilføje kontaktpersoner, skal du trykke på "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" og "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>" for at tilføje eller konfigurere en konto med kontaktpersoner, som du kan synkronisere til tabletcomputeren"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsindstillinger"</b></font>" for at ændre, hvilke kontaktpersoner der er synlige"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontaktperson"</b></font>" for at oprette en helt ny kontaktperson"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>" for at importere kontaktpersoner fra dit SIM-kort eller SD-kort"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Du har ingen kontaktpersoner. (Hvis du lige har tilføjet en konto, kan det tage et par minutter at synkronisere kontaktpersoner)."\n\n"Hvis du vil tilføje kontaktpersoner, skal du trykke på "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" og "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>" for at tilføje eller konfigurere en konto med kontaktpersoner, som du kan synkronisere til telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsindstillinger"</b></font>" for at ændre, hvilke kontaktpersoner der er synlige"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontaktperson"</b></font>" for at oprette en helt ny kontaktperson"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>" for at importere kontaktpersoner fra dit SIM-kort eller SD.kort"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Du har ingen kontaktpersoner."\n\n"Hvis du vil tilføje kontaktpersoner, skal du trykke på "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" og derefter på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>" for at tilføje eller konfigurere en konto med kontaktpersoner, som du kan synkronisere med tabletcomputeren"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontaktperson"</b></font>" for at oprette en helt ny kontaktperson "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>" for at importere kontaktpersoner fra dit SD-kort"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Du har ingen kontaktpersoner."\n\n"Hvis du vil tilføje kontaktpersoner, skal du trykke på "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" og "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>" for at tilføje eller konfigurere en konto med kontaktpersoner, som du kan synkronisere med telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontaktperson"</b></font>" for at oprette en helt ny kontaktperson "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>" for at importere kontaktpersoner fra dit SD-kort"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Du har ingen kontaktpersoner. (Hvis du lige har tilføjet en konto, kan det tage et par minutter at synkronisere kontaktpersoner)."\n\n"Hvis du vil tilføje kontaktpersoner, skal du trykke på "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" og "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>" for at tilføje eller konfigurere en konto med kontaktpersoner, som du kan synkronisere til tabletcomputeren"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsindstillinger"</b></font>" for at ændre, hvilke kontaktpersoner der er synlige"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontaktperson"</b></font>" for at oprette en helt ny kontaktperson"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>" for at importere kontaktpersoner fra dit SD-kort"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Du har ingen kontaktpersoner. (Hvis du lige har tilføjet en konto, kan det tage et par minutter at synkronisere kontaktpersoner)."\n\n"Hvis du vil tilføje kontaktpersoner, skal du trykke på "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" og "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>" for at tilføje eller konfigurere en konto med kontaktpersoner, som du kan synkronisere til telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsindstillinger"</b></font>" for at ændre, hvilke kontaktpersoner der er synlige"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontaktperson"</b></font>" for at oprette en ny kontaktperson fra bunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>" for at importere kontaktpersoner fra dit SD-kort"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Du har ingen favoritter."\n\n"Sådan føjer du en kontaktperson til din liste over favoritter:"\n\n" "<li>"Tryk på fanen "<b>"Kontaktpersoner"</b>\n</li>" "\n<li>"Tryk på den kontaktperson, du ønsker at føje til dine foretrukne"\n</li>" "\n<li>"Tryk på stjerne ud for kontaktpersonens navn"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Alle kontakter"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Stjernemarkerede"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Ring tilbage"</string>
<string name="callAgain" msgid="3197312117049874778">"Ring op igen"</string>
<string name="returnCall" msgid="8171961914203617813">"Ring tilbage"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min. <xliff:g id="SECONDS">%2$s</xliff:g> sek."</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> sek."</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Jævnligt kontaktet"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Tilføj kontakt"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Føj \"<xliff:g id="EMAIL">%s</xliff:g>\" til kontaktpersoner?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Alle"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"et"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"to"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tre"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Scanningen af USB-lager mislykkedes (årsag: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Scanningen af SD-kortet mislykkedes: (Årsag: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"I/O-fejl"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Der er ikke nok hukommelse (filen er muligvis for stor)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"vCard kunne ikke parses pga. en uventet årsag"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"vCard kunne ikke parses, selv om det ser ud til at være et gyldigt format, da den nuværende implementering ikke understøtter det"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Der blev ikke fundet nogen vCard-fil i USB-lageret"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Der blev ikke fundet nogen VCard-fil på SD-kortet"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Formatet understøttes ikke."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"vCard kunne ikke importeres"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Der blev ikke fundet nogen vCard-fil i USB-lageret"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Der blev ikke fundet nogen vCard-fil på SD-kortet"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Metaoplysninger om angivne vCard-filer kunne ikke hentes."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"En eller flere filer blev ikke importeret (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Ukendt fejl"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Vælg vCard-fil"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Læser vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Læser vCard-fil(er)"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Læsning af vCard-data mislykkedes"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> af <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontakter"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> af <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> filer"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Gemmer vCard-fil(er) midlertidigt på lokal lagerplads"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Importøren gemmer vCard-fil(er) midlertidigt på lokal lagerplads. Den egentlige import begynder snart."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importerer <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importerer <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"vCard-data kunne ikke læses"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Læsning af vCard-data blev annulleret"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Import af vCard afsluttet <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Import af <xliff:g id="FILENAME">%s</xliff:g> blev annulleret"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> importeres om et øjeblik."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Filen importeres inden længe."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Anmodning om vCard-import er afslået. Prøv igen senere."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> eksporteres om et øjeblik."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Anmodning om vCard-eksport er afslået. Prøv igen senere."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"kontakt"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Bekræft eksport"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Er du sikker på, at du vil eksportere listen over kontaktpersoner til \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Eksport af kontaktdata mislykkedes"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Der er for mange vCard-filer i USB-lager"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Der er for mange vCard-data på SD-kortet"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Det krævede filnavn er for langt (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Eksport af <xliff:g id="FILENAME">%s</xliff:g> afsluttet"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Eksport af <xliff:g id="FILENAME">%s</xliff:g> blev annulleret"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Eksporterer kontaktdata"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Kontaktdata eksporteres til \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Eksportfunktionen kunne ikke startes: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Der opstod en fejl under eksport: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Kunne ikke hente databaseoplysninger"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Der er ingen kontaktpersoner, der kan eksporteres. Hvis du har kontaktpersoner på din telefon, har en dataudbyder muligvis forbudt, at kontaktpersonerne eksporteres fra telefonen."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Der er ingen kontaktpersoner, der kan eksporteres. Hvis du har kontaktpersoner på din tabletcomputer, har en dataudbyder muligvis forbudt, at kontaktpersonerne eksporteres fra tabletcomputeren."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Der er ingen kontaktpersoner, der kan eksporteres. Hvis du har kontaktpersoner på din telefon, har en dataudbyder muligvis forbudt, at kontaktpersonerne eksporteres fra telefonen."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"vCard-oprettelse ikke korrekt initialiseret"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"\"<xliff:g id="FILE_NAME">%1$s</xliff:g>\" kunne ikke åbnes: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> af <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontakter"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"\"<xliff:g id="FILE_NAME">%s</xliff:g>\" kunne ikke åbnes: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> af <xliff:g id="TOTAL_NUMBER">%s</xliff:g> kontakter"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Annullerer import af vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Er du sikker på, at du vil annullere import af <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Annullerer eksport af vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Er du sikker på, at du vil annullere eksport af <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Import/eksport af vCard ikke annulleret"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Navne på dine kontakter"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Tilføj pause på 2 sek."</string>
<string name="add_wait" msgid="3360818652790319634">"Tilføj Vent"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Der blev ikke fundet noget program til denne handling"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Husk dette valg"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Ukendte"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Ingen data"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Ryd standarder"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Standardindstillinger for denne kontakt:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Ryd"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Konti"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Import/eksport"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importer/eksporter kontakter"</string>
- <string name="menu_share" msgid="943789700636542260">"Del"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Del kontaktperson"</string>
<string name="share_via" msgid="563121028023030093">"Del kontakt via"</string>
<string name="share_error" msgid="4374508848981697170">"Denne kontakt kan ikke deles."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Navn"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organisation"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Websted"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Begivenhed"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Forhold"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grupper"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"O"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Denne kontakt er skrivebeskyttet"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Flere"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Primærnavn"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Opret kontakt under konto"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Fjern synkroniseringsgruppe"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Tilføj synkroniseringsgruppe"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Alle andre kontakter"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Alle kontakter"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Hvis \'<xliff:g id="GROUP">%s</xliff:g>\' fjernes fra synkronisering, vil kontaktpersoner, der ikke er i grupper, også fjernes fra synkroniseringen."</string>
- <string name="account_phone" msgid="3682950835276226870">"Kun telefon, ikke synkroniseret"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Kun tabletcomputer, ikke synkroniseret"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Kun telefon, ikke synkroniseret"</string>
<string name="call_custom" msgid="7756571794763171802">"Ring til <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Ring hjem"</string>
<string name="call_mobile" msgid="7502236805487609178">"Ring til mobil"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chat ved hjælp af ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chat ved hjælp af Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Chat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adresse"</string>
<string name="postal_street" msgid="8133143961580058972">"Gade"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Postboks"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Nabolag"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Stat"</string>
<string name="postal_postcode" msgid="572136414136673751">"Postnummer"</string>
<string name="postal_country" msgid="7638264508416368690">"Land"</string>
+ <string name="full_name" msgid="6602579550613988977">"Navn"</string>
<string name="name_given" msgid="1687286314106019813">"Fornavn"</string>
<string name="name_family" msgid="3416695586119999058">"Efternavn"</string>
<string name="name_prefix" msgid="59756378548779822">"Navnepræfiks"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Søg i kontakter"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Søg efter alle kontaktpersoner"</string>
<string name="take_photo" msgid="7496128293167402354">"Tag billede"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Tag nyt billede"</string>
<string name="pick_photo" msgid="448886509158039462">"Vælg foto fra Galleri"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Kontaktlisten opdateres for at afspejle ændringen af sprog."\n\n"Vent et øjeblik..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Listen over kontaktpersoner opdateres."\n\n"Vent et øjeblik ..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Kontaktpersonerne er ved at blive opgraderet. "\n\n"Opgraderingen kræver ca. <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB intern lagerplads på telefonen."\n\n"Vælg et af følgende:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Vælg nyt billede fra Galleri"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Listen over kontaktpersoner opdateres for at afspejle det nye sprog."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Listen over kontaktpersoner opdateres."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Kontaktpersoner opgraderes."\n\n" Opgraderingsprocessen kræver ca. <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mb intern hukommelse."\n\n"Vælg én af følgende muligheder:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Afinstaller nogle programmer"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Prøv at opgradere igen"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Søgeresultater for: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Søger ..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Vis valgte"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Vis alle"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Vælg alle"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Fravælg alle"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"1 modtager er valgt"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g> modtagere er valgt"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Ingen kontaktpersoner er valgt."</string>
+ <string name="add_field" msgid="2384260056674995230">"Tilføj et felt mere"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"foretrukken"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Rediger kontaktperson"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"blev ikke flettet"</item>
+ <item quantity="other" msgid="425683718017380845">"flettet fra <xliff:g id="COUNT">%0$d</xliff:g> kilder"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Andre"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Saml kontaktpersoner"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Forbind den aktuelle kontaktperson med den valgte kontaktperson?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Rediger valgte kontaktpersoner"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Vil du redigere den valgte kontaktperson? Dine indtastninger kopieres."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Kopier til mine kontaktpersoner"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Indeks <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Søger i alle kontaktpersoner"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Indeks"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Kontaktpersoner"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Oprettelse af privat kopi"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Vælg liste over kontaktpersoner"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Alle kontaktpersoner"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Stjernemarkerede"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Tilpasset"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Tilpas..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Kontaktpersoner med telefonnumre"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kontaktperson"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Angiv tilpasset visning"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Indstillinger"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Indstillinger"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Indstillinger for visning"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Find kontaktpersoner"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Telefonnummer"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Føj til kontaktpersoner"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Luk"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Angiv et år"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Kontaktperson"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Indlæser…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Opret ny kontakt"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Log ind på en konto"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importer kontrakter fra en fil"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Opret en ny gruppe"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Opret en ny gruppe]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Omdøb gruppe"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Slet gruppe"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Er du sikker på, at du vil slette gruppen \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\"? (Selve kontakterne vil ikke blive slettet)."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Indtast kontaktpersonens navn, før du forbinder med en anden kontakt."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Samlet kontaktperson"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Kopieret tekst"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Kasser ændringer"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Er du sikker på, at du vil kassere dine ændringer?"</string>
+ <string name="discard" msgid="1234315037371251414">"Kasser"</string>
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index fc25897..739de54 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Verknüpfung für Kontakt auswählen"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Nummer für den Anruf auswählen"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Nummer für Nachricht auswählen"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Kontakt auswählen"</string>
<string name="starredList" msgid="4817256136413959463">"Markiert"</string>
<string name="frequentList" msgid="7154768136473953056">"Häufig"</string>
<string name="strequentList" msgid="5640192862059373511">"Favoriten"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Kontakt löschen"</string>
<string name="menu_call" msgid="3992595586042260618">"Kontakt anrufen"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Textnachricht an Kontakt"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"E-Mail senden"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Adresse auf der Karte"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Als Standardnr. festlegen"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Standard-E-Mail festlegen"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Teilen"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Kontakte unterteilt"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Gruppe umbenennen"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Gruppe löschen"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Neu"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Kontakt teilen"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Sind Sie sicher, dass Sie diesen Kontakt in mehrere Kontakte teilen möchten, einen für jedes darin enthaltene Kontaktdetail?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Zusammenführen"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Wenn Sie diesen Kontakt löschen, werden Informationen aus mehreren Konten gelöscht."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Dieser Kontakt wird gelöscht."</string>
<string name="menu_done" msgid="796017761764190697">"Fertig"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Rückgängig"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Abbrechen"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Kontakt bearbeiten"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Neuer Kontakt"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Phonetisch"</string>
<string name="label_notes" msgid="8337354953278341042">"Notizen"</string>
<string name="label_sip_address" msgid="124073911714324974">"Internetanruf"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Klingelton"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Vor- und Nachname"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Phonetischer Name"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Unternehmen"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Titel"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Dieser Kontakt existiert nicht."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Neuen Kontakt erstellen"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Wählen Sie ein Label aus."</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-Mail"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Chat"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Postanschrift"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adresse"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Firma/Organisation"</item>
<item msgid="7196592230748086755">"Notiz"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Auf dem Telefon sind keine Bilder verfügbar."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Kontaktsymbol"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Auf dem Tablet sind keine Bilder verfügbar."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Auf dem Telefon sind keine Bilder verfügbar."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Kontaktbild"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Name des benutzerdef. Labels"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Anzeigeoptionen"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Anzeigeoptionen"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Anrufe direkt an Mailbox senden"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Standard"</string>
- <string name="changePicture" msgid="2943329047610967714">"Symbol ändern"</string>
- <string name="removePicture" msgid="3041230993155966350">"Symbol entfernen"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Foto entfernen"</string>
<string name="noContacts" msgid="8579310973261953559">"Keine Kontakte"</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Keine passenden Kontakte gefunden"</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Keine Kontakte mit Telefonnummern"</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Kontakt gespeichert"</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Fehler, Kontaktänderungen konnten nicht gespeichert werden."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"1 Kontakt mit Telefonnummer wird angezeigt."</item>
- <item quantity="other" msgid="6133262880804110289">"Zeigt <xliff:g id="COUNT">%d</xliff:g> Kontakte mit Telefonnummern an"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 Kontakt mit Telefonnummer"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> Kontakte mit Telefonnummern"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Keine sichtbaren Kontakte mit Telefonnummern"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Keine Kontakte mit Telefonnummern"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"1 Kontakt wird angezeigt."</item>
- <item quantity="other" msgid="2865867557378939630">"<xliff:g id="COUNT">%d</xliff:g> Kontakte werden angezeigt."</item>
+ <item quantity="one" msgid="3405747744700823280">"1 Kontakt"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> Kontakte"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Keine sichtbaren Kontakte"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Keine Kontakte"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Keine sichtbaren Kontakte"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Keine markierten Kontakte"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Keine Kontakte in <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"1 Kontakt gefunden"</item>
- <item quantity="other" msgid="7752927996850263152">"<xliff:g id="COUNT">%d</xliff:g> Kontakte gefunden"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 gefunden"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> gefunden"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Kein Kontakt gefunden"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"mehr als <xliff:g id="COUNT">%d</xliff:g> gefunden"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Nicht gefunden"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 Kontakt"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> Kontakte"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 gefunden"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> gefunden"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Kontakte"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoriten"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Kontakte auf SIM-Karte"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Sie haben keine Kontakte. Wenn Sie gerade ein Konto hinzugefügt haben, kann die Synchronisierung der Kontakte einige Minuten dauern."</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Sie haben keine Kontakte."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Es sind keine Kontakte vorhanden."\n\n"Drücken Sie zum Hinzufügen von Kontakten die "<font fgcolor="#ffffffff"><b>"Menütaste"</b></font>" und berühren Sie anschließend "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konten"</b></font>", um ein Konto mit Kontakten, die Sie mit dem Telefon synchronisieren möchten, hinzuzufügen oder zu konfigurieren."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Neuer Kontakt"</b></font>", um einen neuen Kontakt von Grund auf zu erstellen."\n</li>\n<li><font fgcolor="#ffffffff"><b>"Importieren/Exportieren"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Es sind keine Kontakte vorhanden. Wenn Sie gerade ein Konto hinzugefügt haben, kann es einige Minuten dauern, bis die Kontakte synchronisiert sind."\n\n"Drücken Sie zum Hinzufügen von Kontakten die "<font fgcolor="#ffffffff"><b>"Menütaste"</b></font>" und tippen Sie anschließend auf "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konten"</b></font>", um ein Konto mit Kontakten, die Sie mit dem Telefon synchronisieren möchten, hinzuzufügen oder zu konfigurieren."\n" "</li>\n<li><font fgcolor="#ffffffff"><b>"Anzeigeoptionen"</b></font>", um zu ändern, welche Kontakte angezeigt werden."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Neuer Kontakt"</b></font>", um einen neuen Kontakt von Grund auf zu erstellen."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importieren/Exportieren"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Es sind keine Kontakte vorhanden."\n\n"Drücken Sie zum Hinzufügen von Kontakten die "<font fgcolor="#ffffffff"><b>"Menütaste"</b></font>" und berühren Sie anschließend "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konten"</b></font>", um ein Konto mit Kontakten, die Sie mit dem Telefon synchronisieren möchten, hinzuzufügen oder zu konfigurieren."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Neuer Kontakt"</b></font>", um einen neuen Kontakt von Grund auf zu erstellen."\n</li>\n<li><font fgcolor="#ffffffff"><b>"Importieren/Exportieren"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Es sind keine Kontakte vorhanden. Wenn Sie gerade ein Konto hinzugefügt haben, kann es einige Minuten dauern, bis die Kontakte synchronisiert sind."\n\n"Drücken Sie zum Hinzufügen von Kontakten die "<font fgcolor="#ffffffff"><b>"Menütaste"</b></font>" und berühren Sie anschließend "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konten"</b></font>", um ein Konto mit Kontakten, die Sie mit dem Telefon synchronisieren möchten, hinzuzufügen oder zu konfigurieren."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Anzeigeoptionen"</b></font>", um zu ändern, welche Kontakte angezeigt werden."\n</li>\n<li><font fgcolor="#ffffffff"><b>"Neuer Kontakt"</b></font>", um einen neuen Kontakt von Grund auf zu erstellen."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importieren/Exportieren"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Es können keine Kontakte angezeigt werden."\n\n"Tippen Sie zum Hinzufügen von Kontakten auf "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" und dann auf "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konten"</b></font>", um ein Konto hinzuzufügen oder mit Kontakten für die Synchronisierung mit dem Tablet zu konfigurieren"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Neuer Kontakt"</b></font>", um einen Kontakt neu zu erstellen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>", um Kontakte von Ihrer SIM- oder SD-Karte zu importieren"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Es können keine Kontakte angezeigt werden."\n\n"Tippen Sie zum Hinzufügen von Kontakten auf "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" und dann auf "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konten"</b></font>", um ein Konto hinzuzufügen oder mit Kontakten für die Synchronisierung mit dem Telefon zu konfigurieren"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Neuer Kontakt"</b></font>", um einen Kontakt neu zu erstellen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>", um Kontakte aus Ihrer SIM- oder SD-Karte zu importieren"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Es können keine Kontakte angezeigt werden. Wenn Sie gerade ein Konto hinzugefügt haben, kann die Kontaktsynchronisierung etwas dauern."\n\n"Zum Hinzufügen von Kontakten drücken Sie "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" und tippen dann auf "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konten"</b></font>", um ein Konto hinzuzufügen oder mit Kontakten für die Synchronisierung mit dem Tablet zu konfigurieren"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Anzeigeoptionen"</b></font>", um zu ändern, welche Kontakte angezeigt werden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Neuer Kontakt"</b></font>", um einen Kontakt neu zu erstellen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>", um Kontakte von Ihrer SIM- oder SD-Karte zu importieren"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Es können keine Kontakte angezeigt werden. Wenn Sie gerade ein Konto hinzugefügt haben, kann die Kontaktsynchronisierung etwas dauern."\n\n"Zum Hinzufügen von Kontakten drücken Sie auf "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" und berühren "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konten"</b></font>", um ein Konto hinzuzufügen oder mit Kontakten für die Synchronisierung mit dem Telefon zu konfigurieren."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Anzeigeoptionen"</b></font>", um zu ändern, welche Kontakte angezeigt werden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Neuer Kontakt"</b></font>", um einen Kontakt neu zu erstellen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>", um Kontakte aus Ihrer SIM- oder SD-Karte zu importieren"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Es können keine Kontakte angezeigt werden."\n\n"Tippen Sie zum Hinzufügen von Kontakten auf "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" und dann auf "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konten"</b></font>", um ein Konto hinzuzufügen oder mit Kontakten für die Synchronisierung mit dem Tablet zu konfigurieren"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Neuer Kontakt"</b></font>", um einen Kontakt neu zu erstellen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>", um Kontakte von Ihrer SD-Karte zu importieren"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Es können keine Kontakte angezeigt werden."\n\n"Zum Hinzufügen von Kontakten drücken Sie auf "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" und berühren dann "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konten"</b></font>", um ein Konto hinzuzufügen oder mit Kontakten für die Synchronisierung mit dem Telefon zu konfigurieren."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Neuer Kontakt"</b></font>", um einen Kontakt neu zu erstellen"\n</li>"."\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>", um Kontakte aus Ihrer SD-Karte zu importieren"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Es können keine Kontakte angezeigt werden. Wenn Sie gerade ein Konto hinzugefügt haben, kann die Kontaktsynchronisierung etwas dauern."\n\n"Zum Hinzufügen von Kontakten drücken Sie auf "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" und tippen dann auf "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konten"</b></font>", um ein Konto hinzuzufügen oder mit Kontakten für die Synchronisierung mit dem Tablet zu konfigurieren"\n" "</li>\n<li><font fgcolor="#ffffffff"><b>"Anzeigeoptionen"</b></font>", um zu ändern, welche Kontakte angezeigt werden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Neuer Kontakt"</b></font>", um einen Kontakt neu zu erstellen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>", um Kontakte von Ihrer SD-Karte zu importieren"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Es können keine Kontakte angezeigt werden. Wenn Sie gerade ein Konto hinzugefügt haben, kann die Kontaktsynchronisierung etwas dauern."\n\n"Zum Hinzufügen von Kontakten drücken Sie auf "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" und berühren dann "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konten"</b></font>", um ein Konto hinzuzufügen oder mit Kontakten für die Synchronisierung mit dem Telefon zu konfigurieren."\n" "</li>\n<li><font fgcolor="#ffffffff"><b>"Anzeigeoptionen"</b></font>", um zu ändern, welche Kontakte angezeigt werden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Neuer Kontakt"</b></font>", um einen Kontakt neu zu erstellen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Es sind keine Favoriten vorhanden."\n\n"So fügen Sie einen Kontakt zu Ihrer Favoritenliste hinzu:"\n\n" "<li>"Berühren Sie den Tab "<b>"Kontakte"</b>"."\n</li>" "\n<li>"Berühren Sie den Kontakt, den Sie zu Ihren Favoriten hinzufügen möchten."\n</li>" "\n<li>"Berühren Sie den Stern neben dem Kontaktnamen."\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Alle Kontakte"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Markiert"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Rückruf"</string>
<string name="callAgain" msgid="3197312117049874778">"Erneut anrufen"</string>
<string name="returnCall" msgid="8171961914203617813">"Zurückrufen"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> Minuten, <xliff:g id="SECONDS">%2$s</xliff:g> Sekunden"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> Minuten, <xliff:g id="SECONDS">%s</xliff:g> Sekunden"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Häufig kontaktiert"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Kontakt hinzufügen"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"\"<xliff:g id="EMAIL">%s</xliff:g>\" zu den Kontakten hinzufügen?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Alle"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"eins"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"zwei"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"drei"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Scannen des USB-Speichers fehlgeschlagen. Grund: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\""</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Fehler beim Lesen der SD-Karte. Grund: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"E/A-Fehler"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Zu wenig Speicherplatz (Datei ist eventuell zu groß)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Die vCard konnte aus einem unbekannten Grund nicht geparst werden."</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Beim Parsen der vCard ist ein Fehler aufgetreten. Obwohl die vCard anscheinend ein gültiges Format aufweist, wird sie von der aktuellen Implementierung nicht unterstützt."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Im USB-Speicher wurde keine vCard-Datei gefunden."</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Keine VCard-Datei auf der SD-Karte gefunden"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Das Format wird nicht unterstützt."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"vCard kann nicht importiert werden."</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Im USB-Speicher wurde keine vCard-Datei gefunden."</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Auf der SD-Karte wurde keine vCard-Datei gefunden."</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Metainformationen konnten nicht von den angegebenen vCards abgerufen werden."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Fehler beim Import einer oder mehrerer Dateien (%s)"</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Unbekannter Fehler"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"vCard-Datei auswählen"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"vCard wird gelesen."</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"vCard-Dateien werden gelesen."</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Fehler beim Lesen der vCard-Daten"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> von <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> Kontakten"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> von <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> Dateien"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Caching von vCards in lokalen temporären Speicher"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Importprogramm führt gerade Caching der vCard(s) in lokalen temporären Speicher durch. Der eigentliche Import beginnt gleich."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g> wird importiert: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"<xliff:g id="NAME">%s</xliff:g> wird importiert"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Fehler beim Lesen von vCard-Daten"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Lesen von vCard-Daten abgebrochen"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Import der vCard <xliff:g id="FILENAME">%s</xliff:g> abgeschlossen"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Import von <xliff:g id="FILENAME">%s</xliff:g> wurde abgebrochen."</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> wird demnächst importiert."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Die Datei wird in Kürze importiert."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"vCard-Importanfrage wurde abgelehnt. Versuchen Sie es später noch einmal."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> wird demnächst exportiert."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"vCard-Exportanfrage wurde abgelehnt. Versuchen Sie es später noch einmal."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"Kontakt"</string>
+ <string name="percentage" msgid="34897865327092209">"%s %%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Export bestätigen"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Soll Ihre Kontaktliste wirklich an \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\" exportiert werden?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Fehler beim Exportieren von Kontaktdaten"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Zu viele vCard-Dateien im USB-Speicher"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Zu viele vCard-Dateien auf der SD-Karte"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Erforderlicher Dateiname ist zu lang (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Export von <xliff:g id="FILENAME">%s</xliff:g> abgeschlossen"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Export von <xliff:g id="FILENAME">%s</xliff:g> wurde abgebrochen."</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Kontaktdaten werden exportiert."</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Kontaktdaten werden in \"<xliff:g id="FILE_NAME">%s</xliff:g>\" exportiert."</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Exportprogramm konnte nicht initialisiert werden: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Fehler beim Exportieren: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Fehler bei der Ermittlung von Datenbank-Informationen"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Es ist kein exportierbarer Kontakt vorhanden. Falls sich Kontakte auf Ihrem Telefon befinden, ist das Exportieren der Kontakte möglicherweise durch einen Datenanbieter gesperrt."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Es sind keine exportierbaren Kontakte vorhanden. Falls sich Kontakte auf Ihrem Tablet befinden, ist das Exportieren der Kontakte möglicherweise durch Datenanbieter gesperrt."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Es sind keine exportierbaren Kontakte vorhanden. Falls sich Kontakte auf Ihrem Telefon befinden, ist das Exportieren der Kontakte möglicherweise durch Datenanbieter gesperrt."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Das Programm zum Erstellen der vCard wurde nicht richtig initialisiert."</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"\"<xliff:g id="FILE_NAME">%1$s</xliff:g>\" konnte nicht geöffnet werden: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> von <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> Kontakten"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"\"<xliff:g id="FILE_NAME">%s</xliff:g>\" konnte nicht geöffnet werden: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> von <xliff:g id="TOTAL_NUMBER">%s</xliff:g> Kontakten"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"vCard-Import wird abgebrochen."</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Möchten Sie den Import von <xliff:g id="FILENAME">%s</xliff:g> wirklich abbrechen?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"vCard-Export wird abgebrochen."</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Möchten Sie den Export von <xliff:g id="FILENAME">%s</xliff:g> wirklich abbrechen?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"vCard-Import/-Export nicht abgebrochen"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Namen meiner Kontakte"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"2 Sekunden Pause hinzufügen"</string>
<string name="add_wait" msgid="3360818652790319634">"Warten hinzufügen"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Für diese Aktion wurde keine Anwendung gefunden."</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Diese Auswahl speichern"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Unbekannt"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Keine Daten"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Standardeinstellung zurücksetzen"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Festgelegte Standardeinst. für Kontakt:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Zurücksetzen"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Konten"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importieren/Exportieren"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Kontakte importieren/exportieren"</string>
- <string name="menu_share" msgid="943789700636542260">"Weiterleiten"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Kontakt weitergeben"</string>
<string name="share_via" msgid="563121028023030093">"Kontakt weiterleiten über"</string>
<string name="share_error" msgid="4374508848981697170">"Dieser Kontakt kann nicht freigegeben werden."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Name"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Firma/Organisation"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Website"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Termin"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Beziehung"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Gruppen"</string>
<string name="type_short_home" msgid="7770424864090605384">"P"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"A"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"S"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Dieser Kontakt ist schreibgeschützt."</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Mehr"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Hauptname"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Neuen Kontakt unter Konto erstellen"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Synchronisierungsgruppe entfernen"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Synchronisierungsgruppe hinzufügen"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Alle weiteren Kontakte"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Alle Kontakte"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Wenn \"<xliff:g id="GROUP">%s</xliff:g>\" aus der Synchronisierung entfernt wird, werden auch alle nicht gruppierten Kontakte aus der Synchronisierung entfernt."</string>
- <string name="account_phone" msgid="3682950835276226870">"Nur Telefon, nicht synchronisiert"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Nur Tablet, keine Synchronisierung"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Nur Telefon, nicht synchronisiert"</string>
<string name="call_custom" msgid="7756571794763171802">"<xliff:g id="CUSTOM">%s</xliff:g> anrufen"</string>
<string name="call_home" msgid="1990519474420545392">"Anruf (privat)"</string>
<string name="call_mobile" msgid="7502236805487609178">"Anruf (mobil)"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chat über ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chat über Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Chat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adresse"</string>
<string name="postal_street" msgid="8133143961580058972">"Straße"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Postfach"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Nachbarschaft"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Bundesland"</string>
<string name="postal_postcode" msgid="572136414136673751">"Postleitzahl"</string>
<string name="postal_country" msgid="7638264508416368690">"Land"</string>
+ <string name="full_name" msgid="6602579550613988977">"Name"</string>
<string name="name_given" msgid="1687286314106019813">"Vorname"</string>
<string name="name_family" msgid="3416695586119999058">"Nachname"</string>
<string name="name_prefix" msgid="59756378548779822">"Namenpräfix"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Kontakte durchsuchen"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"In allen Kontakten suchen"</string>
<string name="take_photo" msgid="7496128293167402354">"Foto machen"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Neues Foto aufnehmen"</string>
<string name="pick_photo" msgid="448886509158039462">"Foto aus Galerie auswählen"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Die Kontaktliste wird an die geänderte Sprache angepasst."\n\n"Bitte warten..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Die Kontaktliste wird aktualisiert."\n\n"Bitte warten..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Kontakte werden gerade aktualisiert. "\n\n"Das Upgrade erfordert etwa <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>MB des internen Telefonspeichers."\n\n"Wählen Sie eine der folgenden Optionen:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Neues Foto aus Galerie auswählen"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Kontaktliste wird an die geänderte Sprache angepasst..."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Kontaktliste wird aktualisiert..."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Ihre Kontakte werden momentan aktualisiert."\n\n"Für den Aktualisierungsvorgang sind ca. <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB interner Speicher erforderlich."\n\n"Wählen Sie eine der folgenden Optionen:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Einige Anwendungen deinstallieren"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Upgrade wiederholen"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Suchergebnisse für: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Suche..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Auswahl anzeigen"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Alle anzeigen"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Alle auswählen"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Auswahl für alle aufheben"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"Ein Empfänger ausgewählt"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g> Empfänger ausgewählt"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Kein Kontakt ausgewählt"</string>
+ <string name="add_field" msgid="2384260056674995230">"Weiteres Feld hinzufügen"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"über <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> über <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"Favorit"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Kontakt bearbeiten"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"nicht zusammengeführt"</item>
+ <item quantity="other" msgid="425683718017380845">"aus <xliff:g id="COUNT">%0$d</xliff:g> Quellen zusammengeführt"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Sonstige"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Kontakte zusammenführen"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Aktuellen Kontakt mit ausgewähltem Kontakt zusammenführen?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Ausgewählte Kontakte bearbeiten"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Zur Bearbeitung des ausgewählten Kontakts wechseln? Die bisher eingegebenen Informationen werden kopiert."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"In meine Kontakte kopieren"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Verzeichnis <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Alle Kontakte werden durchsucht..."</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Verzeichnis"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Kontakte"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Persönliche Kopie wird erstellt"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Kontaktliste auswählen"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Alle Kontakte"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Markiert"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Benutzerdefiniert"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Wird angepasst..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Kontakte mit Telefonnummern"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kontakt"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Benutzerdefinierte Ansicht festlegen"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Einstellungen"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Einstellungen"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Anzeigeoptionen"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Kontakte suchen"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Telefonnummer"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Zu Kontakten hinzufügen"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Schließen"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Jahr angeben"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Kontakt"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Wird geladen..."</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Neuen Kontakt erstellen"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"In einem Konto anmelden"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Kontakte aus einer Datei importieren"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Neue Gruppe erstellen"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Neue Gruppe erstellen]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Gruppe umbenennen"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Gruppe löschen"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Soll die Gruppe \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\" wirklich gelöscht werden? Die Kontakte selbst werden nicht gelöscht."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Geben Sie bitte einen Namen für den Kontakt ein, bevor Sie ihn mit einem anderen Kontakt zusammenführen."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Zusammengeführter Kontakt"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Text kopiert"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Änderungen verwerfen"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Möchten Sie Ihre Änderungen wirklich verwerfen?"</string>
+ <string name="discard" msgid="1234315037371251414">"Verwerfen"</string>
</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 1d86dcc..a54dd00 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Επιλογή μιας συντόμευσης επαφών"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Επιλογή ενός αριθμού για κλήση"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Επιλογή ενός αριθμού για μήνυμα"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Επιλογή επαφής"</string>
<string name="starredList" msgid="4817256136413959463">"Με αστέρι"</string>
<string name="frequentList" msgid="7154768136473953056">"Συχνές"</string>
<string name="strequentList" msgid="5640192862059373511">"Αγαπ."</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Διαγραφή επαφής"</string>
<string name="menu_call" msgid="3992595586042260618">"Κλήση επαφής"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Αποστολή μηνύματος κειμένου σε επαφή"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Αποστολή μηνύματος ηλεκτρονικού ταχυδρομείου"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Διεύθυνση στον χάρτη"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Ορισμός προεπιλεγμένου αριθμού"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Ορισμός διεύθυνσης ηλεκτρονικού ταχυδρομείου ως προεπιλογή"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Διαχωρισμός"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Οι επαφές διαχωρίστηκαν"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Μετονομασία ομάδας"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Διαγραφή ομάδας"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Νέα"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Διαχωρισμός επαφής"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Είστε βέβαιοι ότι θέλετε να διαχωρίσετε αυτή τη μία επαφή σε πολλές επαφές: μία για κάθε σύνολο στοιχείων επικοινωνίας που προστέθηκε σε αυτήν;"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Σύνδεση"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Εάν διαγράψετε αυτήν την επαφή, θα γίνει διαγραφή των πληροφοριών από πολλούς λογαριασμούς."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Αυτή η επαφή θα διαγραφεί."</string>
<string name="menu_done" msgid="796017761764190697">"Τέλος"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Επαναφορά"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Ακύρωση"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Επεξεργασία επαφής"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Νέα επαφή"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Φωνητική"</string>
<string name="label_notes" msgid="8337354953278341042">"Σημειώσεις"</string>
<string name="label_sip_address" msgid="124073911714324974">"Κλήση Internet"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Ήχος κλήσης"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Όνομα και επίθετο"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Φωνητικό όνομα"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Εταιρεία"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Τίτλος"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Η επαφή δεν υπάρχει."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Δημιουργία νέας επαφής"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Επιλογή ετικέτας"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Τηλέφωνο"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Διεύθυνση ηλεκτρονικού ταχυδρομείου"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Ανταλ.άμεσων μην.(IM)"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Ταχυδρομική διεύθυνση"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Διεύθυνση"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Εταιρεία"</item>
<item msgid="7196592230748086755">"Σημείωση"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Δεν υπάρχουν διαθέσιμες εικόνες στο τηλέφωνο."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Εικονίδιο επαφής"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Δεν υπάρχουν διαθέσιμες εικόνες στο tablet."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Δεν υπάρχουν διαθέσιμες εικόνες στο τηλέφωνο."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Φωτογραφία επαφής"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Προσαρμοσμένο όνομα ετικέτας"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Επιλογές προβολής"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Επιλογές προβολής"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Αποστολή κλήσεων απευθείας στον αυτόματο τηλεφωνητή"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Προεπιλογή"</string>
- <string name="changePicture" msgid="2943329047610967714">"Αλλαγή εικονιδίου"</string>
- <string name="removePicture" msgid="3041230993155966350">"Κατάργηση εικονιδίου"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Κατάργηση εικόνας"</string>
<string name="noContacts" msgid="8579310973261953559">"Δεν υπάρχουν επαφές."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Δεν βρέθηκαν επαφές που να αντιστοιχούν."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Δεν υπάρχουν επαφές με αριθμούς τηλεφώνου."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Η επαφή αποθηκεύτηκε."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Σφάλμα, δεν ήταν δυνατή η αποθήκευση αλλαγών επαφής."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Εμφάνιση 1 επαφής με αριθμό τηλεφώνου"</item>
- <item quantity="other" msgid="6133262880804110289">"Εμφάνιση <xliff:g id="COUNT">%d</xliff:g> επαφών με αριθμούς τηλεφώνων"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 επαφή με αριθμό τηλεφώνου"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> επαφές με αριθμούς τηλεφώνου"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Δεν υπάρχουν ορατές επαφές με αριθμούς τηλεφώνων"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Δεν υπάρχουν επαφές με αριθμούς τηλεφώνου"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Εμφάνιση 1 επαφής"</item>
- <item quantity="other" msgid="2865867557378939630">"Εμφάνιση <xliff:g id="COUNT">%d</xliff:g> επαφών"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 επαφή"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> επαφές"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Δεν υπάρχουν ορατές επαφές"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Δεν υπάρχουν επαφές"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Δεν υπάρχουν ορατές επαφές"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Δεν υπάρχουν επαφές με αστέρι"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Δεν υπάρχουν επαφές στο <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Βρέθηκε 1 επαφή"</item>
- <item quantity="other" msgid="7752927996850263152">"Βρέθηκαν <xliff:g id="COUNT">%d</xliff:g> επαφές"</item>
+ <item quantity="one" msgid="5517063038754171134">"Βρέθηκε 1"</item>
+ <item quantity="other" msgid="3852668542926965042">"Βρέθηκαν <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Δεν βρέθηκε η επαφή"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"βρέθηκαν περισσότερες από <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Δεν βρέθηκε"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 επαφή"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> επαφές"</item>
+ <item quantity="one" msgid="4826918429708286628">"Βρέθηκε 1"</item>
+ <item quantity="other" msgid="7988132539476575389">"Βρέθηκαν <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Επαφές"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Αγαπ."</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Επαφές στην κάρτα SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Δεν υπάρχουν επαφές για προβολή. (Εάν προσθέσατε τώρα ένα λογαριασμό, ο συγχρονισμός των επαφών μπορεί να καθυστερήσει μερικά λεπτά.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Δεν υπάρχουν επαφές για προβολή."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Δεν έχετε επαφές προς προβολή."\n\n"Για προσθήκη επαφών, πατήστε "<font fgcolor="#ffffffff"><b>"Μενού"</b></font>" και αγγίξτε:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Λογαριασμοί"</b></font>" για προσθήκη ή διαμόρφωση λογαριασμού με επαφές που μπορείτε να συγχρονίσετε στο τηλέφωνο"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Νέα επαφή"</b></font>" για δημιουργία νέας επαφής από το μηδέν"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Εισαγωγή/Εξαγωγή"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Δεν έχετε επαφές προς προβολή. (Αν προσθέσατε κάποιον λογαριασμό μόλις τώρα, θα χρειαστούν λίγα λεπτά έως ότου γίνει ο συγχρονισμός.)"\n\n"Για την προσθήκη επαφών, πατήστε "<font fgcolor="#ffffffff"><b>"Μενού"</b></font>" και αγγίξτε την επιλογή:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Λογαριασμοί"</b></font>" για την προσθήκη ή διαμόρφωση λογαριασμού με επαφές τις οποίες μπορείτε να συγχρονίσετε στο τηλέφωνο"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Επιλογές προβολής"</b></font>" για να αλλάξετε τις επαφές που είναι ορατές"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Νέα επαφή"</b></font>" για δημιουργία νέας επαφής από το μηδέν"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Εισαγωγή/Εξαγωγή"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Δεν έχετε επαφές προς προβολή."\n\n"Για προσθήκη επαφών, πατήστε "<font fgcolor="#ffffffff"><b>"Μενού"</b></font>" και αγγίξτε την επιλογή:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Λογαριασμοί"</b></font>" για προσθήκη ή διαμόρφωση λογαριασμού με επαφές που μπορείτε να συγχρονίσετε στο τηλέφωνο"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Νέα επαφή"</b></font>" για δημιουργία νέας επαφής από το μηδέν"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Εισαγωγή/Εξαγωγή"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Δεν έχετε επαφές προς προβολή. (Αν προσθέσατε κάποιον λογαριασμό μόλις τώρα, θα χρειαστούν λίγα λεπτά έως ότου γίνει ο συγχρονισμός.)"\n\n"Για την προσθήκη επαφών, πατήστε "<font fgcolor="#ffffffff"><b>"Μενού"</b></font>" και αγγίξτε την επιλογή:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Λογαριασμοί"</b></font>" για την προσθήκη ή διαμόρφωση λογαριασμού με επαφές τις οποίες μπορείτε να συγχρονίσετε στο τηλέφωνο"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Προβολή επιλογών"</b></font>" για να αλλάξετε τις επαφές που είναι ορατές"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Νέα επαφή"</b></font>" για δημιουργία νέας επαφής από το μηδέν"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Εισαγωγή/Εξαγωγή"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Δεν έχετε επαφές προς προβολή."\n\n"Για προσθήκη επαφών, πατήστε "<font fgcolor="#ffffffff"><b>"Μενού"</b></font>" και αγγίξτε την επιλογή:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Λογαριασμοί"</b></font>" για προσθήκη ή διαμόρφωση λογαριασμού με επαφές που μπορείτε να συγχρονίσετε στο tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Νέα επαφή"</b></font>" για δημιουργία νέας επαφής από το μηδέν"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Εισαγωγή/Εξαγωγή"</b></font>" για την εισαγωγή επαφών από την κάρτα SIM ή SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Δεν έχετε επαφές προς προβολή."\n\n"Για προσθήκη επαφών, πατήστε "<font fgcolor="#ffffffff"><b>"Μενού"</b></font>" και αγγίξτε την επιλογή:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Λογαριασμοί"</b></font>" για προσθήκη ή διαμόρφωση λογαριασμού με επαφές που μπορείτε να συγχρονίσετε στο τηλέφωνο"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Νέα επαφή"</b></font>" για δημιουργία νέας επαφής από το μηδέν"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Εισαγωγή/Εξαγωγή"</b></font>" για την εισαγωγή επαφών από την κάρτα SIM ή SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Δεν έχετε επαφές προς προβολή. (Αν προσθέσατε κάποιο λογαριασμό μόλις τώρα, θα χρειαστούν λίγα λεπτά έως ότου γίνει ο συγχρονισμός.)"\n\n"Για την προσθήκη επαφών, πατήστε "<font fgcolor="#ffffffff"><b>"Μενού"</b></font>" και αγγίξτε την επιλογή:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Λογαριασμοί"</b></font>" για την προσθήκη ή διαμόρφωση λογαριασμού με επαφές τις οποίες μπορείτε να συγχρονίσετε στο tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Προβολή επιλογών"</b></font>" για να αλλάξετε τις επαφές που είναι ορατές"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Νέα επαφή"</b></font>" για δημιουργία νέας επαφής από το μηδέν"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Εισαγωγή/Εξαγωγή"</b></font>" για την εισαγωγή επαφών από την κάρτα SIM ή SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Δεν έχετε επαφές προς προβολή. (Αν προσθέσατε κάποιο λογαριασμό μόλις τώρα, θα χρειαστούν λίγα λεπτά έως ότου γίνει ο συγχρονισμός.)"\n\n"Για την προσθήκη επαφών, πατήστε "<font fgcolor="#ffffffff"><b>"Μενού"</b></font>" και αγγίξτε την επιλογή:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Λογαριασμοί"</b></font>" για την προσθήκη ή διαμόρφωση λογαριασμού με επαφές τις οποίες μπορείτε να συγχρονίσετε στο τηλέφωνο"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Προβολή επιλογών"</b></font>" για να αλλάξετε τις επαφές που είναι ορατές"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Νέα επαφή"</b></font>" για δημιουργία νέας επαφής από το μηδέν"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Εισαγωγή/Εξαγωγή"</b></font>" για την εισαγωγή επαφών από την κάρτα SIM ή SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Δεν έχετε επαφές προς προβολή."\n\n"Για προσθήκη επαφών, πατήστε "<font fgcolor="#ffffffff"><b>"Μενού"</b></font>" και αγγίξτε την επιλογή:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Λογαριασμοί"</b></font>" για προσθήκη ή διαμόρφωση λογαριασμού με επαφές που μπορείτε να συγχρονίσετε στο tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Νέα επαφή"</b></font>" για δημιουργία νέας επαφής από το μηδέν"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Εισαγωγή/Εξαγωγή"</b></font>" για την εισαγωγή επαφών από την κάρτα SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Δεν έχετε επαφές προς προβολή."\n\n"Για προσθήκη επαφών, πατήστε "<font fgcolor="#ffffffff"><b>"Μενού"</b></font>" και αγγίξτε την επιλογή:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Λογαριασμοί"</b></font>" για προσθήκη ή διαμόρφωση λογαριασμού με επαφές που μπορείτε να συγχρονίσετε στο τηλέφωνο"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Νέα επαφή"</b></font>" για δημιουργία νέας επαφής από το μηδέν"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Εισαγωγή/Εξαγωγή"</b></font>" για την εισαγωγή επαφών από την κάρτα SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Δεν έχετε επαφές προς προβολή. (Αν προσθέσατε κάποιον λογαριασμό μόλις τώρα, θα χρειαστούν λίγα λεπτά έως ότου γίνει ο συγχρονισμός.)"\n\n"Για την προσθήκη επαφών, πατήστε "<font fgcolor="#ffffffff"><b>"Μενού"</b></font>" και αγγίξτε την επιλογή:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Λογαριασμοί"</b></font>" για την προσθήκη ή διαμόρφωση λογαριασμού με επαφές τις οποίες μπορείτε να συγχρονίσετε στο tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Προβολή επιλογών"</b></font>" για να αλλάξετε τις επαφές που είναι ορατές"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Νέα επαφή"</b></font>" για δημιουργία νέας επαφής από το μηδέν"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Εισαγωγή/Εξαγωγή"</b></font>" για την εισαγωγή επαφών από την κάρτα SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Δεν έχετε επαφές προς προβολή. (Αν προσθέσατε κάποιον λογαριασμό μόλις τώρα, θα χρειαστούν λίγα λεπτά έως ότου γίνει ο συγχρονισμός.)"\n\n"Για την προσθήκη επαφών, πατήστε "<font fgcolor="#ffffffff"><b>"Μενού"</b></font>" και αγγίξτε την επιλογή:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Λογαριασμοί"</b></font>" για την προσθήκη ή διαμόρφωση λογαριασμού με επαφές τις οποίες μπορείτε να συγχρονίσετε στο τηλέφωνο"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Προβολή επιλογών"</b></font>" για να αλλάξετε τις επαφές που είναι ορατές"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Νέα επαφή"</b></font>" για δημιουργία νέας επαφής από το μηδέν"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Εισαγωγή/Εξαγωγή"</b></font>" για την εισαγωγή επαφών από την κάρτα SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Δεν έχετε αγαπημένα."\n\n"Για προσθήκη επαφής στη λίστα των αγαπημένων σας:"\n\n" "<li>"Αγγίξτε την καρτέλα "<b>"Επαφές"</b>\n</li>" "\n<li>"Αγγίξτε το όνομα της επαφής που θέλετε να προσθέσετε στα αγαπημένα σας"\n</li>" "\n<li>"Αγγίξτε το αστέρι πλάι στο όνομα της επαφής"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Όλες οι επαφές"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Με αστέρι"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Επανάκληση"</string>
<string name="callAgain" msgid="3197312117049874778">"Επανάληψη κλήσης"</string>
<string name="returnCall" msgid="8171961914203617813">"Επιστροφή κλήσης"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> λεπτά <xliff:g id="SECONDS">%2$s</xliff:g> δευτερόλεπτα"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> λεπτά <xliff:g id="SECONDS">%s</xliff:g> δευτερόλεπτα"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Επαφές με τις οποίες έχετε συχνή επικοινωνία"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Προσθήκη επαφής"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Προσθήκη του \"<xliff:g id="EMAIL">%s</xliff:g>\" στις επαφές?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Όλα"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"ένα"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"δύο"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"τρία"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Η σάρωση του αποθηκευτικού χώρου USB απέτυχε (Αιτία: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Η σάρωση της κάρτας SD απέτυχε (Αιτία:\"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Σφάλμα I/O"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Η μνήμη είναι ανεπαρκής (το αρχείο μπορεί να είναι πολύ μεγάλο)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Η ανάλυση της κάρτας vCard απέτυχε εξαιτίας μη αναμενόμενου συμβάντος"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Αν και το VCard φαίνεται να βρίσκεται σε έγκυρη μορφή, η ανάλυσή του απέτυχε, εφόσον η τρέχουσα εφαρμογή δεν το υποστηρίζει"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Δεν βρέθηκαν αρχεία vCard στον αποθηκευτικό σας χώρο USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Δεν βρέθηκε αρχείο VCard στην κάρτα SD"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Η μορφή δεν υποστηρίζεται."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Αποτυχία εισαγωγής vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Δεν βρέθηκαν αρχεία vCard στον αποθηκευτικό σας χώρο USB"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Δεν βρέθηκε αρχείο VCard στην κάρτα SD"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Η συλλογή μετα-πληροφοριών του δεδομένου αρχείου(ων) vCard απέτυχε."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Η εισαγωγή ενός ή περισσοτέρων αρχείων απέτυχε (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Άγνωστο σφάλμα"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Επιλογή αρχείου vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Ανάγνωση vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Ανάγνωση αρχείου(ων) vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Η ανάγνωση των δεδομένων vCard απέτυχε"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> από <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> επαφές"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> από <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> αρχεία"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Αποθήκευση vCard(s) σε τοπικό προσωρινό χώρο αποθήκευσης"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Το πρόγραμμα εισαγωγής αποθηκεύει προσωρινά vCard(s) σε τοπικό χώρο προσωρινής αποθήκευσης. Η κανονική εισαγωγή θα ξεκινήσει σύντομα."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Εισαγωγή <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Εισαγωγή <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Αποτυχία ανάγνωσης δεδομένων vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Η ανάγνωση των δεδομένων vCard ακυρώθηκε"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Η εισαγωγή vCard ολοκληρώθηκε <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Η εισαγωγή της <xliff:g id="FILENAME">%s</xliff:g> ακυρώθηκε"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"Η <xliff:g id="FILENAME">%s</xliff:g> θα εισαχθεί σύντομα."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Η εισαγωγή του αρχείου θα γίνει σύντομα."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Το αίτημα εισαγωγής vCard απορρίφθηκε. Δοκιμάστε αργότερα."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"Η <xliff:g id="FILENAME">%s</xliff:g> θα εξαχθεί σύντομα."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Το αίτημα εξαγωγής vCard απορρίφθηκε. Δοκιμάστε αργότερα."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"επαφή"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Επιβεβαίωση εξαγωγής"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Είστε σίγουροι ότι θέλετε να εξαγάγετε τη λίστα των επαφών σας στο \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\";"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Αποτυχία εξαγωγής δεδομένων επαφής"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Υπερβολικά μεγάλος όγκος αρχείων VCard στον αποθηκευτικό χώρο USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Υπερβολικά μεγάλος όγκος αρχείων VCard στην κάρτα SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Το απαιτούμενο όνομα αρχείου είναι υπερβολικά μεγάλο (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Η εξαγωγή ολοκληρώθηκε<xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Η εξαγωγή της <xliff:g id="FILENAME">%s</xliff:g> ακυρώθηκε"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Εξαγωγή δεδομένων επαφών"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Εξαγωγή δεδομένων επαφών προς \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Δεν ήταν δυνατή η εκκίνηση της εξαγωγής: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Προέκυψε σφάλμα κατά την εξαγωγή: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Αποτυχία λήψης πληροφοριών βάσης δεδομένων"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Δεν υπάρχει επαφή με δυνατότητα εξαγωγής. Αν έχετε επαφές στο τηλέφωνό σας, ενδέχεται να μην επιτρέπεται η εξαγωγή όλων των επαφών εκτός του τηλεφώνου από κάποιον πάροχο δεδομένων."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Δεν υπάρχουν επαφές με δυνατότητα εξαγωγής. Αν έχετε επαφές στο tablet σας, ενδέχεται να μην επιτρέπεται η εξαγωγή όλων των επαφών εκτός του τηλεφώνου από κάποιους παρόχους δεδομένων."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Δεν υπάρχουν επαφές με δυνατότητα εξαγωγής. Αν έχετε επαφές στο τηλέφωνό σας, ενδέχεται να μην επιτρέπεται η εξαγωγή όλων των επαφών εκτός του τηλεφώνου από κάποιους παρόχους δεδομένων."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Δεν έχει γίνει σωστή εκκίνηση του προγράμματος σύνθεσης της vCard"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Δεν ήταν δυνατό το άνοιγμα του \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> από <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> επαφές"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Δεν ήταν δυνατό το άνοιγμα του \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> από <xliff:g id="TOTAL_NUMBER">%s</xliff:g> επαφές"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Ακύρωση εισαγωγής της vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Είστε βέβαιοι ότι θέλετε να ακυρώσετε την εισαγωγή <xliff:g id="FILENAME">%s</xliff:g>;"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Ακύρωση εξαγωγής της vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Είστε βέβαιοι ότι θέλετε να ακυρώσετε την εξαγωγή της <xliff:g id="FILENAME">%s</xliff:g>;"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Η εισαγωγή/εξαγωγή vCard δεν ακυρώθηκε"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Ονόματα ων επαφών σας"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Προσθήκη παύσης 2 δευτερολέπτων"</string>
<string name="add_wait" msgid="3360818652790319634">"Προσθήκη αναμονής"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Δεν βρέθηκε εφαρμογή για τη διαχείριση αυτής της ενέργειας"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Διατήρηση αυτής της επιλογής"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Άγνωστος"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Δεν υπάρχουν δεδομένα"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Εκκαθάριση προεπιλογών"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Προεπιλογές για αυτήν την επαφή:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Εκκαθάριση"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Λογαριασμοί"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Εισαγωγή/Εξαγωγή"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Εισαγωγή/Εξαγωγή επαφών"</string>
- <string name="menu_share" msgid="943789700636542260">"Κοινή χρήση"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Κοινή χρήση επαφής"</string>
<string name="share_via" msgid="563121028023030093">"Κοινή χρήση μέσω"</string>
<string name="share_error" msgid="4374508848981697170">"Δεν είναι δυνατή η κοινή χρήση αυτής της επαφής."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Όνομα"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Οργανισμός"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Ιστότοπος"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Συμβάν"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Σχέση"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Ομάδες"</string>
<string name="type_short_home" msgid="7770424864090605384">"Ο"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"Κ"</string>
<string name="type_short_work" msgid="4925330752504537861">"Ε"</string>
<string name="type_short_pager" msgid="2613818970827594238">"Β"</string>
<string name="type_short_other" msgid="5669407180177236769">"Α"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Αυτή η επαφή είναι μόνο για ανάγνωση"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Περισσότερα"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Κύριο όνομα"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Δημιουργία επαφής στον λογαριασμό"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Κατάργηση ομάδας συγχρονισμού"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Προσθήκη ομάδας συγχρονισμού"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Όλες οι άλλες επαφές"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Όλες οι επαφές"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Η κατάργηση της ομάδας \"<xliff:g id="GROUP">%s</xliff:g>\" από τον συγχρονισμό θα καταργήσει επίσης και τις επαφές χωρίς ομαδοποίηση από τον συγχρονισμό."</string>
- <string name="account_phone" msgid="3682950835276226870">"Μόνο στο τηλέφωνο, χωρίς συγχρονισμό"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Μόνο για tablet, χωρίς συγχρονισμό"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Μόνο στο τηλέφωνο, χωρίς συγχρονισμό"</string>
<string name="call_custom" msgid="7756571794763171802">"Κλήση <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Κλήση οικίας"</string>
<string name="call_mobile" msgid="7502236805487609178">"Κλήση κινητής συσκευής"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Συζήτηση μέσω ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Συζήτηση μέσω Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Συζήτηση"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Διεύθυνση"</string>
<string name="postal_street" msgid="8133143961580058972">"Οδός"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Ταχυδρομική θυρίδα"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Γειτονιά"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Πολιτεία"</string>
<string name="postal_postcode" msgid="572136414136673751">"Ταχυδρομικός κώδικας"</string>
<string name="postal_country" msgid="7638264508416368690">"Χώρα"</string>
+ <string name="full_name" msgid="6602579550613988977">"Όνομα"</string>
<string name="name_given" msgid="1687286314106019813">"Όνομα"</string>
<string name="name_family" msgid="3416695586119999058">"Επίθετο"</string>
<string name="name_prefix" msgid="59756378548779822">"Πρόθεμα ονόματος"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Αναζήτηση επαφών"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Αναζήτηση σε όλες τις επαφές"</string>
<string name="take_photo" msgid="7496128293167402354">"Λήψη φωτογραφίας"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Λήψη νέας φωτογραφίας"</string>
<string name="pick_photo" msgid="448886509158039462">"Επιλογή φωτογραφίας από τη συλλογή"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Η λίστα επαφών ενημερώνεται ώστε να αντικατοπτρίζει την αλλαγή γλώσσας."\n\n"Περιμένετε..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Γίνεται ενημέρωση της λίστας επαφών."\n\n"Περιμένετε..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Γίνεται αναβάθμιση των επαφών. "\n\n"Η διαδικασία αναβάθμισης απαιτεί περίπου <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>Mb από τον εσωτερικό χώρο αποθήκευσης του τηλεφώνου."\n\n"Ορίστε μία από τις παρακάτω επιλογές:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Επιλογή νέας φωτογραφίας από το Gallery"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Γίνεται ενημέρωση της λίστας επαφών για να αντικατοπτριστεί η αλλαγή γλώσσας."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Γίνεται ενημέρωση της λίστας επαφών."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Η αναβάθμιση των επαφών βρίσκεται σε εξέλιξη. "\n\n"Για τη διαδικασία αναβάθμισης απαιτούνται περίπου <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mb εσωτερικού χώρου αποθήκευσης."\n\n"Επιλέξτε μία από τις παρακάτω επιλογές:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Καταργήστε την εγκατάσταση ορισμένων εφαρμογών"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Επανάληψη αναβάθμισης"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Αποτελέσματα αναζήτησης για: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Αναζήτηση..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Εμφάνιση επιλεγμένων"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Εμφάνιση όλων"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Επιλογή όλων"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Αποεπιλογή όλων"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"επιλέχθηκε 1 παραλήπτης"</item>
+ <item quantity="other" msgid="4608837420986126229">"επιλέχθηκαν <xliff:g id="COUNT">%d</xliff:g> παραλήπτες"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Δεν έχουν επιλεγεί επαφές."</string>
+ <string name="add_field" msgid="2384260056674995230">"Προσθήκη άλλου πεδίου"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"μέσω <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> μέσω <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"αγαπημένο"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Επεξεργασία επαφής"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"δεν συγχωνεύθηκαν"</item>
+ <item quantity="other" msgid="425683718017380845">"συγχώνευση από <xliff:g id="COUNT">%0$d</xliff:g> προελεύσεις"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Άλλο"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Ένωση επαφών"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Να γίνει ένωση της τρέχουσας επαφής με την επιλεγμένη επαφή;"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Επεξεργασία επιλεγμένων επαφών"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Να γίνει μετάβαση σε επεξεργασία της επιλεγμένης επαφής; Θα γίνει αντιγραφή των στοιχείων που έχετε εισαγάγει μέχρι τώρα."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Αντιγραφή στις επαφές μου"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Κατάλογος <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Αναζήτηση όλων των επαφών"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Κατάλογος"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Επαφές"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Δημιουργία προσωπικού αντιγράφου"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Επιλογή λίστας επαφών"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Όλες οι επαφές"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Με αστέρι"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Προσαρμοσμένη"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Προσαρμογή..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Επαφές με αριθμούς τηλεφώνου"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Επαφή"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Ορισμός προσαρμοσμένης προβολής"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Ρυθμίσεις"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Ρυθμίσεις"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Επιλογές προβολής"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Εύρεση επαφών"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Αριθμός τηλεφώνου"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Προσθήκη στις επαφές"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Κλείσιμο"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Εισαγάγετε έτος"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Επαφή"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Φόρτωση …"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Δημιουργία νέας επαφής"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Σύνδεση σε έναν λογαριασμό"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Εισαγωγή επαφών από ένα αρχείο"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Δημιουργία νέας ομάδας"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Δημιουργία νέας ομάδας]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Μετονομασία ομάδας"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Διαγραφή ομάδας"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Είστε βέβαιοι ότι θέλετε να διαγράψετε την ομάδα \'<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\'; (Οι επαφές δεν θα διαγραφούν)."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Προτού συνδεθείτε με κάποια άλλη επαφή, εισαγάγετε το όνομα της επαφής."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Συνδεδεμένη επαφή"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Το κείμενο αντιγράφηκε"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Απόρριψη αλλαγών"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Θέλετε να απορριφθούν οι αλλαγές σας;"</string>
+ <string name="discard" msgid="1234315037371251414">"Απόρριψη"</string>
</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 2b51547..a6a58cd 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Choose a contact short cut"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Choose a number to call"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Choose a number to message"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Select a contact"</string>
<string name="starredList" msgid="4817256136413959463">"Starred"</string>
<string name="frequentList" msgid="7154768136473953056">"Frequent"</string>
<string name="strequentList" msgid="5640192862059373511">"Favourites"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Delete contact"</string>
<string name="menu_call" msgid="3992595586042260618">"Call contact"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Text contact"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Send email"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Map address"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Make default number"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Make default email"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Separate"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Contacts separated"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Rename group"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Delete group"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"New"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Separate Contact"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Are you sure that you want to separate this single contact into multiple contacts: one for each set of contact information that was joined into it?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Join"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Deleting this contact will delete information from multiple accounts."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"This contact will be deleted."</string>
<string name="menu_done" msgid="796017761764190697">"Done"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Revert"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Cancel"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Edit contact"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"New contact"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Phonetic"</string>
<string name="label_notes" msgid="8337354953278341042">"Notes"</string>
<string name="label_sip_address" msgid="124073911714324974">"Internet call"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Ring tone"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"First and Last"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Phonetic name"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Company"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Title"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"The contact does not exist."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Create new contact"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Select label"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Phone"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Email"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"IM"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Postal address"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Address"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organisation"</item>
<item msgid="7196592230748086755">"Note"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"No pictures are available on the phone."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Contact icon"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"No pictures are available on the tablet."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"No pictures are available on the phone."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Contact photo"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Custom label name"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Display options"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Display options"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Send calls directly to voicemail"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Default"</string>
- <string name="changePicture" msgid="2943329047610967714">"Change icon"</string>
- <string name="removePicture" msgid="3041230993155966350">"Remove icon"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Remove photo"</string>
<string name="noContacts" msgid="8579310973261953559">"No contacts."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"No matching contacts found."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"No contacts with phone numbers."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Contact saved."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Error, unable to save contact changes."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Displaying 1 contact with phone number"</item>
- <item quantity="other" msgid="6133262880804110289">"Displaying <xliff:g id="COUNT">%d</xliff:g> contacts with phone numbers"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 contact with phone number"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> contacts with phone numbers"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"No visible contacts with phone numbers"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"No contacts with phone numbers"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Displaying 1 contact"</item>
- <item quantity="other" msgid="2865867557378939630">"Displaying <xliff:g id="COUNT">%d</xliff:g> contacts"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 contact"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> contacts"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"No visible contacts"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"No contacts"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"No visible contacts"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"No starred contacts"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"No contacts in <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Found 1 contact"</item>
- <item quantity="other" msgid="7752927996850263152">"Found <xliff:g id="COUNT">%d</xliff:g> contacts"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 found"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> found"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Contact not found"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"more than <xliff:g id="COUNT">%d</xliff:g> found"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Not found"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 contact"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> contacts"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 found"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> found"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Contacts"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favourites"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"SIM card contacts"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"You don\'t have any contacts to display. (If you\'ve just added an account, it can take a few minutes to sync contacts.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"You don\'t have any contacts to display."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"You don\'t have any contacts to display."\n\n"To add contacts, press "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" and touch:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" to add or configure an account with contacts that you can sync to the phone"\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/Export"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"You don\'t have any contacts to display. (If you\'ve just added an account, it can take a few minutes to sync contacts.)"\n\n"To add contacts, press "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" and touch:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" to add or configure an account with contacts that you can sync to the phone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Display options"</b></font>" to change which contacts are visible"\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/Export"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"You don\'t have any contacts to display."\n\n"To add contacts, press "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" and touch:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" to add or configure an account with contacts that you can sync to the phone"\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/Export"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"You don\'t have any contacts to display. (If you\'ve just added an account, it can take a few minutes to sync contacts.)"\n\n"To add contacts, press "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" and touch:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" to add or configure an account with contacts that you can sync to the phone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Display options"</b></font>" to change which contacts are visible"\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/Export"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"You don\'t have any contacts to display."\n\n"To add contacts, press "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" and touch:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" to add or configure an account with contacts that you can sync to the tablet"\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/Export"</b></font>" to import contacts from your SIM or SD card"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"You don\'t have any contacts to display."\n\n"To add contacts, press "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" and touch:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" to add or configure an account with contacts whom you can sync to the phone"\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/Export"</b></font>" to import contacts from your SIM or SD card"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"You don\'t have any contacts to display. (If you\'ve just added an account, it can take a few minutes to sync contacts.)"\n\n"To add contacts, press "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" and touch:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" to add or configure an account with contacts that you can sync to the tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Display options"</b></font>" to change which contacts are visible"\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/Export"</b></font>" to import contacts from your SIM or SD card"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"You don\'t have any contacts to display. (If you\'ve just added an account, it can take a few minutes to sync contacts.)"\n\n"To add contacts, press "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" and touch:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" to add or configure an account with contacts whom you can sync to the phone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Display options"</b></font>" to change which contacts are visible"\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/Export"</b></font>" to import contacts from your SIM or SD card"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"You don\'t have any contacts to display."\n\n"To add contacts, press "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" and touch:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" to add or configure an account with contacts that you can sync to the tablet"\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/Export"</b></font>" to import contacts from your SD card"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"You don\'t have any contacts to display."\n\n"To add contacts, press "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" and touch:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" to add or configure an account with contacts whom you can sync to the phone"\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/Export"</b></font>" to import contacts from your SD card"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"You don\'t have any contacts to display. (If you\'ve just added an account, it can take a few minutes to sync contacts.)"\n\n"To add contacts, press "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" and touch:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" to add or configure an account with contacts that you can sync to the tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Display options"</b></font>" to change which contacts are visible"\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/Export"</b></font>" to import contacts from your SD card"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"You don\'t have any contacts to display. (If you\'ve just added an account, it can take a few minutes to sync contacts.)"\n\n"To add contacts, press "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" and touch:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" to add or configure an account with contacts whom you can sync to the phone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Display options"</b></font>" to change which contacts are visible"\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/Export"</b></font>" to import contacts from your SD card"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"You don\'t have any favourites."\n\n"To add a contact to your list of favourites:"\n\n" "<li>"Touch the "<b>"Contacts"</b>" tab"\n</li>" "\n<li>"Touch the contact that you want to add to your favourites"\n</li>" "\n<li>"Touch the star next to the contact\'s name"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"All contacts"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Starred"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Call back"</string>
<string name="callAgain" msgid="3197312117049874778">"Call again"</string>
<string name="returnCall" msgid="8171961914203617813">"Return call"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> mins <xliff:g id="SECONDS">%2$s</xliff:g> secs"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> mins <xliff:g id="SECONDS">%s</xliff:g> secs"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Frequently contacted"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Add contact"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Add \"<xliff:g id="EMAIL">%s</xliff:g>\" to contacts?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"All"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"one"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"two"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"three"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Scanning USB storage failed (Reason: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Scanning SD card failed (Reason: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"I/O Error"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Memory is insufficient (the file may be too large)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Failed to parse vCard for unexpected reason"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Failed to parse vCard, though it seems in valid format, since the current implementation does not support it"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"No vCard file found in the USB storage"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"No vCard file found on the SD card"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"The format is not supported."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Failed to import vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"No vCard file found in the USB storage"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"No vCard file found on the SD card"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Failed to collect meta information of given vCard file(s)."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"One or more files failed to be imported (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Unknown error"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Select vCard file"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Reading vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Reading vCard file(s)"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Reading of vCard data has failed"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> of <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contacts"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> of <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> files"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Caching vCard(s) to local temporary storage"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Importer is caching vCard(s) to local temporary storage. Actual import will start soon."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importing <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importing <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Failed to Read vCard data"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Reading vCard data was cancelled"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Finished importing vCard <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Importing <xliff:g id="FILENAME">%s</xliff:g> was cancelled"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> will be imported shortly."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"The file will be imported shortly."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"vCard import request has been rejected. Please try later."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> will be exported shortly."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"vCard export request has been rejected. Please try later."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"contact"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Confirm export"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Are you sure that you want to export your contact list to \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Failed to export contact data"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Too many vCard files in the USB storage"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Too many vCard files on the SD card"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Required filename is too long (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Finished exporting <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Exporting <xliff:g id="FILENAME">%s</xliff:g> was cancelled"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Exporting contact data"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Exporting contact data to \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Could not initialise the exporter. \"<xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Error occured during export: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Failed to get database information"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"There is no exportable contact. If you actually have contacts on your phone, all the contacts may be prohibited from being exported outside the phone by some data provider."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"There are no exportable contacts. If you do have contacts on your tablet, all the contacts may be prohibited from being exported outside the tablet by some data providers."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"There are no exportable contacts. If you do have contacts on your phone, all the contacts may be prohibited from being exported outside the phone by some data providers."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"The vCard composer is not initialised correctly"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Could not open \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> of <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contacts"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Could not open \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> of <xliff:g id="TOTAL_NUMBER">%s</xliff:g> contacts"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Cancelling vCard import"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Are you sure that you want to cancel importing <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Cancelling vCard export"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Are you sure that you want to cancel exporting <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Failed to cancel vCard import/export"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Names of your contacts"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Add 2-sec pause"</string>
<string name="add_wait" msgid="3360818652790319634">"Add wait"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"No application found to handle this action"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Remember this choice"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Unknown"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"No data"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Clear defaults"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Defaults set for this contact:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Clear"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Accounts"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Import/Export"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Import/Export contacts"</string>
- <string name="menu_share" msgid="943789700636542260">"Share"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Share contact"</string>
<string name="share_via" msgid="563121028023030093">"Share contact via"</string>
<string name="share_error" msgid="4374508848981697170">"This contact cannot be shared."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Name"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organisation"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Website"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Event"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relationship"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Groups"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"W"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"This contact is read-only"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"More"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Primary name"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Create contact under account"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Remove sync group"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Add sync group"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"All Other Contacts"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"All Contacts"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Removing \'<xliff:g id="GROUP">%s</xliff:g>\' from sync will also remove any ungrouped contacts from sync."</string>
- <string name="account_phone" msgid="3682950835276226870">"Phone-only, unsynced"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Tablet-only, unsynced"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Phone-only, unsynced"</string>
<string name="call_custom" msgid="7756571794763171802">"Call <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Call home"</string>
<string name="call_mobile" msgid="7502236805487609178">"Call mobile"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chat using ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chat using Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Chat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Address"</string>
<string name="postal_street" msgid="8133143961580058972">"Street"</string>
<string name="postal_pobox" msgid="4431938829180269821">"PO box"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Neighbourhood"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"County"</string>
<string name="postal_postcode" msgid="572136414136673751">"Postcode"</string>
<string name="postal_country" msgid="7638264508416368690">"Country"</string>
+ <string name="full_name" msgid="6602579550613988977">"Name"</string>
<string name="name_given" msgid="1687286314106019813">"First name"</string>
<string name="name_family" msgid="3416695586119999058">"Surname"</string>
<string name="name_prefix" msgid="59756378548779822">"Name prefix"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Search contacts"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Search all contacts"</string>
<string name="take_photo" msgid="7496128293167402354">"Take photo"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Take new photo"</string>
<string name="pick_photo" msgid="448886509158039462">"Select photo from Gallery"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Contact list is being updated to reflect the change of language."\n\n"Please wait..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Contact list is being updated."\n\n"Please wait..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Contacts are in the process of being upgraded. "\n\n"The upgrade process requires approximately <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>Mb of internal phone storage."\n\n"Choose one of the following options:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Select new photo from Gallery"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Contact list is being updated to reflect the change of language."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Contact list is being updated."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Contacts are in the process of being upgraded. "\n\n"The upgrade process requires approximately <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB of internal storage."\n\n"Choose one of the following options:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Uninstall some applications"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Retry upgrade"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Search results for: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Searching..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Show selected"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Show all"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Select all"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Unselect all"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"No contacts selected."</string>
+ <string name="add_field" msgid="2384260056674995230">"Add another field"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favourite"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Edit contact"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"not merged"</item>
+ <item quantity="other" msgid="425683718017380845">"merged from <xliff:g id="COUNT">%0$d</xliff:g> sources"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Other"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Join contacts"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Join the current contact with the selected contact?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Edit selected contacts"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Switch to editing the selected contact? Information that you\'ve entered so far will be copied."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Copy to my contacts"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Directory <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Searching all contacts"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Directory"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Contacts"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Creating a personal copy"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Choose contact list"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"All contacts"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Starred"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Customised"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Customise..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Contacts with phone numbers"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Contact"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Define customised view"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Settings"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Settings"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Display options"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Find contacts"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Phone number"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Add to contacts"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Close"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Provide a year"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Contact"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Loading…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Create a new contact"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Sign in to an account"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Import contacts from a file"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Create new group"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Create new group]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Rename group"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Delete group"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Are you sure that you want to delete the group \'<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\'? (Contacts themselves will not be deleted.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Please enter contact name before joining with another contact."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Joined contact"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Text copied"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Discard changes"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Do you want to discard your changes?"</string>
+ <string name="discard" msgid="1234315037371251414">"Discard"</string>
</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index ab5050b..b0d6479 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Seleccionar un acceso directo"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Seleccionar un número para la llamada"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Seleccionar un número para el mensaje"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Seleccionar un contacto"</string>
<string name="starredList" msgid="4817256136413959463">"Marcado con asterisco"</string>
<string name="frequentList" msgid="7154768136473953056">"Frecuente"</string>
<string name="strequentList" msgid="5640192862059373511">"Favoritos"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Suprimir contacto"</string>
<string name="menu_call" msgid="3992595586042260618">"Llamar al contacto"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Enviar texto al contacto"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Enviar correo electrónico"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Dirección del mapa"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Crear número predeterminado"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Crear correo electrónico predeterminado"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Separar"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Contactos separados"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Renombrar grupo"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Eliminar grupo"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Nuevo"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Separar contacto"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"¿Estás seguro de que deseas separar este contacto individual en varios contactos: uno para cada grupo de información de contacto que se unió a él?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Unirse"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Eliminar este contacto suprimirá la información de mútliples cuentas."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Este contacto se suprimirá."</string>
<string name="menu_done" msgid="796017761764190697">"Finalizado"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Revertir"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Cancelar"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Editar contacto"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Nuevo contacto"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonético"</string>
<string name="label_notes" msgid="8337354953278341042">"Notas"</string>
<string name="label_sip_address" msgid="124073911714324974">"Llamada de Internet"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Tono de llamada"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Primero y último"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Nombre fonético"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Empresa"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Título"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"El contacto no existe."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Crear nuevo contacto"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Seleccionar etiqueta"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Teléfono"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Correo elec."</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Mensajería instantánea"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Dirección postal"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Dirección"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organización"</item>
<item msgid="7196592230748086755">"Nota"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"No hay imágenes disponibles en el teléfono."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Ícono de contacto"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"No hay imágenes disponibles en el tablet."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"No hay imágenes disponibles en el teléfono."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Foto de contacto"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Nombre personalizado de etiqueta"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Mostrar opciones"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Mostrar opciones"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Enviar llamadas directamente al correo de voz"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Predeterminado"</string>
- <string name="changePicture" msgid="2943329047610967714">"Cambiar icono"</string>
- <string name="removePicture" msgid="3041230993155966350">"Eliminar ícono"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Eliminar foto"</string>
<string name="noContacts" msgid="8579310973261953559">"No hay contactos."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"No se encontraron contactos coincidentes."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"No hay contactos con números de teléfono."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Contacto guardado."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Error, no se han podido guardar las modificaciones de contacto."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Mostrando 1 contacto con el número de teléfono"</item>
- <item quantity="other" msgid="6133262880804110289">"Se muestran <xliff:g id="COUNT">%d</xliff:g> contactos con números de teléfono."</item>
+ <item quantity="one" msgid="3015357862286673986">"1 contacto con el número de teléfono"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> contactos con números de teléfono"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"No hay contactos visibles con números de teléfono."</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"No hay contactos con números de teléfono"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Mostrando 1 contacto"</item>
- <item quantity="other" msgid="2865867557378939630">"Mostrando <xliff:g id="COUNT">%d</xliff:g> contactos"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 contacto"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> contactos"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"No hay contactos visibles"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"No hay contactos"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"No hay contactos visibles"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"No hay contactos con estrellas"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"No hay contactos en <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Se encontró 1 contacto"</item>
- <item quantity="other" msgid="7752927996850263152">"Se encontraron <xliff:g id="COUNT">%d</xliff:g> contactos"</item>
+ <item quantity="one" msgid="5517063038754171134">"Se encontró uno (1)"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g>Se encontró un"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"No se ha encontrado este contacto."</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"se encontraron más de <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"No se ha encontrado"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 contacto"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> contactos"</item>
+ <item quantity="one" msgid="4826918429708286628">"Se encontró uno (1)"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g>Se encontró un"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Contactos"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoritos"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Contactos de tarjeta SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"No tienes ningún contacto para mostrar. (Si has agregado una cuenta recientemente, la sincronización de los contactos puede demorar algunos minutos)."</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"No tienes ningún contacto para mostrar."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"No tienes ningún contacto para mostrar."\n\n"Para agregar contactos, presiona "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para agregar o configurar una cuenta con contactos que puedes sincronizar en el teléfono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear tú mismo un contacto nuevo"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"No tienes ningún contacto para mostrar. (Si has agregado una cuenta, la sincronización de contactos puede demorar pocos minutos)."\n\n"Para agregar contactos, presiona "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para agregar y configurar una cuenta con contactos que puedes sincronizar en el teléfono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mostrar opciones"</b></font>" para cambiar los contactos que sean visibles"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear tú mismo un contacto nuevo"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"No tienes ningún contacto para mostrar."\n\n"Para agregar contactos, presiona "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para agregar o configurar una cuenta con contactos que puedes sincronizar en el teléfono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear tú mismo un contacto nuevo"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"No tienes ningún contacto para mostrar. (Si has agregado una cuenta, la sincronización de contactos puede demorar pocos minutos)."\n\n"Para agregar contactos, presiona "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para agregar y configurar una cuenta con contactos que puedes sincronizar en el teléfono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mostrar opciones"</b></font>" para cambiar los contactos que sean visibles"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear tú mismo un contacto nuevo"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"No tienes ningún contacto para mostrar."\n\n"Para agregar contactos, presiona "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para agregar o configurar una cuenta con contacto que puedas sincronizar en la tableta"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un contacto desde cero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos desde tu tarjeta SIM o SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"No tienes ningún contacto para mostrar."\n\n"Para agregar contactos, presiona "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para agregar o configurar una cuenta con contacto que puedas sincronizar en el teléfono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un contacto desde cero"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos desde tu tarjeta SIM o SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"No tienes ningún contacto para mostrar. (Si has agregado una cuenta, la sincronización de contactos puede demorar algunos minutos)."\n\n"Para agregar contactos, presiona "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para agregar y configurar una cuenta con contactos que puedes sincronizar en el tablet "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mostrar opciones"</b></font>" para cambiar los contactos que sean visibles"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear tú mismo un contacto nuevo"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos de tu tarjeta SIM o SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"No tienes ningún contacto para mostrar. (Si has agregado una cuenta, la sincronización de contactos puede demorar algunos minutos)."\n\n"Para agregar contactos, presiona "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para agregar y configurar una cuenta con contactos que puedes sincronizar en el teléfono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mostrar opciones"</b></font>" para cambiar los contactos que sean visibles"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear tú mismo un contacto nuevo"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos de tu tarjeta SIM o SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"No tienes ningún contacto para mostrar."\n\n"Para agregar contactos, presiona "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para agregar o configurar una cuenta con contactos que puedes sincronizar en el tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear tú mismo un contacto nuevo"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos desde tu tarjeta SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"No tienes ningún contacto para mostrar."\n\n"Para agregar contactos, presiona "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para agregar o configurar una cuenta con contactos que puedes sincronizar en el teléfono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear tú mismo un contacto nuevo"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos desde tu tarjeta SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"No tienes ningún contacto para mostrar. (Si has agregado una cuenta, la sincronización de contactos puede demorar algunos minutos)."\n\n"Para agregar contactos, presiona "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para agregar y configurar una cuenta con contactos que puedes sincronizar en el tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mostrar opciones"</b></font>" para cambiar los contactos que sean visibles"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear tú mismo un contacto nuevo"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos desde tu tarjeta SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"No tienes ningún contacto para mostrar. (Si has agregado una cuenta, la sincronización de contactos puede demorar algunos minutos)."\n\n"Para agregar contactos, presiona "<font fgcolor="#ffffffff"><b>"Menú"</b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para agregar y configurar una cuenta con contactos que puedes sincronizar en el teléfono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mostrar opciones"</b></font>" para cambiar los contactos que sean visibles"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear tú mismo un contacto nuevo"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos desde tu tarjeta SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"No tienes ningún favorito."\n\n"Para agregar un contacto a tu lista de favoritos:"\n\n<li>"toca la pestaña "<b>"Contactos"</b>" "\n</li>" "\n<li>"toca el contacto que deseas agregar a tus favoritos"\n</li>" "\n<li>"toca el asterisco situado junto al nombre del contacto"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Todos los contactos"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Marcado con asterisco"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Volver a llamar"</string>
<string name="callAgain" msgid="3197312117049874778">"Llamar nuevamente"</string>
<string name="returnCall" msgid="8171961914203617813">"Regresar llamada"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> mins <xliff:g id="SECONDS">%2$s</xliff:g> s"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> mins <xliff:g id="SECONDS">%s</xliff:g> s"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Contactado con frecuencia"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Agregar contacto"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"¿Deseas agregar \"<xliff:g id="EMAIL">%s</xliff:g>\" a los contactos?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Todos"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"uno"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dos"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tres"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"No se pudo explorar el almacenamiento USB (Motivo: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"No se ha podido explorar la tarjeta SD. (Motivo: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Error de E/S"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Memoria insuficiente (es probable que el archivo sea muy grande)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"No se ha podido analizar vCard debido a un motivo inesperado"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"No se ha podido analizar vCard aunque el formato parece válido, ya que la implementación actual no lo admite"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"No se encontró ningún archivo vCard en el almacenamiento USB."</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"No se ha encontrado un archivo de vCard en la Tarjeta SD."</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"El formato no es compatible."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"No se pudo importar vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"No se encontró ningún archivo vCard en el almacenamiento USB."</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"No se ha encontrado un archivo de vCard en la Tarjeta SD."</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Error al recopilar metadatos de un archivo específico de la vCard."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"No se pudieron importar uno o más archivos (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Error desconocido"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Seleccionar archivo de vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Leyendo vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Leyendo archivo(s) de vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"No se han podido leer los datos de vCard"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contactos"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> archivos"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Almacenando vCard(s) en caché local temporal"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"El importador está almacenando la(s) vCard(s) en un caché local temporal. La importación comenzará pronto."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importando <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importando <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Error al leer los datos de la vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Se canceló la lectura de datos de la vCard"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Finalizó la importación de la vCard <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"La importación de <xliff:g id="FILENAME">%s</xliff:g> se canceló."</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> se importará en breve."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"El archivo se importará en breve."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"La solicitud para exportar la vCard se rechazó. Inténtalo nuevamente más tarde."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> se exportará en breve."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"La solicitud para exportar la vCard se rechazó. Inténtalo nuevamente más tarde."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"contacto"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Confirmar exportación"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"¿Estás seguro de que deseas exportar tu lista de contactos a \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"No se han podido exportar los datos de contacto"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Demasiados archivos de vCard en el almacenamiento USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Demasiados archivos de vCard en la tarjeta SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"El nombre de archivo requerido es demasiado largo (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Finalizó la exportación de <xliff:g id="FILENAME">%s</xliff:g>."</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"La exportación de <xliff:g id="FILENAME">%s</xliff:g> se canceló."</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Exportando datos de contacto"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Exportando datos de contacto a \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"No se ha podido inicializar el exportador: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Se produjo un error durante la exportación: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"No se ha podido obtener información de la base de datos"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"No hay ningún contacto exportable. Si en realidad tienes contactos en tu teléfono, es posible que algún proveedor de datos prohíba la exportación de todos los contactos."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"No hay ningún contacto exportable. Si en realidad tienes contactos en tu tablet, es posible que algún proveedor de datos prohíba la exportación de todos los contactos."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"No hay ningún contacto exportable. Si realmente tienes contactos en tu teléfono, es posible que algún proveedor de datos prohíba la exportación de todos los contactos."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"El redactor de vCard no se ha inicializado correctamente"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"No se pudo abrir \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contactos"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"No se pudo abrir \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%s</xliff:g> contactos"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Cancelación de importación de vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"¿Estás seguro de que deseas cancelar la importación de <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Cancelación de exportación de vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"¿Estás seguro de que deseas cancelar la exportación de <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Error al cancelar la importación/exportación de vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Nombres de tus contactos"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Agregar pausa de 2 segundos"</string>
<string name="add_wait" msgid="3360818652790319634">"Agregar espera"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"No se encontró una aplicación para manejar esta acción."</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Recuerda esta opción."</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Desconocido"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Sin datos"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Borrar configuraciones predeterminadas"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Configuraciones predeterminadas establecidas para este contacto:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Borrar"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Cuentas"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importar/Exportar"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importar/Exportar contactos"</string>
- <string name="menu_share" msgid="943789700636542260">"Compartir"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Compartir contacto"</string>
<string name="share_via" msgid="563121028023030093">"Compartir un contacto a través de"</string>
<string name="share_error" msgid="4374508848981697170">"Este contacto no se puede compartir."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Nombre"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organización"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Sitio web"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Evento"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relación"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grupos"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"Lun."</string>
<string name="type_short_work" msgid="4925330752504537861">"Mié."</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"Oct."</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Este contacto es de sólo lectura"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Más"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Nombre principal"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Crear contacto según la cuenta"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Eliminar grupo de sincronización"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Agregar grupo de sincronización"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Todos los otros contactos"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Todos los contactos"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Al suprimir \"<xliff:g id="GROUP">%s</xliff:g>\" de sincronización también se suprimirá cualquier contacto no agrupado de la sincronización."</string>
- <string name="account_phone" msgid="3682950835276226870">"Sólo telefónicamente, no sincronizado"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Sólo en tablet, sin sincronizar"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Sólo telefónicamente, no sincronizado"</string>
<string name="call_custom" msgid="7756571794763171802">"Llamar a <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Llamar a casa"</string>
<string name="call_mobile" msgid="7502236805487609178">"Llamar al celular"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chat mediante ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chat mediante Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Chat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Dirección"</string>
<string name="postal_street" msgid="8133143961580058972">"Dirección postal"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Apartado postal"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Barrio"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Estado"</string>
<string name="postal_postcode" msgid="572136414136673751">"Código POSTAL"</string>
<string name="postal_country" msgid="7638264508416368690">"País"</string>
+ <string name="full_name" msgid="6602579550613988977">"Nombre"</string>
<string name="name_given" msgid="1687286314106019813">"Nombre de pila"</string>
<string name="name_family" msgid="3416695586119999058">"Nombre familiar"</string>
<string name="name_prefix" msgid="59756378548779822">"Prefijo del nombre"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Buscar contactos"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Buscar todos los contactos"</string>
<string name="take_photo" msgid="7496128293167402354">"Tomar foto"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Tomar nueva foto"</string>
<string name="pick_photo" msgid="448886509158039462">"Seleccionar foto de la galería"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"La lista de contactos se está actualizando para reflejar el cambio de idioma."\n\n" Espera."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"La lista de contactos se está actualizando."\n\n" Espera..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"La actualización de los contactos está en proceso. "\n\n"El proceso de actualización requiere aproximadamente <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> Mb de almacenamiento interno en el teléfono."\n\n" Elije una de las siguientes opciones:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Seleccionar nueva foto de galería"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"La lista de contactos se está actualizando para reflejar el cambio de idioma."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"La lista de contactos se está actualizando."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Los contactos se están actualizando. "\n\n"Este proceso requiere aproximadamente <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mb de almacenamiento interno."\n\n"Selecciona una de las siguientes opciones:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Desinstalar algunas aplicaciones"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Intentar actualizar nuevamente"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Resultados de búsqueda para: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Buscando..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Mostrar los seleccionados"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Mostrar todos"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Seleccionar todo"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Desmarcar todos"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"1 destinatario seleccionado"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g> destinatarios seleccionados"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"No hay contactos seleccionados."</string>
+ <string name="add_field" msgid="2384260056674995230">"Agregar otro campo"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"a través de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> a través de <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favorito"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Editar contacto"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"no se combinó"</item>
+ <item quantity="other" msgid="425683718017380845">"combinado a partir de fuentes <xliff:g id="COUNT">%0$d</xliff:g>"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Otro"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Unirse a contactos"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"¿Unir contacto actual a contacto seleccionado?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Modificar contactos seleccionados"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"¿Optar por modificar contacto seleccionado? Se copiará la información que ingresaste hasta ahora."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Copiar a mis contactos"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Directorio <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Buscando todos los contactos"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Directorio"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Contactos"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Creando una copia personal"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Elige una lista de contacto"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Todos los contactos"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Destacado"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Personalizado"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Personalizar..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Contactos con números de teléfono"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Contacto"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Definir vista personalizada"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Configuraciones"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Configuraciones"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Mostrar opciones"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Buscar contactos"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Núm. de tel."</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Agregar a contactos"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Cerrar"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Brindar un año"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Contacto"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Cargando …"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Crear un contacto nuevo"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Accede a una cuenta"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importar contactos desde un archivo"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Crear grupo nuevo"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Crear grupo nuevo]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Renombrar grupo"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Eliminar grupo"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"¿Estás seguro de que deseas eliminar el grupo \'<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\'? (Los contactos no se eliminarán)."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Ingresa el nombre de contacto antes de unirlo con otro contacto."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Contacto que se unió"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Texto copiado"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Descartar cambios"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"¿Deseas descartar las modificaciones?"</string>
+ <string name="discard" msgid="1234315037371251414">"Descartar"</string>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 7ed74fc..93d2136 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Elegir un acceso directo para el contacto"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Elegir un número para la llamada"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Elegir un número para el mensaje"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Selección de un contacto"</string>
<string name="starredList" msgid="4817256136413959463">"Destacados"</string>
<string name="frequentList" msgid="7154768136473953056">"Frecuentes"</string>
<string name="strequentList" msgid="5640192862059373511">"Favoritos"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Eliminar contacto"</string>
<string name="menu_call" msgid="3992595586042260618">"Llamar al contacto"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Enviar SMS al contacto"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Enviar email"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Dirección en mapa"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Convertir en número predeterminado"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Establecer como dirección de email predeterminada"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Dividir"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Contactos divididos"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Cambiar el nombre del grupo"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Eliminar grupo"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Nuevo"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Dividir contacto"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"¿Estás seguro de que quieres dividir este contacto único en varios contactos (uno para cada conjunto de información que se añadió a él)?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Agrupar"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Si se elimina este contacto, se eliminará la información de varias cuentas."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"El contacto se eliminará."</string>
<string name="menu_done" msgid="796017761764190697">"OK"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Volver"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Cancelar"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Editar contacto"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Contacto nuevo"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Pronunciación"</string>
<string name="label_notes" msgid="8337354953278341042">"Notas"</string>
<string name="label_sip_address" msgid="124073911714324974">"Llamada por Internet"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Tono"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Nombre y apellido"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Transcripción fonética del nombre"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Empresa"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Título"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Ese contacto no existe."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Crear contacto nuevo"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Seleccionar etiqueta"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Teléfono"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Email"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"MI"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Dirección postal"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Dirección"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organización"</item>
<item msgid="7196592230748086755">"Nota"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"No hay ninguna imagen disponible en el teléfono."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Icono de contacto"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"No hay ninguna imagen disponible en la tableta."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"No hay ninguna imagen disponible en el teléfono."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Foto de contacto"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Nombre de etiqueta personalizada"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Opciones de visualización"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Opciones de visualización"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Enviar llamadas directamente al buzón de voz"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Predeterminado"</string>
- <string name="changePicture" msgid="2943329047610967714">"Cambiar icono"</string>
- <string name="removePicture" msgid="3041230993155966350">"Eliminar icono"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Eliminar foto"</string>
<string name="noContacts" msgid="8579310973261953559">"No hay ningún contacto."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"No se ha encontrado ningún contacto coincidente."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"No hay ningún contacto con número de teléfono."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"El contacto se ha guardado."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Se ha producido un error al guardar los cambios del contacto."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Mostrando un contacto con número de teléfono"</item>
- <item quantity="other" msgid="6133262880804110289">"<xliff:g id="COUNT">%d</xliff:g> contactos con números de teléfono"</item>
+ <item quantity="one" msgid="3015357862286673986">"Un (1) contacto con número de teléfono"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> contactos con números de teléfono"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"No hay ningún contacto visible con número de teléfono."</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Ningún contacto con número de teléfono"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Mostrando un contacto"</item>
- <item quantity="other" msgid="2865867557378939630">"Mostrando <xliff:g id="COUNT">%d</xliff:g> contactos"</item>
+ <item quantity="one" msgid="3405747744700823280">"Un (1) contacto"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> contactos"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"No hay contactos visibles."</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Ningún contacto"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Ningún contacto visible"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Ningún contacto destacado"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Ningún contacto en <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Se ha encontrado un contacto."</item>
- <item quantity="other" msgid="7752927996850263152">"Se han encontrado <xliff:g id="COUNT">%d</xliff:g> contactos."</item>
+ <item quantity="one" msgid="5517063038754171134">"1 encontrado"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> encontrados"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"No se ha encontrado el contacto."</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"encontrados más de <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"No se ha encontrado."</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 contacto"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> contactos"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 encontrado"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> encontrados"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Contactos"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoritos"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Contactos de tarjeta SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"No tienes ningún contacto que mostrar. (Si acabas de añadir una cuenta, es posible que la sincronización de contactos tarde algunos minutos)."</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"No tienes ningún contacto que mostrar."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"No tienes ningún contacto que mostrar."\n\n"Para añadir contactos, pulsa la tecla de "<font fgcolor="#ffffffff"><b>"menú"</b></font>" y toca en:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para añadir o configurar una cuenta con los contactos que puedes sincronizar en el teléfono,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un nuevo contacto,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/exportar."</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"No tienes ningún contacto que mostrar. (Si acabas de añadir una cuenta, es posible que la sincronización de contactos tarde algunos minutos)."\n\n"Para añadir contactos, pulsa la tecla de "<font fgcolor="#ffffffff"><b>"menú"</b></font>" y toca en:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para añadir o configurar una cuenta con contactos que puedes sincronizar en el teléfono,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opciones de visualización"</b></font>" para modificar los contactos visibles,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un nuevo contacto,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/exportar."</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"No tienes ningún contacto que mostrar."\n\n"Para añadir contactos, pulsa la tecla de "<font fgcolor="#ffffffff"><b>"menú"</b></font>" y toca en:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para añadir o configurar una cuenta con los contactos que puedes sincronizar en el teléfono,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un nuevo contacto,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/exportar."</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"No tienes ningún contacto que mostrar. (Si acabas de añadir una cuenta, es posible que la sincronización de contactos tarde algunos minutos)."\n\n"Para añadir contactos, pulsa la tecla de "<font fgcolor="#ffffffff"><b>"menú"</b></font>" y toca en:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para añadir o configurar una cuenta con contactos que puedes sincronizar en el teléfono,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opciones de visualización"</b></font>" para modificar los contactos visibles,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un nuevo contacto,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/exportar."</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"No tienes contactos."\n\n"Para añadir uno, pulsa la tecla de menú"<font fgcolor="#ffffffff"><b></b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para añadir o configurar una cuenta con los contactos que puedes sincronizar con la tableta,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un nuevo contacto, o"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/exportar"</b></font>" para importar contactos desde la tarjeta SIM o SD"\n</li>"."</string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"No tienes contactos."\n\n"Para añadir uno, pulsa la tecla de menú"<font fgcolor="#ffffffff"><b></b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para añadir o configurar una cuenta con los contactos que puedes sincronizar,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un nuevo contacto,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/exportar"</b></font>" para importar contactos desde la tarjeta SIM o SD"\n</li>"."</string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"No tienes contactos. Si acabas de añadir una cuenta, la sincronización puede tardar."\n\n"Para añadir contactos, pulsa la tecla de menú"<font fgcolor="#ffffffff"><b></b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para añadir o configurar una cuenta con contactos que puedes sincronizar con la tableta,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opciones de visualización"</b></font>" para modificar los contactos visibles,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un nuevo contacto, o"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/exportar"</b></font>" para importar contactos desde la tarjeta SIM o SD"\n</li>"."</string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"No tienes contactos. Si acabas de añadir una cuenta, la sincronización puede tardar."\n\n"Para añadir contactos, pulsa la tecla de menú"<font fgcolor="#ffffffff"><b></b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para añadir o configurar una cuenta con contactos que puedes sincronizar,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opciones de visualización"</b></font>" para modificar los contactos visibles,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un nuevo contacto,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/exportar"</b></font>" para importar contactos desde la tarjeta SIM o SD"\n</li>"."</string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"No tienes contactos."\n\n"Para añadir uno, pulsa la tecla de menú"<font fgcolor="#ffffffff"><b></b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para añadir o configurar una cuenta con los contactos que puedes sincronizar con la tableta,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un nuevo contacto, o"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/exportar"</b></font>" para importar contactos desde la tarjeta SD"\n</li>"."</string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"No tienes contactos."\n\n"Para añadir uno, pulsa la tecla de menú"<font fgcolor="#ffffffff"><b></b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para añadir o configurar una cuenta con los contactos que puedes sincronizar,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un nuevo contacto,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/exportar"</b></font>" para importar contactos desde la tarjeta SD"\n</li>"."</string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"No tienes contactos. Si acabas de añadir una cuenta, la sincronización de contactos puede tardar unos minutos."\n\n"Para añadir contactos, pulsa la tecla de menú"<font fgcolor="#ffffffff"><b></b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para añadir o configurar una cuenta con contactos que puedes sincronizar con la tableta,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opciones de visualización"</b></font>" para modificar los contactos visibles,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un nuevo contacto, o"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/exportar"</b></font>" para importar contactos desde la tarjeta SD"\n</li>"."</string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"No tienes contactos. Si acabas de añadir una cuenta, la sincronización puede tardar."\n\n"Para añadir contactos, pulsa la tecla de menú"<font fgcolor="#ffffffff"><b></b></font>" y toca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Cuentas"</b></font>" para añadir o configurar una cuenta con contactos que puedes sincronizar,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opciones de visualización"</b></font>" para modificar los contactos visibles,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Contacto nuevo"</b></font>" para crear un nuevo contacto,"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/exportar"</b></font>" para importar contactos desde la tarjeta SD"\n</li>"."</string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"No tienes favoritos."\n\n"Para añadir un contacto a tu lista de favoritos, sigue estos pasos:"\n\n" "<li>"Toca en la pestaña "<b>"Contactos"</b>"."\n</li>" "\n<li>"Selecciona el contacto que quieras añadir a tus favoritos."\n</li>" "\n<li>"Toca la estrella situada junto al nombre del contacto."\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Todos los contactos"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Destacados"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Volver a llamar"</string>
<string name="callAgain" msgid="3197312117049874778">"Volver a llamar"</string>
<string name="returnCall" msgid="8171961914203617813">"Devolver llamada"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min. y <xliff:g id="SECONDS">%2$s</xliff:g> seg."</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min. y <xliff:g id="SECONDS">%s</xliff:g> seg."</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Contactos frecuentes"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Añadir contacto"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"¿Deseas añadir \"<xliff:g id="EMAIL">%s</xliff:g>\" a Contactos?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Todo"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"uno"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dos"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tres"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Error al analizar USB (Motivo: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Error al buscar en la tarjeta SD (motivo: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Error de E/S"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Memoria insuficiente (el archivo puede ser demasiado grande)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"No se ha podido analizar el archivo de vCard debido a un error inesperado."</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"No se ha podido analizar el archivo de vCard (a pesar de que su formato parece ser válido) porque no es compatible con la implementación actual."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"No se ha encontrado el archivo de vCard en el almacenamiento USB."</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"No se ha encontrado ningún archivo de vCard en la tarjeta SD."</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"No se admite este formato."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Se ha producido un error al importar vCard."</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"No se ha encontrado el archivo de vCard en el almacenamiento USB."</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"No se ha encontrado ningún archivo de vCard en la tarjeta SD."</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Error al recopilar metainformación de los archivos vCard"</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"No ha sido posible importar uno o varios archivos (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Error desconocido"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Seleccionar archivo de vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Leyendo vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Leyendo archivos de vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Error al leer datos de vCard"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contactos"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> archivos"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Almacenamiento temporal local de vCard(s) en caché"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"El importador está almacenando de forma temporal local vCard(s). La importación empezará pronto."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importando <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importando <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Error al leer los datos de vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Lectura de vCard cancelada"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Importación de <xliff:g id="FILENAME">%s</xliff:g> de vCard finalizada"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Importación de <xliff:g id="FILENAME">%s</xliff:g> cancelada"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> se importará en breve."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"El archivo se importará en breve."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Se ha rechazado la solicitud de importación de vCard. Inténtalo de nuevo más tarde."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> se exportará en breve."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Se ha rechazado la solicitud de exportación de vCard. Inténtalo de nuevo más tarde."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"contacto"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Confirmar la exportación"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"¿Estás seguro de que quieres exportar tu lista de contactos a \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Error al exportar los datos del contacto"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Hay demasiados archivos de vCard en el almacenamiento USB."</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Hay demasiados archivos de vCard en la tarjeta SD."</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"El nombre de archivo necesario es demasiado largo (\"<xliff:g id="FILENAME">%s</xliff:g>\")."</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Exportación de <xliff:g id="FILENAME">%s</xliff:g> finalizada"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Exportación de <xliff:g id="FILENAME">%s</xliff:g> cancelada"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Exportando datos de contacto"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Exportando datos de contacto a \"<xliff:g id="FILE_NAME">%s</xliff:g>\"..."</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"No se ha podido inicializar el exportador: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Se ha producido un error durante la exportación: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Error al obtener información de la base de datos"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"No hay contactos que exportar. Si ya tienes contactos en el teléfono, es posible que algunos proveedores de datos no permitan que se exporten todos los contactos a otros dispositivos."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"No hay contactos que exportar. Si ya tienes contactos en la tableta, es posible que algunos proveedores de datos no permitan que se exporten todos los contactos a otros dispositivos."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"No hay contactos que exportar. Si ya tienes contactos en tu teléfono, es posible que algunos proveedores de datos no permitan que se exporten todos los contactos a otros dispositivos."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"El redactor de vCard no se ha inicializado correctamente."</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"No se ha podido abrir el archivo \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contactos"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"No se ha podido abrir el archivo \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%s</xliff:g> contactos"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Cancelando importación de vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"¿Seguro que deseas cancelar la importación de <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Cancelando exportación de vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"¿Estás seguro de que deseas cancelar la exportación de <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Error al cancelar import/export de vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Nombres de tus contactos"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Añadir pausa de dos segundos"</string>
<string name="add_wait" msgid="3360818652790319634">"Añadir espera"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"No se ha encontrado ninguna aplicación que pueda realizar esta acción."</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Recordar esta opción"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Desconocido"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Ningún dato"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Borrar valores predeterminados"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Valores predeterminados de este contacto:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Borrar"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Cuentas"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importar/exportar"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importar/exportar contactos"</string>
- <string name="menu_share" msgid="943789700636542260">"Compartir"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Compartir contacto"</string>
<string name="share_via" msgid="563121028023030093">"Compartir contacto a través de"</string>
<string name="share_error" msgid="4374508848981697170">"Este contacto no se puede compartir."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Nombre"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organización"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Sitio web"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Evento"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relación"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grupos"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"W"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Este contacto se encuentra en modo de solo lectura."</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Más"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Nombre principal"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Crear contacto en la cuenta"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Eliminar grupo de sincronización"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Añadir grupo de sincronización"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Todos los demás contactos"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Todos los contactos"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Si eliminas \"<xliff:g id="GROUP">%s</xliff:g>\" de la sincronización, también se eliminarán todos los contactos no agrupados."</string>
- <string name="account_phone" msgid="3682950835276226870">"Solo en el teléfono, no sincronizado"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Sólo en la tableta (sin sincronizar)"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Solo en el teléfono, no sincronizado"</string>
<string name="call_custom" msgid="7756571794763171802">"Llamar a <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Llamar a casa"</string>
<string name="call_mobile" msgid="7502236805487609178">"Llamar al móvil"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chatear con ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chatear con Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Chat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Dirección"</string>
<string name="postal_street" msgid="8133143961580058972">"Calle"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Apartado postal"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Vecindario"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Provincia/Estado"</string>
<string name="postal_postcode" msgid="572136414136673751">"Código postal"</string>
<string name="postal_country" msgid="7638264508416368690">"País"</string>
+ <string name="full_name" msgid="6602579550613988977">"Nombre"</string>
<string name="name_given" msgid="1687286314106019813">"Nombre"</string>
<string name="name_family" msgid="3416695586119999058">"Apellidos"</string>
<string name="name_prefix" msgid="59756378548779822">"Prefijo del nombre"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Buscar contactos"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Buscar todos los contactos"</string>
<string name="take_photo" msgid="7496128293167402354">"Hacer una foto"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Hacer una foto nueva"</string>
<string name="pick_photo" msgid="448886509158039462">"Seleccionar foto de la galería"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"La lista de contactos se está actualizando para reflejar el cambio de idioma."\n\n"Por favor, espera..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"La lista de contactos se está actualizando."\n\n"Por favor, espera..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Los contactos se están actualizando. "\n\n"El proceso de actualización necesita aproximadamente <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB de almacenamiento interno del teléfono."\n\n"Selecciona una de las siguientes opciones:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Seleccionar nueva foto de la galería"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Se está actualizando la lista de contactos con el cambio de idioma."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"La lista de contactos se está actualizando."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Los contactos se están actualizando. "\n\n"Este proceso requiere aproximadamente <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mb de espacio de almacenamiento interno."\n\n"Elige una de las siguientes opciones:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Desinstalar algunas aplicaciones"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Reintentar actualización"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Resultados de la búsqueda de: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Buscando..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Mostrar seleccionados"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Mostrar todos"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Seleccionar todo"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Desmarcar todo"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"Un destinatario seleccionado"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g> destinatarios seleccionados"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"No hay ningún contacto seleccionado."</string>
+ <string name="add_field" msgid="2384260056674995230">"Añadir otro campo"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"con <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> con <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favoritos"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Editar contacto"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"sin fusionar"</item>
+ <item quantity="other" msgid="425683718017380845">"fusionados desde <xliff:g id="COUNT">%0$d</xliff:g> fuentes"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Otro"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Agrupar contactos"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"¿Deseas agrupar el contacto actual con el contacto seleccionado?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Editar contactos seleccionados"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"¿Deseas editar el contacto seleccionado? Se copiará la información que hayas introducido hasta el momento."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Copiar en mis contactos"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Directorio <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Buscando todos los contactos"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Directorio"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Contactos"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Creando una copia personal"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Seleccionar lista de contactos"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Todos los contactos"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Destacados"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Personalizar"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Personalizar..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Contactos con números de teléfono"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Contacto"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Definición de vista personalizada"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Ajustes"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Ajustes"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Opciones de visualización"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Buscar contactos"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Número de teléfono"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Añadir a contactos"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Cerrar"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Introducir un año"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Contacto"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Cargando..."</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Crear contacto nuevo"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Acceder a una cuenta"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importar contactos de un archivo"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Crear grupo nuevo"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Crear grupo nuevo]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Cambiar el nombre del grupo"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Eliminar grupo"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"¿Estás seguro de que deseas eliminar el grupo \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\"? (Los contactos no se eliminarán)."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Para agrupar el contacto, debes introducir el nombre del otro contacto."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Contacto añadido"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Texto copiado"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Descartar cambios"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"¿Quieres descartar los cambios?"</string>
+ <string name="discard" msgid="1234315037371251414">"Descartar"</string>
</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 17fb983..fe2d70c 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"انتخاب یک میانبر مخاطب"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"انتخاب یک شماره برای تماس"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"انتخاب یک شماره برای پیام"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"انتخاب یک مخاطب"</string>
<string name="starredList" msgid="4817256136413959463">"ستاره دار"</string>
<string name="frequentList" msgid="7154768136473953056">"مکرر"</string>
<string name="strequentList" msgid="5640192862059373511">"موارد دلخواه"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"حذف مخاطب"</string>
<string name="menu_call" msgid="3992595586042260618">"تماس با مخاطب"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"ارسال متن به مخاطب"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"ارسال ایمیل"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"آدرس نقشه"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"تنظیم به عنوان شماره پیش فرض"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"تنظیم به عنوان ایمیل پیش فرض"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"تفکیک"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"مخاطبین تفکیک شده"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"تغییر نام گروه"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"حذف گروه"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"جدید"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"تفکیک مخاطب"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"آیا مطمئن هستید که می خواهید این یک مخاطب را به چند مخاطب تفکیک کنید: یک مخاطب برای هر مجموعه اطلاعات مخاطبی که به آن ملحق شده است؟"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"پیوستن"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"حذف این مخاطب اطلاعات را از حساب های متعدد حذف می کند."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"این مخاطب حذف می شود."</string>
<string name="menu_done" msgid="796017761764190697">"انجام شد"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"برگشت"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"لغو"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"ویرایش مخاطب"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"مخاطب جدید"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"تلفظ آوایی"</string>
<string name="label_notes" msgid="8337354953278341042">"یادداشت ها"</string>
<string name="label_sip_address" msgid="124073911714324974">"تماس اینترنتی"</string>
<string name="label_ringtone" msgid="8833166825330686244">"آهنگ زنگ"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"نام و نام خانوادگی"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"تلفظ نام"</string>
<string name="ghostData_company" msgid="5414421120553765775">"شرکت"</string>
<string name="ghostData_title" msgid="7496735200318496110">"عنوان"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"مخاطب موجود نیست."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"ایجاد مخاطب جدید"</string>
- <string name="selectLabel" msgid="4255424123394910733">"انتخاب برچسب"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"تلفن"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"ایمیل"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"IM"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"آدرس پستی"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"آدرس"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"سازمان"</item>
<item msgid="7196592230748086755">"توجه"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"عکسی در گوشی موجود نیست."</string>
- <string name="attachToContact" msgid="8820530304406066714">"نماد مخاطب"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"عکسی در رایانه لوحی موجود نیست."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"عکسی در گوشی موجود نیست."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"عکس مخاطب"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"نام برچسب سفارشی"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"گزینه های نمایش"</string>
- <string name="displayGroups" msgid="2278964020773993336">"گزینه های نمایش"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"ارسال تماس به صورت مستقیم به پست صوتی"</string>
<string name="default_ringtone" msgid="9099988849649827972">"پیش فرض"</string>
- <string name="changePicture" msgid="2943329047610967714">"تغییر نماد"</string>
- <string name="removePicture" msgid="3041230993155966350">"حذف نماد"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"حذف عکس"</string>
<string name="noContacts" msgid="8579310973261953559">"مخاطبی موجود نیست."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"مخاطب منطبقی یافت نشد."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"مخاطبی با شماره تلفن موجود نیست."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"مخاطب ذخیره شد."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"خطا، ذخیره تغییرات مخاطب امکان پذیر نیست."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"نمایش 1 مخاطب با شماره تلفن"</item>
- <item quantity="other" msgid="6133262880804110289">"نمایش <xliff:g id="COUNT">%d</xliff:g> مخاطب با شماره تلفن"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 مخاطب با شماره تلفن"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> مخاطب دارای شماره تلفن"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"مخاطب قابل رؤیتی با شماره تلفن موجود نیست"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"مخاطبی با شماره تلفن موجود نیست"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"نمایش 1 مخاطب"</item>
- <item quantity="other" msgid="2865867557378939630">"نمایش <xliff:g id="COUNT">%d</xliff:g> مخاطب"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 مخاطب"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> مخاطب"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"مخاطب قابل رؤیتی موجود نیست"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"مخاطبی موجود نیست"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"مخاطب قابل رؤیتی موجود نیست"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"هیچ مخاطب ستاره داری موجود نیست"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"هیچ مخاطبی در <xliff:g id="NAME">%s</xliff:g> موجود نیست"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"1 مخاطب یافت شد"</item>
- <item quantity="other" msgid="7752927996850263152">"<xliff:g id="COUNT">%d</xliff:g> مخاطب یافت شد"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 مورد پیدا شد"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> یافت شد"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"مخاطب یافت نشد"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"بیش از <xliff:g id="COUNT">%d</xliff:g> پیدا شد"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"یافت نشد"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 مخاطب"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> مخاطب"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 مورد پیدا شد"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> یافت شد"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"مخاطبین"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"موارد دلخواه"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"مخاطبین سیم کارت"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"شما مخاطبی برای نمایش ندارید. (در صورتی که اکنون یک حساب را اضافه کردید، همگام سازی مخاطبین چند دقیقه طول می کشد.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"شما مخاطبی برای نمایش ندارید."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"شما مخاطبی برای نمایش ندارید."\n\n"برای افزودن مخاطبین، "<font fgcolor="#ffffffff"><b>"منو"</b></font>" را فشار دهید و موارد زیر را لمس کنید:"\n" "\n<li><font fgcolor="#ffffffff"><b>"حساب ها"</b></font>" برای افزودن یا پیکربندی یک حساب با مخاطبینی که می توانید در گوشی همگام سازی کنید"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"مخاطب جدید"</b></font>" جهت ایجاد یک مخاطب جدید از ابتدا"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ورود/صدور"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"شما مخاطبی برای نمایش ندارید. (در صورتی که اکنون یک حساب اضافه کردید، همگام سازی مخاطبین چند دقیقه طول می کشد.)"\n\n"برای افزودن مخاطب، "<font fgcolor="#ffffffff"><b>"منو"</b></font>" را فشار داده و این موارد را لمس کنید:"\n" "\n<li><font fgcolor="#ffffffff"><b>"حساب ها"</b></font>" برای افزودن یا پیکربندی یک حساب با مخاطبینی که می توانید در تلفن همگام سازی کنید"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"گزینه های نمایش"</b></font>" برای تغییر مخاطبینی که قابل رؤیت هستند"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"مخاطب جدید"</b></font>" جهت ایجاد یک مخاطب جدید از ابتدا"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ورود/صدور"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"شما مخاطبی برای نمایش ندارید."\n\n"برای افزودن مخاطبین، "<font fgcolor="#ffffffff"><b>"منو"</b></font>" را فشار داده و این موارد را لمس کنید:"\n" "\n<li><font fgcolor="#ffffffff"><b>"حساب ها"</b></font>" جهت افزودن یا پیکربندی یک حساب با مخاطبینی که می توانید در تلفن همگام سازی کنید"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"مخاطب جدید"</b></font>" جهت ایجاد یک مخاطب جدید از ابتدا"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ورود/صدور"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"شما مخاطبی برای نمایش ندارید. (در صورتی که اکنون یک حساب اضافه کردید، همگام سازی مخاطبین چند دقیقه طول می کشد.)"\n\n"برای افزودن مخاطب، "<font fgcolor="#ffffffff"><b>"منو"</b></font>" را فشار داده و این موارد را لمس کنید:"\n" "\n<li><font fgcolor="#ffffffff"><b>"حساب ها"</b></font>" برای افزودن یا پیکربندی یک حساب با مخاطبینی که می توانید در تلفن همگام سازی کنید"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"گزینه های نمایش"</b></font>" برای تغییر مخاطبینی که قابل رؤیت هستند"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"مخاطب جدید"</b></font>" جهت ایجاد یک مخاطب جدید از ابتدا"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ورود/صدور"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"شما مخاطبی برای نمایش ندارید."\n\n"برای افزودن مخاطب، "<font fgcolor="#ffffffff"><b>"منو"</b></font>" را فشار داده و این موارد را لمس کنید:"\n" "\n<li><font fgcolor="#ffffffff"><b>"حساب ها"</b></font>" برای افزودن یا پیکربندی یک حساب با مخاطبینی که می توانید در رایانه لوحی همگام سازی کنید"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"حساب جدید"</b></font>" جهت ایجاد یک مخاطب از ابتدا"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"وارد کردن/صادر کردن"</b></font>" برای وارد کردن مخاطبان از سیم کارت یا کارت SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"شما مخاطبی برای نمایش ندارید."\n\n"برای افزودن مخاطب، "<font fgcolor="#ffffffff"><b>"منو"</b></font>" را فشار داده و این موارد را لمس کنید:"\n" "\n<li><font fgcolor="#ffffffff"><b>"حسابها"</b></font>" برای افزودن یا پیکربندی یک حساب با مخاطبینی که می توانید در تلفن همگام سازی کنید"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"مخاطب جدید"</b></font>" جهت ایجاد یک مخاطب جدید از ابتدا"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"وارد کردن/صادر کردن"</b></font>" برای وارد کردن مخاطبان از سیم کارت یا کارت SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"شما مخاطبی برای نمایش ندارید. (در صورتی که اکنون یک حساب اضافه کرده اید، همگام سازی مخاطبین چند دقیقه طول می کشد.)"\n\n"برای افزودن مخاطب، "<font fgcolor="#ffffffff"><b>"منو"</b></font>" را فشار داده و این موارد را لمس کنید:"\n" "\n<li><font fgcolor="#ffffffff"><b>"حساب ها"</b></font>" برای افزودن یا پیکربندی یک حساب با مخاطبینی که می توانید در رایانه لوحی همگام سازی کنید"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"گزینه های نمایش"</b></font>" برای تغییر مخاطبینی که قابل رؤیت هستند"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"مخاطب جدید"</b></font>" جهت ایجاد یک مخاطب جدید از ابتدا"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"وارد کردن/صادر کردن"</b></font>" برای وارد کردن مخاطبان از سیم کارت یا کارت SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"شما مخاطبی برای نمایش ندارید. (در صورتی که اکنون یک حساب اضافه کردید، همگام سازی مخاطبین چند دقیقه طول می کشد.)"\n\n"برای افزودن مخاطب، "<font fgcolor="#ffffffff"><b>"منو"</b></font>" را فشار داده و این موارد را لمس کنید:"\n" "\n<li><font fgcolor="#ffffffff"><b>"حساب ها"</b></font>" برای افزودن یا پیکربندی یک حساب با مخاطبینی که می توانید در تلفن همگام سازی کنید"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"گزینه های نمایش"</b></font>" برای تغییر مخاطبینی که قابل رؤیت هستند"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"مخاطب جدید"</b></font>" جهت ایجاد یک مخاطب جدید از ابتدا"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"وارد کردن/صادر کردن"</b></font>"برای وارد کردن مخاطبان از سیم کارت یا کارت SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"شما مخاطبی برای نمایش ندارید."\n\n"برای افزودن مخاطب، "<font fgcolor="#ffffffff"><b>"منو"</b></font>" را فشار داده و این موارد را لمس کنید:"\n" "\n<li><font fgcolor="#ffffffff"><b>"حساب ها"</b></font>" برای افزودن یا پیکربندی یک حساب با مخاطبینی که می توانید در رایانه لوحی همگام سازی کنید"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"مخاطب جدید"</b></font>" جهت ایجاد یک مخاطب جدید از ابتدا"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"وارد کردن/صادر کردن"</b></font>" برای وارد کردن مخاطبان از کارت SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"شما مخاطبی برای نمایش ندارید."\n\n"برای افزودن مخاطب "<font fgcolor="#ffffffff"><b>"منو"</b></font>" را فشار داده و این موارد را لمس کنید:"\n" "\n<li><font fgcolor="#ffffffff"><b>"حسابها"</b></font>" برای افزودن یا پیکربندی یک حساب با مخاطبینی که می توانید در تلفن همگام سازی کنید"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"مخاطب جدید"</b></font>" جهت ایجاد یک مخاطب جدید از ابتدا"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"وارد کردن/صادر کردن"</b></font>" برای وارد کردن مخاطبان از کارت SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"شما مخاطبی برای نمایش ندارید. (در صورتی که اکنون یک حساب اضافه کرده اید، همگام سازی مخاطبین چند دقیقه طول می کشد.)"\n\n"برای افزودن مخاطبین، "<font fgcolor="#ffffffff"><b>"منو"</b></font>" را فشار داده و این موارد را لمس کنید:"\n" "\n<li><font fgcolor="#ffffffff"><b>"حساب ها"</b></font>" برای افزودن یا پیکربندی یک حساب با مخاطبینی که می توانید در رایانه لوحی همگام سازی کنید"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"گزینه های نمایش"</b></font>" برای تغییر مخاطبینی که قابل رؤیت هستند"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"مخاطب جدید"</b></font>" برای ایجاد یک مخاطب جدید از ابتدا"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"وارد کردن/صادر کردن"</b></font>" برای وارد کردن مخاطبان از کارت SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"شما مخاطبی برای نمایش ندارید. (در صورتی که اکنون یک حساب اضافه کرده باشید، همگام سازی مخاطبین چند دقیقه طول می کشد.)"\n\n"برای افزودن مخاطب، "<font fgcolor="#ffffffff"><b>"منو"</b></font>" را فشار داده و این موارد را لمس کنید:"\n" "\n<li><font fgcolor="#ffffffff"><b>"حساب ها"</b></font>" برای افزودن یا پیکربندی یک حساب با مخاطبینی که می توانید در تلفن همگام سازی کنید"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"گزینه های نمایش"</b></font>" برای تغییر مخاطبینی که قابل رؤیت هستند"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"مخاطب جدید"</b></font>" جهت ایجاد یک مخاطب جدید از ابتدا"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"وارد کردن/صادر کردن"</b></font>"برای وارد کردن مخاطبان از کارت SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"شما هیچ مورد دلخواهی ندارید."\n\n"برای افزودن یک مخاطب به لیست موارد دلخواه خود: "\n\n" "<li>"برگه "<b>"مخاطبین"</b>\n</li>" را لمس کنید"\n<li>"مخاطبی را که می خواهید به لیست موارد دلخواه خود اضافه کنید لمس کنید"\n</li>" "\n<li>"ستاره واقع در کنار نام مخاطب را لمس کنید"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"همه مخاطبین"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"ستاره دار"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"بازگرداندن تماس"</string>
<string name="callAgain" msgid="3197312117049874778">"تماس مجدد"</string>
<string name="returnCall" msgid="8171961914203617813">"برگشت تماس"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> دقیقه و <xliff:g id="SECONDS">%2$s</xliff:g> ثانیه"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> دقیقه و <xliff:g id="SECONDS">%s</xliff:g> ثانیه"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"مکرراً تماس گرفته شده"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"افزودن مخاطب"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"\"<xliff:g id="EMAIL">%s</xliff:g>\" به مخاطبین افزوده شود؟"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"همه"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"یک"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"دو"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"سه"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"اسکن حافظه USB ناموفق بود (دلیل: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"اسکن کارت SD انجام نشد (دلیل: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"خطای ورودی/خروجی"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"حافظه کافی نیست (ممکن است فایل بسیار بزرگ باشد)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"تجزیه کارت ویزیت به دلیل پیش بینی نشده انجام نشد"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"با وجودیکه به نظر می رسد کارت ویزیت دارای قالب معتبری است، اما تجزیه آن انجام نشد، زیرا اجرای فعلی از آن پشتیبانی نمی کند"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"کارت ویزیتی در حافظه USB پیدا نشد"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"فایل کارت ویزیتی در کارت SD یافت نشد"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"قالب پشتیبانی نمی شود."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"vCard وارد نشد"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"فایل کارت ویزیتی در دستگاه ذخیره USB یافت نشد"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"فایل کارت ویزیتی در کارت SD یافت نشد"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"جمع آوری فوق اطلاعات فایل(های) کارت ویزیت موجود ناموفق بود."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"یک یا چند فایل وارد نشدند (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"خطای ناشناس"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"انتخاب فایل کارت ویزیت"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"خواندن کارت ویزیت"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"خواندن فایل(های) کارت ویزیت"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"خواندن داده های کارت ویزیت انجام نشد"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> از <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> مخاطب"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> از <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> فایل"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"ذخیره کارت(های) ویزیت در حافظه موقت محلی"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"واردکننده درحال ذخیره کارتهای ویزیت در حافظه موقت محلی است. ورود واقعی به زودی آغاز خواهد شد."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"وارد کردن <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"وارد کردن <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"خواندن داده کارت ویزیت ناموفق بود"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"خواندن داده کارت ویزیت لغو شد"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"وارد کردن کارت ویزیت <xliff:g id="FILENAME">%s</xliff:g> پایان یافت"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"وارد کردن <xliff:g id="FILENAME">%s</xliff:g> لغو شد"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> به زودی وارد می شود."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"فایل پس از مدت کوتاهی وارد می شود."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"درخواست ورود کارت ویزیت رد شد. لطفاً بعداً امتحان کنید."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> به زودی صادر می شود."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"درخواست صدور کارت ویزیت رد شد. لطفاً بعداً امتحان کنید."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"مخاطب"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"تأیید صدور"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"آیا مطمئن هستید که می خواهید لیست مخاطب خود را به \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\" صادر کنید؟"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"صدور اطلاعات مخاطب انجام نشد"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"تعداد فایل های کارت ویزیت در حافظه USB بسیار زیاد است"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"فایل های کارت ویزیت بسیار زیادی در کارت SD وجود دارد"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"نام فایل ضروری خیلی طولانی است (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"صادر کردن <xliff:g id="FILENAME">%s</xliff:g> پایان یافت"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"صادر کردن <xliff:g id="FILENAME">%s</xliff:g> لغو شد"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"صدور اطلاعات مخاطب"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"صدور اطلاعات مخاطب به \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"صادر کننده راه اندازی نشد: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"خطایی در هنگام صادر کردن روی داد: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"دریافت اطلاعات پایگاه داده انجام نشد"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"هیچ مخاطب قابل صدوری موجود نیست. در واقع اگر مخاطبین شما در گوشی قرار دارند، ممکن است از صدور همه مخاطبین به خارج از گوشی توسط برخی از ارائه دهندگان داده ممانعت شود."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"هیچ مخاطب قابل صدوری موجود نیست. اگر واقعاً مخاطبین شما در رایانه لوحی قرار دارند، ممکن است برخی از ارائه دهندگان داده صدور همه مخاطبین به خارج از رایانه لوحی را ممنوع کرده باشند."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"هیچ مخاطب قابل صدوری موجود نیست. اگر واقعاً مخاطبین شما در رایانه لوحی قرار دارند، ممکن است برخی از ارائه دهندگان داده صدور همه مخاطبین به خارج از رایانه لوحی را ممنوع کرده باشند."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"مؤلف کارت ویزیت به درستی راه اندازی نشده است"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"\"<xliff:g id="FILE_NAME">%1$s</xliff:g>\" باز نشد: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> از <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> مخاطب"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"\"<xliff:g id="FILE_NAME">%s</xliff:g>\" باز نشد: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> از <xliff:g id="TOTAL_NUMBER">%s</xliff:g> مخاطب"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"لغو وارد کردن کارت ویزیت"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"مطمئنید می خواهید وارد کردن <xliff:g id="FILENAME">%s</xliff:g> را لغو کنید؟"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"لغو صادر کردن کارت ویزیت"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"مطمئنید می خواهید صادر کردن <xliff:g id="FILENAME">%s</xliff:g> لغو شود؟"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"لغو صادر کردن/وارد کردن کارت ویزیت انجام نشد"</string>
<string name="search_settings_description" msgid="2675223022992445813">"نام های مخاطبین شما"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"افزودن یک مکث 2 ثانیه ای"</string>
<string name="add_wait" msgid="3360818652790319634">"افزودن انتظار"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"برنامه ای برای انجام این عملکرد موجود نیست"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"این گزینه را به خاطر بسپار"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"ناشناس"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"داده ای موجود نیست"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"پاک کردن پیش فرض ها"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"پیش فرض های تنظیم شده برای این مخاطب:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"پاک کردن"</string>
<string name="menu_accounts" msgid="8499114602017077970">"حساب ها"</string>
<string name="menu_import_export" msgid="3765725645491577190">"وارد کردن/صادر کردن"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"وارد کردن/صادر کردن مخاطبین"</string>
- <string name="menu_share" msgid="943789700636542260">"اشتراک گذاری"</string>
+ <string name="menu_share" msgid="8746849630474240344">"اشتراک گذاری مخاطب"</string>
<string name="share_via" msgid="563121028023030093">"اشتراک گذاری مخاطب از طریق"</string>
<string name="share_error" msgid="4374508848981697170">"این مخاطب قابل اشتراک گذاری نیست."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"نام"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"سازمان"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"وب سایت"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"رویداد"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"رابطه"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"گروه ها"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"W"</string>
<string name="type_short_pager" msgid="2613818970827594238">"p"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"این مخاطب فقط خواندنی است"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"بیشتر"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"نام اولیه"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"ایجاد مخاطبین تحت حساب"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"حذف گروه همگام سازی"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"افزودن گروه همگام سازی"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"کلیه مخاطبین دیگر"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"همه مخاطبین"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"حذف \"<xliff:g id="GROUP">%s</xliff:g>\" از همگام سازی نیز هر گونه مخاطب گروه بندی نشده ای را از همگام سازی حذف می کند."</string>
- <string name="account_phone" msgid="3682950835276226870">"فقط تلفن، همگام سازی نشده"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"فقط رایانه لوحی، همگام سازی نشده"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"فقط تلفن، همگام سازی نشده"</string>
<string name="call_custom" msgid="7756571794763171802">"تماس با <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"تماس با خانه"</string>
<string name="call_mobile" msgid="7502236805487609178">"تماس با تلفن همراه"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"چت با استفاده از ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"چت با استفاده از Jabber"</string>
<string name="chat" msgid="9025361898797412245">"چت"</string>
+ <string name="postal_address" msgid="8765560217149624536">"آدرس"</string>
<string name="postal_street" msgid="8133143961580058972">"خیابان"</string>
<string name="postal_pobox" msgid="4431938829180269821">"صندوق پستی"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"محله"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"ایالت"</string>
<string name="postal_postcode" msgid="572136414136673751">"کد پستی"</string>
<string name="postal_country" msgid="7638264508416368690">"کشور"</string>
+ <string name="full_name" msgid="6602579550613988977">"نام"</string>
<string name="name_given" msgid="1687286314106019813">"نام"</string>
<string name="name_family" msgid="3416695586119999058">"نام خانوادگی"</string>
<string name="name_prefix" msgid="59756378548779822">"پیشوند نام"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"جستجوی مخاطبین"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"جستجوی همه مخاطبین"</string>
<string name="take_photo" msgid="7496128293167402354">"عکسبرداری"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"گرفتن عکس جدید"</string>
<string name="pick_photo" msgid="448886509158039462">"انتخاب عکس از گالری"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"لیست مخاطبین جهت اعمال تغییر زبان در حال به روزرسانی است."\n\n"لطفاً منتظر بمانید..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"لیست مخاطب در حال به روزرسانی است."\n\n"لطفاً منتظر بمانید..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"مخاطبین در حال ارتقا هستند. "\n\n"فرآیند ارتقا به تقریباً <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> مگابایت از حافظه داخلی گوشی نیاز دارد. "\n\n"یکی از گزینه های زیر را انتخاب کنید:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"انتخاب عکس جدید از گالری"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"لیست مخاطبین در حال به روزرسانی برای منعکس کردن تغییرات زبان است."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"لیست مخاطبین در حال به روزرسانی است."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"مخاطبین در حال انجام فرایند ارتقا است. "\n\n"فرایند ارتقا نیاز به تقریباً <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> مگابایت حافظه داخلی دارد. "\n\n"یکی از گزینه های زیر را انتخاب کنید:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"حذف نصب برخی از برنامه های کاربردی"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"امتحان مجدد برای ارتقا"</string>
- <string name="search_results_for" msgid="8705490885073188513">"نتایج جستجو برای: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"در حال جستجو..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"نمایش موارد انتخاب شده"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"نمایش همه"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"انتخاب همه"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"لغو انتخاب همه موارد"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"مخاطبی انتخاب نشده است."</string>
+ <string name="add_field" msgid="2384260056674995230">"افزودن یک قسمت دیگر"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"از طریق <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> از طریق <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"مورد دلخواه"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"ویرایش مخاطب"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"ادغام نشد"</item>
+ <item quantity="other" msgid="425683718017380845">"ادغام شده از منابع <xliff:g id="COUNT">%0$d</xliff:g>"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"سایر موارد"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"پیوستن به مخاطبین"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"پیوستن به مخاطب فعلی با مخاطب انتخابی؟"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"ویرایش محتویات انتخابی"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"به ویرایش مخاطب انتخابی می روید؟ اطلاعاتی که تا حال وارد کرده اید کپی خواهد شد."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"کپی در مخاطبین من"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"دایرکتوری <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"جستجوی همه مخاطبین"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"دایرکتوری"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"مخاطبین"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"ایجاد یک کپی شخصی"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"انتخاب لیست مخاطب"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"همه مخاطبین"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"ستاره دار"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"سفارشی"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"سفارشی کردن..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"مخاطبین دارای شماره تلفن"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"مخاطب"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"تعیین نمای سفارشی"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"تنظیمات"</string>
+ <string name="menu_settings" msgid="377929915873428211">"تنظیمات"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"گزینه های نمایش"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>، <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"پیدا کردن مخاطبین"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"شماره تلفن"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"افزودن به مخاطبین"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"بستن"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"یک سال وارد کنید"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"مخاطب"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"در حال بارگیری …"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"ایجاد مخاطب جدید"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"وارد شدن به یک حساب"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"وارد کردن مخاطبین از یک فایل"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"ایجاد گروه جدید"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[ایجاد گروه جدید]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"تغییر نام گروه"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"حذف گروه"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"مطمئنید می خواهید گروه \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\" حذف شود؟ (مخاطبین خودشان حذف نخواهند شد.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"لطفاً قبل از پیوستن به مخاطب دیگری نام مخاطب را وارد کنید."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"مخاطب ملحق شده"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"متن کپی شده"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"صرفنظر از تغییرات"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"آیا می خواهید از تغییرات خود صرفنظر کنید؟"</string>
+ <string name="discard" msgid="1234315037371251414">"صرفنظر"</string>
</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index f3b1c62..d452d95 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Valitse pikakuvakkeen yhteystieto"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Valitse vastaanottajan numero"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Valitse vastaanottajan numero"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Valitse yhteystiedot"</string>
<string name="starredList" msgid="4817256136413959463">"Tähdelliset"</string>
<string name="frequentList" msgid="7154768136473953056">"Usein käytetyt"</string>
<string name="strequentList" msgid="5640192862059373511">"Suosikit"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Poista yhteystieto"</string>
<string name="menu_call" msgid="3992595586042260618">"Soita"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Lähetä tekstiviesti yhteystiedolle"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Lähetä sähköpostia"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Osoite kartalla"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Aseta oletusnumeroksi"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Aseta oletussähköpostiosoitteeksi"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Erota"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Yhteystiedot erotettu"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Nimeä ryhmä uudelleen"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Poista ryhmä"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Uusi"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Erota yhteystieto"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Haluatko varmasti erottaa tämän yhteystiedon alkuperäisiin yhteystietojoukkoihin, joista se on muodostettu?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Yhdistä"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Kun tämä yhteystieto poistetaan, tietoja poistetaan useilta tileiltä."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Tämä yhteystieto poistetaan."</string>
<string name="menu_done" msgid="796017761764190697">"Valmis"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Palauta"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Peruuta"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Muokkaa yhteystietoa"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Uusi yhteystieto"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Ääntämistapa"</string>
<string name="label_notes" msgid="8337354953278341042">"Muistiinpanot"</string>
<string name="label_sip_address" msgid="124073911714324974">"Internet-puhelu"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Soittoääni"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Etu- ja sukunimi"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Nimen ääntämistapa"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Yritys"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Nimi"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Yhteystietoa ei ole olemassa."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Luo uusi yhteystieto"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Valitse tunniste"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Puhelin"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Sähköposti"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Pikaviestitili"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Postiosoite"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Osoite"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organisaatio"</item>
<item msgid="7196592230748086755">"Muistiinpano"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Tässä puhelimessa ei ole käytettäviä kuvia."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Yhteystiedon kuvake"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Tablet-laitteella ei ole kuvia."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Tässä puhelimessa ei ole käytettäviä kuvia."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Yhteyshenkilön valokuva"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Oma tunnisteen nimi"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Näyttövalinnat"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Näyttövalinnat"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Ohjaa puheluja suoraan vastaajaan"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Oletus"</string>
- <string name="changePicture" msgid="2943329047610967714">"Vaihda kuvake"</string>
- <string name="removePicture" msgid="3041230993155966350">"Poista kuvake"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Poista kuva"</string>
<string name="noContacts" msgid="8579310973261953559">"Ei yhteystietoja."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Hakua vastaavia yhteystietoja ei löydy."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Ei yhteystietoja, joissa on puhelinnumero."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Yhteystieto tallennettu"</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Yhteystiedon muutosten tallentamisessa tapahtui virhe."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Näytetään 1 yhteystieto, jossa on puhelinnumero"</item>
- <item quantity="other" msgid="6133262880804110289">"Näytetään <xliff:g id="COUNT">%d</xliff:g> yhteystietoa, joissa on puhelinnumero"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 yhteystieto, jossa puhelinnumero"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> yhteystietoa, joissa puhelinnumero"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Ei näkyviä yhteystietoja, joissa on puhelinnumero"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Ei yhteystietoja, joissa on puhelinnumero"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Näytetään 1 yhteystieto"</item>
- <item quantity="other" msgid="2865867557378939630">"Näytetään <xliff:g id="COUNT">%d</xliff:g> yhteystietoa"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 yhteystieto"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> yhteystietoa"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Ei näkyviä yhteystietoja"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Ei yhteystietoja"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Ei näkyviä yhteystietoja"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Ei tähdellisiä yhteystietoja"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Ei yhteystietoja ryhmässä <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"1 yhteystieto löytyi"</item>
- <item quantity="other" msgid="7752927996850263152">"<xliff:g id="COUNT">%d</xliff:g> yhteystietoa löytyi"</item>
+ <item quantity="one" msgid="5517063038754171134">"Löytyi 1"</item>
+ <item quantity="other" msgid="3852668542926965042">"Löytyi <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Yhteystietoa ei löydy"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"löytyi yli <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Ei löydy"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 yhteystieto"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> yhteystietoa"</item>
+ <item quantity="one" msgid="4826918429708286628">"Löytyi 1"</item>
+ <item quantity="other" msgid="7988132539476575389">"Löytyi <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Yhteystiedot"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Suosikit"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"SIM-kortin yhteystiedot"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Ei näytettäviä yhteystietoja. (Jos olet juuri lisännyt tilin, yhteystietojen synkronoinnissa voi mennä hetki.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Ei näytettäviä yhteystietoja."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Ei näytettäviä yhteystietoja."\n\n"Voit lisätä yhteystietoja painamalla "<font fgcolor="#ffffffff"><b>"Valikko"</b></font>"-painiketta ja valitsemalla"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tilit"</b></font>", jos haluat lisätä tai määrittää tilin, jonka yhteystiedot synkronoidaan puhelimeen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uusi yhteystieto"</b></font>", jos haluat luoda uuden yhteystiedon alusta"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuo/Vie"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Ei näytettäviä yhteystietoja. (Jos olet juuri lisännyt tilin, yhteystietojen synkronoinnissa voi mennä hetki.)"\n\n"Voit lisätä yhteystietoja painamalla "<font fgcolor="#ffffffff"><b>"Valikko"</b></font>"-painiketta ja valitsemalla"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tilit"</b></font>", jos haluat lisätä tai määrittää tilin, jonka yhteystiedot synkronoidaan puhelimeen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Näyttövalinnat"</b></font>", jos haluat valita näkyvät yhteystiedot"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uusi yhteystieto"</b></font>", jos haluat luoda uuden yhteystiedon alusta"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuo/Vie"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Ei näytettäviä yhteystietoja."\n\n"Voit lisätä yhteystietoja painamalla "<font fgcolor="#ffffffff"><b>"Valikko"</b></font>"-painiketta ja valitsemalla"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tilit"</b></font>", jos haluat lisätä tai määrittää tilin, jonka yhteystiedot synkronoidaan puhelimeen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uusi yhteystieto"</b></font>", jos haluat luoda uuden yhteystiedon alusta"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuo/Vie"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Ei näytettäviä yhteystietoja. (Jos olet juuri lisännyt tilin, yhteystietojen synkronoinnissa voi mennä hetki.)"\n\n"Voit lisätä yhteystietoja painamalla "<font fgcolor="#ffffffff"><b>"Valikko"</b></font>"-painiketta ja valitsemalla"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tilit"</b></font>", jos haluat lisätä tai määrittää tilin, jonka yhteystiedot synkronoidaan puhelimeen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Näyttövalinnat"</b></font>", jos haluat valita näkyvät yhteystiedot"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uusi yhteystieto"</b></font>", jos haluat luoda uuden yhteystiedon alusta"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuo/Vie"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Ei näytettäviä yhteystietoja."\n\n"Lisää yhteystietoja painamalla "<font fgcolor="#ffffffff"><b>"Valikko"</b></font>" ja koskettamalla "\n" "\n<li><font fgcolor="#ffffffff"><b>"Tilit"</b></font>" lisätäksesi tilin tai muokataksesi tiliä, jonka yhteystiedot voit synkronoida tablet-laitteen kanssa"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uudet yhteystiedot"</b></font>" luodaksesi uudet yhteystiedot alusta alkaen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Vie/Tuo"</b></font>" tuodaksesi yhteystietoja SIM- tai SD-kortiltasi"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Ei näytettäviä yhteystietoja."\n\n"Lisää yhteystietoja painamalla "<font fgcolor="#ffffffff"><b>"Valikko"</b></font>" ja kosketa "\n" "\n<li><font fgcolor="#ffffffff"><b>"Tilit"</b></font>" lisätäksesi tilin tai muokataksesi tiliä, jonka yhteystiedot voit synkronoida puhelimen kanssa"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uudet yhteystiedot"</b></font>" luodaksesi uudet yhteystiedot alusta alkaen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuo/vie"</b></font>" tuodaksesi yhteystietoja SIM- tai SD-kortiltasi"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Ei näytettäviä yhteystietoja. (Jos lisäsit tilin äskettäin, yhteystietojen synkronointi voi kestää muutaman minuutin.)"\n\n"Lisää yhteystietoja painamalla "<font fgcolor="#ffffffff"><b>"Valikko"</b></font>" ja koskettamalla "\n" "\n<li><font fgcolor="#ffffffff"><b>"Tilit"</b></font>" lisätäksesi tilin tai muokataksesi tiliä, jonka yhteystiedot voit synkronoida tablet-laitteen kanssa"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Näyttövalinnat"</b></font>" muuttaaksesi sitä, mitkä yhteystiedot ovat näkyvissä"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uudet yhteystiedot"</b></font>" luodaksesi uudet yhteystiedot alusta alkaen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuo/vie"</b></font>" tuodaksesi yhteystietoja SIM- tai SD-kortiltasi"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Ei näytettäviä yhteystietoja. (Jos lisäsit tilin äskettäin, yhteystietojen synkronointi voi kestää muutaman minuutin.)"\n\n"Lisää yhteystietoja painamalla "<font fgcolor="#ffffffff"><b>"Valikko"</b></font>" ja koskettamalla "\n" "\n<li><font fgcolor="#ffffffff"><b>"Tilit"</b></font>" lisätäksesi tilin tai muokataksesi tiliä, jonka yhteystiedot voit synkronoida puhelimen kanssa"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Näyttövalinnat"</b></font>" muuttaaksesi sitä, mitkä yhteystiedot ovat näkyvissä"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uudet yhteystiedot"</b></font>" luodaksesi uudet yhteystiedot alusta alkaen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuo/vie"</b></font>" tuodaksesi yhteystietoja SIM- tai SD-kortiltasi"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Ei näytettäviä yhteystietoja."\n\n"Lisää yhteystietoja painamalla "<font fgcolor="#ffffffff"><b>"Valikko"</b></font>" ja koskettamalla "\n" "\n<li><font fgcolor="#ffffffff"><b>"Tilit"</b></font>" lisätäksesi tilin tai muokataksesi tiliä, jonka yhteystiedot voit synkronoida puhelimen kanssa"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uudet yhteystiedot"</b></font>" luodaksesi uudet yhteystiedot alusta alkaen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuo/vie"</b></font>" tuodaksesi yhteystietoja SD-kortiltasi"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Ei näytettäviä yhteystietoja."\n\n"Lisää yhteystietoja painamalla "<font fgcolor="#ffffffff"><b>"Valikko"</b></font>" ja koskettamalla "\n" "\n<li><font fgcolor="#ffffffff"><b>"Tilit"</b></font>" lisätäksesi tilin tai muokataksesi tiliä, jonka yhteystiedot voit synkronoida puhelimen kanssa"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uudet yhteystiedot"</b></font>" luodaksesi uudet yhteystiedot alusta alkaen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuo/vie"</b></font>" tuodaksesi yhteystietoja SD-kortiltasi"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Ei näytettäviä yhteystietoja. (Jos lisäsit tilin äskettäin, yhteystietojen synkronointi voi kestää muutaman minuutin.)"\n\n"Lisää yhteystietoja painamalla "<font fgcolor="#ffffffff"><b>"Valikko"</b></font>" ja koskettamalla "\n" "\n<li><font fgcolor="#ffffffff"><b>"Tilit"</b></font>" lisätäksesi tilin tai muokataksesi tiliä, jonka yhteystiedot voit synkronoida tablet-laitteen kanssa"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Näyttövalinnat"</b></font>" muuttaaksesi sitä, mitkä yhteystiedot ovat näkyvissä"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uudet yhteystiedot"</b></font>" luodaksesi uudet yhteystiedot alusta alkaen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuo/vie"</b></font>" tuodaksesi yhteystietoja SD-kortiltasi"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Ei näytettäviä yhteystietoja. (Jos lisäsit tilin äskettäin, yhteystietojen synkronointi voi kestää muutaman minuutin.)"\n\n"Lisää yhteystietoja painamalla "<font fgcolor="#ffffffff"><b>"Valikko"</b></font>" ja koskettamalla "\n" "\n<li><font fgcolor="#ffffffff"><b>"Tilit"</b></font>" lisätäksesi tilin tai muokataksesi tiliä, jonka yhteystiedot voit synkronoida puhelimen kanssa"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Näyttövalinnat"</b></font>" muuttaaksesi sitä, mitkä yhteystiedot ovat näkyvissä"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uudet yhteystiedot"</b></font>" luodaksesi uudet yhteystiedot alusta alkaen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuo/vie"</b></font>" tuodaksesi yhteystietoja SD-kortiltasi"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Sinulla ei ole suosikkeja."\n\n"Yhteystiedon lisääminen suosikkeihin:"\n\n" "<li>"Kosketa "<b>"Yhteystiedot"</b>"-välilehteä"\n</li>" "\n<li>"Kosketa yhteystietoa, jonka haluat lisätä suosikkeihisi"\n</li>" "\n<li>"Kosketa yhteystiedon nimen vieressä näkyvää tähteä"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Kaikki yhteystiedot"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Tähdelliset"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Soita takaisin"</string>
<string name="callAgain" msgid="3197312117049874778">"Soita uudelleen"</string>
<string name="returnCall" msgid="8171961914203617813">"Soita takaisin"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min <xliff:g id="SECONDS">%2$s</xliff:g> s"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Usein käytetyt"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Lisää yhteystieto"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Lisätäänkö <xliff:g id="EMAIL">%s</xliff:g> yhteystietoihin?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Kaikki"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"yksi"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"kaksi"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"kolme"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"USB-tallennustilan lukeminen epäonnistui (syy: <xliff:g id="FAIL_REASON">%s</xliff:g>)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"SD-kortin lukeminen epäonnistui (Syy: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"I/O-virhe"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Muisti ei riitä (tiedosto voi olla liian suuri)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"vCardin jäsennys epäonnistui tuntemattomasta syystä"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"vCard näyttää olevan oikeassa muodossa, mutta sen jäsennys epäonnistui, sillä nykyinen sovellus ei tue muotoa"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"USB-tallennustilasta ei löydy vCard-tiedostoa"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"SD-kortilta ei löydy vCard-tiedostoa"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Muotoa ei tueta."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"vCard-tietojen tuonti epäonnistui"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"USB-tallennustilasta ei löydy vCard-tiedostoa"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"SD-kortilta ei löydy vCard-tiedostoa"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Annettujen vCard-tiedostojen sisällönkuvaustietojen noutaminen epäonnistui."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Ainakin yhden tiedoston tuominen epäonnistui (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Tuntematon virhe"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Valitse vCard-tiedosto"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Luetaan vCardia"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Luetaan vCard-tiedostoja"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"vCard-tietojen lukeminen epäonnistui"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> / <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> yhteystietoa"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> / <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> tiedostoa"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Lisätään vCard-tietojen välimuistiversiot paikalliseen väliaikaistallennustilaan"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Tuontisovellus lisää vCard-tietoja paikalliseen välimuistiin. Tuonti alkaa pian."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Tuodaan <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Tuodaan <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"vCard-tietojen lukeminen epäonnistui"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"vCard-tietojen lukeminen peruutettiin"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"vCardin <xliff:g id="FILENAME">%s</xliff:g> tuonti valmis"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Kohteen <xliff:g id="FILENAME">%s</xliff:g> tuonti peruutettu"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> tuodaan pian."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Tiedosto tuodaan pian."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"vCard-tuontipyyntö hylättiin. Yritä myöhemmin uudelleen."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> viedään pian."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"vCard-vientipyyntö hylättiin. Yritä myöhemmin uudelleen."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"yhteystieto"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Vahvista vienti"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Haluatko varmasti viedä yhteystietoluettelon tiedostoon <xliff:g id="VCARD_FILENAME">%s</xliff:g>?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Yhteystietojen vieminen epäonnistui"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Liikaa vCard-tiedostoja USB-tallennustilassa"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"SD-kortilla on liian monta vCard-tiedostoa"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Tarvittava tiedostonimi on liian pitkä (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Kohteen <xliff:g id="FILENAME">%s</xliff:g> vienti valmis"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Kohteen <xliff:g id="FILENAME">%s</xliff:g> vienti peruutettu"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Viedään yhteystietoja"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Viedään yhteystietoja tiedostoon \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Vientiohjelman alustus epäonnistui: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Viennin aikana tapahtui virhe: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Tietokannan tietojen hakeminen epäonnistui"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Ei vietäviä yhteystietoja. Jos puhelimessasi on yhteystietoja, tietojen toimittaja on voinut kieltää niiden kaikkien viemisen puhelimen ulkopuolelle."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Ei vietäviä yhteystietoja. Jos tablet-laitteellasi on yhteystietoja, tietojen tarjoaja on saattanut estää niiden viemisen tablet-laitteen ulkopuolelle."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Ei vietäviä yhteystietoja. Jos puhelimellasi on yhteystietoja, tietojen tarjoaja on saattanut estää niiden viemisen puhelimen ulkopuolelle."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"vCard-luontia ei ole alustettu oikein"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Tiedostoa \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\" ei voi avata: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> / <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> yhteystietoa"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Tiedostoa \"<xliff:g id="FILE_NAME">%s</xliff:g>\" ei voi avata: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> / <xliff:g id="TOTAL_NUMBER">%s</xliff:g> yhteystietoa"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Peruutetaan vCardin tuontia"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Haluatko varmasti peruuttaa kohteen <xliff:g id="FILENAME">%s</xliff:g> tuonnin?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Peruutetaan vCardin vientiä"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Haluatko varmasti peruuttaa kohteen <xliff:g id="FILENAME">%s</xliff:g> viennin?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"vCardin tuonnin/viennin peruutus epäonnistui"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Yhteystietojen nimet"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Lisää 2 sekunnin tauko"</string>
<string name="add_wait" msgid="3360818652790319634">"Lisää tauko"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Tätä toimintoa käsittelevää sovellusta ei löydy"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Muista valinta"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Tuntematon"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Ei tietoja"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Tyhjennä oletukset"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Tämän yhteystiedon oletukset:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Tyhjennä"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Tilit"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Tuo/Vie"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Tuo/Vie yhteystietoja"</string>
- <string name="menu_share" msgid="943789700636542260">"Jaa"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Jaa yhteystiedot"</string>
<string name="share_via" msgid="563121028023030093">"Jaa yhteystieto"</string>
<string name="share_error" msgid="4374508848981697170">"Tätä yhteystietoa ei voi jakaa."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Nimi"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organisaatio"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Sivusto"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Tapahtuma"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Suhde"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Ryhmät"</string>
<string name="type_short_home" msgid="7770424864090605384">"K"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"T"</string>
<string name="type_short_pager" msgid="2613818970827594238">"H"</string>
<string name="type_short_other" msgid="5669407180177236769">"T"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Tämä yhteystieto on vain luku -tilassa"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Lisää"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Ensisijainen nimi"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Luo yhteystieto tilissä"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Poista synkronointiryhmä"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Lisää synkronointiryhmä"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Kaikki muut yhteystiedot"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Kaikki yhteystiedot"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Ryhmän <xliff:g id="GROUP">%s</xliff:g> poistaminen synkronoinnista lopettaa myös ryhmittelemättömien yhteystietojen synkronoinnin."</string>
- <string name="account_phone" msgid="3682950835276226870">"Vain puhelin, ei synkronoitu"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Vain tablet-laite, ei synkronoitu"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Vain puhelin, ei synkronoitu"</string>
<string name="call_custom" msgid="7756571794763171802">"Soita: <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Soita kotinumeroon"</string>
<string name="call_mobile" msgid="7502236805487609178">"Soita matkapuhelimeen"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Keskustele ICQ:n avulla"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Keskustele Jabberin avulla"</string>
<string name="chat" msgid="9025361898797412245">"Pikaviesti"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Osoite"</string>
<string name="postal_street" msgid="8133143961580058972">"Katuosoite"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Postilokero"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Kaupunginosa"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Lääni"</string>
<string name="postal_postcode" msgid="572136414136673751">"Postinumero"</string>
<string name="postal_country" msgid="7638264508416368690">"Maa"</string>
+ <string name="full_name" msgid="6602579550613988977">"Nimi"</string>
<string name="name_given" msgid="1687286314106019813">"Etunimi"</string>
<string name="name_family" msgid="3416695586119999058">"Sukunimi"</string>
<string name="name_prefix" msgid="59756378548779822">"Nimen etuliite"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Hae yhteystiedoista"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Kaikkien yhteystietojen haku"</string>
<string name="take_photo" msgid="7496128293167402354">"Ota valokuva"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Ota uusi kuva"</string>
<string name="pick_photo" msgid="448886509158039462">"Valitse valokuva galleriasta"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Yhteystietoluetteloa päivitetään vaihdetun kielen mukaiseksi."\n\n"Odota…"</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Yhteystietoluetteloa päivitetään."\n\n"Odota…"</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Yhteystietoja päivitetään. "\n\n"Päivitys vaatii noin <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> Mt puhelimen sisäistä tallennustilaa."\n\n"Valitse jokin seuraavista:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Valitse uusi kuva galleriasta"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Yhteystietoluettelo on päivitetty vastaamaan kielen muutosta."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Yhteystietoluetteloa päivitetään."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Yhteystietoja päivitetään parhaillaan."\n\n"Päivitysprosessi vaatii noin <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mt sisäistä tallennustilaa."\n\n"Valitse jokin seuraavista vaihtoehdoista:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Poista sovelluksia"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Yritä päivitystä uudelleen"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Tulokset haulla <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Haetaan…"</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Näytä valitut"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Näytä kaikki"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Valitse kaikki"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Poista kaikki valinnat"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Ei valittuja yhteystietoja."</string>
+ <string name="add_field" msgid="2384260056674995230">"Lisää toinen kenttä"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"lähteestä: <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> lähteestä: <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"lisää suosikkeihin"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Muokkaa yhteystietoa"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"ei yhdistetty"</item>
+ <item quantity="other" msgid="425683718017380845">"yhdistetty <xliff:g id="COUNT">%0$d</xliff:g> lähteestä"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Muu"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Yhdistä yhteystiedot"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Yhdistetäänkö nykyiset yhteystiedot valittujen yhteystietojen kanssa?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Muokkaa valittuja yhteystietoja"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Haluatko muokata valittuja yhteystietoja? Antamasi tiedot kopioidaan."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Kopioi yhteystietoihini"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Hakemisto <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Haetaan kaikista yhteystiedoista"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Hakemisto"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Yhteystiedot"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Luodaan kopio"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Valitse yhteystietoluettelo"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Kaikki yhteystiedot"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Tähdelliset"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Oma"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Muokkaa..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Yhteystiedot, joissa puhelinnumero"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Yhteystiedot"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Määritä oma näkymä"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Asetukset"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Asetukset"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Näyttövalinnat"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Etsi yhteystietoja"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Puhelinnumero"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Lisää yhteystietoihin"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Sulje"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Anna vuosi"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Yhteystiedot"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Ladataan…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Luo uusi yhteystieto"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Kirjaudu tiliin"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Tuo yhteystietoja tiedostosta"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Luo uusi ryhmä"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Luo uusi ryhmä]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Nimeä ryhmä uudelleen"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Poista ryhmä"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Haluatko varmasti poistaa ryhmän <xliff:g id="GROUP_LABEL">%1$s</xliff:g>? (Ryhmään kuuluvia yhteystietoja ei poisteta.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Anna yhteystiedon nimi ennen kuin liität sen toisiin yhteystietoihin."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Yhdistetyt yhteystiedot"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Teksti kopioitu"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Hylkää muutokset"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Haluatko hylätä tekemäsi muutokset?"</string>
+ <string name="discard" msgid="1234315037371251414">"Hylkää"</string>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index b232718..a243841 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Choisissez un contact pour le raccourci"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Choisissez le numéro à appeler"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Choisissez le numéro auquel envoyer le message"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Sélectionner un contact"</string>
<string name="starredList" msgid="4817256136413959463">"Marqués d\'une étoile"</string>
<string name="frequentList" msgid="7154768136473953056">"Contacts fréquents"</string>
<string name="strequentList" msgid="5640192862059373511">"Favoris"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Supprimer le contact"</string>
<string name="menu_call" msgid="3992595586042260618">"Appeler le contact"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Envoyer un SMS au contact"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Envoyer un e-mail"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Adresse sur un plan"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Numéro téléphone par défaut"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"E-mail par défaut"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Séparer"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Contacts séparés"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Renommer le groupe"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Supprimer le groupe"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Nouveau"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Séparer le contact"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Souhaitez-vous vraiment séparer ce contact en plusieurs instances : une pour chaque ensemble d\'informations associé à ce contact ?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Joindre"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"La suppression de ce contact entraînera celle d\'informations provenant de plusieurs comptes."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Ce contact sera supprimé."</string>
<string name="menu_done" msgid="796017761764190697">"OK"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Annuler"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Annuler"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Modifier le contact"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Nouveau contact"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Prononciation phonétique"</string>
<string name="label_notes" msgid="8337354953278341042">"Notes"</string>
<string name="label_sip_address" msgid="124073911714324974">"Appel VoIP"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Sonnerie"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Nom et prénom"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Nom phonétique"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Entreprise"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Titre"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Ce contact n\'existe pas."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Créer un nouveau contact"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Sélectionnez un libellé"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Téléphone"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-mail"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Chat"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Adresse postale"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adresse"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organisation"</item>
<item msgid="7196592230748086755">"Remarque"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Aucune image disponible sur le téléphone."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Icône de contact"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Aucune image n\'est disponible sur la tablette."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Aucune image disponible sur le téléphone."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Photo du contact"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Libellé personnalisé"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Options d\'affichage"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Options d\'affichage"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Renvoyer les appels directement vers la messagerie vocale"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Par défaut"</string>
- <string name="changePicture" msgid="2943329047610967714">"Changer d\'icône"</string>
- <string name="removePicture" msgid="3041230993155966350">"Supprimer l\'icône"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Supprimer la photo"</string>
<string name="noContacts" msgid="8579310973261953559">"Aucun contact."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Aucun contact correspondant n\'a été trouvé."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Aucun contact disposant d\'un numéro téléphone."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Contact enregistré."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Erreur : impossible d\'enregistrer les modifications du contact"</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Afficher 1 contact avec son numéro de téléphone"</item>
- <item quantity="other" msgid="6133262880804110289">"Affichage de <xliff:g id="COUNT">%d</xliff:g> contacts avec numéro de téléphone"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 contact avec numéro de téléphone"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> contacts avec des n° de téléphone"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Aucun contact visible avec numéro de téléphone"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Aucun contact disposant d\'un numéro de téléphone"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Affichage de 1 contact"</item>
- <item quantity="other" msgid="2865867557378939630">"Affichage de <xliff:g id="COUNT">%d</xliff:g> contacts"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 contact"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> contacts"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Aucun contact visible"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Aucun contact"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Aucun contact visible"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Aucun contact favori"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Aucun contact dans <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"1 contact trouvé"</item>
- <item quantity="other" msgid="7752927996850263152">"<xliff:g id="COUNT">%d</xliff:g> contacts trouvés"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 contact trouvé"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> contact(s) trouvé(s)"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Contact introuvable"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"plus de <xliff:g id="COUNT">%d</xliff:g> résultats trouvés"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Introuvable"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 contact"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> contacts"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 contact trouvé"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> contact(s) trouvé(s)"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Contacts"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoris"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Contacts de carte SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Vous n\'avez aucun contact à afficher. Si vous venez d\'ajouter un compte, la synchronisation des contacts peut prendre quelques minutes."</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Vous n\'avez aucun contact à afficher."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Vous n\'avez aucun contact à afficher."\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>"Comptes"</b></font>" pour ajouter ou configurer un compte dont vous pourrez synchroniser les contacts sur le téléphone ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer un contact ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Exporter."</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Vous n\'avez aucun contact à afficher (si vous venez d\'ajouter un compte, la synchronisation des contacts peut prendre quelques minutes)."\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>"Comptes"</b></font>" pour ajouter ou configurer un compte comportant des contacts que vous pourrez synchroniser sur le téléphone ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Options d\'affichage"</b></font>" pour modifier le paramètre de visibilité des contacts ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer une entrée ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Exporter."</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Vous n\'avez aucun contact à afficher."\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>"Comptes"</b></font>" pour ajouter ou configurer un compte dont vous pourrez synchroniser les contacts sur le téléphone ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer un contact ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Exporter."</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Vous n\'avez aucun contact à afficher (si vous venez d\'ajouter un compte, la synchronisation des contacts peut prendre quelques minutes)."\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>"Comptes"</b></font>" pour ajouter ou configurer un compte comportant des contacts que vous pourrez synchroniser sur le téléphone ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Options d\'affichage"</b></font>" pour modifier le paramètre de visibilité des contacts ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer une entrée ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Exporter."</b></font>\n</li></string>
+ <!-- syntax error in translation for noContactsHelpText (6450346791169710787) org.xmlpull.v1.XmlPullParserException: expected: /b read: font (position:END_TAG </font>@1:560 in java.io.StringReader@7bd63e39) -->
+ <!-- syntax error in translation for noContactsHelpText (7633826236417884130) org.xmlpull.v1.XmlPullParserException: expected: /b read: font (position:END_TAG </font>@1:567 in java.io.StringReader@2e8f4fb3) -->
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Vous n\'avez aucun contact à afficher (si vous venez d\'ajouter un compte, la synchronisation des contacts peut prendre quelques minutes)."\n\n"Pour ajouter des contacts, appuyez sur "<font fgcolor="#ffffffff"><b>"Menu"</b></font>", puis sur :"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" pour ajouter ou configurer un compte dont vous pourrez synchroniser les contacts vers la tablette ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Options d\'affichage"</b></font>" pour modifier les paramètres de visibilité des contacts ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer un contact ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Exporter"</b></font>" pour importer des contacts depuis votre carte SIM ou SD."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Vous n\'avez aucun contact à afficher (si vous venez d\'ajouter un compte, la synchronisation des contacts peut prendre quelques minutes)."\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>"Comptes"</b></font>" pour ajouter ou configurer un compte dont vous pourrez synchroniser les contacts sur le téléphone ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Options d\'affichage"</b></font>" pour modifier le paramètre de visibilité des contacts ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer un contact ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Exporter"</b></font>" pour importer des contacts depuis votre carte SIM ou SD."\n</li></string>
+ <!-- syntax error in translation for noContactsNoSimHelpText (6031363021287849874) org.xmlpull.v1.XmlPullParserException: expected: /b read: font (position:END_TAG </font>@1:565 in java.io.StringReader@7cd0a5d9) -->
+ <!-- syntax error in translation for noContactsNoSimHelpText (467658807711582876) org.xmlpull.v1.XmlPullParserException: expected: /b read: font (position:END_TAG </font>@1:571 in java.io.StringReader@66388993) -->
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Vous n\'avez aucun contact à afficher (si vous venez d\'ajouter un compte, la synchronisation des contacts peut prendre quelques minutes)."\n\n"Pour ajouter des contacts, appuyez sur "<font fgcolor="#ffffffff"><b>"Menu"</b></font>", puis sur :"\n" "\n<li><font fgcolor="#ffffffff"><b>"Comptes"</b></font>" pour ajouter ou configurer un compte dont vous pourrez synchroniser les contacts vers la tablette ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Options d\'affichage"</b></font>" pour modifier les paramètres de visibilité des contacts ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer un contact ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Exporter"</b></font>" pour importer des contacts depuis votre carte SD."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Vous n\'avez aucun contact à afficher (si vous venez d\'ajouter un compte, la synchronisation des contacts peut prendre quelques minutes)."\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>"Comptes"</b></font>" pour ajouter ou configurer un compte dont vous pourrez synchroniser les contacts sur le téléphone ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Options d\'affichage"</b></font>" pour modifier le paramètre de visibilité des contacts ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nouveau contact"</b></font>" pour créer un contact ;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Exporter"</b></font>" pour importer des contacts depuis votre carte SD."\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Vous ne disposez d\'aucun favoris."\n\n"Pour ajouter un contact à la liste de favoris :"\n\n" "<li>"Appuyez sur l\'onglet "<b>"Contacts"</b>"."\n</li>" "\n<li>"Appuyez sur le contact à ajouter à vos favoris."\n</li>" "\n<li>"Appuyez sur l\'étoile en regard du nom du contact."\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Tous les contacts"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Suivis"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Rappeler"</string>
<string name="callAgain" msgid="3197312117049874778">"Renouveler l\'appel"</string>
<string name="returnCall" msgid="8171961914203617813">"Rappeler"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> mn <xliff:g id="SECONDS">%2$s</xliff:g> s"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> mn <xliff:g id="SECONDS">%s</xliff:g> s"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Contactés fréquemment"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Ajouter un contact"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Ajouter \"<xliff:g id="EMAIL">%s</xliff:g>\" aux contacts ?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Tout"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"un"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"deux"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"trois"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Impossible d\'analyser la mémoire de stockage USB (Raison : \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Échec de l\'analyse de la carte SD (Raison : \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Erreur d\'E/S"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Mémoire insuffisante (fichier probablement trop volumineux)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Échec de l\'analyse des données VCard pour une raison inattendue"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Échec de l\'analyse des données VCard. Le format semble valide, mais l\'implémentation actuelle ne le prend pas en charge."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Aucun fichier VCard n\'a été trouvé sur la mémoire de stockage USB."</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Aucun fichier VCard n\'a été trouvé sur la carte SD."</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Le format n\'est pas compatible."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Échec de l\'importation du fichier vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Aucun fichier vCard n\'a été trouvé sur la mémoire de stockage USB."</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Aucun fichier vCard trouvé sur la carte SD"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Échec de la collecte des métadonnées contenues dans le(s) fichier(s) vCard"</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Échec de l\'importation d\'un ou de plusieurs fichiers (%s)"</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Erreur inconnue"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Sélectionner un fichier VCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Lecture des données VCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Lecture des fichiers VCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Échec de la lecture des données VCard"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> sur <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contacts"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> sur <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> fichiers"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Mise en cache de fichier(s) vCard dans l\'espace de stockage temporaire local"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Le programme d\'importation met en cache le(s) fichier(s) vCard dans l\'espace de stockage temporaire local. L\'importation effective va bientôt commencer."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importation en cours <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g> : <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importation en cours : <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Échec de la lecture des données vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Lecture des données vCard annulée"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Fichier vCard <xliff:g id="FILENAME">%s</xliff:g> importé"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Importation du fichier <xliff:g id="FILENAME">%s</xliff:g> annulée"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"Importation du fichier <xliff:g id="FILENAME">%s</xliff:g> imminente"</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Le fichier va bientôt être importé."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Demande d\'importation de fichier vCard rejetée. Veuillez réessayer ultérieurement."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"Exportation du fichier <xliff:g id="FILENAME">%s</xliff:g> imminente"</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Demande d\'exportation de fichier vCard rejetée. Veuillez réessayer ultérieurement."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"contact"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Confirmer l\'exportation"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Voulez-vous vraiment exporter la liste de contacts vers \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\" ?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Échec lors de l\'exportation des données du contact"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"La mémoire de stockage USB contient trop de fichiers VCard."</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"La carte SD contient trop de fichiers VCard."</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Le nom de fichier requis est trop long (\"<xliff:g id="FILENAME">%s</xliff:g>\")."</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Fichier vCard <xliff:g id="FILENAME">%s</xliff:g> exporté"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Exportation du fichier <xliff:g id="FILENAME">%s</xliff:g> annulée"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Exportation des données des contacts"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Exportation des données des contacts vers \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Échec de l\'initialisation du module d\'exportation : \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Une erreur s\'est produite lors de l\'exportation : \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Impossible d\'obtenir les informations concernant la base de données"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Aucun contact n\'est exportable. Si votre téléphone comporte actuellement des contacts, il se peut qu\'un fournisseur de données empêche leur exportation hors de votre téléphone."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Aucun contact ne peut être exporté. Si des contacts sont enregistrés sur votre tablette, il est possible qu\'un fournisseur de données interdise leur exportation vers un autre appareil."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Aucun contact ne peut être exporté. Si des contacts sont enregistrés sur votre téléphone, il est possible qu\'un fournisseur de données interdise leur exportation vers un autre appareil."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Le système de composition vCard n\'est pas correctement initialisé."</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Impossible d\'ouvrir \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\" : <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> sur <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contacts"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Impossible d\'ouvrir \"<xliff:g id="FILE_NAME">%s</xliff:g>\" : <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> sur <xliff:g id="TOTAL_NUMBER">%s</xliff:g> contacts"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Annulation importation fichier vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Voulez-vous vraiment annuler l\'importation du fichier <xliff:g id="FILENAME">%s</xliff:g> ?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Annulation exportation fichier vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Voulez-vous vraiment annuler l\'exportation du fichier <xliff:g id="FILENAME">%s</xliff:g> ?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Échec annulation importation/exportation"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Noms de vos contacts"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Ajouter une pause de 2 s"</string>
<string name="add_wait" msgid="3360818652790319634">"Ajouter Attendre"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Aucune application pour gérer cette action"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Mémoriser ce choix"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Inconnu"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Aucune donnée"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Effacer les valeurs par défaut"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Valeurs par défaut de ce contact :"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Effacer"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Comptes"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importer/Exporter"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importer/Exporter les contacts"</string>
- <string name="menu_share" msgid="943789700636542260">"Partager"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Partager le contact"</string>
<string name="share_via" msgid="563121028023030093">"Partager contact via"</string>
<string name="share_error" msgid="4374508848981697170">"Ce contact ne peut pas être partagé."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Nom"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organisation"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Site Web"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Événement"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relation"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Groupes"</string>
<string name="type_short_home" msgid="7770424864090605384">"D"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"B"</string>
<string name="type_short_pager" msgid="2613818970827594238">"T"</string>
<string name="type_short_other" msgid="5669407180177236769">"A"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Ce contact est en lecture seule."</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Plus"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Nom principal"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Créer un contact sous le compte"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Supprimer le groupe de synchronisation"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Ajouter groupe de synchronisation"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Tous les autres contacts"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Tous les contacts"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Le retrait du groupe \"<xliff:g id="GROUP">%s</xliff:g>\" de la synchronisation entraîne également la suppression de tous les contacts non groupés de la synchronisation."</string>
- <string name="account_phone" msgid="3682950835276226870">"Téléphone uniquement, sans synchronisation"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Tablette uniquement ; non synchronisé"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Téléphone uniquement, sans synchronisation"</string>
<string name="call_custom" msgid="7756571794763171802">"Appeler <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Appeler domicile"</string>
<string name="call_mobile" msgid="7502236805487609178">"Appeler mobile"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chatter via ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chatter via Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Chatter"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adresse"</string>
<string name="postal_street" msgid="8133143961580058972">"Rue"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Boîte postale"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Voisinage"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"État"</string>
<string name="postal_postcode" msgid="572136414136673751">"Code postal"</string>
<string name="postal_country" msgid="7638264508416368690">"Pays"</string>
+ <string name="full_name" msgid="6602579550613988977">"Nom"</string>
<string name="name_given" msgid="1687286314106019813">"Prénom"</string>
<string name="name_family" msgid="3416695586119999058">"Nom de famille"</string>
<string name="name_prefix" msgid="59756378548779822">"Préfixe du nom"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Rechercher..."</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Rechercher tous les contacts"</string>
<string name="take_photo" msgid="7496128293167402354">"Prendre une photo"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Prendre une autre photo"</string>
<string name="pick_photo" msgid="448886509158039462">"Sélectionner une photo dans la galerie"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Mise à jour de la liste des contacts en cours suite au changement de langue."\n\n"Veuillez patienter..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Mise à jour de la liste des contacts en cours."\n\n"Veuillez patienter..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Les contacts sont en cours de mise à niveau. "\n\n"La mise à niveau requiert environ <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> Mo de mémoire interne du téléphone."\n\n"Choisissez l\'une des options suivantes :"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Sélectionner une autre photo dans la galerie"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Mise à jour de la liste des contacts en cours suite au changement de langue."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"La liste de contacts est en cours de mise à jour."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"La fonctionnalité Contacts est en cours de mise à niveau. "\n\n"Cette mise à niveau requiert environ <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mo de mémoire de stockage interne."\n\n"Choisissez l\'une des options suivantes :"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Désinstaller certaines applications"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Réessayer la mise à niveau"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Résultats de recherche pour : <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Recherche en cours..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Afficher la sélection"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Tout afficher"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Tout sélectionner"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Tout désélectionner"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"1 destinataire sélectionné"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g> destinataires sélectionnés"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Aucun contact sélectionné"</string>
+ <string name="add_field" msgid="2384260056674995230">"Ajouter un champ"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favori"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Modifier le contact"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"1 contact non fusionné"</item>
+ <item quantity="other" msgid="425683718017380845">"1 contact fusionné à partir de <xliff:g id="COUNT">%0$d</xliff:g> sources"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Autre"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Joindre les contacts"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Associer ce contact au contact sélectionné ?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Modifier les contacts sélectionnés"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Modifier le contact sélectionné ? Les informations saisies jusqu\'ici seront copiées."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Copier dans Mes contacts"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Annuaire <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Recherche dans tous les contacts"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Annuaire"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Contacts"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Création d\'une copie personnelle"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Sélectionnez une liste de contacts"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Tous les contacts"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Contacts favoris"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Personnalisé"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Personnaliser..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Contacts avec numéro de téléphone"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Contact"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Définir un affichage personnalisé"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Paramètres"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Paramètres"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Options d\'affichage"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Recherchez des contacts."</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Numéro de téléphone"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Ajouter aux contacts"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Fermer"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Indiquer une année"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Contact"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Chargement…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Créer un contact"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Se connecter à un compte"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importer les contacts d\'un fichier"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Créer un groupe"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Créer un groupe]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Renommer le groupe"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Supprimer le groupe"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Voulez-vous vraiment supprimer le groupe \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\" ? Vos contacts ne seront pas supprimés."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Veuillez saisir le nom de ce contact avant de l\'associer à un autre."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Contact associé"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Texte copié"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Supprimer les modifications"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Voulez-vous vraiment supprimer vos modifications ?"</string>
+ <string name="discard" msgid="1234315037371251414">"Supprimer"</string>
</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 8301920..9a09ba6 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Odaberite prečac kontakta"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Odaberite broj za pozivanje"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Odabir broja za poruku"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Odaberite kontakt"</string>
<string name="starredList" msgid="4817256136413959463">"Sa zvjezdicom"</string>
<string name="frequentList" msgid="7154768136473953056">"Često"</string>
<string name="strequentList" msgid="5640192862059373511">"Favoriti"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Izbriši kontakt"</string>
<string name="menu_call" msgid="3992595586042260618">"Nazovi kontakt"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Poruka kontaktu"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Pošalji poruku e-pošte"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Adresa na karti"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Učini zadanim brojem"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Učini zadanom e-poštom"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Zasebno"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Kontakti su razdvojeni"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Preimenovanje grupe"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Izbriši grupu"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Novo"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Odvoji kontakt"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Jeste li sigurni da želite podijeliti ovaj kontakt u više kontakata: jedan za svaki skup podataka za kontakt koji su mu pridruženi?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Pridruži se"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Brisanjem ovog kontakta izbrisat ćete podatke s više računa."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Ovaj će kontakt biti izbrisan."</string>
<string name="menu_done" msgid="796017761764190697">"Gotovo"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Poništi"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Odustani"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Uredi kontakt"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Novi kontakt"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetski"</string>
<string name="label_notes" msgid="8337354953278341042">"Bilješke"</string>
<string name="label_sip_address" msgid="124073911714324974">"Internetski poziv"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Zvuk zvona"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Ime i prezime"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Ime fonetski"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Tvrtka"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Naslov"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Kontakt ne postoji."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Izrada novog kontakta"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Odaberite oznaku"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-pošta"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"IM"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Poštanska adresa"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adresa"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organizacija"</item>
<item msgid="7196592230748086755">"Napomena"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Nema dostupnih slika na telefonu."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Ikona kontakta"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Na tabletnom uređaju nema dostupnih slika."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Nema dostupnih slika na telefonu."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Kontakt fotografija"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Prilagođeni naziv oznake"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Opcije prikaza"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Opcije prikaza"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Pošalji pozive izravno u govornu poštu"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Zadano"</string>
- <string name="changePicture" msgid="2943329047610967714">"Promjena ikone"</string>
- <string name="removePicture" msgid="3041230993155966350">"Ukloni ikonu"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Ukloni fotografiju"</string>
<string name="noContacts" msgid="8579310973261953559">"Nema kontakata."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Nisu pronađeni podudarni kontakti."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nema kontakata s telefonskim brojevima."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Kontakt je spremljen."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Pogreška, nije moguće spremiti promjene kontakta."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Prikazivanje 1 kontakta s telefonskim brojem"</item>
- <item quantity="other" msgid="6133262880804110289">"Prikazivanje <xliff:g id="COUNT">%d</xliff:g> kontakata s telefonskim brojevima"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 kontakt s tel. brojem"</item>
+ <item quantity="other" msgid="3299954047880968205">"Kontakata s tel. brojevima: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Nema vidljivih kontakata s telefonskim brojevima."</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Nema kontakata s telefonskim brojevima"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Prikazivanje 1 kontakta"</item>
- <item quantity="other" msgid="2865867557378939630">"Prikazivanje <xliff:g id="COUNT">%d</xliff:g> kontakata"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 kontakt"</item>
+ <item quantity="other" msgid="3578469907265375314">"Broj kontakata: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Nema vidljivih kontakata"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Nema kontakata"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Nema vidljivih kontakata"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Nema kontakata sa zvjezdicom"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Nema kontakata pod: <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Pronađen je 1 kontakt"</item>
- <item quantity="other" msgid="7752927996850263152">"Pronađeno <xliff:g id="COUNT">%d</xliff:g> kontakata"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 pronađen"</item>
+ <item quantity="other" msgid="3852668542926965042">"Pronađeno kontakata: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Kontakt nije pronađen"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"pronađeno je više od <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Nije pronađeno"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 kontakt"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> kontakata"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 pronađen"</item>
+ <item quantity="other" msgid="7988132539476575389">"Pronađeno kontakata: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Kontakti"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoriti"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Kontakti SIM kartice"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Nemate kontakata za prikazivanje. (Ako ste upravo dodali račun, sinkroniziranje kontakata može potrajati nekoliko minuta.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Nemate kontakata za prikaz"</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Nemate kontakata za prikazivanje."\n\n"Da biste dodali kontakte, pritisnite"<font fgcolor="#ffffffff"><b>"Izbornik"</b></font>" i dodirnite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>" kako biste dodali ili konfigurirali račun kontaktima koje možete sinkronizirati s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novi kontakt"</b></font>" da biste izradili novi kontakt od početka"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/Izvoz"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Nemate kontakata za prikazivanje. (Ako ste upravo dodali račun, sinkroniziranje kontakata može potrajati nekoliko minuta."\n\n"Da biste dodali kontakte, pritisnite "<font fgcolor="#ffffffff"><b>"Izbornik"</b></font>" i dodirnite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>" kako biste dodali ili konfigurirali račun kontaktima koje možete sinkronizirati s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mogućnosti prikazivanja"</b></font>" da biste promijenili koji su kontakti vidljivi"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novi kontakt"</b></font>" za izradu novog kontakta od početka"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/Izvoz"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Nemate kontakata za prikazivanje."\n\n"Da biste dodali kontakte pritisnite"<font fgcolor="#ffffffff"><b>"Izbornik"</b></font>" i dodirnite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>" kako biste dodali ili konfigurirali račun kontaktima koje možete sinkronizirati s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novi kontakt"</b></font>" da biste izradili novi kontakt od početka"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/Izvoz"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Nemate kontakata za prikazivanje. (Ako ste upravo dodali račun, sinkroniziranje kontakata može potrajati nekoliko minuta."\n\n"Da biste dodali kontakte, pritisnite "<font fgcolor="#ffffffff"><b>"Izbornik"</b></font>" i dodirnite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>" kako biste dodali ili konfigurirali račun kontaktima koje možete sinkronizirati s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mogućnosti prikazivanja"</b></font>" da biste promijenili koji su kontakti vidljivi"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novi kontakt"</b></font>" za izradu novog kontakta od početka"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/Izvoz"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Nemate kontakata za prikaz."\n\n"Za dodavanje kontakata pritisnite "<font fgcolor="#ffffffff"><b>"Izbornik"</b></font>" i dodirnite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>" kako biste dodali ili konfigurirali račun s kontaktima koje možete sinkronizirati s tabletnim uređajem"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novi kontakt"</b></font>" kako biste stvorili posve novi kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/Izvoz"</b></font>" kako biste uvezli kontakte sa svoje SIM ili SD kartice"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Nemate kontakata za prikaz."\n\n"Za dodavanje kontakata pritisnite "<font fgcolor="#ffffffff"><b>"Izbornik"</b></font>" i dodirnite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>" kako biste dodali ili konfigurirali račun s kontaktima koje možete sinkronizirati na mobitel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novi kontakt"</b></font>" kako biste stvorili posve novi kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/Izvoz"</b></font>" kako biste uvezli kontakte sa svoje SIM ili SD kartice"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Nemate kontakata za prikaz. (Ako ste upravo dodali račun, sinkronizacija kontakata može potrajati nekoliko minuta.)"\n\n"Za dodavanje kontakata pritisnite "<font fgcolor="#ffffffff"><b>"Izbornik"</b></font>" i dodirnite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>" kako biste dodali ili konfigurirali račun s kontaktima koje možete sinkronizirati s tabletnim uređajem"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcije prikaza"</b></font>" kako biste promijenili koji su kontakti vidljivi"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novi kontakti"</b></font>" kako biste stvorili posve novi kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/Izvoz"</b></font>" kako biste uvezli kontakte sa svoje SIM ili SD kartice"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Nemate kontakata za prikaz. (Ako ste upravo dodali račun, sinkronizacija kontakata može potrajati nekoliko minuta.)"\n\n"Za dodavanje kontakata pritisnite "<font fgcolor="#ffffffff"><b>"Izbornik"</b></font>" i dodirnite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>" kako biste dodali ili konfigurirali račun s kontaktima koje možete sinkronizirati na mobitel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcije prikaza"</b></font>" kako biste promijenili koji su kontakti vidljivi"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novi kontakt"</b></font>" kako biste stvorili posve novi kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/Izvoz"</b></font>" kako biste uvezli kontakte sa svoje SIM ili SD kartice"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Nemate kontakata za prikaz."\n\n"Za dodavanje kontakata pritisnite "<font fgcolor="#ffffffff"><b>"Izbornik"</b></font>" i dodirnite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>" kako biste dodali ili konfigurirali račun s kontaktima koje možete sinkronizirati s tabletnim uređajem"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novi kontakt"</b></font>" kako biste stvorili posve novi kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/Izvoz"</b></font>" kako biste uvezli kontakte sa svoje SD kartice"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Nemate kontakata za prikaz."\n\n"Za dodavanje kontakata pritisnite "<font fgcolor="#ffffffff"><b>"Izbornik"</b></font>" i dodirnite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>" kako biste dodali ili konfigurirali račun s kontaktima koje možete sinkronizirati s mobitelom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novi kontakt"</b></font>" kako biste stvorili posve novi kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/Izvoz"</b></font>" kako biste uvezli kontakte sa svoje SD kartice"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Nemate kontakata za prikaz. (Ako ste upravo dodali račun, sinkronizacija kontakata može potrajati nekoliko minuta.)"\n\n"Za dodavanje kontakata pritisnite "<font fgcolor="#ffffffff"><b>"Izbornik"</b></font>" i dodirnite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>" kako biste dodali ili konfigurirali račun s kontaktima koje možete sinkronizirati s tabletnim uređajem"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcije prikaza"</b></font>" kako biste promijenili koji su kontakti vidljivi"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novi kontakt"</b></font>" kako biste stvorili posve novi kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/Izvoz"</b></font>" kako biste uvezli kontakte sa svoje SD kartice"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Nemate kontakata za prikaz. (Ako ste upravo dodali račun, sinkronizacija kontakata može potrajati nekoliko minuta.)"\n\n"Za dodavanje kontakata pritisnite "<font fgcolor="#ffffffff"><b>"Izbornik"</b></font>" i dodirnite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>" kako biste dodali ili konfigurirali račun s kontaktima koje možete sinkronizirati s mobitelom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcije prikaza"</b></font>" kako biste promijenili koji su kontakti vidljivi"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novi kontakt"</b></font>" kako biste stvorili posve novi kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/Izvoz"</b></font>" kako biste uvezli kontakte sa svoje SIM ili SD kartice"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Nemate favorita."\n\n"Da biste dodali kontakt svojem popisu favorita:"\n\n" "<li>"Dodirnite karticu "<b>"Kontakti"</b>" "\n</li>" "\n<li>"Dodirnite kontakt koji želite dodati u favorite"\n</li>" "\n<li>"Dodirnite zvjezdicu pokraj naziva kontakta"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Svi kontakti"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Sa zvjezdicom"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Povratni poziv"</string>
<string name="callAgain" msgid="3197312117049874778">"Zovi ponovo"</string>
<string name="returnCall" msgid="8171961914203617813">"Povratni poziv"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> minuta <xliff:g id="SECONDS">%2$s</xliff:g> sekundi"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> minuta <xliff:g id="SECONDS">%s</xliff:g> sekundi"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Često kontaktirani"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Dodaj kontakt"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Dodati \"<xliff:g id="EMAIL">%s</xliff:g>\" kontaktima?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Sve"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"jedan"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dva"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tri"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Skeniranje memorije USB nije uspjelo (razlog: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Skeniranje SD kartice nije uspjelo (Razlog: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"I/O pogreška"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Memorija je premala (datoteka je možda prevelika)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Analiza kartice vCard nije uspjela zbog neočekivanog razloga"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Analiza kartice vCard nije uspjela iako se čini da je valjanog formata, budući da je trenutna implementacija ne podržava"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Datoteka vCard nije pronađena na USB memoriji"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Na SD kartici nije pronađena vCard datoteka"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Format nije podržan."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Neuspješan uvoz vCarda"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"U USB pohrani nije pronađena vCard datoteka"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Na SD kartici nije pronađena datoteka vCard"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Neuspješno prikupljanje meta informacija danih datoteka vCard."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Uvoz jedne ili više datoteka nije uspio (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Nepoznata pogreška"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Odaberite vCard datoteku"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Čitanje kartice vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Čitanje datoteka s kartice vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Čitanje podataka s kartice vCard nije uspjelo"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> od <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontakata"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> od <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> datoteka"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Spremanje predmemorije kartice vCard u lokalnu privremenu pohranu"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Alat za uvoz sprema predmemoriju vCard kartice u lokalnu privremenu pohranu. Uvoz će početi uskoro."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Uvoz <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Uvoz <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Neuspješno čitanje vCard podataka"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Čitanje vCard podataka otkazano je"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Završetak uvoza kartice vCard <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Otkazan je uvoz datoteke <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"Datoteka <xliff:g id="FILENAME">%s</xliff:g> uskoro će biti uvezena."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Datoteka će uskoro biti uvezena."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Odbijen je zahtjev za uvoz kartice vCard. Pokušajte kasnije."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"Datoteka <xliff:g id="FILENAME">%s</xliff:g> uskoro će biti izvezena."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Odbijen je zahtjev za izvoz kartice vCard. Pokušajte kasnije."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"kontakt"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Potvrdite izvoz"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Jeste li sigurni da želite izvesti svoj popis kontakata u \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Izvoz podataka o kontaktu nije uspio."</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Previše datoteka vCard u USB memoriji"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Previše vCard datoteka na SD kartici"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Obavezan naziv datoteke je predug (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Završetak izvoza <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Otkazan je izvoz datoteke <xliff:g id="FILENAME">%s</xliff:g>"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Izvoz podataka o kontaktu"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Izvoz podataka o kontaktu u \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Alat za izvoz ne može se inicijalizirati: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Došlo je do pogreške tijekom izvoza: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Dohvat informacija iz baze podataka nije uspio"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Nema kontakata koje je moguće izvesti. Ako imate kontakte na telefonu, neki davatelji podatkovnih usluga mogu zabraniti izvoz svih kontakata na vanjski telefon."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Nema kontakata koji se mogu izvoziti. Ako doista imate kontakte na tabletnom uređaju, moguće je da su neki davatelji podataka zabranili izvoz svih kontakata izvan tabletnog uređaja."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Nema kontakata koji se mogu izvoziti. Ako doista imate kontakte na mobitelu, moguće je da su neki davatelji podataka zabranili izvoz svih kontakata izvan mobitela."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Sastavljač za vCard nije ispravno inicijaliziran"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Nije moguće otvoriti \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> od <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontakata"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Nije moguće otvoriti \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> od <xliff:g id="TOTAL_NUMBER">%s</xliff:g> kontakata"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Otkazivanje uvoza kartice vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Jeste li sigurni da želite otkazati uvoz <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Otkazivanje izvoza kartice vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Sigurno želite otkazati izvoz datoteke <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Neuspjelo otkazivanje uvoza/izvoza vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Nazivi vaših kontakata"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Dodaj pauzu od 2 sek."</string>
<string name="add_wait" msgid="3360818652790319634">"Dodaj čekanje"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Nije pronađena aplikacija za upravljanje ovom radnjom"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Zapamti ovaj izbor"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Nepoznato"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Nema podataka"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Izbriši zadane postavke"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Zadane postavke za ovaj kontakt:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Očisti"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Računi"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Uvoz/izvoz"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Uvoz//Izvoz kontakata"</string>
- <string name="menu_share" msgid="943789700636542260">"Podijeli"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Dijeli kontakt"</string>
<string name="share_via" msgid="563121028023030093">"Dijeli kontakt putem"</string>
<string name="share_error" msgid="4374508848981697170">"Ovaj se kontakt ne može dijeliti."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Ime"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organizacija"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Web-lokacija"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Događaj"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Odnos"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grupe"</string>
<string name="type_short_home" msgid="7770424864090605384">"K"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"P"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"D"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Ovaj je kontakt samo za čitanje"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Više"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Primarni naziv"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Izrada kontakta pod računom"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Ukloni grupu sinkronizacije"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Dodaj grupu sinkroniziranja"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Svi ostali kontakti"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Svi kontakti"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Uklanjanje kontakta \"<xliff:g id="GROUP">%s</xliff:g>\" iz sinkronizacije uklonit će i sve razgrupirane kontakte iz sinkronizacije."</string>
- <string name="account_phone" msgid="3682950835276226870">"Samo mobitel, bez sinkronizacije"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Samo tabletni uređaj, bez sinkronizacije"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Samo mobitel, bez sinkronizacije"</string>
<string name="call_custom" msgid="7756571794763171802">"Nazovi <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Nazovi kućni"</string>
<string name="call_mobile" msgid="7502236805487609178">"Nazovi mobitel"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chatajte koristeći ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chatajte koristeći Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Chat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adresa"</string>
<string name="postal_street" msgid="8133143961580058972">"Ulica"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Poštanski sandučić"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Susjedstvo"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Država"</string>
<string name="postal_postcode" msgid="572136414136673751">"Poštanski broj"</string>
<string name="postal_country" msgid="7638264508416368690">"Zemlja"</string>
+ <string name="full_name" msgid="6602579550613988977">"Ime"</string>
<string name="name_given" msgid="1687286314106019813">"Ime"</string>
<string name="name_family" msgid="3416695586119999058">"Prezime"</string>
<string name="name_prefix" msgid="59756378548779822">"Prefiks imena"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Pretraži kontakte"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Pretraži sve kontakte"</string>
<string name="take_photo" msgid="7496128293167402354">"Snimi fotografiju"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Snimi novu fotografiju"</string>
<string name="pick_photo" msgid="448886509158039462">"Odaberite fotografiju iz Galerije"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Popis kontakata ažurira se tako da odražava promjene jezika."\n\n"Pričekajte..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Poziv kontakata se ažurira."\n\n"Pričekajte..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Kontakti se nadograđuju. "\n\n"Postupak nadogradnje zahtijeva približno <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>Mb prostora za pohranu na telefonu."\n\n"Odaberite jednu od sljedećih opcija:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Odaberite novu fotografiju iz galerije"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Popis kontakata ažurira se da bi se pokazala promjena jezika."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Popis kontakata ažurira se."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Kontakti se upravo nadograđuju. "\n\n"Nadogradnja približno zauzima <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mb interne memorije. "\n\n"Odaberite jednu od sljedećih mogućnosti:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Deinstaliraj neke aplikacije"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Pokušaj ponovo nadogradnju"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Rezultati pretraživanja za: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Pretraživanje..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Prikaži odabrano"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Pokaži sve"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Odaberi sve"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Poništi odabir svega"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Nije odabran nijedan kontakt."</string>
+ <string name="add_field" msgid="2384260056674995230">"Dodaj drugo polje"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"preko izvora <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> preko izvora <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favorit"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Uredi kontakt"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"nije spojen"</item>
+ <item quantity="other" msgid="425683718017380845">"spojen s <xliff:g id="COUNT">%0$d</xliff:g> izvora"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Drugo"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Pridruživanje kontakata"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Pridružiti trenutačni kontakt odabranom kontaktu?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Uredi odabrane kontakte"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Prebaciti se na uređivanje odabranog kontakta? Informacije koje ste unijeli dosad kopirat će se."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Kopiraj u moje kontakte"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Direktorij <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Pretraživanje svih kontakata"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Direktorij"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Kontakti"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Stvaranje osobne kopije"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Odaberite popis kontakata"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Svi kontakti"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Sa zvjezdicom"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Prilagođeno"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Prilagodi..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Kontakti s telefonskim brojevima"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kontakt"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Definiranje prilagođenog prikaza"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Postavke"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Postavke"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Opcije prikaza"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Pronađi kontakte"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Broj telefona"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Dodaj kontaktima"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Zatvori"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Navesti godinu"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Kontakt"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Učitavanje…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Izradi novi kontakt"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Prijavi se na račun"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Uvezi kontakte iz datoteke"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Izrada nove grupe"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Izradi novu grupu]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Preimenovanje grupe"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Izbriši grupu"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Jeste li sigurni da želite izbrisati grupu \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\"? (Sami kontakti neće biti izbrisani.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Unesite ime kontakta prije pridruživanja drugom kontaktu."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Pridruženi kontakt"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Tekst kopiran"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Odbaci promjene"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Zaista želite odbaciti promjene?"</string>
+ <string name="discard" msgid="1234315037371251414">"Odbaci"</string>
</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 7775efe..a47dff7 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Válasszon névjegyet a parancsikonhoz"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Válasszon telefonszámot a híváshoz"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Válasszon telefonszámot az üzenetküldéshez"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Válasszon ki egy névjegyet"</string>
<string name="starredList" msgid="4817256136413959463">"Csillaggal megjelölt"</string>
<string name="frequentList" msgid="7154768136473953056">"Gyakori"</string>
<string name="strequentList" msgid="5640192862059373511">"Kedvencek"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Névjegy törlése"</string>
<string name="menu_call" msgid="3992595586042260618">"Ismerős hívása"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"SMS küldése ismerősnek"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"E-mail küldése"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Postai cím"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Legyen ez az alapértelmezett szám"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Legyen ez az alapértelmezett e-mail cím"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Szétválasztás"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"A névjegyek szétválasztása megtörtént"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Csoport átnevezése"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Csoport törlése"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Új"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Névjegy szétválasztása"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Biztosan több névjegybe választja szét ezt az egy névjegyet, hogy minden információtípushoz tartozzon egy?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Összekapcsolás"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"A névjegy törlésével több fiókból is töröl adatokat."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"A névjegy törlésre kerül."</string>
<string name="menu_done" msgid="796017761764190697">"Kész"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Visszavonás"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Mégse"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Névjegy szerkesztése"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Új névjegy"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetikusan"</string>
<string name="label_notes" msgid="8337354953278341042">"Jegyzetek"</string>
<string name="label_sip_address" msgid="124073911714324974">"Internetes hívás"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Csengőhang"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Utó- és vezetéknév"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Név fonetikusan"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Cég"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Beosztás"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"A névjegy nem létezik."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Új névjegy létrehozása"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Címke kiválasztása"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-mail küldése"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Azonnali üzenetküldés"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Postacím"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Cím"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Szervezet"</item>
<item msgid="7196592230748086755">"Jegyzet"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Nincsenek elérhető képek a telefonon."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Névjegyikon"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Nem állnak rendelkezésre képek a táblagépen."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Nincsenek elérhető képek a telefonon."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Ismerős fotója"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Egyéni címkenév"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Megjelenítési beállítások"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Megjelenítési beállítások"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Hívások közvetlen átirányítása a hangpostára"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Alapértelmezett"</string>
- <string name="changePicture" msgid="2943329047610967714">"Ikon megváltoztatása"</string>
- <string name="removePicture" msgid="3041230993155966350">"Ikon eltávolítása"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Fénykép törlése"</string>
<string name="noContacts" msgid="8579310973261953559">"Nincsenek névjegyek."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Nincsenek a keresési feltételeknek megfelelő névjegyek."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nincsenek telefonszámot is tartalmazó névjegyek."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Névjegy mentve."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Hiba; nem sikerült menteni a névjegy módosításait."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"1 telefonszámmal rendelkező névjegy megjelenítése"</item>
- <item quantity="other" msgid="6133262880804110289">"<xliff:g id="COUNT">%d</xliff:g>, telefonszámmal rendelkező névjegy megjelenítése."</item>
+ <item quantity="one" msgid="3015357862286673986">"1 névjegy telefonszámmal"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> névjegy telefonszámmal"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Nincsenek telefonszámmal rendelkező látható névjegyek."</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Nincsenek telefonszámot is tartalmazó névjegyek"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"1 névjegy megjelenítése"</item>
- <item quantity="other" msgid="2865867557378939630">"<xliff:g id="COUNT">%d</xliff:g> névjegy megjelenítve"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 névjegy"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> névjegy"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Nincsenek látható névjegyek"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Nincsenek névjegyek"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Nincsenek látható névjegyek"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Nincs csillaggal megjelölt névjegy"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Nincsenek névjegyek itt: <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"1 névjegytalálat"</item>
- <item quantity="other" msgid="7752927996850263152">"<xliff:g id="COUNT">%d</xliff:g> névjegy"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 találat"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> találat"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Nem található névjegy"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"több mint <xliff:g id="COUNT">%d</xliff:g> találat"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Nem található"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 névjegy"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> névjegy"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 találat"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> találat"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Címtár"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Kedvencek"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Névjegyek a SIM-kártyán"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Nincsenek megjeleníthető névjegyei. (Ha a fiókot csak most adta hozzá, a névjegyek szinkronizálása néhány percig is eltarthat.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Nincsenek megjeleníthető névjegyek."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Nincsenek megjeleníthető névjegyei."\n\n"Névjegy hozzáadásához nyomja meg a "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" gombot, majd érintse meg a(z):"\n" "\n<li><font fgcolor="#ffffffff"><b>"Fiókok"</b></font>" lehetőséget a telefonjával szinkronizálható, névjegyekkel rendelkező fiók hozzáadásához vagy beállításához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Új névjegy"</b></font>" lehetőséget teljesen új névjegy létrehozásához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importálás/exportálás"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Nincsenek megjeleníthető névjegyei. (Ha a fiókot csak most adta hozzá, a névjegyek szinkronizálása néhány percig is eltarthat.)"\n\n"Névjegyek hozzáadásához nyomja meg a "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" gombot, és érintse meg a(z):"\n" "\n<li><font fgcolor="#ffffffff"><b>"Fiókok"</b></font>" lehetőséget a telefonjával szinkronizálható, névjegyekkel rendelkező fiók hozzáadásához vagy beállításához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Megjelenítési beállítások"</b></font>" lehetőséget a látható névjegyek módosításához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Új névjegy"</b></font>" lehetőséget teljesen új névjegy létrehozásához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importálás/exportálás"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Nincsenek megjeleníthető névjegyei."\n\n"Névjegy hozzáadásához nyomja meg a "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" gombot, majd érintse meg a(z):"\n" "\n<li><font fgcolor="#ffffffff"><b>"Fiókok"</b></font>" lehetőséget a telefonjával szinkronizálható, névjegyekkel rendelkező fiók hozzáadásához vagy beállításához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Új névjegy"</b></font>" lehetőséget teljesen új névjegy létrehozásához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importálás/exportálás"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Nincsenek megjeleníthető névjegyei. (Ha a fiókot csak most adta hozzá, a névjegyek szinkronizálása néhány percig is eltarthat.)"\n\n"Névjegyek hozzáadásához nyomja meg a "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" gombot, és érintse meg a(z):"\n" "\n<li><font fgcolor="#ffffffff"><b>"Fiókok"</b></font>" lehetőséget a telefonjával szinkronizálható, névjegyekkel rendelkező fiók hozzáadásához vagy beállításához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Megjelenítési beállítások"</b></font>" lehetőséget a látható névjegyek módosításához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Új névjegy"</b></font>" lehetőséget teljesen új névjegy létrehozásához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importálás/exportálás"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Nincsenek megjeleníthető névjegyei."\n\n"Névjegyek hozzáadásához nyomja meg a "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" gombot, majd érintse meg a"\n" "\n<li><font fgcolor="#ffffffff"><b>"Fiókok"</b></font>" lehetőséget olyan, névjegyekkel rendelkező fiók hozzáadásához vagy konfigurálásához, amelyet szinkronizálhat a táblagépen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Új névjegy"</b></font>" teljesen új névjegy létrehozásához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importálás/Exportálás"</b></font>" SIM- vagy SD-kártyáról történő importáláshoz"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Nincsenek megjeleníthető névjegyei."\n\n"Névjegyek hozzáadásához nyomja meg a "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" gombot, majd érintse meg a"\n" "\n<li><font fgcolor="#ffffffff"><b>"Fiókok"</b></font>" lehetőséget olyan, névjegyekkel rendelkező fiók hozzáadásához vagy konfigurálásához, amelyet szinkronizálhat a telefonon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Új névjegy"</b></font>" teljesen új névjegy létrehozásához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importálás/Exportálás"</b></font>" SIM- vagy SD-kártyáról történő importáláshoz"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Nincsenek megjeleníthető névjegyei. (Ha most adott hozzá egy fiókot, a szinkronizálás eltarthat pár percig.)"\n\n"Névjegyek hozzáadásához nyomja meg a "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" gombot, majd érintse meg a"\n" "\n<li><font fgcolor="#ffffffff"><b>"Fiókok"</b></font>" lehetőséget olyan, névjegyekkel rendelkező fiók hozzáadásához vagy konfigurálásához, amelyet szinkronizálhat a táblagépen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Megjelenítési beállítások"</b></font>" annak beállításához, hogy mely névjegyek láthatók"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Új névjegy"</b></font>" teljesen új névjegy létrehozásához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importálás/Exportálás"</b></font>" SIM- vagy SD-kártyáról történő importáláshoz"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Nincsenek megjeleníthető névjegyei. (Ha most adott hozzá egy fiókot, a szinkronizálás eltarthat pár percig.)"\n\n"Névjegyek hozzáadásához nyomja meg a "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" gombot, majd érintse meg a"\n" "\n<li><font fgcolor="#ffffffff"><b>"Fiókok"</b></font>" lehetőséget olyan, névjegyekkel rendelkező fiók hozzáadásához vagy konfigurálásához, amelyet szinkronizálhat a telefonon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Megjelenítési beállítások"</b></font>" annak beállításához, hogy mely névjegyek láthatók"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Új névjegy"</b></font>" teljesen új névjegy létrehozásához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importálás/Exportálás"</b></font>" SIM- vagy SD-kártyáról történő importáláshoz"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Nincsenek megjeleníthető névjegyei."\n\n"Névjegyek hozzáadásához nyomja meg a "<font fgcolor="#ffffffff">" "<b>"Menü"</b></font>" gombot, majd érintse meg a"\n" "\n<li><font fgcolor="#ffffffff"><b>"Fiókok"</b></font>" lehetőséget olyan, névjegyekkel rendelkező fiók hozzáadásához vagy konfigurálásához, amelyet szinkronizálhat a táblagépen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Új névjegy"</b></font>" teljesen új névjegy létrehozásához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importálás/Exportálás"</b></font>" SIM- vagy SD-kártyáról történő importáláshoz"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Nincsenek megjeleníthető névjegyei."\n\n"Névjegyek hozzáadásához nyomja meg a "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" gombot, majd érintse meg a"\n" "\n<li><font fgcolor="#ffffffff"><b>"Fiókok"</b></font>" lehetőséget olyan, névjegyekkel rendelkező fiók hozzáadásához vagy konfigurálásához, amelyet szinkronizálhat a telefonon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Új névjegy"</b></font>" teljesen új névjegy létrehozásához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importálás/Exportálás"</b></font>" SIM- vagy SD-kártyáról történő importáláshoz"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Nincsenek megjeleníthető névjegyei. (Ha most adott hozzá egy fiókot, a szinkronizálás eltarthat pár percig.)"\n\n"Névjegyek hozzáadásához nyomja meg a "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" gombot, majd érintse meg a"\n" "\n<li><font fgcolor="#ffffffff"><b>"Fiókok"</b></font>" lehetőséget olyan, névjegyekkel rendelkező fiók hozzáadásához vagy konfigurálásához, amelyet szinkronizálhat a táblagépen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Megjelenítési beállítások"</b></font>" annak beállításához, hogy mely névjegyek láthatók"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Új névjegy"</b></font>" teljesen új névjegy létrehozásához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importálás/Exportálás"</b></font>" SIM- vagy SD-kártyáról történő importáláshoz"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Nincsenek megjeleníthető névjegyei. (Ha most adott hozzá egy fiókot, a szinkronizálás eltarthat pár percig.)"\n\n"Névjegyek hozzáadásához nyomja meg a "<font fgcolor="#ffffffff"><b>"Menü"</b></font>" gombot, majd érintse meg a"\n" "\n<li><font fgcolor="#ffffffff"><b>"Fiókok"</b></font>" lehetőséget olyan, névjegyekkel rendelkező fiók hozzáadásához vagy konfigurálásához, amelyet szinkronizálhat a telefonon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Megjelenítési beállítások"</b></font>" annak beállításához, hogy mely névjegyek láthatók"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Új névjegy"</b></font>" teljesen új névjegy létrehozásához"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importálás/Exportálás"</b></font>" SIM- vagy SD-kártyáról történő importáláshoz"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Nincsenek kedvencei."\n\n"Így adhat hozzá névjegyet a kedvencek listájához:"\n\n" "<li>"Érintse meg a "<b>"Címtár"</b>" lapot"\n</li>" "\n<li>"Érintse meg a kedvencekhez hozzáadni kívánt névjegyet"\n</li>" "\n<li>"Érintse meg az ismerős neve melletti csillagot"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Összes névjegy"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Csillaggal megjelölt"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Visszahívás"</string>
<string name="callAgain" msgid="3197312117049874778">"Hívásismétlés"</string>
<string name="returnCall" msgid="8171961914203617813">"Visszahívás"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> perc <xliff:g id="SECONDS">%2$s</xliff:g> másodperc"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> perc <xliff:g id="SECONDS">%s</xliff:g> másodperc"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Gyakran keresett"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Névjegy hozzáadása"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Hozzáadja a(z) \"<xliff:g id="EMAIL">%s</xliff:g>\"címet a Címtárhoz?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Összes"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"egy"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"kettő"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"három"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Az USB-tár beolvasása sikertelen volt (A hiba oka: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Az SD-kártya beolvasása nem sikerült (oka: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"I/O hiba"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Nincs elég memória (lehet, hogy túl nagy a fájl)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Váratlan ok miatt nem sikerült a vCard szintaktikai elemzése"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Bár úgy tűnik, hogy a formátuma érvényes, nem sikerült szintaktikailag elemezni a vCardot, mert a jelenlegi verzió nem támogatja"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Nem található vCard-fájl az USB-táron"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Nem található vCard fájl az SD-kártyán"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"A formátum nem támogatott."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Nem sikerült a vCard importálása"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Nem található vCard fájl az USB-táron"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Nem található vCard fájl az SD-kártyán"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Nem sikerült begyűjteni a vCard fájl(ok) metaadatait."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Egy vagy több fájlt nem sikerült importálni (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Ismeretlen hiba történt"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Válassza ki a vCard fájlt"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"vCard olvasása"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"vCard fájl(ok) beolvasása"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"A vCard adatainak beolvasása nem sikerült"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> névjegy"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> fájl"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"vCard(ok) mentése ideiglenes helyi tárolóba"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Az importáló elmenti a vCardo(ka)t az ideiglenes helyi tárolóba. A tényleges importálás hamarosan megkezdődik."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importálás - <xliff:g id="TOTAL_NUMBER">%s</xliff:g>/<xliff:g id="CURRENT_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importálás - <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"A vCard-adatok beolvasása sikertelen volt"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"A vCard-adatok beolvasása megszakítva"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"A(z) <xliff:g id="FILENAME">%s</xliff:g> vCard importálása befejeződött"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"A(z) <xliff:g id="FILENAME">%s</xliff:g> importálása megszakítva"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"A(z) <xliff:g id="FILENAME">%s</xliff:g> hamarosan importálásra kerül."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"A fájl importálása hamarosan megtörténik."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"A vCard-importálási kérelem elutasítva. Próbálja újra később."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"A(z) <xliff:g id="FILENAME">%s</xliff:g> hamarosan exportálásra kerül."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"A vCard-exportálási kérelem elutasítva. Próbálja újra később."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"kapcsolat"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Exportálás megerősítése"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Biztosan exportálja a Címtárat a(z) \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\" fájlba?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Nem sikerült exportálni a névjegyadatokat"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Túl sok vCard-fájl van az USB-táron"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Túl sok vCard fájl van az SD-kártyán"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"A fájlnév túl hosszú (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"A(z) <xliff:g id="FILENAME">%s</xliff:g> exportálása befejeződött"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"A(z) <xliff:g id="FILENAME">%s</xliff:g> exportálása megszakítva"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Névjegyadatok exportálása"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Névjegyadatok importálása a(z) \"<xliff:g id="FILE_NAME">%s</xliff:g>\" fájlba"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Nem sikerült inicializálni az exportálót: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Hiba történt az exportálás során: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Nem sikerült lekérni az adatbázis adatait"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Nincs exportálható névjegy. Ha vannak névjegyek a telefonján, lehet, hogy az adatok szolgáltatója letiltotta a névjegyek telefonon kívülre történő exportálását."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Nincsenek exportálható névjegyek. Ha vannak névjegyek táblagépén, akkor lehet, hogy az adatszolgáltatók megtiltották azok exportálását a táblagépen kívülre."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Nincsenek exportálható névjegyek. Ha vannak névjegyek telefonján, akkor lehet, hogy az adatszolgáltatók megtiltották azok exportálását a telefonján kívülre."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"A vCard-készítő inicializálása nem sikerült"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"A(z) \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\" fájl nem nyitható meg: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> névjegy"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"A(z) \"<xliff:g id="FILE_NAME">%s</xliff:g>\" fájl nem nyitható meg: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g> névjegy"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"vCard importálásának megszakítása"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Biztosan megszakítja a(z) <xliff:g id="FILENAME">%s</xliff:g> importálását?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"vCard exportálásának megszakítása"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Biztosan megszakítja a(z) <xliff:g id="FILENAME">%s</xliff:g> exportálását?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"vCard importálás/exportálás megszakítása sikertelen"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Ismerősök nevei"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"2 mp-es szünet hozzáadása"</string>
<string name="add_wait" msgid="3360818652790319634">"Várakozás hozzáadása"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Nincs megfelelő alkalmazás a művelet elvégzésére"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Választás megjegyzése"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Ismeretlen"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Nincs adat"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Alapértelmezések törlése"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"A névjegy alapbeállításai:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Törlés"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Fiókok"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importálás/exportálás"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Névjegyek importálása/exportálása"</string>
- <string name="menu_share" msgid="943789700636542260">"Megosztás"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Névjegy megosztása"</string>
<string name="share_via" msgid="563121028023030093">"Névjegy megosztása a következőn:"</string>
<string name="share_error" msgid="4374508848981697170">"Ez a névjegy nem osztható meg."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Név"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Szervezet"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Webhely"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Esemény"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Kapcsolat"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Csoportok"</string>
<string name="type_short_home" msgid="7770424864090605384">"O"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"Mh"</string>
<string name="type_short_pager" msgid="2613818970827594238">"Cs"</string>
<string name="type_short_other" msgid="5669407180177236769">"E"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Ez a névjegy írásvédett"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Egyebek"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Elsődleges név"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Névjegy létrehozása a következő fiókban:"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Szinkronizálási csoport eltávolítása"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Szinkronizálási csoport hozzáadása"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Az összes többi névjegy"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Összes névjegy"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Ha leállítja a(z) \"<xliff:g id="GROUP">%s</xliff:g>\" csoport szinkronizálását, ugyanez történik a nem csoportosított névjegyekkel is."</string>
- <string name="account_phone" msgid="3682950835276226870">"Csak a telefonon, nem szinkronizált"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Csak a táblagépen, nem szinkronizált"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Csak a telefonon, nem szinkronizált"</string>
<string name="call_custom" msgid="7756571794763171802">"<xliff:g id="CUSTOM">%s</xliff:g> hívása"</string>
<string name="call_home" msgid="1990519474420545392">"Otthoni szám hívása"</string>
<string name="call_mobile" msgid="7502236805487609178">"Mobil hívása"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Csevegés az ICQ-n"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Csevegés Jabberen"</string>
<string name="chat" msgid="9025361898797412245">"Csevegés"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Cím"</string>
<string name="postal_street" msgid="8133143961580058972">"Utca"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Postafiók"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Környék"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Állapot"</string>
<string name="postal_postcode" msgid="572136414136673751">"Irányítószám"</string>
<string name="postal_country" msgid="7638264508416368690">"Ország"</string>
+ <string name="full_name" msgid="6602579550613988977">"Név"</string>
<string name="name_given" msgid="1687286314106019813">"Utónév"</string>
<string name="name_family" msgid="3416695586119999058">"Vezetéknév"</string>
<string name="name_prefix" msgid="59756378548779822">"Név előtagja"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Keresés a névjegyek között"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Keresés az összes névjegyben"</string>
<string name="take_photo" msgid="7496128293167402354">"Fotó készítése"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Új fénykép készítése"</string>
<string name="pick_photo" msgid="448886509158039462">"Fotó kiválasztása a Galériából"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"A nyelv módosítása miatt folyamatban van a Címtár frissítése."\n\n"Kérjük, várjon..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"A Címtár frissítése folyamatban van."\n\n"Kérjük, várjon..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"A névjegyek verziófrissítése folyamatban. "\n\n"A frissítéshez körülbelül <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB belső telefonmemóriára van szükség."\n\n"Válasszon egyet a következő lehetőségek közül:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Új fotó kiválasztása a Galériából"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Az ismerőslista frissítése folyamatban van, hogy tükrözze a nyelv módosítását."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Az ismerőslista frissítése folyamatban van."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Az ismerősök frissítése folyamatban van. "\n" "\n" A frissítési folyamathoz kb. <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB belső tárhelyre van szükség. "\n" "\n" Válasszon az alábbi lehetőségek közül:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Távolítson el néhány alkalmazást"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Verziófrissítés újrapróbálása"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Találatok a következőre: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Keresés..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Kiválasztottak megjelenítése"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Összes megjelenítése"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Összes kijelölése"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Összes kijelölés megszüntetése"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Nincs kijelölt névjegy."</string>
+ <string name="add_field" msgid="2384260056674995230">"Más mező hozzáadása"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"- <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> - <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"kedvenc"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Névjegy szerkesztése"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"nincs egyesítve"</item>
+ <item quantity="other" msgid="425683718017380845">"egyesítve <xliff:g id="COUNT">%0$d</xliff:g> forrásból"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Egyéb"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Névjegyek összekapcsolása"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Összekapcsolja a jelenlegi névjegyet a kiválasztott névjeggyel?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"A kiválasztott névjegyek szerkesztése"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"A kiválasztott névjegy szerkesztésére vált? Az eddig beírt információk át lesznek másolva."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Másolás a saját névjegyeim közé"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Mappa: <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Keresés a névjegyek között"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Mappa"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Címtár"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Személyes másolat készítése"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Címtár kiválasztása"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Összes névjegy"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Csillaggal megjelölt"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Egyéni"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Személyre szabás..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Névjegyek telefonszámmal"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kapcsolat"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Egyéni nézet megadása"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Beállítások"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Beállítások"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Megjelenítési beállítások"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Névjegy keresése"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Telefonszám"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Hozzáadása a névjegyekhez"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Bezárás"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Születési év megadása"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Névjegy"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Betöltés…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Új névjegy létrehozása"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Jelentkezzen be fiókjába"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Névjegyek importálása fájlból"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Új csoport létrehozása"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Új csoport létrehozása]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Csoport átnevezése"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Csoport törlése"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Biztosan törölni szeretné a(z) \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\" csoportot? (Maguk a névjegyek nem kerülnek törlésre.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Kérjük, adja meg az ismerős nevét, mielőtt összekapcsolná egy másik ismerősével."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Összekapcsolt névjegy"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Másolt szöveg"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Módosítások elvetése"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Biztosan elveti a módosításokat?"</string>
+ <string name="discard" msgid="1234315037371251414">"Elvetés"</string>
</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index a165fcd..159be21 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Pilih pintasan kenalan"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Pilih nomor untuk dipanggil"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Pilih nomor untuk dikirimi pesan"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Pilih kenalan"</string>
<string name="starredList" msgid="4817256136413959463">"Yang berkilau bintangnya"</string>
<string name="frequentList" msgid="7154768136473953056">"Sering"</string>
<string name="strequentList" msgid="5640192862059373511">"Favorit"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Hapus kenalan"</string>
<string name="menu_call" msgid="3992595586042260618">"Hubungi kenalan"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"SMS kenalan"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Kirim email"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Alamat pada peta"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Jadikan nomor bawaan"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Jadikan email bawaan"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Pisahkan"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Kenalan telah berpisah"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Ganti nama grup"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Hapus grup"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Baru"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Pisahkan Kenalan"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Apakah Anda yakin ingin memisahkan satu kenalan ini menjadi beberapa kenalan: satu untuk setiap kumpulan informasi kenalan yang telah digabungkan ke dalamnya?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Gabung"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Menghapus kenalan ini akan menghapus informasi dari beberapa akun."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Kenalan ini akan dihapus."</string>
<string name="menu_done" msgid="796017761764190697">"Selesai"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Kembalikan"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Batal"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Edit data kenalan"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Kenalan baru"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetik"</string>
<string name="label_notes" msgid="8337354953278341042">"Catatan"</string>
<string name="label_sip_address" msgid="124073911714324974">"Panggilan internet"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Nada dering"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Pertama dan Terakhir"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Nama fonetik"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Perusahaan"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Judul"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Kenalan tidak ada."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Buat kenalan baru"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Pilih label"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telepon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Email"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"IM"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Alamat pos"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Alamat"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organisasi"</item>
<item msgid="7196592230748086755">"Catatan"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Tidak ada gambar pada ponsel."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Ikon kenalan"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Tidak ada gambar yang tersedia pada tablet."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Tidak ada gambar pada ponsel."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Foto kenalan"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Nama label ubahsuaian"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Tampilkan opsi"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Tampilkan opsi"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Kirim panggilan ke kotak pesan secara langsung"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Bawaan"</string>
- <string name="changePicture" msgid="2943329047610967714">"Ubah ikon"</string>
- <string name="removePicture" msgid="3041230993155966350">"Hapus ikon"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Hapus foto"</string>
<string name="noContacts" msgid="8579310973261953559">"Tidak ada kenalan."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Tidak ditemukan kenalan yang cocok."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Tidak ada kenalan dengan nomor telepon."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Kenalan disimpan."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Galat, tidak dapat menyimpan perubahan kenalan."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Menampilkan 1 kenalan dengan nomor telepon"</item>
- <item quantity="other" msgid="6133262880804110289">"Menampilkan <xliff:g id="COUNT">%d</xliff:g> kenalan dengan nomor telepon"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 kenalan dengan nomor telepon"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> kenalan dengan nomor telepon"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Tidak ada kenalan dengan nomor telepon"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Tidak ada kenalan dengan nomor telepon"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Menampilkan 1 kenalan"</item>
- <item quantity="other" msgid="2865867557378939630">"Menampilkan <xliff:g id="COUNT">%d</xliff:g> kenalan"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 kenalan"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> kenalan"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Tidak ada kenalan yang terlihat"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Tidak ada kenalan"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Tidak ada kenalan yang terlihat"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Tidak ada kenalan yang berkilau bintangnya"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Tidak ada kenalan di <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Menemukan 1 kenalan"</item>
- <item quantity="other" msgid="7752927996850263152">"Menemukan <xliff:g id="COUNT">%d</xliff:g> kenalan"</item>
+ <item quantity="one" msgid="5517063038754171134">"Ada 1"</item>
+ <item quantity="other" msgid="3852668542926965042">"Ada <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Kenalan tidak ditemukan"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"ditemukan lebih dari <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Tidak ditemukan"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 kenalan"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> kenalan"</item>
+ <item quantity="one" msgid="4826918429708286628">"Ada 1"</item>
+ <item quantity="other" msgid="7988132539476575389">"Ada <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Kenalan"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favorit"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Kenalan pada kartu SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Anda tidak memiliki kenalan untuk ditampilkan. (Jika Anda baru saja menambahkan akun, diperlukan waktu beberapa menit untuk menyinkronkan kenalan.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Anda tak memiliki kenalan untuk ditampilkan."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Anda tidak memiliki kenalan untuk ditampilkan."\n\n"Untuk menambahkan kenalan, tekan "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" dan sentuh:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Akun"</b></font>" untuk menambahkan atau mengonfigurasi akun tertentu dengan kenalan yang dapat Anda sinkronkan ke ponsel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Kenalan baru"</b></font>" untuk membuat kenalan baru dari awal"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Impor/Ekspor"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Anda tidak memiliki kenalan untuk ditampilkan. (Jika Anda baru saja menambahkan akun, diperlukan waktu beberapa menit untuk menyinkronkan kenalan.)"\n\n"Untuk menambahkan kenalan, tekan "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" dan sentuh:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Akun"</b></font>" untuk menambahkan atau mengonfigurasi akun tertentu dengan kenalan yang dapat Anda sinkronkan ke ponsel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opsi tampilan"</b></font>" untuk mengubah kenalan mana yang terlihat"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Kenalan baru"</b></font>" untuk membuat kenalan baru dari awal"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Impor/Ekspor"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Anda tidak memiliki kenalan untuk ditampilkan."\n\n"Untuk menambahkan kenalan, tekan "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" dan sentuh:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Akun"</b></font>" untuk menambahkan atau mengonfigurasi akun tertentu dengan kenalan yang dapat Anda sinkronkan ke ponsel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Kenalan baru"</b></font>" untuk membuat kenalan baru dari awal"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Impor/Ekspor"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Anda tidak memiliki kenalan untuk ditampilkan. (Jika Anda baru saja menambahkan akun, diperlukan waktu beberapa menit untuk menyinkronkan kenalan.)"\n\n"Untuk menambahkan kenalan, tekan "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" dan sentuh:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Akun"</b></font>" untuk menambahkan atau mengonfigurasi akun tertentu dengan kenalan yang dapat Anda sinkronkan ke ponsel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opsi tampilan"</b></font>" untuk mengubah kenalan mana yang terlihat"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Kenalan baru"</b></font>" untuk membuat kenalan baru dari awal"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Impor/Ekspor"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Anda tidak memiliki kenalan untuk ditampilkan."\n\n"Untuk menambah kenalan, tekan "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" dan sentuh:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Akun"</b></font>" untuk menambah atau mengonfigurasi akun dengan kenalan yang dapat Anda sinkronkan dengan tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Kenalan baru"</b></font>" untuk membuat kenalan baru dari awal"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Impor/Eskpor"</b></font>" untuk mengimpor kenalan dari kartu SIM atau SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Anda tidak memiliki kenalan untuk ditampilkan."\n\n"Untuk menambah kenalan, tekan "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" dan sentuh:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Akun"</b></font>" untuk menambah atau mengonfigurasi akun dengan kenalan yang dapat Anda sinkronkan dengan ponsel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Kenalan baru"</b></font>" untuk membuat kenalan baru dari awal"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Impor/Eskpor"</b></font>" untuk mengimpor kenalan dari kartu SIM atau SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Anda tidak memiliki kenalan untuk ditampilkan. (Jika Anda baru saja menambahkan akun, dibutuhkan beberapa menit untuk menyinkronkan kenalan.)"\n\n"Untuk menambahkan kenalan, tekan "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" dan sentuh:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Akun"</b></font>" untuk menambahkan atau mengonfigurasi akun dengan kenalan yang dapat Anda sinkronkan ke tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opsi tampilan"</b></font>" untuk mengubah kenalan mana yang dapat dilihat"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Kenalan baru"</b></font>" untuk membuat kenalan baru dari awal"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Impor/Ekspor"</b></font>" untuk mengimpor kenalan dari kartu SIM atau SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Anda tidak memiliki kenalan untuk ditampilkan. (Jika Anda baru saja menambahkan akun, dibutuhkan beberapa menit untuk menyinkronkan kenalan.)"\n\n"Untuk menambahkan kenalan, tekan "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" dan sentuh:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Akun"</b></font>" untuk menambahkan atau mengonfigurasi akun dengan kenalan yang dapat Anda sinkronkan ke ponsel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opsi tampilan"</b></font>" untuk mengubah kenalan mana yang dapat dilihat"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Kenalan baru"</b></font>" untuk membuat kenalan baru dari awal"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Impor/Ekspor"</b></font>" untuk mengimpor kenalan dari kartu SIM atau SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Anda tidak memiliki kenalan untuk ditampilkan."\n\n"Untuk menambah kenalan, tekan "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" dan sentuh:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Akun"</b></font>" untuk menambah atau mengonfigurasi akun dengan kenalan yang dapat Anda sinkronkan dengan tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Kenalan baru"</b></font>" untuk membuat kenalan baru dari awal"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Impor/Eskpor"</b></font>" untuk mengimpor kenalan dari kartu SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Anda tidak memiliki kenalan untuk ditampilkan."\n\n"Untuk menambah kenalan, tekan "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" dan sentuh:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Akun"</b></font>" untuk menambah atau mengonfigurasi akun dengan kenalan yang dapat Anda sinkronkan dengan ponsel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Kenalan baru"</b></font>" untuk membuat kenalan baru dari awal"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Impor/Eskpor"</b></font>" untuk mengimpor kenalan dari kartu SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Anda tidak memiliki kenalan untuk ditampilkan. (Jika Anda baru saja menambahkan akun, dibutuhkan beberapa menit untuk menyinkronkan kenalan.)"\n\n"Untuk menambahkan kenalan, tekan "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" dan sentuh:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Akun"</b></font>" untuk menambahkan atau mengonfigurasi akun dengan kenalan yang dapat Anda sinkronkan ke tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opsi tampilan"</b></font>" untuk mengubah kenalan mana yang dapat dilihat"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Kenalan baru"</b></font>" untuk membuat kenalan baru dari awal"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Impor/Ekspor"</b></font>" untuk mengimpor kenalan dari kartu SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Anda tidak memiliki kenalan untuk ditampilkan. (Jika Anda baru saja menambahkan akun, dibutuhkan beberapa menit untuk menyinkronkan kenalan.)"\n\n"Untuk menambahkan kenalan, tekan "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" dan sentuh:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Akun"</b></font>" untuk menambahkan atau mengonfigurasi akun dengan kenalan yang dapat Anda sinkronkan ke ponsel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opsi tampilan"</b></font>" untuk mengubah kenalan mana yang dapat dilihat"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Kenalan baru"</b></font>" untuk membuat kenalan baru dari awal"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Impor/Ekspor"</b></font>" untuk mengimpor kenalan dari kartu SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Anda tidak memiliki favorit."\n\n"Untuk menambahkan kenalan ke daftar favorit:"\n\n" "<li>"Sentuh tab "<b>"Kenalan"</b>\n</li>" "\n<li>"Sentuh kenalan yang ingin Anda tambahkan ke favorit"\n</li>" "\n<li>"Sentuh bintang di samping nama kenalan"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Semua kenalan"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Yang berkilau bintangnya"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Hubungi kembali"</string>
<string name="callAgain" msgid="3197312117049874778">"Hubungi sekali lagi"</string>
<string name="returnCall" msgid="8171961914203617813">"Panggilan kembali"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> menit <xliff:g id="SECONDS">%2$s</xliff:g> detik"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> menit <xliff:g id="SECONDS">%s</xliff:g> detik"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Paling sering dihubungi"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Tambahkan kenalan"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Tambahkan \"<xliff:g id="EMAIL">%s</xliff:g>\" ke kenalan?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Semua"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"satu"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dua"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tiga"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Memindai penyimpanan USB gagal (Alasan: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Kartu SD gagal dipindai (Alasan: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Galat I/O"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Memori tidak memadai (berkas mungkin terlalu besar)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Gagal melakukan parse pada vCard karena alasan yang tidak diharapkan"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Gagal melakukan parse pada vCard meskipun dalam format yang valid, karena implementasi saat ini tidak mendukungnya"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Tidak ada berkas vCard dalam penyimpanan USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Tidak ada berkas vCard yang ditemukan pada kartu SD"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Format tidak didukung."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Gagal mengimpor vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Tidak ada berkas vCard yang ditemukan pada penyimpanan USB"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Tidak ada berkas vCard yang ditemukan pada kartu SD"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Gagal mengumpulkan informasi meta dari berkas vCard yang diberikan."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Satu berkas atau lebih gagal diimpor (%s)"</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Galat tidak dikenal"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Pilih berkas vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Membaca vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Membaca berkas vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Gagal membaca data vCard"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> dari <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kenalan"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> dari <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> berkas"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Menyimpan vCard ke penyimpanan sementara lokal"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Pengimpor menyimpan vCard ke penyimpanan sementara lokal. Impor sesungguhnya akan segera dimulai."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Mengimpor <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Mengimpor <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Gagal Membaca data vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Membaca data vCard telah dibatalkan"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Selesai mengimpor vCard <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Impor <xliff:g id="FILENAME">%s</xliff:g> dibatalkan"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> akan segera diimpor."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Berkas akan diimpor segera."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Permintaan impor vCard ditolak. Harap coba lagi nanti."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> akan segera diekspor."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Permintaan ekspor vCard ditolak. Harap coba lagi nanti."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"kenalan"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Konfirmasi ekspor"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Apakah Anda yakin ingin mengekspor daftar kenalan ke \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Gagal mengekspor data kenalan"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Terlalu banyak berkas vCard dalam penyimpanan USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Terlalu banyak berkas vCard pada kartu SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Nama berkas yang diperlukan terlalu panjang (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Selesai mengekspor <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Ekspor <xliff:g id="FILENAME">%s</xliff:g> dibatalkan"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Mengekspor data kenalan"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Mengekspor data kenalan ke \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Tidak dapat menginisiasi pengeskpor: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Terjadi galat selama ekspor: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Gagal mendapatkan informasi database"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Tidak ada kenalan yang dapat diekspor. Jika Anda sudah memiliki kenalan pada ponsel, semua kenalan mungkin dilarang untuk diekspor keluar ponsel oleh sebagian penyedia data."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Tidak ada kenalan yang dapat diekspor. Jika Anda memiliki kenalan pada tablet, semua kenalan mungkin dilarang diekspor ke luar tablet oleh beberapa penyedia data."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Tidak ada kenalan yang dapat diekspor. Jika Anda memiliki kenalan pada ponsel, semua kenalan mungkin dilarang diekspor ke luar ponsel oleh beberapa penyedia data."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Penyusun vCard tidak dimulai dengan benar"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Tidak dapat membuka \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> dari <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kenalan"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Tidak dapat membuka \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> dari <xliff:g id="TOTAL_NUMBER">%s</xliff:g> kenalan"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Membatalkan impor vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Yakin ingin membatalkan mengimpor <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Membatalkan ekspor vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Yakin ingin membatalkan mengekspor <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Gagal membatalkan impor/ekspor vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Nama kenalan Anda"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Tambahkan jeda 2-det"</string>
<string name="add_wait" msgid="3360818652790319634">"Tambahkan tunggu"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Tidak ada aplikasi yang ditemukan untuk menangani tindakan ini"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Ingat pilihan ini"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Tidak diketahui"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Tidak ada data"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Hapus bawaan"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Bawaan disetel untuk kenalan ini:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Hapus"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Akun"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Impor/Ekspor"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Impor/Ekspor kenalan"</string>
- <string name="menu_share" msgid="943789700636542260">"Bagikan"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Berbagi kenalan"</string>
<string name="share_via" msgid="563121028023030093">"Bagikan kenalan melalui"</string>
<string name="share_error" msgid="4374508848981697170">"Kenalan ini tidak dapat dibagikan"</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Nama"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organisasi"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Situs web"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Acara"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Hubungan"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grup"</string>
<string name="type_short_home" msgid="7770424864090605384">"R"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"S"</string>
<string name="type_short_work" msgid="4925330752504537861">"K"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Kenalan ini hanya-baca"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Lainnya"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Nama utama"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Buat kenalan di bawah akun"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Hapus grup sinkronisasi"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Tambahkan grup sinkronisasi"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Semua Kenalan Lainnya"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Semua Kenalan"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Menghapus \'<xliff:g id="GROUP">%s</xliff:g>\' dari sinkronisasi juga akan menghapus kenalan yang tidak dikelompokkan dari sinkronisasi."</string>
- <string name="account_phone" msgid="3682950835276226870">"Ponsel saja, tidak disinkronkan"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Tablet saja, tidak disinkronkan"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Ponsel saja, tidak disinkronkan"</string>
<string name="call_custom" msgid="7756571794763171802">"Panggil <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Panggil nomor telepon rumah"</string>
<string name="call_mobile" msgid="7502236805487609178">"Panggil nomor seluler"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Ngobrol menggunakan ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Ngobrol menggunakan Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Ngobrol"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Alamat"</string>
<string name="postal_street" msgid="8133143961580058972">"Jalan"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Kotak pos"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Lingkungan"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Neg. Bagian/Prov"</string>
<string name="postal_postcode" msgid="572136414136673751">"Kode pos"</string>
<string name="postal_country" msgid="7638264508416368690">"Negara"</string>
+ <string name="full_name" msgid="6602579550613988977">"Nama"</string>
<string name="name_given" msgid="1687286314106019813">"Nama depan"</string>
<string name="name_family" msgid="3416695586119999058">"Nama keluarga"</string>
<string name="name_prefix" msgid="59756378548779822">"Awalan nama"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Telusuri kenalan"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Telusuri semua kenalan"</string>
<string name="take_photo" msgid="7496128293167402354">"Ambil foto"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Ambil foto baru"</string>
<string name="pick_photo" msgid="448886509158039462">"Pilih foto dari galeri"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Daftar kenalan sedang diperbarui untuk mencerminkan perubahan bahasa."\n\n"Harap tunggu..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Daftar kenalan sedang diperbarui."\n\n"Harap tunggu..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Kenalan sedang dalam proses peningkatan versi. "\n\n"Proses peningkatan versi memerlukan sekitar <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>Mb dari penyimpanan ponsel internal."\n\n"Pilih salah satu opsi berikut:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Pilih foto baru dari Galeri"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Daftar kenalan sedang diperbarui untuk mencerminkan perubahan bahasa."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Daftar kenalan sedang diperbarui."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Kenalan sedang dalam proses peningkatan. "\n\n"Proses peningkatan membutuhkan sekitar <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mb penyimpanan internal."\n\n"Pilih salah satu dari opsi berikut:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Copot pemasangan beberapa aplikasi"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Coba peningkatan versi sekali lagi"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Hasil penelusuran untuk: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Menelusuri..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Tampilkan yang dipilih"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Tampilkan semua"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Pilih semua"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Batal pilih semua"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Tidak ada kenalan yang dipilih."</string>
+ <string name="add_field" msgid="2384260056674995230">"Tambahkan bidang lain"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"melalui <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> melalui <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favorit"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Edit kenalan"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"tidak digabung"</item>
+ <item quantity="other" msgid="425683718017380845">"digabungkan dari <xliff:g id="COUNT">%0$d</xliff:g> sumber"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Lainnya"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Gabungkan kenalan"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Gabungkan kenalan ini dengan kenalan yang dipilih?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Edit kenalan yang dipilih"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Beralih ke mengedit kenalan yang dipilih? Informasi yang telah Anda masukkan sejauh ini akan disalin."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Salin ke kenalan saya"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Direktori <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Menelusuri semua kenalan"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Direktori"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Kenalan"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Membuat salinan pribadi"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Pilih daftar kenalan"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Semua kenalan"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Yang berkilau bintangnya"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Ubahsuaian"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Ubahsuaikan..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Kenalan dengan nomor telepon"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kenalan"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Tentukan tampilan ubahsuaian"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Setelan"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Setelan"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Opsi tampilan"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Temukan kenalan"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Nomor telepon"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Tambahkan ke kenalan"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Tutup"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Berikan tahun"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Kenalan"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Memuat …"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Buat kenalan baru"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Masuk ke akun."</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Mengimpor data kenalan dari berkas"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Buat grup baru"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Buat grup baru]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Ganti nama grup"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Hapus grup"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Anda yakin ingin menghapus grup \'<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\'? (Data kenalan tidak akan dihapus.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Masukkan nama kenalan sebelum bergabung dengan kenalan lain."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Kenalan yang bergabung"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Teks disalin"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Singkirkan perubahan"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Apakah Anda ingin menyingkirkan perubahan?"</string>
+ <string name="discard" msgid="1234315037371251414">"Singkirkan"</string>
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index d541876..6632e78 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Scegli una scorciatoia contatto"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Scegli un numero da chiamare"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Scegli un numero a cui inviare il messaggio"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Seleziona un contatto"</string>
<string name="starredList" msgid="4817256136413959463">"Speciali"</string>
<string name="frequentList" msgid="7154768136473953056">"Frequenti"</string>
<string name="strequentList" msgid="5640192862059373511">"Preferiti"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Elimina contatto"</string>
<string name="menu_call" msgid="3992595586042260618">"Chiama"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Invia SMS"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Invia email"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Indirizzo su mappa"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Rendi numero predefinito"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Rendi ind. email predefinito"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Separa"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Contatti separati"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Rinomina gruppo"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Elimina gruppo"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Nuovo"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Separa contatto"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Separare questo contatto singolo in più contatti, uno per ogni serie di informazioni di contatto unita in precedenza?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Unisci"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"L\'eliminazione di questo contatto causerà l\'eliminazione di informazioni da più account."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Il contatto verrà eliminato."</string>
<string name="menu_done" msgid="796017761764190697">"Salva"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Annulla"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Annulla"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Modifica contatto"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Nuovo contatto"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetica"</string>
<string name="label_notes" msgid="8337354953278341042">"Note"</string>
<string name="label_sip_address" msgid="124073911714324974">"Chiamata Internet"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Suoneria"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Nome e cognome"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Nome fonetico"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Società"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Titolo"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Il contatto non esiste."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Crea nuovo contatto"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Seleziona etichetta"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefono"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Email"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Chat"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Indirizzo postale"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Indirizzo"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organizzazione"</item>
<item msgid="7196592230748086755">"Nota"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Nessuna foto disponibile."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Icona del contatto"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Nessuna foto disponibile sul tablet."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Nessuna foto disponibile."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Foto del contatto"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Nome etichetta personalizzata"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Opzioni di visualizzazione"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Opzioni di visualizzazione"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Inoltra chiamate direttamente alla segreteria"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Predefinita"</string>
- <string name="changePicture" msgid="2943329047610967714">"Cambia icona"</string>
- <string name="removePicture" msgid="3041230993155966350">"Rimuovi icona"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Rimuovi foto"</string>
<string name="noContacts" msgid="8579310973261953559">"Nessun contatto."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Nessun contatto corrispondente trovato."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nessun contatto con numeri di telefono."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Contatto salvato."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Errore, impossibile salvare le modifiche ai contatti."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"1 contatto con numero di telefono visualizzato"</item>
- <item quantity="other" msgid="6133262880804110289">"<xliff:g id="COUNT">%d</xliff:g> contatti con numeri di telefono visualizzati"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 contatto con numero di telefono"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> contatti con numeri di telefono"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Nessun contatto con numeri di telefono visibile"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Nessun contatto con numeri di telefono"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"1 contatto visualizzato"</item>
- <item quantity="other" msgid="2865867557378939630">"<xliff:g id="COUNT">%d</xliff:g> contatti visualizzati"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 contatto"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> contatti"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Nessun contatto visibile"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Nessun contatto"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Nessun contatto visibile"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Nessun contatto aggiunto a speciali"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Nessun contatto in <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"1 contatto trovato"</item>
- <item quantity="other" msgid="7752927996850263152">"<xliff:g id="COUNT">%d</xliff:g> contatti trovati"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 trovato"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> trovati"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Contatto non trovato"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"ne sono stati trovati più di <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Non trovati"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 contatto"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> contatti"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 trovato"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> trovati"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Contatti"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Preferiti"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Contatti SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Non ci sono contatti da visualizzare (se hai appena aggiunto un account, la sincronizzazione dei contatti potrebbe richiedere alcuni minuti)."</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Non ci sono contatti da visualizzare."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Non sono presenti contatti da visualizzare."\n\n"Per aggiungere contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e tocca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Account"</b></font>" per aggiungere o configurare un account con contatti sincronizzabili con il telefono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare da zero un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/esporta"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Non sono presenti contatti da visualizzare (se hai appena aggiunto un account, la sincronizzazione dei contatti potrebbe richiedere alcuni minuti)."\n\n"Per aggiungere contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e tocca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Account"</b></font>" per aggiungere o configurare un account con contatti sincronizzabili con il telefono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opzioni di visualizzazione"</b></font>" per modificare i contatti visibili"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare da zero un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/esporta"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Non sono presenti contatti da visualizzare."\n\n"Per aggiungere contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e tocca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Account"</b></font>" per aggiungere o configurare un account con contatti sincronizzabili con il telefono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare da zero un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/esporta"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Non sono presenti contatti da visualizzare (se hai appena aggiunto un account, la sincronizzazione dei contatti potrebbe richiedere alcuni minuti)."\n\n"Per aggiungere contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e tocca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Account"</b></font>" per aggiungere o configurare un account con contatti sincronizzabili con il telefono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opzioni di visualizzazione"</b></font>" per modificare i contatti visibili"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare da zero un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/esporta"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Non sono presenti contatti da visualizzare."\n\n"Per aggiungere contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e tocca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Account"</b></font>" per aggiungere o configurare un account con contatti sincronizzabili con il tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare da zero un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/esporta"</b></font>" per importare contatti dalla SIM o dalla scheda SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Non sono presenti contatti da visualizzare."\n\n"Per aggiungere contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e tocca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Account"</b></font>" per aggiungere o configurare un account con contatti sincronizzabili con il telefono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare da zero un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/esporta"</b></font>" per importare contatti dalla SIM o dalla scheda SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Non sono presenti contatti da visualizzare (se hai appena aggiunto un account, la sincronizzazione dei contatti potrebbe richiedere alcuni minuti)."\n\n"Per aggiungere contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e tocca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Account"</b></font>" per aggiungere o configurare un account con contatti sincronizzabili con il tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opzioni di visualizzazione"</b></font>" per modificare i contatti visibili"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare da zero un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/esporta"</b></font>" per importare contatti dalla SIM o dalla scheda SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Non sono presenti contatti da visualizzare (se hai appena aggiunto un account, la sincronizzazione dei contatti potrebbe richiedere alcuni minuti)."\n\n"Per aggiungere contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e tocca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Account"</b></font>" per aggiungere o configurare un account con contatti sincronizzabili con il telefono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opzioni di visualizzazione"</b></font>" per modificare i contatti visibili"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare da zero un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/esporta"</b></font>" per importare contatti dalla SIM o dalla scheda SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Non sono presenti contatti da visualizzare."\n\n"Per aggiungere contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e tocca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Account"</b></font>" per aggiungere o configurare un account con contatti sincronizzabili con il tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare da zero un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/esporta"</b></font>" per importare contatti dalla scheda SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Non sono presenti contatti da visualizzare."\n\n"Per aggiungere contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e tocca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Account"</b></font>" per aggiungere o configurare un account con contatti sincronizzabili con il telefono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare da zero un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/esporta"</b></font>" per importare contatti dalla scheda SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Non sono presenti contatti da visualizzare (se hai appena aggiunto un account, la sincronizzazione dei contatti potrebbe richiedere alcuni minuti)."\n\n"Per aggiungere contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e tocca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Account"</b></font>" per aggiungere o configurare un account con contatti sincronizzabili con il tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opzioni di visualizzazione"</b></font>" per modificare i contatti visibili"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare da zero un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/esporta"</b></font>" per importare contatti dalla scheda SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Non sono presenti contatti da visualizzare (se hai appena aggiunto un account, la sincronizzazione dei contatti potrebbe richiedere alcuni minuti)."\n\n"Per aggiungere contatti, premi "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e tocca:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Account"</b></font>" per aggiungere o configurare un account con contatti sincronizzabili con il telefono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opzioni di visualizzazione"</b></font>" per modificare i contatti visibili"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nuovo contatto"</b></font>" per creare da zero un nuovo contatto"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importa/esporta"</b></font>" per importare contatti dalla scheda SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Non sono presenti preferiti."\n\n"Per aggiungere un contatto al tuo elenco di preferiti:"\n\n<li>"Tocca la scheda "<b>"Contatti."</b>\n</li>" "\n<li>"Tocca il contatto da aggiungere ai preferiti."\n</li>" "\n<li>"Tocca la stella accanto al nome del contatto."\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Tutti i contatti"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Speciali"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Richiama"</string>
<string name="callAgain" msgid="3197312117049874778">"Richiama"</string>
<string name="returnCall" msgid="8171961914203617813">"Chiama numero"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min. <xliff:g id="SECONDS">%2$s</xliff:g> sec."</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> sec."</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Contattati spesso"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Aggiungi contatto"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Aggiungi \"<xliff:g id="EMAIL">%s</xliff:g>\" ai contatti?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Tutti"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"uno"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"due"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tre"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Scansione dell\'archivio USB non riuscita (motivo: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Analisi scheda SD non riuscita (motivo: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Errore I/O"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Memoria insufficiente (il file potrebbe essere di dimensioni eccessive)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Impossibile analizzare la vCard per motivi imprevisti"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Impossibile analizzare la vCard nonostante sembri avere un formato valido perché l\'implementazione corrente non la supporta"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Nessun file vCard trovato nell\'archivio USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Nessun file vCard trovato sulla scheda SD"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Il formato non è supportato."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Importazione vCard non riuscita"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Nessun file vCard trovato nell\'archivio USB"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Nessun file vCard trovato sulla scheda SD"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Raccolta dei metadati dei file vCard forniti non riuscita."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Impossibile importare uno o più file (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Errore sconosciuto"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Seleziona file vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Lettura vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Lettura file vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Lettura dati vCard non riuscita"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> contatti su <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> file su <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Memorizzazione delle vCard nella cache di archiviazione temporanea locale"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"L\'importazione memorizzerà le vCard nella cache di archiviazione temporanea locale. L\'importazione reale inizierà a breve."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importazione di <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importazione di <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Lettura dati vCard non riuscita"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Lettura dati vCard annullata"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Importazione vCard <xliff:g id="FILENAME">%s</xliff:g> terminata"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Importazione di <xliff:g id="FILENAME">%s</xliff:g> annullata"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"La vCard <xliff:g id="FILENAME">%s</xliff:g> verrà importata a breve."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Il file sarà importato a breve."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Richiesta importazione vCard rifiutata. Prova più tardi."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"La vCard <xliff:g id="FILENAME">%s</xliff:g> verrà esportata a breve."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Richiesta esportazione vCard rifiutata. Prova più tardi."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"contatto"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Conferma esportazione"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Esportare l\'elenco di contatti in \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Esportazione dati contatti non riuscita"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Troppi file vCard nell\'archivio USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Troppi dati vCard sulla scheda SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Il nome file richiesto è troppo lungo (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Esportazione di <xliff:g id="FILENAME">%s</xliff:g> terminata"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Esportazione di <xliff:g id="FILENAME">%s</xliff:g> annullata"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Esportazione dati di contatto"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Esportazione dati di contatto in \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Impossibile inizializzare l\'utilità di esportazione: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Si è verificato un errore durante l\'esportazione: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Recupero informazioni database non riuscito"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Nessun contatto esportabile. Se nel tuo telefono sono presenti dei contatti, qualche provider di dati potrebbe impedire l\'esportazione di tutti i contatti all\'esterno del telefono."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Non sono presenti contatti esportabili. Se sul tuo tablet sono presenti dei contatti, qualche provider di dati potrebbe impedire l\'esportazione di tutti i contatti all\'esterno del tablet."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Non sono presenti contatti esportabili. Se nel tuo telefono sono presenti dei contatti, qualche provider di dati potrebbe impedire l\'esportazione di tutti i contatti all\'esterno del telefono."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Il compositore di vCard non è correttamente inizializzato"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Impossibile aprire \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> contatti su <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Impossibile aprire \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> contatti su <xliff:g id="TOTAL_NUMBER">%s</xliff:g>"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Annullamento importazione vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Annullare l\'importazione di <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Annullamento esportazione vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Annullare l\'esportazione di <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Impossibile annullare l\'operazione"</string>
<string name="search_settings_description" msgid="2675223022992445813">"I nomi dei tuoi contatti"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Aggiungi pausa 2 sec"</string>
<string name="add_wait" msgid="3360818652790319634">"Aggiungi attesa"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Nessuna applicazione trovata in grado di gestire questa azione"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Memorizza questa scelta"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Sconosciuto"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Nessun dato"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Cancella impostazioni predefinite"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Impostazioni predefinite per il contatto:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Cancella"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Account"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importa/esporta"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importa/esporta contatti"</string>
- <string name="menu_share" msgid="943789700636542260">"Condividi"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Condividi contatto"</string>
<string name="share_via" msgid="563121028023030093">"Condividi contatto tramite"</string>
<string name="share_error" msgid="4374508848981697170">"Impossibile condividere il contatto."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Nome"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organizzazione"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Sito web"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Evento"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relazione"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Gruppi"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"W"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Questo contatto è di sola lettura"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Altro"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Nome principale"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Crea contatto sotto account"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Rimuovi gruppo sinc."</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Aggiungi gruppo sinc."</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Tutti gli altri contatti"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Tutti i contatti"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Se rimuovi \"<xliff:g id="GROUP">%s</xliff:g>\" dalla sincronizzazione, verranno rimossi anche tutti i contatti separati."</string>
- <string name="account_phone" msgid="3682950835276226870">"Solo su telefono, non sincronizzato"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Solo su tablet, non sincronizzato"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Solo su telefono, non sincronizzato"</string>
<string name="call_custom" msgid="7756571794763171802">"Chiama n. <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Chiama casa"</string>
<string name="call_mobile" msgid="7502236805487609178">"Chiama cellulare"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chatta su ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chatta su Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Chatta"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Indirizzo"</string>
<string name="postal_street" msgid="8133143961580058972">"Indirizzo postale"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Casella postale"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Zona"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Provincia"</string>
<string name="postal_postcode" msgid="572136414136673751">"Codice postale"</string>
<string name="postal_country" msgid="7638264508416368690">"Paese"</string>
+ <string name="full_name" msgid="6602579550613988977">"Nome"</string>
<string name="name_given" msgid="1687286314106019813">"Nome fornito"</string>
<string name="name_family" msgid="3416695586119999058">"Cognome"</string>
<string name="name_prefix" msgid="59756378548779822">"Prefisso nome"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Cerca contatti"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Cerca tutti i contatti"</string>
<string name="take_photo" msgid="7496128293167402354">"Scatta foto"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Scatta nuova foto"</string>
<string name="pick_photo" msgid="448886509158039462">"Seleziona foto da galleria"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"L\'elenco contatti verrà aggiornato per rispecchiare il cambio di lingua."\n\n"Attendi..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Elenco contatti in fase di aggiornamento."\n\n"Attendi..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Contatti in fase di aggiornamento. "\n\n"Il processo di aggiornamento richiede circa <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB di memoria interna del telefono."\n\n"Scegli una delle seguenti opzioni:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Seleziona nuova foto da galleria"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Elenco contatti in fase di aggiornamento per l\'applicazione della modifica della lingua."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Elenco contatti in fase di aggiornamento."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Upgrade dei contatti in corso."\n\n"La procedura di upgrade richiede circa <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB di memoria interna."\n\n"Scegli una delle seguenti opzioni:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Disinstalla alcune applicazioni"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Riprova l\'aggiornamento"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Risultati di ricerca per: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Ricerca in corso..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Mostra selezionati"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Mostra tutto"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Seleziona tutto"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Deseleziona tutto"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"1 destinatario selezionato"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g> destinatari selezionati"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Nessun contatto selezionato."</string>
+ <string name="add_field" msgid="2384260056674995230">"Aggiungi un altro campo"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"tramite <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> tramite <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"preferiti"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Modifica contatto"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"non uniti"</item>
+ <item quantity="other" msgid="425683718017380845">"uniti da <xliff:g id="COUNT">%0$d</xliff:g> origini"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Altro"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Unisci contatti"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Unire il contatto corrente al contatto selezionato?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Modifica i contatti selezionati"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Passare alla modifica del contatto selezionato? Le informazioni inserite finora verranno copiate."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Copia nei miei contatti"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Directory <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Ricerca di tutti i contatti"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Directory"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Contatti"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Creazione di una copia personale"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Scegli elenco contatti"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Tutti i contatti"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Speciali"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Personalizzato"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Personalizza..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Contatti con numeri di telefono"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Contatto"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Definisci visualizzazione personalizzata"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Impostazioni"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Impostazioni"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Opzioni di visualizzazione"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Trova contatti"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Numero di telefono"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Aggiungi a contatti"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Chiudi"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Specifica un anno"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Contatto"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Caricamento…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Crea nuovo contatto"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Accedi a un account"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importa contatti da file"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Crea nuovo gruppo"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Crea nuovo gruppo]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Rinomina gruppo"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Elimina gruppo"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Eliminare il gruppo \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\"? I contatti non verranno eliminati."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Inserisci il nome del contatto prima di unirlo a un altro contatto."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Contatto unito"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Testo copiato"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Ignora modifiche"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Ignorare le modifiche?"</string>
+ <string name="discard" msgid="1234315037371251414">"Ignora"</string>
</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index d3ba350..5fa32b3 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"בחר קיצור דרך של איש קשר"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"בחר מספר להתקשר אליו"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"בחר מספר לשליחת הודעה"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"בחר איש קשר"</string>
<string name="starredList" msgid="4817256136413959463">"מסומן בכוכב"</string>
<string name="frequentList" msgid="7154768136473953056">"לעתים קרובות"</string>
<string name="strequentList" msgid="5640192862059373511">"מועדפים"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"מחק איש קשר"</string>
<string name="menu_call" msgid="3992595586042260618">"התקשר לאיש קשר"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"שלח הודעת טקסט לאיש קשר"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"שלח דוא\"ל"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"הצג כתובת במפה"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"הגדר מספר כברירת מחדל"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"הגדר דוא\"ל כברירת מחדל"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"הפרד"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"אנשי קשר נפרדים"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"שנה את שם הקבוצה"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"מחק את הקבוצה"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"חדש"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"הפרדת איש קשר"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"האם אתה בטוח שברצונך להפריד איש קשר יחיד זה לאנשי קשר מרובים: אחד לכל קבוצת פרטי קשר שצורפה אליו?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"הצטרף"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"מחיקת איש קשר זה תמחק מידע מחשבונות מרובים."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"איש קשר זה יימחק."</string>
<string name="menu_done" msgid="796017761764190697">"בוצע"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"חזור למצב קודם"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"ביטול"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"ערוך איש קשר"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"איש קשר חדש"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"פונטי"</string>
<string name="label_notes" msgid="8337354953278341042">"הערות"</string>
<string name="label_sip_address" msgid="124073911714324974">"שיחת אינטרנט"</string>
<string name="label_ringtone" msgid="8833166825330686244">"צלצול"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"שם פרטי ומשפחה"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"שם פונטי"</string>
<string name="ghostData_company" msgid="5414421120553765775">"חברה"</string>
<string name="ghostData_title" msgid="7496735200318496110">"כותרת"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"איש הקשר אינו קיים."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"צור איש קשר חדש"</string>
- <string name="selectLabel" msgid="4255424123394910733">"בחר תווית"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"טלפון"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"דוא\"ל"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"הודעה מיידית"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"כתובת דואר"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"כתובת"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"ארגון"</item>
<item msgid="7196592230748086755">"הערה"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"אין תמונות זמינות בטלפון."</string>
- <string name="attachToContact" msgid="8820530304406066714">"סמל של איש קשר"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"אין תמונות זמינות במחשב הלוח."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"אין תמונות זמינות בטלפון."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"תמונה של איש קשר"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"שם תווית מותאם אישית"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"הצג אפשרויות"</string>
- <string name="displayGroups" msgid="2278964020773993336">"הצג אפשרויות"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"שלח שיחות ישירות לדואר הקולי"</string>
<string name="default_ringtone" msgid="9099988849649827972">"ברירת מחדל"</string>
- <string name="changePicture" msgid="2943329047610967714">"שנה סמל"</string>
- <string name="removePicture" msgid="3041230993155966350">"הסר סמל"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"הסר תמונה"</string>
<string name="noContacts" msgid="8579310973261953559">"אין אנשי קשר."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"לא נמצאו אנשי קשר תואמים."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"אין אנשי קשר עם מספרי טלפון."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"איש הקשר נשמר."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"שגיאה, אין אפשרות לשמור שינויים באיש הקשר."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"מציג איש קשר אחד עם מספר טלפון"</item>
- <item quantity="other" msgid="6133262880804110289">"מציג <xliff:g id="COUNT">%d</xliff:g> אנשי קשר עם מספרי טלפון"</item>
+ <item quantity="one" msgid="3015357862286673986">"איש קשר אחד עם מספר טלפון"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g>אנשי קשר עם מספרי טלפון"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"אין אנשי קשר גלויים עם מספרי טלפון."</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"אין אנשי קשר עם מספרי טלפון"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"מציג איש קשר אחד"</item>
- <item quantity="other" msgid="2865867557378939630">"מציג <xliff:g id="COUNT">%d</xliff:g> אנשי קשר"</item>
+ <item quantity="one" msgid="3405747744700823280">"איש קשר 1"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> אנשי קשר"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"אין אנשי קשר גלויים"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"אין אנשי קשר"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"אין אנשי קשר גלויים"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"אין אנשי קשר המסומנים בכוכב"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"אין אנשי קשר ב<xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"נמצא איש קשר אחד"</item>
- <item quantity="other" msgid="7752927996850263152">"נמצאו <xliff:g id="COUNT">%d</xliff:g> אנשי קשר"</item>
+ <item quantity="one" msgid="5517063038754171134">"נמצא אחד"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> נמצאו"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"איש הקשר לא נמצא"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"נמצאו יותר מ-<xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"לא נמצא"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"איש קשר אחד"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> אנשי קשר"</item>
+ <item quantity="one" msgid="4826918429708286628">"נמצא אחד"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> נמצאו"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"אנשי קשר"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"מועדפים"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"אנשי קשר בכרטיס SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"אין לך אנשי קשר לתצוגה. (אם הוספת הרגע חשבון, יחלפו מספר דקות עד לסנכרון אנשי הקשר.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"אין לך אנשי קשר לתצוגה."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"אין לך אנשי קשר לתצוגה."\n\n"כדי להוסיף אנשי קשר, לחץ על "<font fgcolor="#ffffffff"><b>"תפריט"</b></font>" וגע ב:"\n" "\n<li><font fgcolor="#ffffffff"><b>"חשבונות"</b></font>" כדי להוסיף חשבון או להגדיר את תצורתו עם אנשי קשר שתוכל לסנכרן לטלפון"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"איש קשר חדש"</b></font>" כדי ליצור איש קשר חדש מאפס"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ייבא/ייצא"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"אין לך אנשי קשר לתצוגה. (אם הוספת הרגע חשבון, יחלפו מספר דקות עד לסנכרון אנשי הקשר.)"\n\n"כדי להוסיף אנשי קשר, לחץ על "<font fgcolor="#ffffffff"><b>"תפריט"</b></font>" וגע ב:"\n" "\n<li><font fgcolor="#ffffffff"><b>"חשבונות"</b></font>" כדי להוסיף חשבון או להגדיר את תצורתו עם אנשי קשר שתוכל לסנכרן לטלפון"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"הצג אפשרויות"</b></font>" כדי לשנות אילו אנשי קשר גלויים"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"אנשי קשר חדשים"</b></font>" כדי ליצור איש קשר חדש מאפס"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ייבא/ייצא"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"אין לך אנשי קשר לתצוגה."\n\n"כדי להוסיף אנשי קשר, לחץ על "<font fgcolor="#ffffffff"><b>"תפריט"</b></font>" וגע ב:"\n" "\n<li><font fgcolor="#ffffffff"><b>"חשבונות"</b></font>" כדי להוסיף חשבון או להגדיר את תצורתו עם אנשי קשר שתוכל לסנכרן לטלפון"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"איש קשר חדש"</b></font>" כדי ליצור איש קשר חדש מאפס"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ייבא/ייצא"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"אין לך אנשי קשר לתצוגה. (אם הוספת הרגע חשבון, יחלפו מספר דקות עד לסנכרון אנשי הקשר.)"\n\n"כדי להוסיף אנשי קשר, לחץ על "<font fgcolor="#ffffffff"><b>"תפריט"</b></font>" וגע ב:"\n" "\n<li><font fgcolor="#ffffffff"><b>"חשבונות"</b></font>" כדי להוסיף חשבון או להגדיר את תצורתו עם אנשי קשר שתוכל לסנכרן לטלפון"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"הצג אפשרויות"</b></font>" כדי לשנות אילו אנשי קשר גלויים"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"אנשי קשר חדשים"</b></font>" כדי ליצור איש קשר חדש מאפס"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ייבא/ייצא"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"אין לך אנשי קשר להצגה."\n\n"כדי להוסיף אנשי קשר, הקש על "<font fgcolor="#ffffffff"><b>"תפריט"</b></font>" וגע באפשרות:"\n" "\n<li><font fgcolor="#ffffffff"><b>"חשבונות"</b></font>" כדי להוסיף או להגדיר חשבון עם אנשי קשר שאותם תוכל לסנכרן למחשב הלוח"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"איש קשר חדש"</b></font>" כדי ליצור איש קשר חדש לגמרי"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ייבוא/ייצוא"</b></font>" כדי לייבא אנשי קשר מכרטיס ה-SIM או כרטיס ה-SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"אין לך אנשי קשר להצגה."\n\n"כדי להוסיף אנשי קשר, הקש על "<font fgcolor="#ffffffff"><b>"תפריט"</b></font>" וגע באפשרות:"\n" "\n<li><font fgcolor="#ffffffff"><b>"חשבונות"</b></font>" כדי להוסיף או להגדיר חשבון עם אנשי קשר שאותם תוכל לסנכרן לטלפון"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"איש קשר חדש"</b></font>" כדי ליצור איש קשר חדש"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ייבוא/ייצוא"</b></font>" כדי לייבא אנשי קשר מכרטיס ה-SIM או כרטיס ה-SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"אין לך אנשי קשר לתצוגה. (אם הוספת הרגע חשבון, דרושות מספר דקות לסנכרון אנשי הקשר.)"\n\n"כדי להוסיף אנשי קשר, הקש על "<font fgcolor="#ffffffff"><b>"תפריט"</b></font>" וגע באפשרות:"\n" "\n<li><font fgcolor="#ffffffff"><b>"חשבונות"</b></font>" כדי להוסיף או להגדיר חשבון עם אנשי קשר שתוכל לסנכרן למחשב הלוח"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"אפשרויות תצוגה"</b></font>" כדי לשנות את אנשי הקשר הגלויים"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"איש קשר חדש"</b></font>" כדי ליצור איש קשר חדש לגמרי"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ייבוא/ייצוא"</b></font>" כדי לייבא אנשי קשר מכרטיס ה-SIM או כרטיס ה-SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"אין לך אנשי קשר לתצוגה. (אם הוספת הרגע חשבון, דרושות מספר דקות לסנכרון אנשי הקשר.)"\n\n"כדי להוסיף אנשי קשר, הקש על "<font fgcolor="#ffffffff"><b>"תפריט"</b></font>" וגע באפשרות:"\n" "\n<li><font fgcolor="#ffffffff"><b>"חשבונות"</b></font>" כדי להוסיף או להגדיר חשבון עם אנשי קשר שתוכל לסנכרן לטלפון"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"אפשרויות תצוגה"</b></font>" כדי לשנות את אנשי הקשר הגלויים"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"איש קשר חדש"</b></font>" כדי ליצור איש קשר חדש לגמרי"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ייבוא/ייצוא"</b></font>" כדי לייבא אנשי קשר מכרטיס ה-SIM או כרטיס ה-SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"אין לך אנשי קשר להצגה."\n\n"כדי להוסיף אנשי קשר, הקש על "<font fgcolor="#ffffffff"><b>"תפריט"</b></font>" וגע באפשרות:"\n" "\n<li><font fgcolor="#ffffffff"><b>"חשבונות"</b></font>" כדי להוסיף או להגדיר חשבון עם אנשי קשר שאותם תוכל לסנכרן למחשב הלוח"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"איש קשר חדש"</b></font>" כדי ליצור איש קשר חדש לגמרי"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ייבוא/ייצוא"</b></font>" כדי לייבא אנשי קשר מכרטיס ה-SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"אין לך אנשי קשר להצגה."\n\n"כדי להוסיף אנשי קשר, הקש על "<font fgcolor="#ffffffff"><b>"תפריט"</b></font>" וגע באפשרות:"\n" "\n<li><font fgcolor="#ffffffff"><b>"חשבונות"</b></font>" כדי להוסיף או להגדיר חשבון עם אנשי קשר שאותם תוכל לסנכרן לטלפון"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"איש קשר חדש"</b></font>" כדי ליצור איש קשר חדש לגמרי"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ייבוא/ייצוא"</b></font>" כדי לייבא אנשי קשר מכרטיס ה-SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"אין לך אנשי קשר לתצוגה. (אם הוספת הרגע חשבון, דרושות מספר דקות לסנכרון אנשי הקשר.)"\n\n"כדי להוסיף אנשי קשר, הקש על "<font fgcolor="#ffffffff"><b>"תפריט"</b></font>" וגע באפשרות:"\n" "\n<li><font fgcolor="#ffffffff"><b>"חשבונות"</b></font>" כדי להוסיף או להגדיר חשבון עם אנשי קשר שתוכל לסנכרן למחשב הלוח"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"אפשרויות תצוגה"</b></font>" כדי לשנות את אנשי הקשר הגלויים"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"איש קשר חדש"</b></font>" כדי ליצור איש קשר חדש לגמרי"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ייבוא/ייצוא"</b></font>" כדי לייבא אנשי קשר מכרטיס ה-SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"אין לך אנשי קשר לתצוגה. (אם הוספת הרגע חשבון, דרושות מספר דקות לסנכרון אנשי הקשר.)"\n\n"כדי להוסיף אנשי קשר, הקש על "<font fgcolor="#ffffffff"><b>"תפריט"</b></font>" וגע באפשרות:"\n" "\n<li><font fgcolor="#ffffffff"><b>"חשבונות"</b></font>" כדי להוסיף או להגדיר חשבון עם אנשי קשר שתוכל לסנכרן לטלפון"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"אפשרויות תצוגה"</b></font>" כדי לשנות את אנשי הקשר הגלויים"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"איש קשר חדש"</b></font>" כדי ליצור איש קשר חדש לגמרי"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ייבוא/ייצוא"</b></font>" כדי לייבא אנשי קשר מכרטיס ה-SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"אין לך מועדפים."\n\n"כדי להוסיף איש קשר לרשימת המועדפים:"\n\n" "<li>"גע בכרטיסייה "<b>"אנשי קשר"</b>" "\n</li>" "\n<li>"גע באיש הקשר שברצונך להוסיף למועדפים"\n</li>" "\n<li>"גע בכוכב שליד השם של איש הקשר"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"כל אנשי הקשר"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"מסומן בכוכב"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"התקשר חזרה"</string>
<string name="callAgain" msgid="3197312117049874778">"התקשר שוב"</string>
<string name="returnCall" msgid="8171961914203617813">"התקשר בחזרה"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> דק\' <xliff:g id="SECONDS">%2$s</xliff:g> שנ\'"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> דק\' <xliff:g id="SECONDS">%s</xliff:g> שנ\'"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"מתקשר לעיתים קרובות"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"הוסף איש קשר"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"להוסיף את \"<xliff:g id="EMAIL">%s</xliff:g>\" לאנשי הקשר?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"הכל"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"אחד"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"שתיים"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"שלוש"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"סריקת אמצעי אחסון מסוג USB נכשלה (סיבה: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"נכשלה סריקת כרטיס SD (סיבה: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"שגיאת קלט/פלט"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"אין מספיק זיכרון (ייתכן שהקובץ גדול מדי)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"נכשל ניתוח vCard עקב סיבה לא צפויה"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"נכשל ניתוח vCard אף שנראה שהוא בפורמט תקין, כיוון שהיישום הנוכחי אינו תומך בו"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"לא נמצא קובץ vCard באמצעי אחסון מסוג USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"לא נמצא קובץ vCard בכרטיס ה-SD"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"הפורמט אינו נתמך."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"נכשל ייבוא vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"לא נמצא קובץ vCard באמצעי אחסון מסוג USB"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"לא נמצא קובץ vCard בכרטיס ה-SD"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"נכשל איסוף מידע-על מקובצי vCard נתונים."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"נכשל הייבוא של קובץ אחד או יותר (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"שגיאה לא ידועה"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"בחר קובץ vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"קורא vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"קורא קובצי vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"קריאת נתוני vCard נכשלה"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> מתוך <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> אנשי קשר"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> מתוך <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> קבצים"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"מעביר קובצי vCard לקובץ שמור לאחסון מקומי זמני"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"המייבא מעביר לקובץ השמור קובצי vCard באחסון זמני מקומי. הייבוא בפועל יתחיל בקרוב."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"מייבא <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"מייבא את <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"נכשלה קריאת נתוני vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"קריאת נתוני vCard בוטלה"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"הייבוא של ה-vCard <xliff:g id="FILENAME">%s</xliff:g> הסתיים"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"הייבוא של <xliff:g id="FILENAME">%s</xliff:g> בוטל"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"הייבוא של <xliff:g id="FILENAME">%s</xliff:g> יתבצע תוך זמן קצר."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"יבוא הקובץ יתבצע תוך זמן קצר."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"בקשת ייבוא vCard נדחתה. נסה שוב מאוחר יותר."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"הייצוא של <xliff:g id="FILENAME">%s</xliff:g> יתבצע תוך זמן קצר."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"בקשת ייצוא vCard נדחתה. נסה שוב מאוחר יותר."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"איש קשר"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"אשר ייצוא"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"האם אתה בטוח שברצונך לייצא את רשימת אנשי הקשר אל \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"נכשל ייצוא הנתונים של איש הקשר"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"קובצי vCard רבים מדי באמצעי אחסון מסוג USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"קובצי vCard רבים מדי בכרטיס ה-SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"שם הקובץ הדרוש ארוך מדי (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"הייצוא של <xliff:g id="FILENAME">%s</xliff:g> הסתיים"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"הייצוא של <xliff:g id="FILENAME">%s</xliff:g> בוטל"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"מייצא נתונים של אנשי קשר"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"מייצא נתוני אנשי קשר אל \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"אין אפשרות לאתחל את המייצא: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"אירעה שגיאה במהלך ייצוא: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"נכשלה השגת מידע על מסד נתונים"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"אין איש קשר שניתן לייצא. אם יש לך אנשי קשר בפועל בטלפון, ייתכן שיש איסור של ספק נתונים כלשהו לייצא את כל אנשי הקשר מחוץ לטלפון."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"אין אנשי קשר לייצוא. אם יש אנשי קשר בפועל במחשב הלוח, ייתכן שהייצוא של כל אנשי הקשר נאסר מחוץ למחשב הלוח על ידי ספקי נתונים מסוימים."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"אין אנשי קשר לייצוא. אם יש אנשי קשר בפועל בטלפון, ייתכן שהייצוא של כל אנשי הקשר נאסר מחוץ לטלפון על ידי ספקי נתונים מסוימים."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"מחבר ה- vCard אינו מאותחל כראוי"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"לא הייתה אפשרות לפתוח את \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> מתוך <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> אנשי קשר"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"לא הייתה אפשרות לפתוח את \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> מתוך <xliff:g id="TOTAL_NUMBER">%s</xliff:g> אנשי קשר"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"מבטל יבוא של vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"האם אתה בטוח שברצונך לבטל את הייבוא של <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"מבטל יצוא של vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"האם אתה בטוח שברצונך לבטל את הייצוא של <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"הביטול של יבוא/יצוא vCard נכשל"</string>
<string name="search_settings_description" msgid="2675223022992445813">"השמות של אנשי הקשר"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"הוסף השהיה של 2 שניות"</string>
<string name="add_wait" msgid="3360818652790319634">"הוסף המתנה"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"לא נמצא יישום שיכול לטפל בפעולה זו"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"זכור בחירה זו"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"לא ידוע"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"אין נתונים"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"נקה ברירות מחדל"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"ברירות מחדל שהוגדרו לאיש קשר זה:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"נקה"</string>
<string name="menu_accounts" msgid="8499114602017077970">"חשבונות"</string>
<string name="menu_import_export" msgid="3765725645491577190">"ייבוא/ייצוא"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"ייבא/יצא אנשי קשר"</string>
- <string name="menu_share" msgid="943789700636542260">"שתף"</string>
+ <string name="menu_share" msgid="8746849630474240344">"שתף איש קשר"</string>
<string name="share_via" msgid="563121028023030093">"שתף איש קשר באמצעות"</string>
<string name="share_error" msgid="4374508848981697170">"אין אפשרות לשתף איש קשר זה."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"שם"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"ארגון"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"אתר"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"אירוע"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"קשר"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"קבוצות"</string>
<string name="type_short_home" msgid="7770424864090605384">"ב"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"נ"</string>
<string name="type_short_work" msgid="4925330752504537861">"ע"</string>
<string name="type_short_pager" msgid="2613818970827594238">"ז"</string>
<string name="type_short_other" msgid="5669407180177236769">"א"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"איש קשר זה הוא לקריאה בלבד"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"עוד"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"שם ראשי"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"צור איש קשר בחשבון"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"הסר קבוצת סנכרון"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"הוסף קבוצת סנכרון"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"כל אנשי הקשר האחרים"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"כל אנשי הקשר"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"הסרת \'<xliff:g id="GROUP">%s</xliff:g>\' מהסנכרון תסיר מהסנכרון גם אנשי קשר שאינם מקובצים."</string>
- <string name="account_phone" msgid="3682950835276226870">"טלפון בלבד, לא מסונכרן"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"מחשב לוח בלבד, לא מסונכרן"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"טלפון בלבד, לא מסונכרן"</string>
<string name="call_custom" msgid="7756571794763171802">"התקשר אל <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"התקשר לבית"</string>
<string name="call_mobile" msgid="7502236805487609178">"התקשר לנייד"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"שוחח בצ\'אט באמצעות ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"שוחח בצ\'אט באמצעות Jabber"</string>
<string name="chat" msgid="9025361898797412245">"שוחח בצ\'אט"</string>
+ <string name="postal_address" msgid="8765560217149624536">"כתובת"</string>
<string name="postal_street" msgid="8133143961580058972">"רחוב"</string>
<string name="postal_pobox" msgid="4431938829180269821">"תא דואר"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"שכונה"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"מדינה"</string>
<string name="postal_postcode" msgid="572136414136673751">"מיקוד"</string>
<string name="postal_country" msgid="7638264508416368690">"ארץ"</string>
+ <string name="full_name" msgid="6602579550613988977">"שם"</string>
<string name="name_given" msgid="1687286314106019813">"שם פרטי"</string>
<string name="name_family" msgid="3416695586119999058">"שם משפחה"</string>
<string name="name_prefix" msgid="59756378548779822">"קידומת לשם"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"חפש אנשי קשר"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"חפש בכל אנשי הקשר"</string>
<string name="take_photo" msgid="7496128293167402354">"צלם תמונה"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"צלם תמונה חדשה"</string>
<string name="pick_photo" msgid="448886509158039462">"בחר תמונה מהגלריה"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"רשימת אנשי הקשר מתעדכנת כדי לשקף את שינוי השפה."\n\n"נא המתן..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"רשימת אנשי הקשר מתעדכנת."\n\n"נא המתן..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"מתבצע שדרוג של אנשי הקשר. "\n\n"תהליך השדרוג דורש <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>Mb בקירוב של שטח אחסון פנימי בטלפון."\n\n"בחר אחת מהאפשרויות הבאות:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"בחר תמונה חדשה מהגלריה"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"רשימת הקשר מתעדכנת כדי לשקף את שינוי השפה."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"רשימת הקשר מתעדכנת."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"אנשי הקשר נמצאים בתהליך של שדרוג. "\n\n"תהליך השדרוג דורש כ-Mb <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g>של אחסון פנימי."\n\n"בחר באחת מהאפשרויות הבאות:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"הסר התקנה של יישומים מסוימים"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"נסה שוב לשדרג"</string>
- <string name="search_results_for" msgid="8705490885073188513">"תוצאות חיפוש עבור: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"מחפש..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"הצג פריטים שנבחרו"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"הצג הכל"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"בחר הכל"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"בטל את הבחירה של הכל"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"לא נבחרו אנשי קשר."</string>
+ <string name="add_field" msgid="2384260056674995230">"הוסף שדה נוסף"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"דרך <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> דרך <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"מועדף"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"ערוך איש קשר"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"לא מוזגו"</item>
+ <item quantity="other" msgid="425683718017380845">"מוזגו מ-<xliff:g id="COUNT">%0$d</xliff:g> מקורות"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"אחר"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"צרף אנשי קשר"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"לצרף את איש הקשר הנוכחי לאיש הקשר שנבחר?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"ערוך את אנשי הקשר שנבחרו"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"לעבור לעריכה של איש הקשר שנבחר? המידע שהזנת עד עכשיו יועתק."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"העתק לאנשי הקשר שלי"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"הספריה <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"מחפש בכל אנשי הקשר"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"ספריה"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"אנשי קשר"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"יצירת עותק אישי"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"בחר רשימת אנשי קשר"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"כל אנשי הקשר"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"מסומן בכוכב"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"מותאם אישית"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"התאם אישית..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"אנשי קשר עם מספרי טלפון"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"איש קשר"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"הגדר תצוגה מותאמת אישית"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"הגדרות"</string>
+ <string name="menu_settings" msgid="377929915873428211">"הגדרות"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"אפשרויות תצוגה"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"מצא אנשי קשר"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"מספר טלפון"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"הוסף לאנשי הקשר"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"סגור"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"הוסף שנה"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"איש קשר"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"טוען…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"צור איש קשר חדש"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"היכנס לחשבון"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"יבא אנשי קשר מקובץ"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"צור קבוצה חדשה"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[צור קבוצה חדשה]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"שנה שם קבוצה"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"מחק קבוצה"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"האם אתה בטוח שברצונך למחוק את הקבוצה \'<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\'? (אנשי הקשר עצמם לא יימחקו)."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"הזן שם של איש קשר לפני צירוף לאיש קשר אחר."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"איש קשר מצורף"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"טקסט שהועתק"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"מחק שינויים"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"האם אתה רוצה למחוק את השינויים?"</string>
+ <string name="discard" msgid="1234315037371251414">"מחק"</string>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 35f9d70..946b813 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"連絡先ショートカットを選択"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"発信する番号の選択"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"メッセージを送る番号の選択"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"連絡先の選択"</string>
<string name="starredList" msgid="4817256136413959463">"スター付き"</string>
<string name="frequentList" msgid="7154768136473953056">"よく使う連絡先"</string>
<string name="strequentList" msgid="5640192862059373511">"お気に入り"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"連絡先を削除"</string>
<string name="menu_call" msgid="3992595586042260618">"連絡先に発信"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"連絡先にSMS"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"メールを送信"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"地図でみる"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"メインの番号に設定する"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"既定のメールに設定"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"分割"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"連絡先を分割しました"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"グループ名を変更"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"グループを削除"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"新規"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"連絡先を分割"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"この連絡先を複数の連絡先に分割してもよろしいですか?統合前の連絡先情報にそれぞれ分割されます。"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"統合"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"この連絡先を削除すると、複数のアカウント情報が削除されます。"</string>
<string name="deleteConfirmation" msgid="811706994761610640">"この連絡先を削除します。"</string>
<string name="menu_done" msgid="796017761764190697">"完了"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"キャンセル"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"キャンセル"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"連絡先を編集"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"連絡先を新規登録"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"読み"</string>
<string name="label_notes" msgid="8337354953278341042">"メモ"</string>
<string name="label_sip_address" msgid="124073911714324974">"インターネット通話"</string>
<string name="label_ringtone" msgid="8833166825330686244">"着信音"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"名前"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"よみがな"</string>
<string name="ghostData_company" msgid="5414421120553765775">"会社"</string>
<string name="ghostData_title" msgid="7496735200318496110">"役職"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"この連絡先は削除されました"</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"連絡先を新規登録"</string>
- <string name="selectLabel" msgid="4255424123394910733">"選択してください"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"電話番号"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"メール"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"チャット"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"住所"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"住所"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"所属"</item>
<item msgid="7196592230748086755">"メモ"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"画像がありません。"</string>
- <string name="attachToContact" msgid="8820530304406066714">"連絡先のアイコン"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"タブレット内に写真がありません。"</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"画像がありません。"</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"連絡先の写真"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"新しいラベル名"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"表示オプション"</string>
- <string name="displayGroups" msgid="2278964020773993336">"表示オプション"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"ボイスメールに自動転送する"</string>
<string name="default_ringtone" msgid="9099988849649827972">"プリセット着信音"</string>
- <string name="changePicture" msgid="2943329047610967714">"アイコンを変更"</string>
- <string name="removePicture" msgid="3041230993155966350">"アイコンを削除"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"写真を削除"</string>
<string name="noContacts" msgid="8579310973261953559">"連絡先がありません。"</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"一致する連絡先が見つかりません。"</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"電話番号付きの連絡先はありません。"</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"連絡先を保存しました。"</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"エラー、連絡先の変更を保存できません。"</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"電話番号のある連絡先1件を表示"</item>
- <item quantity="other" msgid="6133262880804110289">"電話番号のある連絡先<xliff:g id="COUNT">%d</xliff:g>件を表示"</item>
+ <item quantity="one" msgid="3015357862286673986">"電話番号のある連絡先1件"</item>
+ <item quantity="other" msgid="3299954047880968205">"電話番号のある連絡先<xliff:g id="COUNT">%d</xliff:g>件"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"表示可能な電話番号付きの連絡先はありません"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"電話番号付きの連絡先はありません"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"連絡先を1件表示"</item>
- <item quantity="other" msgid="2865867557378939630">"連絡先を<xliff:g id="COUNT">%d</xliff:g>件表示"</item>
+ <item quantity="one" msgid="3405747744700823280">"連絡先1件"</item>
+ <item quantity="other" msgid="3578469907265375314">"連絡先<xliff:g id="COUNT">%d</xliff:g>件"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"表示可能な連絡先はありません"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"連絡先はありません"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"表示可能な連絡先はありません"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"スター付きの連絡先はありません"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"<xliff:g id="NAME">%s</xliff:g>の連絡先はありません"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"連絡先が1件あります"</item>
- <item quantity="other" msgid="7752927996850263152">"連絡先が<xliff:g id="COUNT">%d</xliff:g>件あります"</item>
+ <item quantity="one" msgid="5517063038754171134">"1件見つかりました"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g>件見つかりました"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"連絡先が見つかりません"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"<xliff:g id="COUNT">%d</xliff:g>件以上見つかりました"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"見つかりませんでした"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"連絡先1件"</item>
- <item quantity="other" msgid="5660384247071761844">"連絡先<xliff:g id="COUNT">%d</xliff:g>件"</item>
+ <item quantity="one" msgid="4826918429708286628">"1件見つかりました"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g>件見つかりました"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"連絡先"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"お気入り"</string>
@@ -160,11 +161,15 @@
<string name="simContacts_title" msgid="27341688347689769">"SIMカードの連絡先"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"表示する連絡先がありません(アカウントを追加した直後の場合は、連絡先の同期に数分かかることがあります)。"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"表示する連絡先がありません。"</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"表示できる連絡先がありません。"\n\n"連絡先を追加するには、まず"<font fgcolor="#ffffffff"><b>"MENU"</b></font>"キーを押し: "\n" "\n<li>"電話との同期が可能な連絡先のアカウントを追加または設定する場合は["<font fgcolor="#ffffffff"><b>"アカウント"</b></font>"]をタップします"\n</li>" "\n<li>"新しい連絡先を一から作成する場合は["<font fgcolor="#ffffffff"><b>"連絡先を新規登録"</b></font>"]をタップします"\n</li>" "\n<li>"["<font fgcolor="#ffffffff"><b>"インポート/エクスポート"</b></font>"]"\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"表示できる連絡先がありません(アカウントを追加した場合は、連絡先が同期されるまでに数分かかることがあります)。"\n\n"連絡先を追加するには、まず"<font fgcolor="#ffffffff"><b>"MENU"</b></font>"キーを押し: "\n" "\n<li>"電話との同期が可能な連絡先のアカウントを追加または設定する場合は["<font fgcolor="#ffffffff"><b>"アカウント"</b></font>"]をタップします"\n</li>" "\n<li>"表示される連絡先を変更するには["<font fgcolor="#ffffffff"><b>"表示オプション"</b></font>"]をタップします"\n</li>" "\n<li>"新しい連絡先を最初から作成する場合は["<font fgcolor="#ffffffff"><b>"連絡先を新規登録"</b></font>"]をタップします"\n</li>" "\n<li>"["<font fgcolor="#ffffffff"><b>"インポート/エクスポート"</b></font>"]"\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"表示できる連絡先がありません。"\n\n"連絡先を追加するには、まず"<font fgcolor="#ffffffff"><b>"MENU"</b></font>"キーを押し: "\n" "\n<li>"電話との同期が可能な連絡先のアカウントを追加または設定する場合は["<font fgcolor="#ffffffff"><b>"アカウント"</b></font>"]をタップします"\n</li>" "\n<li>"新しい連絡先を最初から作成する場合は["<font fgcolor="#ffffffff"><b>"連絡先を新規登録"</b></font>"]をタップします"\n</li>" "\n<li>"["<font fgcolor="#ffffffff"><b>"インポート/エクスポート"</b></font>"]"\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"表示できる連絡先がありません(アカウントを追加した場合は、連絡先が同期されるまでに数分かかることがあります)。"\n\n"連絡先を追加するには、まず"<font fgcolor="#ffffffff"><b>"MENU"</b></font>"キーを押し: "\n" "\n<li>"電話との同期が可能な連絡先のアカウントを追加または設定する場合は["<font fgcolor="#ffffffff"><b>"アカウント"</b></font>"]をタップします"\n</li>" "\n<li>"表示される連絡先を変更するには["<font fgcolor="#ffffffff"><b>"表示オプション"</b></font>"]をタップします"\n</li>" "\n<li>"新しい連絡先を最初から作成する場合は["<font fgcolor="#ffffffff"><b>"連絡先を新規登録"</b></font>"]をタップします"\n</li>" "\n<li>"["<font fgcolor="#ffffffff"><b>"インポート/エクスポート"</b></font>"]"\n</li></string>
- <string name="noFavoritesHelpText" msgid="3744655776704833277">"お気に入りはありません。"\n\n"お気に入りのリストに連絡先を追加するには: "\n\n" "<li>"["<b>"連絡先"</b>"]タブをタップします"\n</li>" "\n<li>"お気に入りに追加する連絡先をタップします"\n</li>" "\n<li>"連絡先名の横にあるスターをタップします"\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"表示できる連絡先がありません。"\n\n"連絡先を追加するには、まず"<font fgcolor="#ffffffff"><b>"MENU"</b></font>"キーを押し:"\n\n<li>"タブレットと同期する連絡先のアカウントを追加または設定する場合は["<font fgcolor="#ffffffff"><b>"アカウント"</b></font>"]をタップします"\n</li>\n<li>"新しい連絡先を最初から作成する場合は["<font fgcolor="#ffffffff"><b>"連絡先を新規登録"</b></font>"]をタップします"\n</li>" "\n<li>"SIMまたはSDカードから連絡先をインポートする場合は["<font fgcolor="#ffffffff"><b>"インポート/エクスポート"</b></font>"]をタップします"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"表示できる連絡先がありません。"\n\n"連絡先を追加するには、まず"<font fgcolor="#ffffffff"><b>"MENU"</b></font>"キーを押し:"\n\n<li>"電話との同期が可能な連絡先のアカウントを追加または設定する場合は["<font fgcolor="#ffffffff"><b>"アカウント"</b></font>"]をタップします"\n</li>\n<li>"新しい連絡先を最初から作成する場合は["<font fgcolor="#ffffffff"><b>"連絡先を新規登録"</b></font>"]をタップします"\n</li>" "\n<li>"SIMまたはSDカードから連絡先をインポートする場合は["<font fgcolor="#ffffffff"><b>"インポート/エクスポート"</b></font>"]をタップします"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"表示できる連絡先がありません(アカウントを追加した場合は、連絡先が同期されるまでに数分かかることがあります)。"\n\n"連絡先を追加するには、まず"<font fgcolor="#ffffffff"><b>"MENU"</b></font>"キーを押し:"\n\n<li>"タブレットと同期する連絡先のアカウントを追加または設定する場合は["<font fgcolor="#ffffffff"><b>"アカウント"</b></font>"]をタップします"\n</li>\n<li>"表示される連絡先を変更するには["<font fgcolor="#ffffffff"><b>"表示オプション"</b></font>"]をタップします"\n</li>" "\n<li>"新しい連絡先を最初から作成する場合は["<font fgcolor="#ffffffff"><b>"連絡先を新規登録"</b></font>"]をタップします"\n</li>" "\n<li>"SIMまたはSDカードから連絡先をインポートする場合は["<font fgcolor="#ffffffff"><b>"インポート/エクスポート"</b></font>"]をタップします"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"表示できる連絡先がありません(アカウントを追加した場合は、連絡先が同期されるまでに数分かかることがあります)。"\n\n"連絡先を追加するには、まず"<font fgcolor="#ffffffff"><b>"MENU"</b></font>"キーを押し:"\n\n<li>"電話との同期が可能な連絡先のアカウントを追加または設定する場合は["<font fgcolor="#ffffffff"><b>"アカウント"</b></font>"]をタップします"\n</li>\n<li>"表示される連絡先を変更するには["<font fgcolor="#ffffffff"><b>"表示オプション"</b></font>"]をタップします"\n</li>" "\n<li>"新しい連絡先を最初から作成する場合は["<font fgcolor="#ffffffff"><b>"連絡先を新規登録"</b></font>"]をタップします"\n</li>" "\n<li>"SIMまたはSDカードから連絡先をインポートする場合は["<font fgcolor="#ffffffff"><b>"インポート/エクスポート"</b></font>"]をタップします"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"表示できる連絡先がありません。"\n\n"連絡先を追加するには、まず"<font fgcolor="#ffffffff"><b>"MENU"</b></font>"キーを押し:"\n\n<li>"タブレットと同期する連絡先のアカウントを追加または設定する場合は["<font fgcolor="#ffffffff"><b>"アカウント"</b></font>"]をタップします"\n</li>\n<li>"新しい連絡先を最初から作成する場合は["<font fgcolor="#ffffffff"><b>"連絡先を新規登録"</b></font>"]をタップします"\n</li>" "\n<li>"SDカードから連絡先をインポートする場合は["<font fgcolor="#ffffffff"><b>"インポート/エクスポート"</b></font>"]をタップします"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"表示できる連絡先がありません。"\n\n"連絡先を追加するには、まず"<font fgcolor="#ffffffff"><b>"MENU"</b></font>"キーを押し:"\n\n<li>"電話との同期が可能な連絡先のアカウントを追加または設定する場合は["<font fgcolor="#ffffffff"><b>"アカウント"</b></font>"]をタップします"\n</li>\n<li>"新しい連絡先を最初から作成する場合は["<font fgcolor="#ffffffff"><b>"連絡先を新規登録"</b></font>"]をタップします"\n</li>" "\n<li>"SDカードから連絡先をインポートする場合は["<font fgcolor="#ffffffff"><b>"インポート/エクスポート"</b></font>"]をタップします"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"表示できる連絡先がありません(アカウントを追加した場合は、連絡先が同期されるまでに数分かかることがあります)。"\n\n"連絡先を追加するには、まず"<font fgcolor="#ffffffff"><b>"MENU"</b></font>"キーを押し:"\n\n<li>"タブレットと同期する連絡先のアカウントを追加または設定する場合は["<font fgcolor="#ffffffff"><b>"アカウント"</b></font>"]をタップします"\n</li>\n<li>"表示される連絡先を変更するには["<font fgcolor="#ffffffff"><b>"表示オプション"</b></font>"]をタップします"\n</li>" "\n<li>"新しい連絡先を最初から作成する場合は["<font fgcolor="#ffffffff"><b>"連絡先を新規登録"</b></font>"]をタップします"\n</li>" "\n<li>"SDカードから連絡先をインポートする場合は["<font fgcolor="#ffffffff"><b>"インポート/エクスポート"</b></font>"]をタップします"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"表示できる連絡先がありません(アカウントを追加した場合は、連絡先が同期されるまでに数分かかることがあります)。"\n\n"連絡先を追加するには、まず"<font fgcolor="#ffffffff"><b>"MENU"</b></font>"キーを押し:"\n\n<li>"電話との同期が可能な連絡先のアカウントを追加または設定する場合は["<font fgcolor="#ffffffff"><b>"アカウント"</b></font>"]をタップします"\n</li>\n<li>"表示される連絡先を変更するには["<font fgcolor="#ffffffff"><b>"表示オプション"</b></font>"]をタップします"\n</li>" "\n<li>"新しい連絡先を最初から作成する場合は["<font fgcolor="#ffffffff"><b>"連絡先を新規登録"</b></font>"]をタップします"\n</li>" "\n<li>"SDカードから連絡先をインポートする場合は["<font fgcolor="#ffffffff"><b>"インポート/エクスポート"</b></font>"]をタップします"\n</li></string>
+ <string name="noFavoritesHelpText" msgid="3744655776704833277">"お気に入りはありません。"\n\n"お気に入りのリストに連絡先を追加するには: "\n\n<li>"["<b>"連絡先"</b>"]タブをタップします"\n</li>" "\n<li>"お気に入りに追加する連絡先をタップします"\n</li>" "\n<li>"連絡先名の横にあるスターをタップします"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"すべての連絡先"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"スター付き"</string>
<string name="liveFolder_phones_label" msgid="1709786878793436245">"電話"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"コールバック"</string>
<string name="callAgain" msgid="3197312117049874778">"再発信"</string>
<string name="returnCall" msgid="8171961914203617813">"発信"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g>分<xliff:g id="SECONDS">%2$s</xliff:g>秒"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g>分<xliff:g id="SECONDS">%s</xliff:g>秒"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"よく使う連絡先"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"連絡先を追加"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"<xliff:g id="EMAIL">%s</xliff:g> を連絡先に追加しますか?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"すべて"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"1"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"2"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"3"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"USBストレージをスキャンできませんでした(理由: 「<xliff:g id="FAIL_REASON">%s</xliff:g>」)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"SDカードのスキャンに失敗しました(理由: <xliff:g id="FAIL_REASON">%s</xliff:g>)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"送受信エラー"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"メモリが不足しています(ファイルが大きすぎる可能性があります)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"予期しない理由によりvCardの解析に失敗しました"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"vCardの解析に失敗しました。正しいフォーマットですが、現在サポートされていません。"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"USBストレージ内にvCardファイルが見つかりません"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"SDカードでvCardファイルが見つかりません"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"このフォーマットはサポートされません。"</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"vCardをインポートできませんでした。"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"USBストレージ内にvCardファイルが見つかりません"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"SDカード内にvCardファイルが見つかりません"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"指定されたvCardファイルのメタ情報を取得できませんでした。"</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"1つ以上のファイルをインポートできませんでした(%s)。"</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"不明なエラー"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"vCardファイルの選択"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"vCardを読み取り中"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"vCardファイルを読み取り中"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"vCardデータの読み取りに失敗しました"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>件の連絡先"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>件のファイル"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"vCardをローカル一時ストレージにキャッシュしています"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"vCardをローカル一時ストレージにキャッシュしています。間もなくインポート処理を開始します。"</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>件(<xliff:g id="NAME">%s</xliff:g>)をインポート中"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"<xliff:g id="NAME">%s</xliff:g>をインポート中"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"vCardデータを読み取れませんでした"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"vCardの読み取りがキャンセルされました"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"vCard<xliff:g id="FILENAME">%s</xliff:g>のインポートが終了しました"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"<xliff:g id="FILENAME">%s</xliff:g>のインポートはキャンセルされました"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g>はまもなくインポートされます。"</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"ファイルはまもなくインポートされます。"</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"vCardのインポートリクエストが拒否されました。しばらくしてからもう一度お試しください。"</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g>はまもなくエクスポートされます。"</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"vCardのエクスポートリクエストが拒否されました。しばらくしてからもう一度お試しください。"</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"連絡先"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"エクスポートの確認"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"連絡先リストを「<xliff:g id="VCARD_FILENAME">%s</xliff:g>」にエクスポートしますか?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"連絡先データのエクスポートに失敗しました"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"USBストレージ内のvCardファイルが多すぎます"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"SDカードのvCardファイルが多すぎます"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"指定したファイル名が長すぎます(「<xliff:g id="FILENAME">%s</xliff:g>」)"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"<xliff:g id="FILENAME">%s</xliff:g>のエクスポートが終了しました"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"<xliff:g id="FILENAME">%s</xliff:g>のエクスポートはキャンセルされました"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"連絡先データのエクスポート"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"連絡先データを\"<xliff:g id="FILE_NAME">%s</xliff:g>\"にエクスポートしています"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"エクスポータを初期化できませんでした: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"エクスポート中にエラーが発生しました: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"データベース情報の取得に失敗しました"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"エクスポートできる連絡先がありません。携帯電話に連絡先を登録している場合、データプロバイダが外部への連絡先のエクスポートをすべて禁止している可能性があります。"</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"エクスポートできる連絡先がありません。タブレットに連絡先を登録している場合、データプロバイダが外部への連絡先のエクスポートをすべて禁止している可能性があります。"</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"エクスポートできる連絡先がありません。携帯端末に連絡先を登録している場合、データプロバイダが外部への連絡先のエクスポートをすべて禁止している可能性があります。"</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"vCardコンポーザーが正しく初期化されていません"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"\"<xliff:g id="FILE_NAME">%1$s</xliff:g>\"を開けませんでした: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>件のファイル"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"\"<xliff:g id="FILE_NAME">%s</xliff:g>\"を開けませんでした: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>件のファイル"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"vCardインポートのキャンセル"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"<xliff:g id="FILENAME">%s</xliff:g>のインポートをキャンセルしてもよろしいですか?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"vCardエクスポートのキャンセル"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"<xliff:g id="FILENAME">%s</xliff:g>のエクスポートをキャンセルしてもよろしいですか?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"vCardインポート/エクスポートのキャンセルに失敗"</string>
<string name="search_settings_description" msgid="2675223022992445813">"連絡先の名前"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"2秒間の停止を追加"</string>
<string name="add_wait" msgid="3360818652790319634">"着信を追加"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"この操作を行うアプリケーションが見つかりません"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"この選択を保存"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"不明"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"データがありません"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"初期設定を消去"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"この連絡先に初期設定されている項目:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"消去"</string>
<string name="menu_accounts" msgid="8499114602017077970">"アカウント"</string>
<string name="menu_import_export" msgid="3765725645491577190">"インポート/エクスポート"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"連絡先のインポート/エクスポート"</string>
- <string name="menu_share" msgid="943789700636542260">"共有"</string>
+ <string name="menu_share" msgid="8746849630474240344">"連絡先を共有"</string>
<string name="share_via" msgid="563121028023030093">"連絡先の共有ツール"</string>
<string name="share_error" msgid="4374508848981697170">"この連絡先は共有できません。"</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"名前"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"所属"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"ウェブサイト"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"予定"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"関係"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"グループ"</string>
<string name="type_short_home" msgid="7770424864090605384">"自宅"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"携帯"</string>
<string name="type_short_work" msgid="4925330752504537861">"勤務先"</string>
<string name="type_short_pager" msgid="2613818970827594238">"ポケベル"</string>
<string name="type_short_other" msgid="5669407180177236769">"その他"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"この連絡先は読み取り専用です"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"その他"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"メインの名前"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"アカウントに連絡先を作成"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"同期グループを削除"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"同期グループに追加"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"その他の連絡先"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"すべての連絡先"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"「<xliff:g id="GROUP">%s</xliff:g>」を同期から除外すると、グループに含まれない連絡先もすべて同期から除外されます。"</string>
- <string name="account_phone" msgid="3682950835276226870">"電話のみ(非同期)"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"タブレットのみ(非同期)"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"電話のみ(非同期)"</string>
<string name="call_custom" msgid="7756571794763171802">"<xliff:g id="CUSTOM">%s</xliff:g>に発信"</string>
<string name="call_home" msgid="1990519474420545392">"自宅に発信"</string>
<string name="call_mobile" msgid="7502236805487609178">"携帯電話に発信"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"ICQでチャット"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Jabberでチャット"</string>
<string name="chat" msgid="9025361898797412245">"チャット"</string>
+ <string name="postal_address" msgid="8765560217149624536">"住所"</string>
<string name="postal_street" msgid="8133143961580058972">"番地"</string>
<string name="postal_pobox" msgid="4431938829180269821">"私書箱"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"街区 (中国等で使用)"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"都道府県"</string>
<string name="postal_postcode" msgid="572136414136673751">"郵便番号"</string>
<string name="postal_country" msgid="7638264508416368690">"国"</string>
+ <string name="full_name" msgid="6602579550613988977">"名前"</string>
<string name="name_given" msgid="1687286314106019813">"名"</string>
<string name="name_family" msgid="3416695586119999058">"姓"</string>
<string name="name_prefix" msgid="59756378548779822">"敬称(名前の前)"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"連絡先を検索"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"すべての連絡先を検索"</string>
<string name="take_photo" msgid="7496128293167402354">"写真を撮影"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"新しい写真を撮る"</string>
<string name="pick_photo" msgid="448886509158039462">"ギャラリーから写真を選ぶ"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"言語の変更に伴い連絡先リストを更新しています。"\n\n"しばらくお待ちください..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"連絡先リストを更新しています。"\n\n"お待ちください..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"連絡先をアップグレードしています。"\n\n"アップグレード処理には約<xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>MBの内部ストレージが必要です。"\n\n"次のいずれかのオプションを選択してください:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"ギャラリーから新しい写真を選ぶ"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"言語の変更に伴い連絡先リストを更新しています。"</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"連絡先リストを更新しています。"</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"連絡先をアップグレードしています。"\n\n"アップグレード処理には約<xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g>MBの内部ストレージが必要です。"\n\n"次のいずれかのオプションを選択してください:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"一部のアプリケーションをアンインストール"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"アップグレードを再試行"</string>
- <string name="search_results_for" msgid="8705490885073188513">"検索結果: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"検索中..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"選択した連絡先を表示"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"すべて表示"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"すべて選択"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"選択をすべて解除"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"1件の宛先を選択済み"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g>件の宛先を選択済み"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"連絡先が選択されていません。"</string>
+ <string name="add_field" msgid="2384260056674995230">"別のフィールドを追加"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"<xliff:g id="SOURCE">%1$s</xliff:g>経由"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g>、<xliff:g id="SOURCE">%2$s</xliff:g>経由"</string>
+ <string name="description_star" msgid="2605854427360036550">"お気に入り"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"連絡先の編集"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"結合する連絡先はありません"</item>
+ <item quantity="other" msgid="425683718017380845">"<xliff:g id="COUNT">%0$d</xliff:g>件の連絡先が結合されました"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"その他"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"連絡先の統合"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"現在の連絡先を選択した連絡先に統合しますか?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"選択した連絡先の編集"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"選択した連絡先の編集に切り替えますか?これまでに入力した情報はコピーされます。"</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Myコンタクトにコピー"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"ディレクトリ<xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"すべての連絡先を検索中"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"ディレクトリ"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"連絡先"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"個人用コピーを作成しています"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"連絡先リストを選択"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"すべての連絡先"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"スター付き"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"カスタム"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"カスタマイズ..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"電話番号のある連絡先"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"連絡先"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"カスタム表示の設定"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"設定"</string>
+ <string name="menu_settings" msgid="377929915873428211">"設定"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"表示オプション"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>、<xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"連絡先を検索"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"電話番号"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"連絡先に追加"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"閉じる"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g>(<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"年を入力する"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"連絡先"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"読み込み中..."</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"新しい連絡先を作成"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"アカウントにログイン"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"ファイルから連絡先をインポート"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"新しいグループの作成"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[新しいグループを作成]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"グループ名の変更"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"グループの削除"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"グループ「<xliff:g id="GROUP_LABEL">%1$s</xliff:g>」を削除してもよろしいですか?(連絡先自体は削除されません)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"別の連絡先と統合する前に連絡先の名前を入力してください。"</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"統合された連絡先"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"テキストをコピーしました"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"変更を破棄"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"変更を破棄してもよろしいですか?"</string>
+ <string name="discard" msgid="1234315037371251414">"破棄"</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 8edce7e..016221f 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"연락처 바로가기 선택"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"전화번호 선택"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"메시지를 보낼 번호 선택"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"연락처 선택"</string>
<string name="starredList" msgid="4817256136413959463">"중요주소록"</string>
<string name="frequentList" msgid="7154768136473953056">"자주 사용하는 연락처"</string>
<string name="strequentList" msgid="5640192862059373511">"즐겨찾기"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"연락처 삭제"</string>
<string name="menu_call" msgid="3992595586042260618">"연락처로 전화 걸기"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"연락처에 문자 보내기"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"이메일 보내기"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"지도상의 주소"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"기본 번호로 설정"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"기본 이메일로 설정"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"분리"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"연락처가 분할되었습니다."</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"그룹 이름 변경"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"그룹 삭제"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"새 연락처"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"연락처 분리"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"연락처 하나를 여러 주소록으로 분리하시겠습니까? 가입된 연락처 정보 세트마다 하나씩 만드시겠습니까?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"통합"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"이 연락처를 삭제하면 여러 계정의 정보가 삭제됩니다."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"연락처가 삭제됩니다."</string>
<string name="menu_done" msgid="796017761764190697">"완료"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"되돌리기"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"취소"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"연락처 수정"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"새 연락처"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"소리나는 대로"</string>
<string name="label_notes" msgid="8337354953278341042">"메모"</string>
<string name="label_sip_address" msgid="124073911714324974">"인터넷 통화"</string>
<string name="label_ringtone" msgid="8833166825330686244">"벨소리"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"이름"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"이름(소리나는 대로)"</string>
<string name="ghostData_company" msgid="5414421120553765775">"회사"</string>
<string name="ghostData_title" msgid="7496735200318496110">"제목"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"연락처가 없습니다."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"새 연락처 만들기"</string>
- <string name="selectLabel" msgid="4255424123394910733">"라벨 선택"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"전화"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"이메일"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"메신저"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"주소"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"주소"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"조직"</item>
<item msgid="7196592230748086755">"메모"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"휴대전화에 사진이 없습니다."</string>
- <string name="attachToContact" msgid="8820530304406066714">"연락처 아이콘"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"태블릿에 사진이 없습니다."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"휴대전화에 사진이 없습니다."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"연락처 사진"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"라벨 이름 맞춤 설정"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"표시 옵션"</string>
- <string name="displayGroups" msgid="2278964020773993336">"표시 옵션"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"수신전화를 바로 음성사서함으로 보내기"</string>
<string name="default_ringtone" msgid="9099988849649827972">"기본값"</string>
- <string name="changePicture" msgid="2943329047610967714">"아이콘 변경"</string>
- <string name="removePicture" msgid="3041230993155966350">"아이콘 삭제"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"사진 삭제"</string>
<string name="noContacts" msgid="8579310973261953559">"주소록이 없습니다."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"일치하는 연락처가 없습니다."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"전화번호가 포함된 주소록이 없습니다."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"연락처를 저장했습니다."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"오류가 발생해서 연락처 변경사항을 저장할 수 없습니다."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"전화번호와 함께 연락처 1개 표시"</item>
- <item quantity="other" msgid="6133262880804110289">"전화번호가 포함된 주소록 <xliff:g id="COUNT">%d</xliff:g>개 표시"</item>
+ <item quantity="one" msgid="3015357862286673986">"전화번호가 포함된 연락처 1개"</item>
+ <item quantity="other" msgid="3299954047880968205">"전화번호가 포함된 연락처 <xliff:g id="COUNT">%d</xliff:g>개"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"전화번호가 포함된, 표시할 수 있는 주소록이 없습니다."</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"전화번호가 포함된 연락처가 없습니다."</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"연락처 1개 표시"</item>
- <item quantity="other" msgid="2865867557378939630">"<xliff:g id="COUNT">%d</xliff:g>개 연락처 표시"</item>
+ <item quantity="one" msgid="3405747744700823280">"연락처 1개"</item>
+ <item quantity="other" msgid="3578469907265375314">"연락처 <xliff:g id="COUNT">%d</xliff:g>개"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"표시할 수 있는 주소록이 없습니다."</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"주소록이 없습니다."</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"표시할 수 있는 연락처가 없습니다."</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"별표표시한 연락처가 없습니다."</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"<xliff:g id="NAME">%s</xliff:g>에 주소록이 없습니다."</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"연락처 1개를 찾았습니다."</item>
- <item quantity="other" msgid="7752927996850263152">"연락처 <xliff:g id="COUNT">%d</xliff:g>개를 찾았습니다."</item>
+ <item quantity="one" msgid="5517063038754171134">"1개를 찾았습니다."</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g>개를 찾았습니다."</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"연락처를 찾을 수 없습니다."</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"<xliff:g id="COUNT">%d</xliff:g>개 이상 찾음"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"찾을 수 없음"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1개의 연락처"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g>개의 연락처"</item>
+ <item quantity="one" msgid="4826918429708286628">"1개를 찾았습니다."</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g>개를 찾았습니다."</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"주소록"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"즐겨찾기"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"SIM 카드 주소록"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"표시할 연락처가 없습니다. 방금 계정을 추가한 경우 연락처를 동기화하는 데 몇 분 정도 걸릴 수 있습니다."</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"표시할 연락처가 없습니다."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"표시할 연락처가 없습니다."\n\n"연락처를 추가하려면 "<font fgcolor="#ffffffff"><b>"메뉴"</b></font>"를 누르고 다음을 터치합니다. "\n" "\n<li><font fgcolor="#ffffffff"><b>"계정"</b></font>": 휴대전화에 동기화할 수 있는 연락처가 있는 계정을 구성하거나 추가하려면 터치합니다. "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"새 연락처"</b></font>": 연락처를 새로 만들려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"가져오기/내보내기"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"표시할 연락처가 없습니다. 방금 계정을 추가한 경우 연락처를 동기화하는 데 몇 분 정도 걸릴 수 있습니다."\n\n"연락처를 추가하려면 "<font fgcolor="#ffffffff"><b>"메뉴"</b></font>"를 누르고 다음을 터치합니다. "\n" "\n<li><font fgcolor="#ffffffff"><b>"계정"</b></font>": 휴대전화에 동기화할 수 있는 연락처가 있는 계정을 구성하거나 추가하려면 터치합니다. "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"표시 옵션"</b></font>": 표시되는 연락처를 변경하려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"새 연락처"</b></font>": 연락처를 새로 만들려면 터치합니다. "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"가져오기/내보내기"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"표시할 연락처가 없습니다"\n\n"연락처를 추가하려면 "<font fgcolor="#ffffffff"><b>"메뉴"</b></font>"를 누르고 다음을 터치합니다. "\n" "\n<li><font fgcolor="#ffffffff"><b>"계정"</b></font>": 휴대전화에 동기화할 수 있는 연락처가 있는 계정을 구성하거나 추가하려는 경우 터치합니다. "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"새 연락처"</b></font>": 연락처를 새로 만들려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"가져오기/내보내기"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"표시할 연락처가 없습니다. 방금 계정을 추가한 경우 연락처를 동기화하는 데 몇 분 정도 걸릴 수 있습니다."\n\n"연락처를 추가하려면 "<font fgcolor="#ffffffff"><b>"메뉴"</b></font>"를 누르고 다음을 터치합니다. "\n" "\n<li><font fgcolor="#ffffffff"><b>"계정"</b></font>": 휴대전화에 동기화할 수 있는 연락처가 있는 계정을 구성하거나 추가하려면 터치합니다. "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"표시 옵션"</b></font>": 표시되는 연락처를 변경하려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"새 연락처"</b></font>": 연락처를 새로 만들려면 터치합니다. "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"가져오기/내보내기"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"표시할 연락처가 없습니다."\n\n"연락처를 추가하려면 "<font fgcolor="#ffffffff"><b>"메뉴"</b></font>"를 누르고 다음을 터치합니다."\n" "\n<li><font fgcolor="#ffffffff"><b>"계정"</b></font>": 태블릿에 동기화할 수 있는 연락처가 있는 계정을 구성하거나 추가하려는 경우 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"새 연락처"</b></font>": 연락처를 새로 만들려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"가져오기/내보내기"</b></font>": SIM 또는 SD 카드에서 연락처를 가져오려면 터치합니다."\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"표시할 연락처가 없습니다."\n\n"연락처를 추가하려면 "<font fgcolor="#ffffffff"><b>"메뉴"</b></font>"를 누르고 다음을 터치합니다."\n" "\n<li><font fgcolor="#ffffffff"><b>"계정"</b></font>": 휴대전화에 동기화할 수 있는 연락처가 있는 계정을 구성하거나 추가하려는 경우 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"새 연락처"</b></font>": 연락처를 새로 만들려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"가져오기/내보내기"</b></font>": SIM 또는 SD 카드에서 연락처를 가져오려면 터치합니다."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"표시할 연락처가 없습니다. 방금 계정을 추가한 경우 연락처를 동기화하는 데 몇 분 정도 걸릴 수 있습니다."\n\n"연락처를 추가하려면 "<font fgcolor="#ffffffff"><b>"메뉴"</b></font>"를 누르고 다음을 터치합니다."\n" "\n<li><font fgcolor="#ffffffff"><b>"계정"</b></font>": 태블릿에 동기화할 수 있는 연락처가 있는 계정을 구성하거나 추가하려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"표시 옵션"</b></font>": 표시할 연락처를 변경하려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"새 연락처"</b></font>": 연락처를 새로 만들려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"가져오기/내보내기"</b></font>": SIM 또는 SD 카드에서 연락처를 가져오려면 터치합니다."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"표시할 연락처가 없습니다. 방금 계정을 추가한 경우 연락처를 동기화하는 데 몇 분 정도 걸릴 수 있습니다."\n\n"연락처를 추가하려면 "<font fgcolor="#ffffffff"><b>"메뉴"</b></font>"를 누르고 다음을 터치합니다."\n" "\n<li><font fgcolor="#ffffffff"><b>"계정"</b></font>": 휴대전화에 동기화할 수 있는 연락처가 있는 계정을 구성하거나 추가하려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"표시 옵션"</b></font>": 표시할 연락처를 변경하려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"새 연락처"</b></font>": 연락처를 새로 만들려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"가져오기/내보내기"</b></font>": SIM 또는 SD 카드에서 연락처를 가져오려면 터치합니다."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"표시할 연락처가 없습니다."\n\n"연락처를 추가하려면 "<font fgcolor="#ffffffff"><b>"메뉴"</b></font>"를 누르고 다음을 터치합니다."\n" "\n<li><font fgcolor="#ffffffff"><b>"계정"</b></font>": 태블릿에 동기화할 수 있는 연락처가 있는 계정을 구성하거나 추가하려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"새 연락처"</b></font>": 연락처를 새로 만들려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"가져오기/내보내기"</b></font>": SD 카드에서 연락처를 가져오려면 터치합니다."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"표시할 연락처가 없습니다."\n\n"연락처를 추가하려면 "<font fgcolor="#ffffffff"><b>"메뉴"</b></font>"를 누르고 다음을 터치합니다."\n" "\n<li><font fgcolor="#ffffffff"><b>"계정"</b></font>": 휴대전화에 동기화할 수 있는 연락처가 있는 계정을 구성하거나 추가하려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"새 연락처"</b></font>": 연락처를 새로 만들려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"가져오기/내보내기"</b></font>": SD 카드에서 연락처를 가져오려면 터치합니다."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"표시할 연락처가 없습니다. 방금 계정을 추가한 경우 연락처를 동기화하는 데 몇 분 정도 걸릴 수 있습니다."\n\n"연락처를 추가하려면 "<font fgcolor="#ffffffff"><b>"메뉴"</b></font>"를 누르고 다음을 터치합니다."\n" "\n<li><font fgcolor="#ffffffff"><b>"계정"</b></font>": 태블릿에 동기화할 수 있는 연락처가 있는 계정을 구성하거나 추가하려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"표시 옵션"</b></font>": 표시되는 연락처를 변경하려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"새 연락처"</b></font>": 연락처를 새로 만들려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"가져오기/내보내기"</b></font>": SD 카드에서 연락처를 가져오려면 터치합니다."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"표시할 연락처가 없습니다. 방금 계정을 추가한 경우 연락처를 동기화하는 데 몇 분 정도 걸릴 수 있습니다."\n\n"연락처를 추가하려면 "<font fgcolor="#ffffffff"><b>"메뉴"</b></font>"를 누르고 다음을 터치합니다."\n" "\n<li><font fgcolor="#ffffffff"><b>"계정"</b></font>": 휴대전화에 동기화할 수 있는 연락처가 있는 계정을 구성하거나 추가하려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"표시 옵션"</b></font>": 표시되는 연락처를 변경하려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"새 연락처"</b></font>": 연락처를 새로 만들려면 터치합니다."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"가져오기/내보내기"</b></font>": SD 카드에서 연락처를 가져오려면 터치합니다."\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"즐겨찾기가 없습니다. "\n\n"즐겨찾기 목록에 연락처를 추가하려면 "\n\n<li><b>"주소록"</b>" 탭을 터치하고"\n</li>" "\n<li>"즐겨찾기에 추가할 연락처를 터치하고"\n</li>" "\n<li>"연락처 이름 옆의 별표를 터치합니다."\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"모든 연락처"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"중요 주소록"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"전화 걸기"</string>
<string name="callAgain" msgid="3197312117049874778">"다시 걸기"</string>
<string name="returnCall" msgid="8171961914203617813">"전화 걸기"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g>분 <xliff:g id="SECONDS">%2$s</xliff:g>초"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g>분 <xliff:g id="SECONDS">%s</xliff:g>초"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"자주 연락하는 사람들의 연락처"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"연락처 추가"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"\'<xliff:g id="EMAIL">%s</xliff:g>\'을(를) 주소록에 추가하겠습니까?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"모두"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"1"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"2"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"3"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"USB 저장소 스캔 실패(이유: \'<xliff:g id="FAIL_REASON">%s</xliff:g>\')"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"SD 카드 스캔 실패(이유: \'<xliff:g id="FAIL_REASON">%s</xliff:g>\')"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"I/O 오류"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"메모리가 부족합니다. 파일이 너무 크기 때문일 수 있습니다."</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"예기치 못한 이유로 인해 vCard를 구문분석하지 못했습니다."</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"올바른 형식처럼 보이지만 현재 구현 환경에서는 VCard를 지원하지 않기 때문에 VCard 구문 분석에 실패했습니다."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"vCard 파일이 USB 저장소에 없습니다."</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"SD 카드에 VCard 파일이 없습니다."</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"지원되지 않는 형식입니다."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"vCard를 가져오지 못했습니다."</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"vCard 파일이 USB 저장소에 없습니다."</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"SD 카드에 VCard 파일이 없습니다."</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"지정한 vCard 파일에 대한 메타 정보를 수집하지 못했습니다."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"하나 이상의 파일을 가져오지 못했습니다(%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"알 수 없는 오류"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"vCard 파일 선택"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"vCard 읽는 중"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"vCard 파일 읽는 중"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"vCard 데이터를 읽어오지 못했습니다."</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"연락처 <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g>개(총 <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>개) 내보내는 중"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"파일 <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>개 중 <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g>개"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"vCard를 로컬 임시 저장공간에 캐시하는 중"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"가져오기 도구가 vCard를 로컬 임시 저장공간에 캐시하는 중입니다. 곧 실제 가져오기가 시작됩니다."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g> 가져오는 중: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"<xliff:g id="NAME">%s</xliff:g> 가져오는 중"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"vCard 데이터를 읽지 못했습니다."</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"vCard 데이터 읽기가 취소되었습니다."</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"<xliff:g id="FILENAME">%s</xliff:g> vCard 가져오기 완료"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"<xliff:g id="FILENAME">%s</xliff:g> 가져오기가 취소됨"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g>을(를) 곧 가져옵니다."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"파일을 곧 가져옵니다."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"vCard 가져오기 요청이 거부되었습니다. 나중에 시도해 주세요."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g>을(를) 곧 내보냅니다."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"vCard 내보내기 요청이 거부되었습니다. 나중에 시도해 주세요."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"연락처"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"내보내기 확인"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"\'<xliff:g id="VCARD_FILENAME">%s</xliff:g>\'(으)로 연락처 목록을 내보내시겠습니까?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"연락처 데이터를 내보내지 못했습니다."</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"USB 저장소에 vCard 파일이 너무 많습니다."</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"SD 카드에 vCard 파일이 너무 많습니다."</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"필수 파일 이름이 너무 깁니다(\'<xliff:g id="FILENAME">%s</xliff:g>\')."</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"<xliff:g id="FILENAME">%s</xliff:g> 내보내기 완료됨"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"<xliff:g id="FILENAME">%s</xliff:g> 내보내기가 취소됨"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"연락처 데이터 내보내기"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"연락처 데이터를 \'<xliff:g id="FILE_NAME">%s</xliff:g>\'(으)로 내보내는 중"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"내보내기를 초기화하지 못했습니다. \'<xliff:g id="EXACT_REASON">%s</xliff:g>\'"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"내보내는 중에 오류가 발생했습니다. \'<xliff:g id="EXACT_REASON">%s</xliff:g>\'"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"데이터베이스 정보를 가져오지 못했습니다."</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"내보낼 수 있는 연락처가 없습니다. 휴대전화에 실제로 연락처가 있다면 일부 데이터 제공업체에서 모든 연락처를 휴대전화 외부로 내보내지 못하도록 금지했을 수 있습니다."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"내보낼 수 있는 연락처가 없습니다. 태블릿에 실제로 연락처가 있다면 일부 데이터 제공업체에서 모든 연락처를 외부로 내보내지 못하도록 금지했을 수 있습니다."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"내보낼 수 있는 연락처가 없습니다. 휴대전화에 실제로 연락처가 있다면 일부 데이터 제공업체에서 모든 연락처를 휴대전화 외부로 내보내지 못하도록 금지했을 수 있습니다."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"vCard 작성기가 바르게 초기화되지 않았습니다."</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"\'<xliff:g id="FILE_NAME">%1$s</xliff:g>\'을(를) 열 수 없습니다. <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"연락처 <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g>개(총 <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>개) 내보내는 중"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"\'<xliff:g id="FILE_NAME">%s</xliff:g>\'을(를) 열 수 없습니다. <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"연락처 <xliff:g id="CURRENT_NUMBER">%s</xliff:g>개(총 <xliff:g id="TOTAL_NUMBER">%s</xliff:g>개) 내보내는 중"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"vCard 가져오기 취소 중"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"<xliff:g id="FILENAME">%s</xliff:g> 가져오기를 취소하시겠습니까?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"vCard 내보내기 취소 중"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"<xliff:g id="FILENAME">%s</xliff:g> 내보내기를 취소하시겠습니까?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"vCard 가져오기/내보내기 취소 실패"</string>
<string name="search_settings_description" msgid="2675223022992445813">"연락처 명단"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"2초 간 일시 정지 추가"</string>
<string name="add_wait" msgid="3360818652790319634">"대기 시간 추가"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"이 작업을 처리하는 애플리케이션을 찾을 수 없습니다."</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"이 선택사항 저장"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"알 수 없음"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"데이터가 없습니다."</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"기본 설정 지우기"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"연락처의 기본 설정:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"지우기"</string>
<string name="menu_accounts" msgid="8499114602017077970">"계정"</string>
<string name="menu_import_export" msgid="3765725645491577190">"가져오기/내보내기"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"연락처 가져오기/내보내기"</string>
- <string name="menu_share" msgid="943789700636542260">"공유"</string>
+ <string name="menu_share" msgid="8746849630474240344">"연락처 공유"</string>
<string name="share_via" msgid="563121028023030093">"연락처 공유에 사용할 애플리케이션:"</string>
<string name="share_error" msgid="4374508848981697170">"연락처를 공유할 수 없습니다."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"이름"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"조직"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"웹사이트"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"일정"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"관계"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"그룹"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"W"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"연락처가 읽기 전용입니다."</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"더보기"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"기본 이름"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"계정에서 연락처 만들기"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"동기화 그룹 삭제"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"동기화 그룹 추가"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"다른 모든 연락처"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"모든 연락처"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"\'<xliff:g id="GROUP">%s</xliff:g>\'을(를) 동기화에서 제거하면 그룹화되지 않은 연락처도 동기화에서 제거됩니다."</string>
- <string name="account_phone" msgid="3682950835276226870">"휴대전화 전용(동기화되지 않음)"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"태블릿 전용, 동기화되지 않음"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"휴대전화 전용(동기화되지 않음)"</string>
<string name="call_custom" msgid="7756571794763171802">"<xliff:g id="CUSTOM">%s</xliff:g>(으)로 전화걸기"</string>
<string name="call_home" msgid="1990519474420545392">"집으로 전화걸기"</string>
<string name="call_mobile" msgid="7502236805487609178">"휴대전화로 전화걸기"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"ICQ로 채팅"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Jabber로 채팅"</string>
<string name="chat" msgid="9025361898797412245">"채팅"</string>
+ <string name="postal_address" msgid="8765560217149624536">"주소"</string>
<string name="postal_street" msgid="8133143961580058972">"번지"</string>
<string name="postal_pobox" msgid="4431938829180269821">"사서함"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"인근 지역"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"시/도"</string>
<string name="postal_postcode" msgid="572136414136673751">"우편번호"</string>
<string name="postal_country" msgid="7638264508416368690">"국가"</string>
+ <string name="full_name" msgid="6602579550613988977">"이름"</string>
<string name="name_given" msgid="1687286314106019813">"이름"</string>
<string name="name_family" msgid="3416695586119999058">"성"</string>
<string name="name_prefix" msgid="59756378548779822">"경칭"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"연락처 검색"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"모든 연락처 검색"</string>
<string name="take_photo" msgid="7496128293167402354">"사진 찍기"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"새 사진 가져오기"</string>
<string name="pick_photo" msgid="448886509158039462">"갤러리에서 사진 선택"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"변경된 언어를 반영하도록 연락처 목록을 업데이트하는 중입니다."\n\n"잠시 기다려 주세요."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"연락처 목록을 업데이트하고 있습니다."\n\n"잠시 기다려 주세요..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"연락처를 업그레이드하는 중입니다. "\n\n"업그레이드 프로세스에는 약 <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>Mb의 휴대전화 내부 저장공간이 필요합니다."\n\n"다음 옵션 중 하나를 선택하세요."</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"갤러리에서 새 사진 선택"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"변경된 언어를 반영하도록 주소록을 업데이트하는 중입니다. 잠시 기다려 주세요."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"주소록을 업데이트하고 있습니다."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"주소록을 업그레이드하는 중입니다. "\n\n"업그레이드 프로세스에는 약 <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g>MB의 내부 저장공간이 필요합니다."\n\n"다음 옵션 중 하나를 선택하세요."</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"일부 애플리케이션 제거"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"업그레이드 다시 시도"</string>
- <string name="search_results_for" msgid="8705490885073188513">"<xliff:g id="QUERY">%s</xliff:g>에 대한 검색결과"</string>
<string name="search_results_searching" msgid="7755623475227227314">"검색 중..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"선택한 항목 표시"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"모두 표시"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"모두 선택"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"모두 선택취소"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"수신자 1명이 선택됨"</item>
+ <item quantity="other" msgid="4608837420986126229">"수신자 <xliff:g id="COUNT">%d</xliff:g>명이 선택됨"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"선택한 연락처가 없습니다."</string>
+ <string name="add_field" msgid="2384260056674995230">"다른 입력란 추가"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"출처: <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g>(출처: <xliff:g id="SOURCE">%2$s</xliff:g>)"</string>
+ <string name="description_star" msgid="2605854427360036550">"즐겨찾기"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"연락처 수정"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"병합되지 않았습니다."</item>
+ <item quantity="other" msgid="425683718017380845">"<xliff:g id="COUNT">%0$d</xliff:g>개 출처에서 병합했습니다."</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"기타"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"연락처 통합"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"이 연락처와 선택한 연락처를 통합하시겠습니까?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"선택한 연락처 수정"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"선택한 연락처를 수정하시겠습니까? 지금까지 입력하신 정보는 복사됩니다."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"내 주소록에 복사"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"디렉토리 <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"모든 주소록 검색"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"디렉토리"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"주소록"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"개인 사본 작성 중"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"연락처 목록 선택"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"모든 연락처"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"별표"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"맞춤설정"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"맞춤설정..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"전화번호가 포함된 주소록"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"연락처"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"맞춤 보기 정의"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"설정"</string>
+ <string name="menu_settings" msgid="377929915873428211">"설정"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"표시 옵션"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"연락처 찾기"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"전화번호"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"주소록에 추가"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"닫기"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g>(<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"출생연도 입력 선택"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"연락처"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"로드 중..."</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"새 연락처 만들기"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"계정에 로그인"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"파일에서 주소록 가져오기"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"새 그룹 만들기"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[새 그룹 만들기]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"그룹 이름 변경"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"그룹 삭제"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"\'<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\' 그룹을 삭제하시겠습니까? (연락처는 삭제되지 않음)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"다른 연락처와 통합하기 전에 통합할 연락처 이름을 입력하세요."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"통합된 연락처"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"텍스트 복사됨"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"변경사항 취소"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"변경사항을 취소하시겠습니까?"</string>
+ <string name="discard" msgid="1234315037371251414">"취소"</string>
</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index a02fa86..11c0912 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Pasirinkti adresato nuorodą"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Pasirinkite numerį, kuriuo skambinsite"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Pasirinkti numerį, kuriuo bus siunčiamas pranešimas"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Pasirinkti kontaktą"</string>
<string name="starredList" msgid="4817256136413959463">"Pažymėta žvaigždute"</string>
<string name="frequentList" msgid="7154768136473953056">"Dažnai naudojami"</string>
<string name="strequentList" msgid="5640192862059373511">"Adresynas"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Ištrinti adresatą"</string>
<string name="menu_call" msgid="3992595586042260618">"Skambinti adresatui"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Siųsti pranešimą adresatui"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Siųsti el. laišką"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Adresas žemėlapyje"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Padaryti numatytuoju numeriu"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Padaryti numatytuoju el. paštu"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Atskiras"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Atskirti adresatai"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Pervardyti grupę"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Ištrinti grupę"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Naujas"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Atskiras adresatas"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Ar tikrai norite skirstyti šį adresatą į kelis adresatus: po vieną kiekvienam adresato informacijos rinkiniui, kuris buvo pridėtas?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Sujungti"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Ištrynus šią informaciją, bus ištrinta kelių paskyrų informacija."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Šis adresatas bus ištrintas."</string>
<string name="menu_done" msgid="796017761764190697">"Atlikta"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Grąžinti"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Atšaukti"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Redaguoti adresatą"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Naujas adresatas"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetinis"</string>
<string name="label_notes" msgid="8337354953278341042">"Pastabos"</string>
<string name="label_sip_address" msgid="124073911714324974">"Skambutis internetu"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Skambėjimo tonas"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Pirmas ir paskutinis"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Fonetinis vardas"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Įmonė"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Pareigos"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Adresatas neegzistuoja."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Kurti naują adresatą"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Pasirinkti etiketę"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefonas"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"El. paštas"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"TP"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Pašto adresas"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adresas"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organizacija"</item>
<item msgid="7196592230748086755">"Pastaba"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Telefone nėra galimų paveikslėlių."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Adresato piktograma"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Jokie paveikslėliai nepasiekiami naudojant planšetinį kompiuterį."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Telefone nėra galimų paveikslėlių."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Kontakto nuotrauka"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Priskirtas etiketės pavadinimas"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Pateikties parinktys"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Pateikties parinktys"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Skambinti tiesiogiai į balso paštą"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Numatytasis"</string>
- <string name="changePicture" msgid="2943329047610967714">"Keisti piktogramą"</string>
- <string name="removePicture" msgid="3041230993155966350">"Pašalinti piktogramą"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Pašalinti nuotrauką"</string>
<string name="noContacts" msgid="8579310973261953559">"Adresatų nėra."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Nerasta atitinkančių adresatų."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nėra adresatų su telefono numeriais."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Adresatas išsaugotas."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Klaida, neįmanoma išsaugoti adresatų pakeitimų."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Pateikiamas vienas adresatas su telefono numeriu"</item>
- <item quantity="other" msgid="6133262880804110289">"Pateikiami <xliff:g id="COUNT">%d</xliff:g> adresatai (-ų) su telefono numeriais"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 kontaktas su telefono numeriu"</item>
+ <item quantity="other" msgid="3299954047880968205">"Kontaktai (-ų) su telefonų numeriais: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Nėra matomų adresatų su telefono numeriais"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Nėra kontaktų su telefonų numeriais"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Rodomas 1 adresatas"</item>
- <item quantity="other" msgid="2865867557378939630">"Pateikiami (-a) <xliff:g id="COUNT">%d</xliff:g> adresatai (-ų)"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 kontaktas"</item>
+ <item quantity="other" msgid="3578469907265375314">"Kontaktai: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Nėra matomų adresatų"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Kontaktų nėra"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Nėra matomų kontaktų"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Nėra žvaigždutėmis pažymėtų kontaktų"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"„<xliff:g id="NAME">%s</xliff:g>“ kontaktų nėra"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Rastas 1 adresatas"</item>
- <item quantity="other" msgid="7752927996850263152">"Rasti adresatų: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="5517063038754171134">"rastas 1"</item>
+ <item quantity="other" msgid="3852668542926965042">"Rasta <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Adresatas nerastas"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"rasta daugiau nei <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Nerasta"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 adresatas"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> adresatai (-ų)"</item>
+ <item quantity="one" msgid="4826918429708286628">"rastas 1"</item>
+ <item quantity="other" msgid="7988132539476575389">"Rasta <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Adresinė"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Adresynas"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"SIM kortelės adresatai"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Neturite adresatų, kuriuos būtų galima pateikti. (Jei ką tik pridėjote paskyrą, adresatų sinchronizavimas gali užtrukti kelias minutes.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Neturite adresatų, kuriuos būtų galima pateikti."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Neturite adresatų, kuriuos būtų galima pateikti."\n\n"Jei norite pridėti adresatų, paspauskite "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" ir palieskite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Paskyros"</b></font>", jei norite pridėti ar konfigūruoti paskyrą su adresatais, kuriuos galite sinchronizuoti su telefonu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Naujas adresatas"</b></font>", jei norite nuo pradžių sukurti naują adresatą"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuoti / eksportuoti"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Neturite adresatų, kuriuos būtų galima pateikti. (Jei ką tik pridėjote adresatą, sinchronizavimas gali užtrukti kelias minutes.)"\n\n"Jei norite pridėti adresatų, paspauskite "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" ir palieskite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Paskyros"</b></font>", jei norite pridėti ar konfigūruoti paskyrą su adresatais, kuriuos galite sinchronizuoti su telefonu "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Pateikties parinktys"</b></font>", jei norite pakeisti nustatymą, kurie adresatai yra matomi"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Naujas adresatas"</b></font>", jei norite nuo pradžių sukurti naują adresatą"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuoti / eksportuoti"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Neturite adresatų, kuriuos būtų galima pateikti."\n\n"Jei norite pridėti adresatų, paspauskite "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" ir palieskite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Paskyros"</b></font>", jei norite pridėti ar konfigūruoti paskyrą su adresatais, kuriuos galite sinchronizuoti su telefonu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Naujas adresatas"</b></font>", jei norite nuo pradžių sukurti naują adresatą"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuoti / eksportuoti"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Neturite adresatų, kuriuos būtų galima pateikti. (Jei ką tik pridėjote adresatą, sinchronizavimas gali užtrukti kelias minutes.)"\n\n"Jei norite pridėti adresatų, paspauskite "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" ir palieskite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Paskyros"</b></font>", jei norite pridėti ar konfigūruoti paskyrą su adresatais, kuriuos galite sinchronizuoti su telefonu "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Pateikties parinktys"</b></font>", jei norite pakeisti nustatymą, kurie adresatai yra matomi"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Naujas adresatas"</b></font>", jei norite nuo pradžių sukurti naują adresatą"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuoti / eksportuoti"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Neturite pateiktinų kontaktų."\n\n"Jei norite pridėti kontaktų, paspauskite "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" ir palieskite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Paskyros"</b></font>", kad pridėtumėte ar konfigūruotumėte paskyrą su kontaktais, kuriuos galite sinchronizuoti su planšetiniu kompiuteriu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Naujas kontaktas"</b></font>", kad sukurtumėte visiškai naują kontaktą;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuoti / eksportuoti"</b></font>", kad importuotumėte kontaktus iš SIM ar SD kortelės"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Neturite pateiktinų kontaktų."\n\n"Jei norite pridėti kontaktų, paspauskite "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" ir palieskite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Paskyros"</b></font>", kad pridėtumėte ar konfigūruotumėte paskyrą su kontaktais, kuriuos galite sinchronizuoti su telefonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Naujas kontaktas"</b></font>", kad sukurtumėte visiškai naują kontaktą;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuoti / eksportuoti"</b></font>", kad importuotumėte kontaktus iš SIM ar SD kortelės"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Neturite pateiktinų kontaktų. (Jei ką tik pridėjote paskyrą, kontaktų sinchronizavimas gali užtrukti kelias minutes.)"\n\n"Jei norite pridėti kontaktų, paspauskite "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" ir palieskite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Paskyros"</b></font>", kad pridėtumėte ar konfigūruotumėte paskyrą su kontaktais, kuriuos galite sinchronizuoti su planšetiniu kompiuteriu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Pateikties parinktys"</b></font>", kad pakeistumėte matomus kontaktus;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Naujas kontaktas"</b></font>", kad sukurtumėte visiškai naują kontaktą;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuoti / eksportuoti"</b></font>", kad importuotumėte kontaktus iš SIM ar SD kortelės"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Neturite pateiktinų kontaktų. (Jei ką tik pridėjote paskyrą, kontaktų sinchronizavimas gali užtrukti kelias minutes.)"\n\n"Jei norite pridėti kontaktų, paspauskite "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" ir palieskite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Paskyros"</b></font>", kad pridėtumėte ar konfigūruotumėte paskyrą su kontaktais, kuriuos galite sinchronizuoti su telefonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Pateikties parinktys"</b></font>", kad pakeistumėte matomus kontaktus;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Naujas kontaktas"</b></font>", kad sukurtumėte visiškai naują kontaktą;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuoti / eksportuoti"</b></font>", kad importuotumėte kontaktus iš SIM ar SD kortelės"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Neturite pateiktinų kontaktų."\n\n"Jei norite pridėti kontaktų, paspauskite "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" ir palieskite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Paskyros"</b></font>", kad pridėtumėte ar konfigūruotumėte paskyrą su kontaktais, kuriuos galite sinchronizuoti su planšetiniu kompiuteriu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Naujas kontaktas"</b></font>", kad sukurtumėte visiškai naują kontaktą;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuoti / eksportuoti"</b></font>", kad importuotumėte kontaktus iš SD kortelės"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Neturite pateiktinų kontaktų."\n\n"Jei norite pridėti kontaktų, paspauskite "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" ir palieskite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Paskyros"</b></font>", kad pridėtumėte ar konfigūruotumėte paskyrą su kontaktais, kuriuos galite sinchronizuoti su telefonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Naujas kontaktas"</b></font>", kad sukurtumėte visiškai naują kontaktą;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuoti / eksportuoti"</b></font>", kad importuotumėte kontaktus iš SD kortelės"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Neturite pateiktinų kontaktų. (Jei ką tik pridėjote paskyrą, kontaktų sinchronizavimas gali užtrukti kelias minutes.)"\n\n"Jei norite pridėti kontaktų, paspauskite "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" ir palieskite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Paskyros"</b></font>", kad pridėtumėte ar konfigūruotumėte paskyrą su kontaktais, kuriuos galite sinchronizuoti su planšetiniu kompiuteriu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Pateikties parinktys"</b></font>", kad pakeistumėte matomus kontaktus;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Naujas kontaktas"</b></font>", kad sukurtumėte visiškai naują kontaktą;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuoti / eksportuoti"</b></font>", kad importuotumėte kontaktus iš SD kortelės"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Neturite pateiktinų kontaktų. (Jei ką tik pridėjote paskyrą, kontaktų sinchronizavimas gali užtrukti kelias minutes.)"\n\n"Jei norite pridėti kontaktų, paspauskite "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" ir palieskite:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Paskyros"</b></font>", kad pridėtumėte ar konfigūruotumėte paskyrą su kontaktais, kuriuos galite sinchronizuoti su telefonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Pateikties parinktys"</b></font>", kad pakeistumėte matomus kontaktus;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Naujas kontaktas"</b></font>", kad sukurtumėte visiškai naują kontaktą;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuoti / eksportuoti"</b></font>", kad importuotumėte kontaktus iš SD kortelės"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Nėra adresyno įrašų."\n\n"Jei norite pridėti adresatą prie adresyno sąrašo:"\n\n" "<li>"Palieskite skirtuką "<b>"Adresinė"</b>\n</li>" "\n<li>"Palieskite adresatą, kurį norite pridėti prie adresyno"\n</li>" "\n<li>"Palieskite šalia adresato vardo esančią žvaigždutę"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Visi adresatai"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Pažymėta žvaigždute"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Perskambinti"</string>
<string name="callAgain" msgid="3197312117049874778">"Skambinti dar kartą"</string>
<string name="returnCall" msgid="8171961914203617813">"Grįžtamasis skambutis"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min. <xliff:g id="SECONDS">%2$s</xliff:g> sek."</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> sek."</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Dažnai susisiekiama"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Pridėti adresatą"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Pridėti „<xliff:g id="EMAIL">%s</xliff:g>“ prie adresatų?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Visi"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"vienas"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"du"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"trys"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Nepavyko nuskaityti USB atmintinės (Priežastis: „<xliff:g id="FAIL_REASON">%s</xliff:g>“)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"SD kortelės nuskaitymas nepavyko (priežastis: „<xliff:g id="FAIL_REASON">%s</xliff:g>“)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Įvesties / išvesties klaida"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Nepakanka atminties (failas gali būti per didelis)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Dėl netikėtos priežasties nepavyko išanalizuoti „vCard“"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Nors „vCard“ yra tinkamo formato, jos nepavyko išanalizuoti, nes dabartinis diegimas to formato nepalaiko"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"USB atmintinėje nerasta jokių el. vizitinės kortelės failų"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"SD kortelėje nerastas „vCard“ failas"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Formatas nepalaikomas."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Nepavyko import. el. vizitinės kortelės"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"USB atmintinėje nerasta jokių el. vizitinių kortelių failų"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"SD kortelėje nerasta jokių el. vizitinių kortelių failų"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Nepavyko surinkti nurodyto (-ų) el. vizitinės kortelės failo (-ų) metainformacijos."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Nepavyko importuoti vieno ar daugiau failų (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Nežinoma klaida"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Pasirinkti „vCard“ failą"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Skaitoma „vCard“"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Skaitomas (-i) „vCard“ failas (-ai)"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"„vCard“ duomenų nuskaitymas nepavyko"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> iš <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> adresatų"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> iš <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> failų"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Padedamas (-i) el. vizitinės kortelės failas (-ai) į vietinę laikinąją atmintinę"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Importavimo programa padeda el. vizitinės kortelės failą (-us) į vietinę laikinąją atmintinę. Netrukus bus pradėtas tikrasis importavimas."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importuojama <xliff:g id="CURRENT_NUMBER">%s</xliff:g> / <xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importuojama <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Nepavyko perskaityti el. vizitinės kortelės duomenų"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"El. vizitinės kortelės duomenų skaitymas buvo atšauktas"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Baigtas „<xliff:g id="FILENAME">%s</xliff:g>“ el. vizitinės kortelės importavimas"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"<xliff:g id="FILENAME">%s</xliff:g> importavimas atšauktas"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> bus netrukus importuotas."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Failas bus netrukus importuotas."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"El. vizitinės kortelės importavimo užklausa atmesta. Bandykite vėliau."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> bus netrukus eksportuotas."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"El. vizitinės kortelės eksportavimo užklausa atmesta. Bandykite vėliau."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"kontaktas"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Patvirtinti eksportavimą"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Ar tikrai norite eksportuoti adresatų sąrašą į „<xliff:g id="VCARD_FILENAME">%s</xliff:g>“?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Nepavyko eksportuoti adresatų duomenų"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Per daug el. vizitinės kortelės failų USB atmintinėje"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Per daug „vCard“ failų SD kortelėje"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Reikalingo failo pavadinimas per ilgas („<xliff:g id="FILENAME">%s</xliff:g>“)"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Baigta eksportuoti <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"<xliff:g id="FILENAME">%s</xliff:g> eksportavimas atšauktas"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Eksportuojami adresatų duomenys"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Adresato duomenys eksportuojami į „<xliff:g id="FILE_NAME">%s</xliff:g>“"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Nepavyko inicijuoti eksportuotojo: „<xliff:g id="EXACT_REASON">%s</xliff:g>“"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Eksportuojant įvyko klaida: „<xliff:g id="EXACT_REASON">%s</xliff:g>“"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Nepavyko gauti duomenų informacijos"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Nėra eksportuoti galimo adresato. Jei iš tikrųjų telefone turite adresatų, gali būti, kad kažkuris iš duomenų teikėjų draudžia eksportuoti visus adresatus iš telefono."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Nėra eksportuojamų kontaktų. Jei planšetiniame kompiuteryje iš tiesų yra kontaktų, kai kurie duomenų teikėjai gali drausti eksportuoti visus kontaktus iš planšetinio kompiuterio."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Nėra eksportuojamų kontaktų. Jei telefone iš tiesų yra kontaktų, kai kurie duomenų teikėjai gali drausti eksportuoti visus kontaktus iš telefono."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"„vCard“ rašyklė netinkamai inicijuota"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Nepavyko atidaryti „<xliff:g id="FILE_NAME">%1$s</xliff:g>“: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> iš <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> adresatų"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Nepavyko atidaryti „<xliff:g id="FILE_NAME">%s</xliff:g>“: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> iš <xliff:g id="TOTAL_NUMBER">%s</xliff:g> adresatų"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Atšaukiamas el. vizitinės kortelės importavimas"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Ar tikrai norite atšaukti „<xliff:g id="FILENAME">%s</xliff:g>“ importavimą?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Atšaukiamas el. vizitinės kortelės eksportavimas"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Ar tikrai norite atšaukti <xliff:g id="FILENAME">%s</xliff:g> eksportavimą?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Nepavyko atšaukti el. vizitinės kortelės importavimo / eksportavimo"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Adresatų vardai"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Pridėti 2 sek. pauzę"</string>
<string name="add_wait" msgid="3360818652790319634">"Pridėti laukimą"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Nerasta programa šiam veiksmui apdoroti"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Atsiminti šį pasirinkimą"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Nežinomas"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Duomenų nėra"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Išvalyti numatytuosius nustatymus"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Šiam kontaktui nustatyti numatyt. nust.:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Išvalyti"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Paskyros"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importuoti / eksportuoti"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importuoti / eksportuoti adresatus"</string>
- <string name="menu_share" msgid="943789700636542260">"Bendrinti"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Bendrinti kontaktą"</string>
<string name="share_via" msgid="563121028023030093">"Bendrinti adresatą naudojant"</string>
<string name="share_error" msgid="4374508848981697170">"Šio adresato negalima bendrinti."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Pavadinimas"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organizacija"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Svetainė"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Įvykis"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Ryšys"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grupės"</string>
<string name="type_short_home" msgid="7770424864090605384">"N"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"D"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"K"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Šis adresatas yra tik skaitomas"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Daugiau"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Pirminis vardas"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Kurti adresatą paskyroje"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Pašalinti sinchronizuojamą grupę"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Pridėti sinchronizuotą grupę"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Visi kiti adresatai"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Visi adresatai"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Iš sinchronizavimo pašalinus „<xliff:g id="GROUP">%s</xliff:g>“, bus pašalinti ir visi nesugrupuoti adresatai."</string>
- <string name="account_phone" msgid="3682950835276226870">"Tik telefonas, nesinchronizuojama"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Tik planšetinis kompiuteris, nesinchronizuojama"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Tik telefonas, nesinchronizuojama"</string>
<string name="call_custom" msgid="7756571794763171802">"Skambinti <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Skambinti į namų telefoną"</string>
<string name="call_mobile" msgid="7502236805487609178">"Skambinti į mobilųjį telefoną"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Kalbėti naudojant ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Kalbėti naudojant „Jabber“"</string>
<string name="chat" msgid="9025361898797412245">"Kalbėti"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adresas"</string>
<string name="postal_street" msgid="8133143961580058972">"Gatvė"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Pašto dėžutė"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Kaimynystė"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Būsena"</string>
<string name="postal_postcode" msgid="572136414136673751">"Pašto kodas"</string>
<string name="postal_country" msgid="7638264508416368690">"Šalis"</string>
+ <string name="full_name" msgid="6602579550613988977">"Vardas"</string>
<string name="name_given" msgid="1687286314106019813">"Suteiktas pavadinimas"</string>
<string name="name_family" msgid="3416695586119999058">"Pavardė"</string>
<string name="name_prefix" msgid="59756378548779822">"Priešvardis"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Ieškoti adresatų"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Ieškoti visų adresatų"</string>
<string name="take_photo" msgid="7496128293167402354">"Fotografuoti"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Iš naujo fotografuoti"</string>
<string name="pick_photo" msgid="448886509158039462">"Pasirinkti nuotrauką iš galerijos"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Adresatų sąrašas atnaujinamas, kad būtų pakeista kalba."\n\n"Palaukite..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Adresatų sąrašas atnaujinamas."\n\n"Palaukite..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Adresatai yra naujovinami. "\n\n"Norint naujovinti, reikia apytiksliai <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> Mb vidinės telefono saugyklos vietos."\n\n"Pasirinkite vieną iš toliau pateiktų parinkčių:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Pasirinkti naują nuotrauką iš galerijos"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Kontaktų sąrašas yra atnaujinamas, kad atspindėtų kalbos pakeitimą."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Atnaujinamas kontaktų sąrašas."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Vyksta kontaktų naujovinimo procesas. "\n\n"Naujovinimo procesui reikia apytiksliai <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mb vidinės atminties."\n\n"Pasirinkite vieną iš šių parinkčių:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Pašalinti kai kurias programas"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Bandyti naujovinti iš naujo"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Paieškos rezultatai pagal: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Ieškoma..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Rodyti pasirinktus"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Rodyti viską"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Pasirinkti visus"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Atšaukti visų pasirinkimą"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Nepasirinkote kontaktų."</string>
+ <string name="add_field" msgid="2384260056674995230">"Pridėti kitą lauką"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"naudojant „<xliff:g id="SOURCE">%1$s</xliff:g>“"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> naudojant „<xliff:g id="SOURCE">%2$s</xliff:g>“"</string>
+ <string name="description_star" msgid="2605854427360036550">"įtraukti į adresyną"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Redaguoti kontaktą"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"nesujungta"</item>
+ <item quantity="other" msgid="425683718017380845">"sujungta iš <xliff:g id="COUNT">%0$d</xliff:g> šaltinių (-io)"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Kita"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Sujungti kontaktus"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Sujungti dabartinį kontaktą su pasirinktu?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Redaguoti pasirinktus kontaktus"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Perjungti į pasirinkto kontakto redagavimą? Iki šiol įvesta informacija bus nukopijuota."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Kopijuoti į mano kontaktus"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"„<xliff:g id="TYPE">%1$s</xliff:g>“ katalogas"</string>
+ <string name="search_label" msgid="6789295859496641042">"Ieškoma visų kontaktų"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Katalogas"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Kontaktai"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Kuriama asmeninė kopija"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Pasirinkti kontaktų sąrašą"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Visi kontaktai"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Pažymėta žvaigždute"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Priskirtas"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Tinkinti..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Kontaktai su telefonų numeriais"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kontaktas"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Nustatyti priskirtą rodinį"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Nustatymai"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Nustatymai"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Pateikties parinktys"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"„<xliff:g id="COMPANY_0">%2$s</xliff:g>“, „<xliff:g id="COMPANY_1">%1$s</xliff:g>“"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Ieškoti kontaktų"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Telefono numeris"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Pridėti prie kontaktų"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Uždaryti"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Nurodyti metus"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Kontaktas"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Įkeliama…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Sukurti naują kontaktą"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Prisijunkite prie paskyros"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importuoti kontaktus iš failo"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Sukurti naują grupę"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Sukurti naują grupę]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Pervardyti grupę"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Ištrinti grupę"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Ar tikrai norite ištrinti „<xliff:g id="GROUP_LABEL">%1$s</xliff:g>“ grupę? (Kontaktai nebus ištrinti.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Prieš prisijungdami prie kito kontakto, įveskite kontakto vardą."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Prijungtas kontaktas"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Tekstas nukopijuotas"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Atmesti pakeitimus"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Ar tikrai norite atmesti pakeitimus?"</string>
+ <string name="discard" msgid="1234315037371251414">"Atmesti"</string>
</resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index c8f96d1..b5d597a 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Izvēlieties kontaktpersonas saīsni"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Izvēlieties numuru, uz kuru zvanīt"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Izvēlieties numuru, uz kuru sūtīt īsziņu"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Kontaktpersonas atlase"</string>
<string name="starredList" msgid="4817256136413959463">"Atzīmēti ar zvaigznīti"</string>
<string name="frequentList" msgid="7154768136473953056">"Bieži"</string>
<string name="strequentList" msgid="5640192862059373511">"Izlase"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Dzēst kontaktpersonu"</string>
<string name="menu_call" msgid="3992595586042260618">"Zvanīt kontaktpersonai"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Sūtīt īsziņu kontaktpersonai"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Sūtīt e-pasta ziņojumu"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Parādīt adresi kartē"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Iestatīt kā noklusējuma numuru"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Iestatīt kā noklusējuma e-pasta adresi"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Sadalīt"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Kontaktpersonas ir sadalītas"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Pārdēvēt grupu"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Dzēst grupu"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Jauns"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Sadalīt kontaktpersonu"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Vai tiešām vēlaties sadalīt šo kontaktpersonu un izveidot atsevišķu kontaktpersonu katrai kontaktinformācijas kopai, kas tai tika pievienota?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Apvienot"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Dzēšot šo kontaktpersonu, tiks dzēsta informācija no vairākiem kontiem."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Šī kontaktpersona tiks dzēsta."</string>
<string name="menu_done" msgid="796017761764190697">"Gatavs"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Atjaunot"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Atcelt"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Rediģēt kontaktpersonu"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Jauna kontaktpersona"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Izruna"</string>
<string name="label_notes" msgid="8337354953278341042">"Piezīmes"</string>
<string name="label_sip_address" msgid="124073911714324974">"Interneta zvans"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Zvana signāls"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Vārds un uzvārds"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Vārda izruna"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Uzņēmums"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Nosaukums"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Kontaktpersona nepastāv."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Izveidot jaunu kontaktpersonu"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Iezīmes atlase"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Tālrunis"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-pasts"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Tūlītējā ziņojumapmaiņa"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Pasta adrese"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adrese"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organizācija"</item>
<item msgid="7196592230748086755">"Piezīme"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Tālrunī nav pieejams neviens attēls."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Kontaktpersonas ikona"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Planšetdatorā nav pieejams neviens attēls."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Tālrunī nav pieejams neviens attēls."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Kontaktpersonas fotoattēls"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Pielāgots iezīmes nosaukums"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Attēlojuma opcijas"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Attēlojuma opcijas"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Pāradresēt zvanus tieši uz balss pastu"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Noklusējums"</string>
- <string name="changePicture" msgid="2943329047610967714">"Mainīt ikonu"</string>
- <string name="removePicture" msgid="3041230993155966350">"Noņemt ikonu"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Noņemt fotoattēlu"</string>
<string name="noContacts" msgid="8579310973261953559">"Nav kontaktpersonu."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Nav atrasta neviena atbilstoša kontaktpersona."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nav nevienas kontaktpersonas ar tālruņa numuru."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Kontaktpersona ir saglabāta."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Radās kļūda, nevar saglabāt kontaktpersonas izmaiņas."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Tiek attēlota 1 kontaktpersona ar tālruņa numuru."</item>
- <item quantity="other" msgid="6133262880804110289">"Tiek attēlota(-as) <xliff:g id="COUNT">%d</xliff:g> kontaktpersona(-as) ar tālruņa numuriem."</item>
+ <item quantity="one" msgid="3015357862286673986">"1 kontaktpersona ar tālruņa numuru"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> kontaktpersonas ar tālruņa numuriem"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Nav redzamu kontaktpersonu ar tālruņa numuriem."</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Nav nevienas kontaktpersonas ar tālruņa numuru"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Tiek attēlota 1 kontaktpersona."</item>
- <item quantity="other" msgid="2865867557378939630">"Tiek attēlota(-as) <xliff:g id="COUNT">%d</xliff:g> kontaktpersona(-as)"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 kontaktpersona"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> kontaktpersonas"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Nav redzamu kontaktpersonu"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Nav kontaktpersonu"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Nav redzamu kontaktpersonu"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Nav ar zvaigznīti atzīmētu kontaktpersonu"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Nav kontaktpersonu šādā sarakstā: <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Tika atrasta 1 kontaktpersona."</item>
- <item quantity="other" msgid="7752927996850263152">"Tika atrasta(-as) <xliff:g id="COUNT">%d</xliff:g> kontaktpersona(-as)"</item>
+ <item quantity="one" msgid="5517063038754171134">"Atrasta 1"</item>
+ <item quantity="other" msgid="3852668542926965042">"Atrastas <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Kontaktpersona nav atrasta."</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"atrastas vairāk nekā <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Nav atrastas"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 kontaktpersona"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> kontaktpersona(-as)"</item>
+ <item quantity="one" msgid="4826918429708286628">"Atrasta 1"</item>
+ <item quantity="other" msgid="7988132539476575389">"Atrastas <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Kontaktpersonas"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Izlase"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Kontaktpersonas SIM kartē"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Nav nevienas kontaktpersonas, ko attēlot. (Ja tikko pievienojāt kontu, kontaktpersonu sinhronizācija var ilgt dažas minūtes.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Nav nevienas kontaktpersonas, ko attēlot."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Nav nevienas kontaktpersonas, ko attēlot."\n\n"Lai pievienotu kontaktpersonas, nospiediet "<font fgcolor="#ffffffff"><b>"Izvēlne"</b></font>" un pieskarieties attiecīgajam vienumam:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti,"</b></font>" lai pievienotu vai konfigurētu kontu ar kontaktpersonām, ko var sinhronizēt ar tālruni;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Jauna kontaktpersona,"</b></font>" lai izveidotu jaunu kontaktpersonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importēt/eksportēt."</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Nav nevienas kontaktpersonas, ko attēlot. (Ja tikko pievienojāt kontu, kontaktpersonu sinhronizācija var ilgt dažas minūtes.)"\n\n"Lai pievienotu kontaktpersonas, nospiediet "<font fgcolor="#ffffffff"><b>"Izvēlne"</b></font>" un pieskarieties attiecīgajam vienumam:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti,"</b></font>" lai pievienotu vai konfigurētu kontu ar kontaktpersonām, ko var sinhronizēt ar tālruni;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Attēlojuma opcijas,"</b></font>" lai mainītu attēlotās kontaktpersonas;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Jauna kontaktpersona,"</b></font>" lai izveidotu jaunu kontaktpersonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importēt/eksportēt."</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Nav nevienas kontaktpersonas, ko attēlot."\n\n"Lai pievienotu kontaktpersonas, nospiediet "<font fgcolor="#ffffffff"><b>"Izvēlne"</b></font>" un pieskarieties attiecīgajam vienumam:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti,"</b></font>" lai pievienotu vai konfigurētu kontu ar kontaktpersonām, ko var sinhronizēt ar tālruni;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Jauna kontaktpersona,"</b></font>" lai izveidotu jaunu kontaktpersonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importēt/eksportēt."</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Nav nevienas kontaktpersonas, ko attēlot. (Ja tikko pievienojāt kontu, kontaktpersonu sinhronizācija var ilgt dažas minūtes.)"\n\n"Lai pievienotu kontaktpersonas, nospiediet "<font fgcolor="#ffffffff"><b>"Izvēlne"</b></font>" un pieskarieties attiecīgajam vienumam:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti,"</b></font>" lai pievienotu vai konfigurētu kontu ar kontaktpersonām, ko var sinhronizēt ar tālruni;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Attēlojuma opcijas,"</b></font>" lai mainītu attēlotās kontaktpersonas;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Jauna kontaktpersona,"</b></font>" lai izveidotu jaunu kontaktpersonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importēt/eksportēt."</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Nav parādāmu kontaktpersonu."\n\n"Lai pievienotu kontaktpersonas, nospiediet "<font fgcolor="#ffffffff"><b>"Izvēlne"</b></font>" un pieskarieties:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>", lai pievienotu vai konfigurētu kontu ar kontaktpersonām, ko var sinhronizēt ar tālruni;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Jauna kontaktpersona"</b></font>", lai izveidotu jaunu kontaktpersonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importēt/eksportēt"</b></font>", lai importētu kontaktpersonas no SIM vai SD kartes."\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Nav parādāmu kontaktpersonu."\n\n"Lai pievienotu kontaktpersonas, nospiediet "<font fgcolor="#ffffffff"><b>"Izvēlne"</b></font>" un pieskarieties:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>", lai pievienotu vai konfigurētu kontu ar kontaktpersonām, ko var sinhronizēt ar tālruni;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Jauna kontaktpersona"</b></font>", lai izveidotu jaunu kontaktpersonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importēt/eksportēt"</b></font>", lai importētu kontaktpersonas no SIM vai SD kartes."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Nav parādāmu kontaktpersonu. (Ja tikko pievienojāt kontu, kontaktpersonu sinhronizēšana var ilgt dažas minūtes.)"\n\n"Lai pievienotu kontaktpersonas, nospiediet "<font fgcolor="#ffffffff"><b>"Izvēlne"</b></font>" un pieskarieties:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>", lai pievienotu vai konfigurētu kontu ar kontaktpersonām, ko var sinhronizēt ar tālruni;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Attēlošanas opcijas"</b></font>", lai mainītu redzamās kontaktpersonas;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Jauna kontaktpersona"</b></font>", lai izveidotu jaunu kontaktpersonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importēt/eksportēt"</b></font>", lai importētu kontaktpersonas no SIM vai SD kartes."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Nav parādāmu kontaktpersonu. (Ja tikko pievienojāt kontu, kontaktpersonu sinhronizēšana var ilgt dažas minūtes.)"\n\n"Lai pievienotu kontaktpersonas, nospiediet "<font fgcolor="#ffffffff"><b>"Izvēlne"</b></font>" un pieskarieties:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>", lai pievienotu vai konfigurētu kontu ar kontaktpersonām, ko var sinhronizēt ar tālruni;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Attēlošanas opcijas"</b></font>", lai mainītu redzamās kontaktpersonas;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Jauna kontaktpersona"</b></font>", lai izveidotu jaunu kontaktpersonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importēt/eksportēt"</b></font>", lai importētu kontaktpersonas no SIM vai SD kartes."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Nav parādāmu kontaktpersonu."\n\n"Lai pievienotu kontaktpersonas, nospiediet "<font fgcolor="#ffffffff"><b>"Izvēlne"</b></font>" un pieskarieties:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>", lai pievienotu vai konfigurētu kontu ar kontaktpersonām, ko var sinhronizēt ar planšetdatoru;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Jauna kontaktpersona"</b></font>", lai izveidotu jaunu kontaktpersonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importēt/eksportēt"</b></font>", lai importētu kontaktpersonas no SD kartes."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Nav parādāmu kontaktpersonu."\n\n"Lai pievienotu kontaktpersonas, nospiediet "<font fgcolor="#ffffffff"><b>"Izvēlne"</b></font>" un pieskarieties:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>", lai pievienotu vai konfigurētu kontu ar kontaktpersonas, ko var sinhronizēt ar tālruni;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Jauna kontaktpersona"</b></font>", lai izveidotu jaunu kontaktpersonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importēt/eksportēt"</b></font>", lai importētu kontaktpersonas no SD kartes."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Nav parādāmu kontaktpersonu. (Ja tikko pievienojāt kontu, kontaktpersonu sinhronizēšana var ilgt dažas minūtes.)"\n\n"Lai pievienotu kontaktpersonas, nospiediet "<font fgcolor="#ffffffff"><b>"Izvēlne"</b></font>" un pieskarieties:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>", lai pievienotu vai konfigurētu kontu ar kontaktpersonām, ko var sinhronizēt ar planšetdatoru;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Attēlošanas opcijas"</b></font>", lai mainītu redzamās kontaktpersonas;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Jauna kontaktpersona"</b></font>", lai izveidotu jaunu kontaktpersonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importēt/eksportēt"</b></font>", lai importētu kontaktpersonas no SD kartes."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Nav parādāmu kontaktpersonu. (Ja tikko pievienojāt kontu, kontaktpersonu sinhronizēšana var ilgt dažas minūtes.)"\n\n"Lai pievienotu kontaktpersonas, nospiediet "<font fgcolor="#ffffffff"><b>"Izvēlne"</b></font>" un pieskarieties:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konti"</b></font>", lai pievienotu vai konfigurētu kontu ar kontaktpersonām, ko var sinhronizēt ar tālruni;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Attēlošanas opcijas"</b></font>", lai mainītu redzamās kontaktpersonas;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Jauna kontaktpersona"</b></font>", lai izveidotu jaunu kontaktpersonu;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importēt/eksportēt"</b></font>", lai importētu kontaktpersonas no SD kartes."\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Jūs neesat izveidojis izlasi."\n\n"Lai pievienotu kontaktpersonu izlasei, rīkojieties šādi:"\n\n" "<li>"Pieskarieties cilnei "<b>"Kontaktpersonas"</b>"."\n</li>" "\n<li>"Pieskarieties kontaktpersonai, kuru vēlaties pievienot izlasei."\n</li>" "\n<li>"Pieskarieties zvaigznītei blakus kontaktpersonas vārdam."\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Visas kontaktpersonas"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Atzīmēti ar zvaigznīti"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Atzvanīt"</string>
<string name="callAgain" msgid="3197312117049874778">"Zvanīt vēlreiz"</string>
<string name="returnCall" msgid="8171961914203617813">"Atzvanīt"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min <xliff:g id="SECONDS">%2$s</xliff:g> s"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Bieža komunikācija"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Pievienot kontaktpersonu"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Vai pievienot “<xliff:g id="EMAIL">%s</xliff:g>” kontaktpersonām?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Visi"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"viens"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"divi"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"trīs"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"USB atmiņas skenēšana neizdevās (iemesls: <xliff:g id="FAIL_REASON">%s</xliff:g>)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"SD kartes skenēšana neizdevās (cēlonis: “<xliff:g id="FAIL_REASON">%s</xliff:g>”)."</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Ievades/izvades kļūda"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Atmiņā nepietiek vietas (iespējams, fails ir pārāk liels)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Neparedzēta iemesla dēļ neizdevās parsēt vCard failu."</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Neizdevās parsēt vCard failu, lai gan tā formāts šķiet derīgs, jo pašreizējā ieviešana to neatbalsta."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"USB atmiņā nav atrasts neviens vCard fails."</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"SD kartē netika atrasts vCard fails."</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Šis formāts nav atbalstīts."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"vCard importēšana neizdevās."</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"USB atmiņā nav atrasts neviens vCard fails."</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"SD kartē nav atrasts neviens vCard fails."</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Neizdevās iegūt meta informāciju par konkrēto(-ajiem) vCard failu(-iem)."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Neizdevās importēt vienu vai vairākus failus (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Nezināma kļūda"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"vCard faila atlase"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"vCard nolasīšana"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Notiek vCard faila(-u) nolasīšana"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Neizdevās nolasīt vCard faila datus."</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> no <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontaktpersonām"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g>. no <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> failiem"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"vCard kešdarbe vietējā pagaidu krātuvē"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Importētājs veic vCard kešdarbi vietējā pagaidu krātuvē. Drīz sāksies faktiskā importēšana."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importē <xliff:g id="CURRENT_NUMBER">%s</xliff:g> no <xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Notiek imp.: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Neizdevās nolasīt vCard datus."</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"vCard datu lasīšana tika atcelta"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"vCard faila <xliff:g id="FILENAME">%s</xliff:g> importēšana ir pabeigta"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Faila <xliff:g id="FILENAME">%s</xliff:g> importēšana tika atcelta"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"Fails <xliff:g id="FILENAME">%s</xliff:g> tiks drīzumā importēts."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Fails drīzumā tiks importēts."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"vCard faila importēšanas pieprasījums ir noraidīts. Lūdzu, mēģiniet vēlreiz."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"Fails <xliff:g id="FILENAME">%s</xliff:g> tiks drīzumā eksportēts."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"vCard faila eksportēšanas pieprasījums ir noraidīts. Lūdzu, mēģiniet vēlreiz."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"kontaktpersona"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Eksportēšanas apstiprinājums"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Vai tiešām vēlaties eksportēt kontaktpersonu sarakstu uz “<xliff:g id="VCARD_FILENAME">%s</xliff:g>”?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Neizdevās eksportēt kontaktpersonas datus."</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"USB atmiņā ir pārāk daudz vCard failu."</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"SD kartē ir pārāk daudz vCard failu."</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Faila nosaukums ir pārāk garš (“<xliff:g id="FILENAME">%s</xliff:g>”)."</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Faila <xliff:g id="FILENAME">%s</xliff:g> eksportēšana ir pabeigta"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Faila <xliff:g id="FILENAME">%s</xliff:g> eksportēšana tika atcelta"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Kontaktpersonu datu eksportēšana"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Notiek kontaktpersonu datu eksportēšana uz “<xliff:g id="FILE_NAME">%s</xliff:g>”"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Nevarēja inicializēt eksportētāju: “<xliff:g id="EXACT_REASON">%s</xliff:g>”"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Eksportēšanas laikā radās kļūda: “<xliff:g id="EXACT_REASON">%s</xliff:g>”"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Neizdevās iegūt informāciju no datu bāzes."</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Nav eksportējamu kontaktpersonu. Ja tālrunī ir kontaktpersonas, iespējams, datu sniedzējs ir aizliedzis kontaktpersonu datu eksportēšanu."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Nav eksportējamu kontaktpersonu. Ja planšetdatorā tomēr ir kontaktpersonas, iespējams, daži datu nodrošinātāji ir aizlieguši visu kontaktpersonu eksportēšanu ārpus planšetdatora."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Nav eksportējamu kontaktpersonu. Ja tālrunī tomēr ir kontaktpersonas, iespējams, daži datu nodrošinātāji ir aizlieguši visu kontaktpersonu eksportēšanu ārpus tālruņa."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"vCard veidotājs nav inicializēts pareizi."</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Nevarēja atvērt failu “<xliff:g id="FILE_NAME">%1$s</xliff:g>”: <xliff:g id="EXACT_REASON">%2$s</xliff:g>."</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> no <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontaktpersonas(-ām)"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Nevarēja atvērt failu “<xliff:g id="FILE_NAME">%s</xliff:g>”: <xliff:g id="EXACT_REASON">%s</xliff:g>."</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> no <xliff:g id="TOTAL_NUMBER">%s</xliff:g> kontaktpersonas(-ām)"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"vCard importēšanas atcelšana"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Vai tiešām vēlaties atcelt faila <xliff:g id="FILENAME">%s</xliff:g> importēšanu?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"vCard eksportēšanas atcelšana"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Vai tiešām vēlaties atcelt faila <xliff:g id="FILENAME">%s</xliff:g> eksportēšanu?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Neizdevās atcelt vCard importēšanu/eksportēšanu"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Kontaktpersonu vārdi"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Pievienot 2 sekundes ilgu pauzi"</string>
<string name="add_wait" msgid="3360818652790319634">"Pievienot gaidīšanu"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Netika atrasta neviena lietojumprogramma, kas varētu veikt šo darbību."</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Atcerēties šo izvēli"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Nav zināms"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Nav datu"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Notīrīt noklusējuma iestatījumus"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Noklusējuma iestat. šai kontaktpersonai:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Notīrīt"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Konti"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importēt/eksportēt"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Kontaktpersonu importēšana/eksportēšana"</string>
- <string name="menu_share" msgid="943789700636542260">"Kopīgot"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Kopīgot kontaktpersonu"</string>
<string name="share_via" msgid="563121028023030093">"Kopīgot kontaktpersonu, izmantojot"</string>
<string name="share_error" msgid="4374508848981697170">"Šo kontaktpersonu nevar kopīgot."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Vārds"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organizācija"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Vietne"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Pasākums"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Saistība"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grupas"</string>
<string name="type_short_home" msgid="7770424864090605384">"M"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"D"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"C"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Šī kontaktpersona ir tikai lasāma"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Vairāk"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Galvenais nosaukums"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Izveidot jaunu kontaktpersonu kontā"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Noņemt sinhronizējamo grupu"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Pievienot sinhronizējamu grupu"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Citas kontaktpersonas"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Visas kontaktpersonas"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Noņemot grupu “<xliff:g id="GROUP">%s</xliff:g>” no sinhronizācijas, tiks noņemtas arī visas negrupētās kontaktpersonas."</string>
- <string name="account_phone" msgid="3682950835276226870">"Tikai tālrunī, bez sinhronizācijas"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Tikai planšetdatorā, bez sinhronizācijas"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Tikai tālrunī, bez sinhronizācijas"</string>
<string name="call_custom" msgid="7756571794763171802">"Zvanīt: <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Zvanīt uz mājas numuru"</string>
<string name="call_mobile" msgid="7502236805487609178">"Zvanīt uz mobilo tālruni"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Tērzēt, izmantojot ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Tērzēt, izmantojot Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Tērzēt"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adrese"</string>
<string name="postal_street" msgid="8133143961580058972">"Iela"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Abonenta kastīte"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Apkaime"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Štats"</string>
<string name="postal_postcode" msgid="572136414136673751">"Pasta indekss"</string>
<string name="postal_country" msgid="7638264508416368690">"Valsts"</string>
+ <string name="full_name" msgid="6602579550613988977">"Vārds"</string>
<string name="name_given" msgid="1687286314106019813">"Vārds"</string>
<string name="name_family" msgid="3416695586119999058">"Uzvārds"</string>
<string name="name_prefix" msgid="59756378548779822">"Uzruna"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Meklēt kontaktpersonas"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Meklēt visas kontaktpersonas"</string>
<string name="take_photo" msgid="7496128293167402354">"Uzņemt fotoattēlu"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Uzņemt jaunu fotoattēlu"</string>
<string name="pick_photo" msgid="448886509158039462">"Atlasīt fotoattēlu no galerijas"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Kontaktpersonu saraksts tiek atjaunināts, lai atainotu valodas maiņu."\n\n"Lūdzu, uzgaidiet..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Kontaktpersonu saraksts tiek atjaunināts."\n\n"Lūdzu, uzgaidiet..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Kontaktpersonas tiek jauninātas."\n\n"Jaunināšanai nepieciešami aptuveni <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB iekšējās tālruņa krātuves."\n\n"Izvēlieties vienu no šīm opcijām:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Atlasīt jaunu fotoattēlu no galerijas"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Kontaktpersonu saraksts tiek atjaunināts, lai atspoguļotu valodas maiņu."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Kontaktpersonu saraksts tiek atjaunināts."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Kontaktpersonas tiek jauninātas. "\n\n"Jaunināšanai ir nepieciešami aptuveni <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB iekšējās atmiņas."\n\n"Izvēlieties vienu no šīm opcijām:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Atinstalēt dažas lietojumprogrammas"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Mēģināt jaunināt vēlreiz"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Meklēšanas rezultāti vaicājumam “<xliff:g id="QUERY">%s</xliff:g>”"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Notiek meklēšana..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Parādīt atlasi"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Rādīt visu"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Atlasīt visu"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Noņemt atlasi visam"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Nav atlasīta neviena kontaktpersona."</string>
+ <string name="add_field" msgid="2384260056674995230">"Pievienot vēl vienu lauku"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"izmantojot <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g>, izmantojot <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"izlase"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Kontaktpersonu rediģēšana"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"nav sapludinātas"</item>
+ <item quantity="other" msgid="425683718017380845">"sapludināts no <xliff:g id="COUNT">%0$d</xliff:g> avotiem"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Cits"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Kontaktpersonu savienošana"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Vai savienot pašreizējo saturu ar atlasīto kontaktpersonu?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Atlasīto kontaktpersonu rediģēšana"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Vai pāriet uz atlasītās kontaktpersonas rediģēšanu? Līdz šim ievadītā informācija tiks kopēta."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Kopēt uz manām kontaktpersonām"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Katalogs <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Notiek visu kontaktpersonu meklēšana"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Katalogs"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Kontaktpersonas"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Notiek personīgā eksemplāra izveide"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Izvēlieties kontaktpersonu sarakstu"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Visas kontaktpersonas"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Atzīmēts ar zvaigznīti"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Pielāgotas"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Pielāgot..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Kontaktpersonas ar tālruņa numuriem"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kontaktpersona"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Definēt pielāgoto skatījumu"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Iestatījumi"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Iestatījumi"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Attēlošanas opcijas"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Meklēt kontaktpersonas"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Tālruņa numurs"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Piev. kontaktpersonām"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Aizvērt"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Norādīt gadu"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Kontaktpersona"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Notiek ielāde…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Izveidot jaunu kontaktpersonu"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Pierakstīties kontā"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importēt kontaktpersonas no faila"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Jaunas grupas izveide"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Izveidot jaunu grupu]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Grupas pārdēvēšana"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Grupas dzēšana"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Vai tiešām vēlaties dzēst grupu <xliff:g id="GROUP_LABEL">%1$s</xliff:g>? (Kontaktpersonas netiks dzēstas.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Lūdzu, ievadiet kontaktpersonas vārdu, pirms apvienojat to ar citu kontaktpersonu."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Apvienotā kontaktpersona"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Teksts ir nokopēts"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Atmest izmaiņas"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Vai vēlaties atmest izmaiņas?"</string>
+ <string name="discard" msgid="1234315037371251414">"Atmest"</string>
</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 86709a2..9e3333a 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Velg en kontaktsnarvei"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Velg et nummer å ringe"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Velg et nummer å sende melding til"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Velg en kontakt"</string>
<string name="starredList" msgid="4817256136413959463">"Med stjerne"</string>
<string name="frequentList" msgid="7154768136473953056">"Mest brukt"</string>
<string name="strequentList" msgid="5640192862059373511">"Favoritter"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Slett kontakt"</string>
<string name="menu_call" msgid="3992595586042260618">"Ring kontakt"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Send SMS til kontakt"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Send e-post"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Se i kart"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Gjør til foretrukket nummer"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Bruk som standard e-post"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Del"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Kontakt delt"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Gi nytt navn til gruppen"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Slett gruppe"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Ny"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Del opp kontakt"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Er du sikker på at du ønsker å dele opp denne kontakten i flere, en for hvert sett med kontaktinformasjon som den er satt sammen av?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Foren"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Sletter du denne kontakten, vil du slette informasjon fra flere kontoer."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Denne kontakten vil bli slettet."</string>
<string name="menu_done" msgid="796017761764190697">"Lagre"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Tilbakestill"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Avbryt"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Rediger kontakt"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Ny kontakt"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetisk"</string>
<string name="label_notes" msgid="8337354953278341042">"Notater"</string>
<string name="label_sip_address" msgid="124073911714324974">"Internett-anrop"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Ringetone"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"For- og etternavn"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Fonetisk navn"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Firma"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Tittel"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Kontakten finnes ikke."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Opprett ny kontakt"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Velg etikett"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-post"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Lynmeldinger"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Postadresse"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adresse"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organisasjon"</item>
<item msgid="7196592230748086755">"Notat"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Det er ingen bilder på telefonen."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Kontaktikon"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Ingen bilder er tilgjengelige på nettbrettet."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Det er ingen bilder på telefonen."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Kontaktens bilde"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Egendefinert etikett"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Vis grupper"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Vis grupper"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Send anrop direkte til telefonsvarer."</string>
<string name="default_ringtone" msgid="9099988849649827972">"Standardvalg"</string>
- <string name="changePicture" msgid="2943329047610967714">"Endre ikon"</string>
- <string name="removePicture" msgid="3041230993155966350">"Fjern ikon"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Fjern bilde"</string>
<string name="noContacts" msgid="8579310973261953559">"Ingen kontakter."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Fant ingen kontakter."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Ingen kontakter med telefonnummer."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Kontakt lagret."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Kunne ikke lagre kontaktendringene."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Viser 1 kontakt med telefonnummer"</item>
- <item quantity="other" msgid="6133262880804110289">"Viser <xliff:g id="COUNT">%d</xliff:g> kontakter med telefonnumre"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 kontakt med telefonnummer"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> kontakter med telefonnumre"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Ingen synlige kontakter med telefonnummer"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Ingen kontakter med telefonnummer"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Viser 1 kontakt"</item>
- <item quantity="other" msgid="2865867557378939630">"Viser <xliff:g id="COUNT">%d</xliff:g> kontakter"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 kontakt"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> kontakter"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Ingen synlige kontakter"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Ingen kontakter"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Ingen synlige kontakter"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Ingen stjernemerkede kontakter"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Ingen kontakter i <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Fant 1 kontakt"</item>
- <item quantity="other" msgid="7752927996850263152">"Fant <xliff:g id="COUNT">%d</xliff:g> kontakter"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 funnet"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> funnet"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Fant ingen kontakter"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"fant mer enn <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Ikke funnet"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 kontakt"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> kontakter"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 funnet"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> funnet"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Kontakter"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoritter"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Kontakter på SIM-kort"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Du har ingen kontakter å vise. (Hvis du nettopp har lagt til en konto, kan det ta noen minutter å synkronisere kontaktene.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Du har ingen kontakter å vise."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Du har ingen kontakter å vise. "\n\n"Slik legger du til en kontakt: Trykk på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" og trykk deretter på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Kontoer"</b></font>" for å legge til eller konfigurere en konto med kontakter som kan synkroniseres til telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" for å opprette en ny kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importér/eksportér"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Du har ingen kontakter å vise. (Hvis du nylig la til en konto, kan det ta noen minutter å synkronisere kontaktene.)"\n\n"Slik legger du til kontakter: Trykk på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" og trykk deretter på: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Kontoer "</b></font>" for å legge til eller konfigurere en konto med kontakter som kan synkroniseres til telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativer"</b></font>" for å endre hvilke kontakter som vises"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" for å opprette en ny kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importér/Eksportér"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Du har ingen kontakter å vise. "\n\n"Slik legger du til en kontakt: Trykk på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" og trykk deretter på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Kontoer"</b></font>" for å legge til eller konfigurere en konto med kontakter som kan synkroniseres til telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" for å opprette en ny kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importér/eksportér"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Du har ingen kontakter å vise. (Hvis du nylig la til en konto, kan det ta noen minutter å synkronisere kontaktene.)"\n\n"Slik legger du til kontakter: Trykk på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" og trykk deretter på: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Kontoer "</b></font>" for å legge til eller konfigurere en konto med kontakter som kan synkroniseres til telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativer"</b></font>" for å endre hvilke kontakter som vises"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" for å opprette en ny kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importér/Eksportér"</b></font>\n</li></string>
+ <!-- syntax error in translation for noContactsHelpText (6450346791169710787) org.xmlpull.v1.XmlPullParserException: expected: /li read: font (position:END_TAG </font>@1:292 in java.io.StringReader@68916a2) -->
+ <!-- syntax error in translation for noContactsHelpText (7633826236417884130) org.xmlpull.v1.XmlPullParserException: expected: /li read: font (position:END_TAG </font>@1:293 in java.io.StringReader@122ce908) -->
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Du har ingen kontakter å vise. (Hvis du nylig la til en konto, kan det ta noen minutter å synkronisere kontaktene.)"\n\n"Slik legger du til kontakter: Trykk på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>", og trykk deretter på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Kontoer"</b></font>" for å legge til eller konfigurere en konto med kontakter som kan synkroniseres til nettbrettet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativer"</b></font>" for å endre hvilke kontakter som vises"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" for å opprette en ny kontakt fra grunnen av"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/Eksporter"</b></font>" for å importere kontakter fra SIM- eller SD-kort"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Du har ingen kontakter å vise. (Hvis du nylig la til en konto, kan det ta noen minutter å synkronisere kontaktene.)"\n\n"Slik legger du til kontakter: Trykk på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" og trykk deretter på: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Kontoer "</b></font>" for å legge til eller konfigurere en konto med kontakter som kan synkroniseres til telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativer"</b></font>" for å endre hvilke kontakter som vises"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" for å opprette en ny kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importér/Eksportér"</b></font>" for å importere kontakter fra SIM- eller SD-kort"\n</li></string>
+ <!-- syntax error in translation for noContactsNoSimHelpText (6031363021287849874) org.xmlpull.v1.XmlPullParserException: expected: /li read: font (position:END_TAG </font>@1:297 in java.io.StringReader@1827391d) -->
+ <!-- syntax error in translation for noContactsNoSimHelpText (467658807711582876) org.xmlpull.v1.XmlPullParserException: expected: /li read: font (position:END_TAG </font>@1:297 in java.io.StringReader@4bb1aa65) -->
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Du har ingen kontakter å vise. (Hvis du nylig la til en konto, kan det ta noen minutter å synkronisere kontaktene.)"\n\n"Slik legger du til kontakter: Trykk på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>", og trykk deretter på: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Kontoer"</b></font>" for å legge til eller konfigurere en konto med kontakter som kan synkroniseres til nettbrettet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativer"</b></font>" for å endre hvilke kontakter som vises"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" for å opprette en ny kontakt fra grunnen av"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importer/eksporter"</b></font>" for å importere kontakter fra SD-kortet"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Du har ingen kontakter å vise. (Hvis du nylig la til en konto, kan det ta noen minutter å synkronisere kontaktene.)"\n\n"Slik legger du til kontakter: Trykk på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" og trykk deretter på: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Kontoer "</b></font>" for å legge til eller konfigurere en konto med kontakter som kan synkroniseres til telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativer"</b></font>" for å endre hvilke kontakter som vises"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" for å opprette en ny kontakt"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importér/eksportér"</b></font>" for å importere kontakter fra SD-kortet"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Du har ingen favoritter."\n\n"Slik legger du til en kontakt i favorittlisten:"\n\n" "<li>"Trykk på fanen "<b>"Kontakter"</b>" "\n</li>" "\n<li>"Trykk på kontakten du vil legge til i favoritter"\n</li>" "\n<li>"Trykk på stjernen ved siden av kontaktnavnet"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Alle kontakter"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Med stjerne"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Ring tilbake"</string>
<string name="callAgain" msgid="3197312117049874778">"Ring på nytt"</string>
<string name="returnCall" msgid="8171961914203617813">"Ring tilbake"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min <xliff:g id="SECONDS">%2$s</xliff:g> sek"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> sek"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Ofte kontaktet"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Legg til kontakt"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Legg til «<xliff:g id="EMAIL">%s</xliff:g>» som kontakt?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Alle"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"en"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"to"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tre"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Skanning av USB-lagring mislyktes (årsak: <xliff:g id="FAIL_REASON">%s</xliff:g>)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Kunne ikke skanne minnekort (grunn: «<xliff:g id="FAIL_REASON">%s</xliff:g>»)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Inn/ut-feil (årsak: «FAIL_REASON»)"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Ikke tilstrekkelig minne (filen kan være for stor)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Kan ikke analysere VCard av uventet årsak"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Kan ikke analysere VCard selv om det ser ut til å være i riktig format, fordi den aktuelle implementeringen ikke støtter det."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Finner ingen vCard-filer i USB-lagring"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Fant ingen VCard-filer på minnekortet"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Formatet støttes ikke."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Kunne ikke importere vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Finner ingen vCard-fil i USB-lagring"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Fant ingen vCard-fil på SD-kortet"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Kan ikke hente inn metainformasjon for de(n) aktuelle vCard-filen(e)."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Importeringen av én eller flere filer mislyktes (%s)"</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Ukjent feil"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Velg VCard-fil"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Leser VCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Leser VCard-fil(er)"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Kunne ikke lese VCard-data"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> av <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontakter"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> av <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> filer"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Bufrer vCard-fil(er) til lokal midlertidig lagring"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Importprogrammet bufrer vCard-filen(e) til lokal midlertidig lagring. Importen av selve filen starter snart."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importerer <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importerer <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Kan ikke lese vCard-data"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Lesing av vCard-data ble avbrutt"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Import av vCard-filen <xliff:g id="FILENAME">%s</xliff:g> er fullført"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Import av <xliff:g id="FILENAME">%s</xliff:g> ble avbrutt"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> vil importeres snart."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Filen importeres snart."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Forespørselen om vCard-import er avslått. Prøv på nytt senere."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> vil eksporteres snart."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Forespørselen om vCard-eksport er avslått. Prøv på nytt senere."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"kontakt"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Eksportbekreftelse"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Vil du eksportere kontaktlisten din til «<xliff:g id="VCARD_FILENAME">%s</xliff:g>»?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Kunne ikke eksportere kontaktlisten"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"For mange vCard-filer på USB-lagring"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"For mange VCard-datafiler på minnekortet"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"For langt filnavn kreves («<xliff:g id="FILENAME">%s</xliff:g>»)"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Eksport av <xliff:g id="FILENAME">%s</xliff:g> er fullført"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Eksport av <xliff:g id="FILENAME">%s</xliff:g> ble avbrutt"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Eksporterer kontaktdata"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Eksporterer kontaktdata til «<xliff:g id="FILE_NAME">%s</xliff:g>»"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Kunne ikke starte eksporteringen: «<xliff:g id="EXACT_REASON">%s</xliff:g>»"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Det oppstod feil ved eksport: «<xliff:g id="EXACT_REASON">%s</xliff:g>»"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Klarte ikke å hente databaseinformasjon"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Det finnes ingen eksporterbare kontakter. Hvis du har kontakter på telefonen, kan disse i enkelte tilfeller ikke eksporteres. Dette skyldes i så fall at dataleverandøren ikke tillater det."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Det finnes ingen eksporterbare kontakter. Hvis du faktisk har kontakter på nettbrettet, kan disse i enkelte tilfeller ikke eksporteres fra nettbrettet. Dette skyldes i så fall at dataleverandøren ikke tillater det."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Det finnes ingen eksporterbare kontakter. Hvis du faktisk har kontakter på telefonen, kan disse i enkelte tilfeller ikke eksporteres fra telefonen. Dette skyldes i så fall at dataleverandøren ikke tillater det."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Objektet for vCard-redigering er ikke riktig initialisert"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Kunne ikke åpne «<xliff:g id="FILE_NAME">%1$s</xliff:g>»: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> av <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontakter"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Kunne ikke åpne «<xliff:g id="FILE_NAME">%s</xliff:g>»: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> av <xliff:g id="TOTAL_NUMBER">%s</xliff:g> kontakter"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Avbryt import av vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Er du sikker på at du vil avbryte import av <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Avbryt eksport av vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Er du sikker på at du vil avbryte eksport av <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Kunne ikke avbryte import/eksp. av vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Navn på kontakter"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Legg til pause på 2 sek."</string>
<string name="add_wait" msgid="3360818652790319634">"Legg til Vent"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Ingen programmer som kan utføre denne handlingen ble funnet"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Husk dette valget"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Ukjent"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Ingen data"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Fjern standard"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Standardinnstill. for denne kontakten:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Tøm"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Kontoer"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Import/eksport"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importér/eksportér kontakter"</string>
- <string name="menu_share" msgid="943789700636542260">"Del"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Del kontakt"</string>
<string name="share_via" msgid="563121028023030093">"Del kontakt via"</string>
<string name="share_error" msgid="4374508848981697170">"Kan ikke dele kontakten."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Navn"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organisering"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Nettsted"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Aktivitet"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relasjon"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grupper"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"J"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"A"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Kontakten er skrivebeskyttet"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Andre detaljer"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Hovednavn"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Opprett kontakt under konto"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Fjern synkronisert gruppe"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Legg til synkronisert gruppe"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"(Ugrupperte kontakter)"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Alle kontakter"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Hvis «<xliff:g id="GROUP">%s</xliff:g>» fjernes·fra synkroniseringen, vil også alle ugrupperte kontakter fjernes fra synkroniseringen."</string>
- <string name="account_phone" msgid="3682950835276226870">"Kun telefon (usynkronisert)"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Kun nettbrett (usynkronisert)"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Kun telefon (usynkronisert)"</string>
<string name="call_custom" msgid="7756571794763171802">"Ring <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Ring (privat)"</string>
<string name="call_mobile" msgid="7502236805487609178">"Ring mobil"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Nettprat med ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Nettprat med Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Nettprat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adresse"</string>
<string name="postal_street" msgid="8133143961580058972">"Gate"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Postboks"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Nabolag"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Poststed"</string>
<string name="postal_postcode" msgid="572136414136673751">"Postnummer"</string>
<string name="postal_country" msgid="7638264508416368690">"Land"</string>
+ <string name="full_name" msgid="6602579550613988977">"Navn"</string>
<string name="name_given" msgid="1687286314106019813">"Fornavn"</string>
<string name="name_family" msgid="3416695586119999058">"Etternavn"</string>
<string name="name_prefix" msgid="59756378548779822">"Første del av navn"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Søk etter kontakter"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Søk i alle kontakter"</string>
<string name="take_photo" msgid="7496128293167402354">"Ta bilde"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Ta nytt bilde"</string>
<string name="pick_photo" msgid="448886509158039462">"Velg bilde fra galleriet"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Kontaktlisten er oppdatert med nytt språk."\n\n"Vent litt ..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Kontaktlisten oppdateres."\n\n"Vent litt ..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Oppgradering av kontakter pågår. "\n\n"Oppgraderingsprosessen krever cirka <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB internt minne på telefonen."\n\n"Velg ett av følgende alternativer:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Velg nytt bilde fra galleriet"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Kontaktlisten er oppdatert med nytt språk."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Kontaktlisten er under oppdatering."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Kontaktene oppgraderes."\n\n"Oppgraderingsprosessen krever omtrent <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB intern lagringsplass."\n\n"Velg ett av følgende alternativer:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Avinstaller noen programmer"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Prøv å oppgradere på nytt"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Søkeresultater for <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Søker ..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Vis valgte"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Vis alle"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Marker alle"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Fjern alle markeringer"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"Én mottager valgt"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g> mottagere valgt"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Ingen kontakter valgt."</string>
+ <string name="add_field" msgid="2384260056674995230">"Legg til et annet felt"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favoritt"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Rediger kontakt"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"ikke slått sammen"</item>
+ <item quantity="other" msgid="425683718017380845">"sammenslått fra <xliff:g id="COUNT">%0$d</xliff:g> kilder"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Andre"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Slå sammen kontakter"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Slå sammen gjeldende kontakt med valgt kontakt?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Rediger valgte kontakter"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Bytt til redigering av gjeldende kontakt? Informasjonen du har lagt til så langt blir kopiert."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Kopier til mine kontakter"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Katalog <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Søker i alle kontakter"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Katalog"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Kontakter"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Oppretter en personlig kopi"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Velg kontaktliste"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Alle kontakter"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Stjernemerket"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Egendefinert"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Tilpass"</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Kontakter med telefonnumre"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kontakt"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Definer tilpasset visning"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Innstillinger"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Innstillinger"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Vis grupper"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Finn kontakter"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Telefonnummer"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Legg til i kontakter"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Lukk"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Oppgi år"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Kontakt"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Laster inn ..."</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Opprett en ny kontakt"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Logg deg på en konto"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importer kontakter fra en fil"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Opprett ny gruppe"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Opprett ny gruppe]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Gi nytt navn til gruppe"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Slett gruppe"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Er du sikker på at du vil slette gruppen «<xliff:g id="GROUP_LABEL">%1$s</xliff:g>»? (De enkelte kontaktene vil ikke slettes.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Skriv inn kontaktnavn før du registrerer deg med en annen kontakt."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Sammenslått kontakt"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Tekst kopiert"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Forkast endringer"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Vil du forkaste endringene?"</string>
+ <string name="discard" msgid="1234315037371251414">"Forkast"</string>
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 33ea0a5..eb7c20c 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Kies een contactsnelkoppeling"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Kies een nummer om te bellen"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Kies een nummer voor bericht"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Selecteer een contact"</string>
<string name="starredList" msgid="4817256136413959463">"Met ster"</string>
<string name="frequentList" msgid="7154768136473953056">"Vaak"</string>
<string name="strequentList" msgid="5640192862059373511">"Favorieten"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Contact verwijderen"</string>
<string name="menu_call" msgid="3992595586042260618">"Contact bellen"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Sms\'en naar contact"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"E-mail verzenden"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Adres op kaart weergeven"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Als standaardnummer instellen"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Instellen als standaard e-mailadres"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Scheiden"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Contacten gescheiden"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Naam van groep wijzigen"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Groep verwijderen"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Nieuw"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Contacten scheiden"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Weet u zeker dat u dit enkele contact wilt scheiden in meerdere contacten: één contact voor elke verzameling contactgegevens die aan het contact is gekoppeld?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Samenvoegen"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Als u dit contact verwijdert, worden gegevens van meerdere accounts verwijderd."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Dit contact wordt verwijderd."</string>
<string name="menu_done" msgid="796017761764190697">"Gereed"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Terugzetten"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Annuleren"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Contact bewerken"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Nieuw contact"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetisch"</string>
<string name="label_notes" msgid="8337354953278341042">"Opmerkingen"</string>
<string name="label_sip_address" msgid="124073911714324974">"Internetoproep"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Beltoon"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Voor- en achternaam"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Fonetisch gespelde naam"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Bedrijf"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Titel"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Het contact bestaat niet."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Nieuw contact maken"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Label selecteren"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefoon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-mailadres"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Chat"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Postadres"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adres"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organisatie"</item>
<item msgid="7196592230748086755">"Opmerking"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Er zijn geen foto\'s beschikbaar op de telefoon."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Pictogram voor contact"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Er zijn geen foto\'s beschikbaar op de tablet."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Er zijn geen foto\'s beschikbaar op de telefoon."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Contactfoto"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Aangepaste labelnaam"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Weergaveopties"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Weergaveopties"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Oproepen rechtstreeks naar voicemail verzenden"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Standaard"</string>
- <string name="changePicture" msgid="2943329047610967714">"Pictogram wijzigen"</string>
- <string name="removePicture" msgid="3041230993155966350">"Pictogram verwijderen"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Foto verwijderen"</string>
<string name="noContacts" msgid="8579310973261953559">"Geen contacten."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Kan geen overeenkomende contacten vinden."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Geen contacten met telefoonnummers."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Contact opgeslagen."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Fout. Kan wijzigingen in contact niet opslaan."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"1 contact met telefoonnummer"</item>
- <item quantity="other" msgid="6133262880804110289">"<xliff:g id="COUNT">%d</xliff:g> contacten met telefoonnummers"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 contact met telefoonnummer"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> contacten met telefoonnummers"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Geen zichtbare contacten met telefoonnummers"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Geen contacten met telefoonnummers"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"1 contact"</item>
- <item quantity="other" msgid="2865867557378939630">"<xliff:g id="COUNT">%d</xliff:g> contacten"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 contact"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> contacten"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Geen zichtbare contacten"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Geen contacten"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Geen zichtbare contacten"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Geen contacten met ster"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Geen contacten in <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"1 contact gevonden"</item>
- <item quantity="other" msgid="7752927996850263152">"<xliff:g id="COUNT">%d</xliff:g> contacten gevonden"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 gevonden"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> gevonden"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Contact niet gevonden"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"meer dan <xliff:g id="COUNT">%d</xliff:g> gevonden"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Niet gevonden"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 contact"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> contacten"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 gevonden"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> gevonden"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Contact"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoriet"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Contacten op SIM-kaart"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"U heeft geen contacten om weer te geven. Als u zojuist een account heeft toegevoegd, kan het enkele minuten duren voordat de contacten zijn gesynchroniseerd."</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"U heeft geen contacten om weer te geven."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"U heeft geen contacten om weer te geven."\n\n"Als u contacten wilt toevoegen, drukt u op "<font fgcolor="#ffffffff"><b>"\'Menu\'"</b></font>" en raakt u de volgende opties aan:"\n" "\n<li><font fgcolor="#ffffffff"><b>"\'Accounts\'"</b></font>" om een account toe te voegen of een account te configureren met contacten die u kunt synchroniseren met de telefoon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Nieuw contact\'"</b></font>" om een geheel nieuw contact te maken"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Importeren/exporteren\'"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"U heeft geen contacten om weer te geven. (Als u net een account heeft toegevoegd, kan het enkele minuten duren voordat de contacten zijn gesynchroniseerd.)"\n\n"Als u contacten wilt toevoegen, drukt u op "<font fgcolor="#ffffffff"><b>"\'Menu\'"</b></font>" en raakt u de volgende opties aan:"\n" "\n<li><font fgcolor="#ffffffff"><b>"\'Accounts\'"</b></font>" om een account toe te voegen of een account te configureren met contacten die u kunt synchroniseren met de telefoon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Weergaveopties\'"</b></font>" om de zichtbaarheid van contacten te wijzigen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Nieuw contact\'"</b></font>" om een geheel nieuw contact te maken"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Importeren/exporteren\'"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"U heeft geen contacten om weer te geven."\n\n"Als u contacten wilt toevoegen, drukt u op "<font fgcolor="#ffffffff"><b>"\'Menu\'"</b></font>" en raakt u de volgende opties aan:"\n" "\n<li><font fgcolor="#ffffffff"><b>"\'Account "</b></font>" om een account toe te voegen of een account te configureren met contacten die u kunt synchroniseren met de telefoon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Nieuw contact\'"</b></font>" om een geheel nieuw contact te maken"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Importeren/exporteren\'"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"U heeft geen contacten om weer te geven. (Als u net een account heeft toegevoegd, kan het enkele minuten duren voordat de contacten zijn gesynchroniseerd.)"\n\n"Als u contacten wilt toevoegen, drukt u op "<font fgcolor="#ffffffff"><b>"\'Menu\'"</b></font>" en raakt u de volgende opties aan:"\n" "\n<li><font fgcolor="#ffffffff"><b>"\'Accounts\'"</b></font>" om een account toe te voegen of een account te configureren met contacten die u kunt synchroniseren met de telefoon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Weergaveopties\'"</b></font>" om de zichtbaarheid van contacten te wijzigen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Nieuw contact\'"</b></font>" om een geheel nieuw contact te maken"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Importeren/exporteren\'"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"U heeft geen contacten om weer te geven."\n\n"Als u contacten wilt toevoegen, drukt u op "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" en raakt u de volgende opties aan:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" om een account toe te voegen of te configureren met contacten die u kunt synchroniseren met de tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nieuw contact"</b></font>" om een geheel nieuw contact te maken"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importeren/exporteren"</b></font>" om contacten te importeren van uw SIM- of SD-kaart"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"U heeft geen contacten om weer te geven."\n\n"Als u contacten wilt toevoegen, drukt u op "<font fgcolor="#ffffffff"><b>"\'Menu\'"</b></font>" en raakt u de volgende opties aan:"\n" "\n<li><font fgcolor="#ffffffff"><b>"\'Accounts\'"</b></font>" om een account toe te voegen of te configureren met contacten die u kunt synchroniseren met de telefoon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Nieuw contact\'"</b></font>" om een geheel nieuw contact te maken"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Importeren/exporteren\'"</b></font>" om contacten te importeren van uw SIM- of SD-kaart"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"U heeft geen contacten om weer te geven. (Als u net een account heeft toegevoegd, kan het enkele minuten duren voordat de contacten zijn gesynchroniseerd.)"\n\n"Als u contacten wilt toevoegen, drukt u op "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" en raakt u de volgende opties aan:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" om een account toe te voegen of te configureren met contacten die u kunt synchroniseren met de tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Weergaveopties"</b></font>" om de zichtbaarheid van contacten te wijzigen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nieuw contact"</b></font>" om een geheel nieuw contact toe te voegen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importeren/exporteren"</b></font>" om contacten van uw SIM- of SD-kaart te importeren"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"U heeft geen contacten om weer te geven. (Als u net een account heeft toegevoegd, kan het enkele minuten duren voordat de contacten zijn gesynchroniseerd.)"\n\n"Als u contacten wilt toevoegen, drukt u op "<font fgcolor="#ffffffff"><b>"\'Menu\'"</b></font>" en raakt u de volgende opties aan:"\n" "\n<li><font fgcolor="#ffffffff"><b>"\'Accounts\'"</b></font>" om een account toe te voegen of te configureren met contacten die u kunt synchroniseren met de telefoon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Weergaveopties\'"</b></font>" om de zichtbaarheid van contacten te wijzigen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Nieuw contact\'"</b></font>" om een geheel nieuw contact toe te voegen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Importeren/exporteren\'"</b></font>" om contacten van uw SIM- of SD-kaart te importeren"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"U heeft geen contacten om weer te geven."\n\n"Als u contacten wilt toevoegen, drukt u op "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" en raakt u de volgende opties aan:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" om een account toe te voegen of te configureren met contacten die u kunt synchroniseren met de tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nieuw contact"</b></font>" om een geheel nieuw contact te maken"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importeren/exporteren"</b></font>" om contacten van uw SD-kaart te importeren"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"U heeft geen contacten om weer te geven."\n\n"Als u contacten wilt toevoegen, drukt u op "<font fgcolor="#ffffffff"><b>"\'Menu\'"</b></font>" en raakt u de volgende opties aan:"\n" "\n<li><font fgcolor="#ffffffff"><b>"\'Accounts\'"</b></font>" om een account toe te voegen of te configureren met contacten die u kunt synchroniseren met de telefoon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Nieuw contact\'"</b></font>" om een geheel nieuw contact te maken"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Importeren/exporteren\'"</b></font>" om contacten van uw SD-kaart te importeren"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"U heeft geen contacten om weer te geven. (Als u net een account heeft toegevoegd, kan het enkele minuten duren voordat de contacten zijn gesynchroniseerd.)"\n\n"Als u contacten wilt toevoegen, drukt u op "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" en raakt u de volgende opties aan:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Accounts"</b></font>" om een account toe te voegen of te configureren met contacten die u kunt synchroniseren met de tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Weergaveopties"</b></font>" om de zichtbaarheid van contacten te wijzigen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nieuw contact"</b></font>" om een geheel nieuw contact te maken"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importeren/exporteren"</b></font>" om contacten van uw SD-kaart te importeren"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"U heeft geen contacten om weer te geven. (Als u net een account heeft toegevoegd, kan het enkele minuten duren voordat de contacten zijn gesynchroniseerd.)"\n\n"Als u contacten wilt toevoegen, drukt u op "<font fgcolor="#ffffffff"><b>"\'Menu\'"</b></font>" en raakt u de volgende opties aan:"\n" "\n<li><font fgcolor="#ffffffff"><b>"\'Accounts\'"</b></font>" om een account toe te voegen of te configureren met contacten die u kunt synchroniseren met de telefoon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Weergaveopties\'"</b></font>" om de zichtbaarheid van contacten te wijzigen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Nieuw contact\'"</b></font>" om een geheel nieuw contact te maken"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"\'Importeren/exporteren\'"</b></font>" om contacten van uw SD-kaart te importeren"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"U heeft geen favorieten."\n\n"U kunt als volgt een contact toevoegen aan uw lijst met favorieten:"\n\n" "<li>"Raak het tabblad \'"<b>"Contacten"</b>"\' aan."\n</li>" "\n<li>"Raak het contact aan dat u wilt toevoegen aan uw favorieten."\n</li>" "\n<li>"Raak de ster aan die wordt weergegeven naast de naam van het contact."\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Alle contacten"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Met ster"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Terugbellen"</string>
<string name="callAgain" msgid="3197312117049874778">"Opnieuw bellen"</string>
<string name="returnCall" msgid="8171961914203617813">"Terugbellen"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min. <xliff:g id="SECONDS">%2$s</xliff:g> sec."</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> sec."</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Regelmatig contact"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Contact toevoegen"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Voeg \"<xliff:g id="EMAIL">%s</xliff:g>\" toe aan contactpersonen?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Alles"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"één"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"twee"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"drie"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Scannen van USB-opslag is mislukt (reden: \'<xliff:g id="FAIL_REASON">%s</xliff:g>\')"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Scannen van SD-kaart is mislukt: (Reden: \'<xliff:g id="FAIL_REASON">%s</xliff:g>\')"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"I/O-fout"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Onvoldoende geheugen (het bestand is mogelijk te groot)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Parseren van vCard is mislukt om onbekende reden"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Kan vCard niet parseren, ook al lijkt de indeling geldig. vCard wordt niet ondersteund door de huidige implementatie"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Geen vCard-bestand gevonden in USB-opslag"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Geen vCard-bestand gevonden op SD-kaart"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"De indeling wordt niet ondersteund."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Kan vCard niet importeren"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Geen vCard-bestand gevonden in USB-opslag"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Geen vCard-bestand gevonden op SD-kaart"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Verzamelen van metagegevens van betreffende vCard-bestanden is mislukt."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Een of meer bestanden kunnen niet worden geïmporteerd (%s)"</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Onbekende fout"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"vCard-bestand selecteren"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"vCard lezen"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"vCard-bestand(en) lezen"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Lezen van vCard-gegevens is mislukt"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> van <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contacten"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> van <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> bestanden"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"vCard(s) cachen in lokale tijdelijke opslag"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"De importer cacht vCard(s) in de lokale tijdelijke opslag. Daadwerkelijk importeren begint gauw."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importeren <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"<xliff:g id="NAME">%s</xliff:g> importeren"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Lezen van vCard-gegevens is mislukt"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Lezen van vCard-gegevens is geannuleerd"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Importeren van vCard <xliff:g id="FILENAME">%s</xliff:g> voltooid"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Importeren van <xliff:g id="FILENAME">%s</xliff:g> is geannuleerd"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> wordt binnenkort geïmporteerd."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Het bestand wordt binnenkort geïmporteerd."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Verzoek voor importeren van vCard is geweigerd. Probeer het later opnieuw."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> wordt binnenkort geëxporteerd."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Verzoek voor exporteren van vCard is geweigerd. Probeer het later opnieuw."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"contact"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Exporteren bevestigen"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Weet u zeker dat u uw lijst met contacten wilt exporteren naar \'<xliff:g id="VCARD_FILENAME">%s</xliff:g>\'?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Exporteren van contactgegevens mislukt"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Te veel vCard-bestanden in de USB-opslag"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Te veel vCard-gegevens op de SD-kaart"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Vereiste bestandsnaam is te lang (\'<xliff:g id="FILENAME">%s</xliff:g>\')"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Exporteren van <xliff:g id="FILENAME">%s</xliff:g> voltooid"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Exporteren van <xliff:g id="FILENAME">%s</xliff:g> is geannuleerd"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Contactgegevens exporteren"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Contactgegevens exporteren naar \'<xliff:g id="FILE_NAME">%s</xliff:g>\'"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Kan het exportprogramma niet initialiseren: \'<xliff:g id="EXACT_REASON">%s</xliff:g>\'"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Fout opgetreden tijdens export: \'<xliff:g id="EXACT_REASON">%s</xliff:g>\'"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Databasegegevens ophalen mislukt"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Er is geen contact dat kan worden geëxporteerd. Als er contacten op uw telefoon staan, kan het exporteren van alle contacten vanaf uw telefoon mogelijk worden geblokkeerd door een gegevensprovider."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Er zijn geen contacten die kunnen worden geëxporteerd. Als er contacten op uw tablet staan, kan het exporteren van alle contacten vanaf uw tablet mogelijk worden geblokkeerd door bepaalde gegevensproviders."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Er zijn geen contacten die kunnen worden geëxporteerd. Als er contacten op uw telefoon staan, kan het exporteren van alle contacten vanaf uw telefoon mogelijk worden geblokkeerd door bepaalde gegevensproviders."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"De vCard-editor is niet juist geïnitialiseerd"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Kan \'<xliff:g id="FILE_NAME">%1$s</xliff:g>\' niet openen: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> van <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contacten"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Kan \'<xliff:g id="FILE_NAME">%s</xliff:g>\' niet openen: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> van <xliff:g id="TOTAL_NUMBER">%s</xliff:g> contacten"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Importeren van vCard annuleren"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Weet u zeker dat u het importeren van <xliff:g id="FILENAME">%s</xliff:g> annuleren?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Exporteren van vCard annuleren"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Weet u zeker dat u het exporteren van <xliff:g id="FILENAME">%s</xliff:g> wilt annuleren?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Annuleren van import/export vCard mislukt"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Namen van uw contacten"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Pauze van 2 seconden toevoegen"</string>
<string name="add_wait" msgid="3360818652790319634">"Wachten toevoegen"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Er is geen toepassing gevonden om deze actie uit te voeren"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Deze keuze onthouden"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Onbekend"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Geen gegevens"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Standaardwaarden wissen"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Standaardwaarden ingesteld voor contact:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Wissen"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Accounts"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importeren/exporteren"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Contacten importeren/exporteren"</string>
- <string name="menu_share" msgid="943789700636542260">"Delen"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Contact delen"</string>
<string name="share_via" msgid="563121028023030093">"Contact delen via"</string>
<string name="share_error" msgid="4374508848981697170">"Dit contact kan niet worden gedeeld."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Naam"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organisatie"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Website"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Afspraak"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relatie"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Groepen"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"W"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Dit contact is alleen-lezen"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Meer"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Primaire naam"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Contact in account maken"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Synchronisatiegroep verwijderen"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Synchronisatiegroep toevoegen"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Alle andere contacten"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Alle contacten"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Als u \'<xliff:g id="GROUP">%s</xliff:g>\' verwijdert uit de synchronisatie, worden ook contacten die niet bij een groep horen uit de synchronisatie verwijderd."</string>
- <string name="account_phone" msgid="3682950835276226870">"Alleen voor telefoon, niet gesynchroniseerd"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Alleen tablet, niet gesynchroniseerd"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Alleen voor telefoon, niet gesynchroniseerd"</string>
<string name="call_custom" msgid="7756571794763171802">"<xliff:g id="CUSTOM">%s</xliff:g> bellen"</string>
<string name="call_home" msgid="1990519474420545392">"Bellen naar huis"</string>
<string name="call_mobile" msgid="7502236805487609178">"Bellen naar mobiel"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chatten via ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chatten via Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Chat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adres"</string>
<string name="postal_street" msgid="8133143961580058972">"Straat"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Postbus"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Wijk"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Staat"</string>
<string name="postal_postcode" msgid="572136414136673751">"Postcode"</string>
<string name="postal_country" msgid="7638264508416368690">"Land"</string>
+ <string name="full_name" msgid="6602579550613988977">"Naam"</string>
<string name="name_given" msgid="1687286314106019813">"Roepnaam"</string>
<string name="name_family" msgid="3416695586119999058">"Achternaam"</string>
<string name="name_prefix" msgid="59756378548779822">"Voorvoegsel"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Contacten zoeken"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Alle contacten zoeken"</string>
<string name="take_photo" msgid="7496128293167402354">"Foto maken"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Nieuwe foto nemen"</string>
<string name="pick_photo" msgid="448886509158039462">"Foto selecteren in Galerij"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Lijst met contacten wordt bijgewerkt om de gewijzigde taal te weerspiegelen."\n\n"Een ogenblik geduld..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Lijst met contacten wordt bijgewerkt."\n\n"Een ogenblik geduld..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Contacten worden bijgewerkt. "\n\n"Voor het upgradeproces is ongeveer <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB interne telefoonopslag vereist."\n\n"Kies een van de volgende opties:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Nieuwe foto selecteren in Galerij"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Lijst met contacten wordt bijgewerkt om de gewijzigde taal te weerspiegelen."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Lijst met contactpersonen wordt bijgewerkt."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Contacten worden bijgewerkt. "\n\n"Voor het upgradeproces is ongeveer <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB interne telefoonopslag vereist."\n\n"Kies een van de volgende opties."</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Verwijder enkele toepassingen"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Upgrade opnieuw proberen"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Zoekresultaten voor: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Zoeken..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Selectie weergeven"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Alles weergeven"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Alles selecteren"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Selectie ongedaan maken"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"1 ontvanger geselecteerd"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g> ontvangers geselecteerd"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Geen contacten geselecteerd."</string>
+ <string name="add_field" msgid="2384260056674995230">"Nog een veld toevoegen"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favoriet"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Contact bewerken"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"niet samengevoegd"</item>
+ <item quantity="other" msgid="425683718017380845">"samengevoegd uit <xliff:g id="COUNT">%0$d</xliff:g> bronnen"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Overig"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Contacten samenvoegen"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Wilt u het huidige contact samenvoegen met het geselecteerde contact?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Geselecteerde contacten bewerken"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Wilt u overschakelen naar het bewerken van het geselecteerde contact? Gegevens die u tot nu toe heeft ingevoerd, worden gekopieerd."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Kopiëren naar mijn contacten"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Directory <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Alle contacten doorzoeken"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Directory"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Contacten"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Een persoonlijke kopie maken"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Contactlijst kiezen"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Alle contacten"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Met ster"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Aangepast"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Aanpassen..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Contacten met telefoonnummers"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Contacten"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Aangepaste weergave definiëren"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Instellingen"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Instellingen"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Weergaveopties"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Contacten vinden"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Telefoonnummer"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Toevoegen aan contacten"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Sluiten"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Een jaar opgeven"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Contacten"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Laden…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Een nieuw contact maken"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Aanmelden bij een account"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Contacten importeren uit een bestand"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Nieuwe groep maken"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Nieuwe groep maken]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Naam van groep wijzigen"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Groep verwijderen"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Weet u zeker dat u de groep \'<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\' wilt verwijderen\'? (De contacten worden niet verwijderd.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Geef contactnaam op voordat u dit contact samenvoegt met een ander contact."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Samengevoegd contact"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Tekst gekopieerd"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Wijzigingen annuleren"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Wilt u uw wijzigingen annuleren?"</string>
+ <string name="discard" msgid="1234315037371251414">"Verwijderen"</string>
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index ec8b946..40aa3b2 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Wybierz skrót kontaktu"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Wybierz numer, aby nawiązać połączenie"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Wybierz numer, aby wysłać wiadomość"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Wybierz kontakt"</string>
<string name="starredList" msgid="4817256136413959463">"Oznaczony gwiazdką"</string>
<string name="frequentList" msgid="7154768136473953056">"Częste"</string>
<string name="strequentList" msgid="5640192862059373511">"Ulubione"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Usuń kontakt"</string>
<string name="menu_call" msgid="3992595586042260618">"Zadzwoń do kontaktu"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Wyślij tekst do kontaktu"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Wyślij e-mail"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Pokaż adres na mapie"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Ustaw ten numer jako domyślny"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Ustaw jako domyślny adres e-mail"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Podziel"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Kontakty zostały podzielone"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Zmień nazwę grupy"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Usuń grupę"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Nowy"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Podziel kontakt"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Czy na pewno chcesz podzielić ten pojedynczy kontakt na kilka kontaktów: po jednym dla każdego zawartego w nim zestawu informacji kontaktowych?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Połącz"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Usunięcie tego kontaktu spowoduje usunięcie informacji z wielu kont."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Ten kontakt zostanie usunięty."</string>
<string name="menu_done" msgid="796017761764190697">"Gotowe"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Przywróć"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Anuluj"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Edytuj kontakt"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Nowy kontakt"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetycznie"</string>
<string name="label_notes" msgid="8337354953278341042">"Notatki"</string>
<string name="label_sip_address" msgid="124073911714324974">"Rozmowa internetowa"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Dzwonek"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Imię i nazwisko"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Nazwisko (fonetycznie)"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Firma"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Stanowisko"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Kontakt nie istnieje."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Utwórz nowy kontakt"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Wybierz etykietę"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-mail"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Komunikatory"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Adres pocztowy"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adres"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organizacja"</item>
<item msgid="7196592230748086755">"Notatka"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"W telefonie brak dostępnych zdjęć."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Ikona kontaktu"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Brak zdjęć dostępnych w tablecie."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"W telefonie brak dostępnych zdjęć."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Zdjęcie kontaktu"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Nazwa etykiety niestandardowej"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Opcje wyświetlania"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Opcje wyświetlania"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Przekieruj połączenia bezpośrednio na pocztę głosową"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Domyślny"</string>
- <string name="changePicture" msgid="2943329047610967714">"Zmień ikonę"</string>
- <string name="removePicture" msgid="3041230993155966350">"Usuń ikonę"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Usuń zdjęcie"</string>
<string name="noContacts" msgid="8579310973261953559">"Brak kontaktów"</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Nie znaleziono pasujących kontaktów."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Brak kontaktów z numerami telefonów"</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Kontakt został zapisany."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Błąd. Nie można zapisać zmian wprowadzonych w kontakcie."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Wyświetlanie 1 kontaktu z numerem telefonu"</item>
- <item quantity="other" msgid="6133262880804110289">"Wyświetlanych kontaktów z numerami telefonów: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="3015357862286673986">"Liczba kontaktów z numerem telefonu: 1"</item>
+ <item quantity="other" msgid="3299954047880968205">"Liczba kontaktów z numerami telefonów: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Brak widocznych kontaktów z numerami telefonów"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Brak kontaktów z numerami telefonów"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Wyświetlanie 1 kontaktu"</item>
- <item quantity="other" msgid="2865867557378939630">"Wyświetlanych kontaktów: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="3405747744700823280">"Liczba kontaktów: 1"</item>
+ <item quantity="other" msgid="3578469907265375314">"Liczba kontaktów: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Brak widocznych kontaktów"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Brak kontaktów"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Brak widocznych kontaktów"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Brak kontaktów oznaczonych gwiazdką"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Brak kontaktów w grupie <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Znaleziono 1 kontakt"</item>
- <item quantity="other" msgid="7752927996850263152">"Znalezionych kontaktów: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="5517063038754171134">"Znaleziono: 1"</item>
+ <item quantity="other" msgid="3852668542926965042">"Znaleziono: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Nie znaleziono kontaktu"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"znaleziono więcej niż <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Nie znaleziono"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"Liczba kontaktów: 1"</item>
- <item quantity="other" msgid="5660384247071761844">"Liczba kontaktów: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="4826918429708286628">"Znaleziono: 1"</item>
+ <item quantity="other" msgid="7988132539476575389">"Znaleziono: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Kontakty"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Ulubione"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Kontakty z karty SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Nie masz żadnych kontaktów do wyświetlenia (jeśli konto zostało dopiero dodane, synchronizacja kontaktów może potrwać kilka minut)."</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Nie masz żadnych kontaktów do wyświetlenia."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Nie masz żadnych kontaktów do wyświetlenia."\n\n"Aby dodać kontakty, naciśnij przycisk "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" i dotknij opcji:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konta"</b></font>", aby dodać lub skonfigurować konto zawierające kontakty, które można synchronizować z telefonem."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nowy kontakt"</b></font>", aby utworzyć nowy kontakt od początku."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuj/eksportuj"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Nie masz żadnych kontaktów do wyświetlenia. (Jeśli przed chwilą dodano konto, synchronizacja kontaktów może potrwać kilka minut)."\n\n"Aby dodać kontakty, naciśnij przycisk "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" i dotknij opcji:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konta"</b></font>", aby dodać lub skonfigurować konto zawierające kontakty, które można synchronizować z telefonem."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcje wyświetlania"</b></font>", aby zmienić widoczne typy kontaktów."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nowy kontakt"</b></font>", aby utworzyć nowy kontakt od początku."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuj/eksportuj"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Nie masz żadnych kontaktów do wyświetlenia."\n\n"Aby dodać kontakty, naciśnij przycisk "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" i dotknij opcji:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konta"</b></font>", aby dodać lub skonfigurować konto zawierające kontakty, które można synchronizować z telefonem."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nowy kontakt"</b></font>", aby utworzyć nowy kontakt od początku."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuj/eksportuj"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Nie masz żadnych kontaktów do wyświetlenia. (Jeśli przed chwilą dodano konto, synchronizacja kontaktów może potrwać kilka minut)."\n\n"Aby dodać kontakty, naciśnij przycisk "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" i dotknij opcji:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konta"</b></font>", aby dodać lub skonfigurować konto zawierające kontakty, które można synchronizować z telefonem."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcje wyświetlania"</b></font>", aby zmienić widoczne typy kontaktów."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nowy kontakt"</b></font>", aby utworzyć nowy kontakt od początku."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuj/eksportuj"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Nie masz żadnych kontaktów do wyświetlenia."\n\n"Aby dodać kontakty, naciśnij przycisk "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" i dotknij opcji:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konta"</b></font>", aby dodać lub skonfigurować konto zawierające kontakty, które można synchronizować z tabletem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nowy kontakt"</b></font>", aby utworzyć nowy kontakt od początku;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuj/eksportuj"</b></font>", aby zaimportować kontakty z karty SIM lub SD."\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Nie masz żadnych kontaktów do wyświetlenia."\n\n"Aby dodać kontakty, naciśnij przycisk "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" i dotknij opcji:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konta"</b></font>", aby dodać lub skonfigurować konto zawierające kontakty, które można synchronizować z telefonem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nowy kontakt"</b></font>", aby utworzyć nowy kontakt od początku;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuj/eksportuj"</b></font>", aby zaimportować kontakty z karty SIM lub SD."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Nie masz żadnych kontaktów do wyświetlenia. (Jeśli przed chwilą dodano konto, synchronizacja kontaktów może potrwać kilka minut)."\n\n"Aby dodać kontakty, naciśnij przycisk "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" i dotknij opcji:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konta"</b></font>", aby dodać lub skonfigurować konto zawierające kontakty, które można synchronizować z tabletem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcje wyświetlania"</b></font>", aby zmienić widoczne typy kontaktów;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nowy kontakt"</b></font>", aby utworzyć nowy kontakt od początku;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuj/eksportuj"</b></font>", aby zaimportować kontakty z karty SIM lub SD."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Nie masz żadnych kontaktów do wyświetlenia. (Jeśli przed chwilą dodano konto, synchronizacja kontaktów może potrwać kilka minut)."\n\n"Aby dodać kontakty, naciśnij przycisk "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" i dotknij opcji:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konta"</b></font>", aby dodać lub skonfigurować konto zawierające kontakty, które można synchronizować z telefonem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcje wyświetlania"</b></font>", aby zmienić widoczne typy kontaktów;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nowy kontakt"</b></font>", aby utworzyć nowy kontakt od początku;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuj/eksportuj"</b></font>", aby zaimportować kontakty z karty SIM lub SD."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Nie masz żadnych kontaktów do wyświetlenia."\n\n"Aby dodać kontakty, naciśnij przycisk "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" i dotknij opcji:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konta"</b></font>", aby dodać lub skonfigurować konto zawierające kontakty, które można synchronizować z tabletem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nowy kontakt"</b></font>", aby utworzyć nowy kontakt od początku;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuj/eksportuj"</b></font>", aby zaimportować kontakty z karty SD."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Nie masz żadnych kontaktów do wyświetlenia."\n\n"Aby dodać kontakty, naciśnij przycisk "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" i dotknij opcji:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konta"</b></font>", aby dodać lub skonfigurować konto zawierające kontakty, które można synchronizować z telefonem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nowy kontakt"</b></font>", aby utworzyć nowy kontakt od początku;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuj/eksportuj"</b></font>", aby zaimportować kontakty z karty SD."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Nie masz żadnych kontaktów do wyświetlenia. (Jeśli przed chwilą dodano konto, synchronizacja kontaktów może potrwać kilka minut)."\n\n"Aby dodać kontakty, naciśnij przycisk "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" i dotknij opcji:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konta"</b></font>", aby dodać lub skonfigurować konto zawierające kontakty, które można synchronizować z tabletem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcje wyświetlania"</b></font>", aby zmienić widoczne typy kontaktów;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nowy kontakt"</b></font>", aby utworzyć nowy kontakt od początku;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuj/eksportuj"</b></font>", aby zaimportować kontakty z karty SD."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Nie masz żadnych kontaktów do wyświetlenia. (Jeśli przed chwilą dodano konto, synchronizacja kontaktów może potrwać kilka minut)."\n\n"Aby dodać kontakty, naciśnij przycisk "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" i dotknij opcji: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Konta"</b></font>", aby dodać lub skonfigurować konto zawierające kontakty, które można synchronizować z telefonem;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opcje wyświetlania"</b></font>", aby zmienić widoczne typy kontaktów;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nowy kontakt"</b></font>", aby utworzyć nowy kontakt od początku;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importuj/eksportuj"</b></font>", aby zaimportować kontakty z karty SD."\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Nie masz żadnych ulubionych kontaktów."\n\n"Aby dodać kontakt do listy ulubionych:"\n\n" "<li>"Dotknij karty "<b>"Kontakty"</b>"."\n</li>" "\n<li>"Dotknij kontaktu, który chcesz dodać do ulubionych."\n</li>" "\n<li>"Dotknij ikony gwiazdki obok nazwy kontaktu."\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Wszystkie kontakty"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Oznaczone gwiazdką"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Oddzwoń"</string>
<string name="callAgain" msgid="3197312117049874778">"Zadzwoń ponownie"</string>
<string name="returnCall" msgid="8171961914203617813">"Połączenie zwrotne"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min <xliff:g id="SECONDS">%2$s</xliff:g> s"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Częste kontakty"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Dodaj kontakt"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Czy dodać adres „<xliff:g id="EMAIL">%s</xliff:g>” do kontaktów?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Wszystkie"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"jeden"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dwa"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"trzy"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Skanowanie nośnika USB nie powiodło się (przyczyna: „<xliff:g id="FAIL_REASON">%s</xliff:g>”)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Skanowanie karty SD nie powiodło się (przyczyna: „<xliff:g id="FAIL_REASON">%s</xliff:g>”)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Błąd wejścia/wyjścia"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Niewystarczająca ilość pamięci (plik może być zbyt duży)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Nie można zanalizować pliku vCard z nieoczekiwanego powodu"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Nie można zanalizować pliku vCard, mimo że prawdopodobnie ma on prawidłowy format. Bieżąca wersja go nie obsługuje."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Na nośniku USB nie znaleziono pliku vCard"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Na karcie SD nie znaleziono żadnego pliku vCard"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Ten format nie jest obsługiwany."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Nie można zaimportować karty vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Na nośniku USB nie znaleziono żadnego pliku vCard"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Na karcie SD nie znaleziono żadnego pliku vCard"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Nie można zebrać metainformacji z podanych plików vCard."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Nie można zaimportować co najmniej jednego pliku (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Nieznany błąd"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Wybierz plik vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Odczytywanie danych vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Odczytywanie plików vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Odczytywanie danych vCard nie powiodło się"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"Kontakt <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> z <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"plik <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> z <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Buforowanie kart vCard w lokalnym obszarze tymczasowym"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Program importujący buforuje karty vCard w lokalnym obszarze tymczasowym. Rzeczywisty proces importowania rozpocznie się za chwilę."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importowanie <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importowanie <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Nie można odczytać danych karty vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Odczytywanie danych kart vCard zostało anulowane"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Zakończono importowanie pliku vCard <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Importowanie pliku <xliff:g id="FILENAME">%s</xliff:g> zostało anulowane"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"Plik <xliff:g id="FILENAME">%s</xliff:g> zostanie za chwilę zaimportowany."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Plik zostanie za chwilę zaimportowany."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Żądanie importu danych vCard zostało odrzucone. Spróbuj ponownie później."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"Plik <xliff:g id="FILENAME">%s</xliff:g> zostanie za chwilę wyeksportowany."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Żądanie eksportu danych vCard zostało odrzucone. Spróbuj ponownie później."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"kontakt"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Potwierdź eksport"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Czy na pewno chcesz wyeksportować listę kontaktów do pliku „<xliff:g id="VCARD_FILENAME">%s</xliff:g>”?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Nie można wyeksportować danych kontaktu"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Zbyt wiele plików vCard na nośniku USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Zbyt dużo plików vCard na karcie SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Wymagana nazwa pliku jest zbyt długa („<xliff:g id="FILENAME">%s</xliff:g>”)"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Zakończono eksportowanie pliku <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Eksportowanie pliku <xliff:g id="FILENAME">%s</xliff:g> zostało anulowane"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Eksportowanie danych kontaktowych"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Eksportowanie danych kontaktowych do pliku „<xliff:g id="FILE_NAME">%s</xliff:g>”"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Nie można zainicjować programu eksportującego: „<xliff:g id="EXACT_REASON">%s</xliff:g>”"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Wystąpił błąd podczas eksportowania: „<xliff:g id="EXACT_REASON">%s</xliff:g>”"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Nie można pobrać informacji o bazie danych"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Brak kontaktów, które można wyeksportować. Jeśli faktycznie masz kontakty w telefonie, wyeksportowanie któregokolwiek z nich poza telefon może być zabronione przez dostawcę danych."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Brak kontaktów, które można wyeksportować. Jeśli faktycznie masz kontakty w tablecie, wyeksportowanie ich poza tablet może być zabronione przez dostawcę danych."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Brak kontaktów, które można wyeksportować. Jeśli faktycznie masz kontakty w telefonie, wyeksportowanie ich poza telefon może być zabronione przez dostawcę danych."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Obiekt tworzenia danych vCard nie został poprawnie zainicjowany"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Nie można otworzyć pliku „<xliff:g id="FILE_NAME">%1$s</xliff:g>”: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"Kontakt <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> z <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Nie można otworzyć pliku „<xliff:g id="FILE_NAME">%s</xliff:g>”: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"Kontakt <xliff:g id="CURRENT_NUMBER">%s</xliff:g> z <xliff:g id="TOTAL_NUMBER">%s</xliff:g>"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Anulowanie importowania pliku vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Czy na pewno anulować importowanie pliku <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Anulowanie eksportowania pliku vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Czy na pewno chcesz anulować eksportowanie pliku <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Nie można anulować imp./eksp. pliku vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Imiona i nazwiska oraz nazwy w Twoich kontaktach"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Dodaj 2-sekundową pauzę"</string>
<string name="add_wait" msgid="3360818652790319634">"Dodaj oczekiwanie"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Nie znaleziono aplikacji do obsługi tej akcji"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Zapamiętaj ten wybór"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Nieznane"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Brak danych"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Wyczyść wartości domyślne"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Wartości domyślne dla tego kontaktu:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Wyczyść"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Konta"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importuj/eksportuj"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importuj/eksportuj kontakty"</string>
- <string name="menu_share" msgid="943789700636542260">"Udostępnij"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Udostępnij kontakt"</string>
<string name="share_via" msgid="563121028023030093">"Udostępnij kontakt przez"</string>
<string name="share_error" msgid="4374508848981697170">"Tego kontaktu nie można udostępniać."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Nazwa"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organizacja"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Adres witryny"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Wydarzenie"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relacja"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grupy"</string>
<string name="type_short_home" msgid="7770424864090605384">"D"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"K"</string>
<string name="type_short_work" msgid="4925330752504537861">"S"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"I"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Ten kontakt jest tylko do odczytu"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Więcej"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Nazwa główna"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Utwórz kontakt na koncie"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Usuń grupę synchronizacji"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Dodaj grupę synchronizacji"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Wszystkie inne kontakty"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Wszystkie kontakty"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Usunięcie grupy „<xliff:g id="GROUP">%s</xliff:g>” z ustawień synchronizacji spowoduje również usunięcie wszelkich rozgrupowanych kontaktów z tych ustawień."</string>
- <string name="account_phone" msgid="3682950835276226870">"Tylko telefon, brak synchronizacji"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Tylko tablet, brak synchronizacji"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Tylko telefon, brak synchronizacji"</string>
<string name="call_custom" msgid="7756571794763171802">"Połącz – <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Połącz – domowy"</string>
<string name="call_mobile" msgid="7502236805487609178">"Połącz – komórka"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Czat w ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Czat w Jabberze"</string>
<string name="chat" msgid="9025361898797412245">"Czat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adres"</string>
<string name="postal_street" msgid="8133143961580058972">"Ulica"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Skrytka pocztowa"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Dzielnica"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Województwo"</string>
<string name="postal_postcode" msgid="572136414136673751">"Kod pocztowy"</string>
<string name="postal_country" msgid="7638264508416368690">"Kraj"</string>
+ <string name="full_name" msgid="6602579550613988977">"Imię i nazwisko"</string>
<string name="name_given" msgid="1687286314106019813">"Imię"</string>
<string name="name_family" msgid="3416695586119999058">"Nazwisko"</string>
<string name="name_prefix" msgid="59756378548779822">"Przedrostek nazwiska"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Szukaj kontaktów"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Szukaj wszystkich kontaktów"</string>
<string name="take_photo" msgid="7496128293167402354">"Zrób zdjęcie"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Zrób nowe zdjęcie"</string>
<string name="pick_photo" msgid="448886509158039462">"Wybierz zdjęcie z galerii"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Lista kontaktów jest aktualizowana, aby odzwierciedlić zmianę języka."\n\n"Czekaj..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Lista kontaktów jest aktualizowana."\n\n"Czekaj..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Trwa proces uaktualniania kontaktów. "\n\n"Proces uaktualniania wymaga w przybliżeniu <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB wewnętrznej pamięci telefonu."\n\n"Wybierz jedną z następujących opcji:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Wybierz nowe zdjęcie z galerii"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Lista kontaktów jest aktualizowana, aby odzwierciedlić zmianę języka."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Lista kontaktów jest aktualizowana."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Trwa proces uaktualniania kontaktów. "\n\n"Wymaga on około <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB pamięci wewnętrznej."\n\n"Wybierz jedną z następujących opcji:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Odinstaluj część aplikacji"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Ponów próbę uaktualnienia"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Wyniki wyszukiwania dla: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Trwa wyszukiwanie..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Pokaż wybrane"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Pokaż wszystkie"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Wybierz wszystko"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Anuluj wybór wszystkich"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"Wybrano 1 odbiorcę"</item>
+ <item quantity="other" msgid="4608837420986126229">"Liczba wybranych odbiorców: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Nie wybrano kontaktów."</string>
+ <string name="add_field" msgid="2384260056674995230">"Dodaj inne pole"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"za pośrednictwem: <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g>, za pośrednictwem: <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"ulubione"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Edytuj kontakt"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"nie scalono"</item>
+ <item quantity="other" msgid="425683718017380845">"scalono z <xliff:g id="COUNT">%0$d</xliff:g> źródeł"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Inne"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Połącz kontakty"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Czy połączyć bieżący kontakt z wybranym?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Edytuj wybrane kontakty"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Czy chcesz edytować wybrany kontakt? Wprowadzone dotąd informacje zostaną skopiowane."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Kopiuj do moich kontaktów"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Katalog <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Wyszukiwanie wszystkich kontaktów"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Katalog"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Kontakty"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Tworzenie kopii osobistej"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Wybierz listę kontaktów"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Wszystkie kontakty"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Oznaczone gwiazdką"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Niestandardowy"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Dostosuj..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Kontakty z numerami telefonów"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kontakt"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Zdefiniuj widok niestandardowy"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Ustawienia"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Ustawienia"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Opcje wyświetlania"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Znajdź kontakty"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Numer telefonu"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Dodaj do kontaktów"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Zamknij"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Podaj rok"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Kontakt"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Wczytywanie…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Utwórz nowy kontakt"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Zaloguj się na konto"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Zaimportuj kontakty z pliku"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Utwórz nową grupę"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Utwórz nową grupę]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Zmień nazwę grupy"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Usuń grupę"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Czy na pewno chcesz usunąć grupę „<xliff:g id="GROUP_LABEL">%1$s</xliff:g>”? (Kontakty nie zostaną usunięte)."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Wprowadź w kontakcie imię i nazwisko, zanim połączysz go z innym kontaktem."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Łączony kontakt"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Tekst skopiowany"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Odrzuć zmiany"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Czy chcesz odrzucić wprowadzone zmiany?"</string>
+ <string name="discard" msgid="1234315037371251414">"Odrzuć"</string>
</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index ec651e3..4d1a10c 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Seleccionar um atalho de contacto"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Seleccionar um número a marcar"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Seleccionar um número para enviar mensagem"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Seleccionar um contacto"</string>
<string name="starredList" msgid="4817256136413959463">"Marcado com estrela"</string>
<string name="frequentList" msgid="7154768136473953056">"Frequentes"</string>
<string name="strequentList" msgid="5640192862059373511">"Favoritos"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Eliminar contacto"</string>
<string name="menu_call" msgid="3992595586042260618">"Ligar para contacto"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Enviar SMS/MMS para contacto"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Enviar e-mail"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Endereço no mapa"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Utilizar como número predefinido"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Predefinir e-mail"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Separar"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Contactos separados"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Alterar nome do grupo"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Eliminar grupo"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Novo"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Separar contacto"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Tem a certeza de que quer separar este contacto único em vários contactos: um para cada conjunto de informações de contacto que lhe foi agregado?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Associar"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"A eliminação deste contacto eliminará informações de várias contas."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Este contacto será eliminado."</string>
<string name="menu_done" msgid="796017761764190697">"Concluído"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Reverter"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Cancelar"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Editar contacto"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Novo contacto"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonético"</string>
<string name="label_notes" msgid="8337354953278341042">"Notas"</string>
<string name="label_sip_address" msgid="124073911714324974">"Chamada por internet"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Toque"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Primeiro e último"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Nome fonético"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Empresa"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Título"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"O contacto não existe."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Criar novo contacto"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Seleccionar etiqueta"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefone"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-mail"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"MI"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Endereço postal"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Endereço"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organização"</item>
<item msgid="7196592230748086755">"Nota"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Não existem imagens disponíveis no telefone."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Ícone de contacto"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Não existem imagens disponíveis no tablet."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Não existem imagens disponíveis no telefone."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Fotografia do contacto"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Nome da etiqueta personalizada"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Opções de visualização"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Opções de visualização"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Enviar as chamadas directamente para o correio de voz"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Predefinido"</string>
- <string name="changePicture" msgid="2943329047610967714">"Alterar ícone"</string>
- <string name="removePicture" msgid="3041230993155966350">"Remover ícone"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Remover fotografia"</string>
<string name="noContacts" msgid="8579310973261953559">"Sem contactos."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Não foram encontrados contactos correspondentes."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Sem contactos com números de telefone."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Contacto guardado."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Erro, impossível guardar alterações do contacto."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"1 contacto com número de telefone"</item>
- <item quantity="other" msgid="6133262880804110289">"<xliff:g id="COUNT">%d</xliff:g> contactos com números de telefone"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 contacto com número de telefone"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> contactos com números de telefone"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Sem contactos visíveis com números de telefone"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Sem contactos com números de telefone"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"1 contacto"</item>
- <item quantity="other" msgid="2865867557378939630">"<xliff:g id="COUNT">%d</xliff:g> contactos"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 contacto"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> contactos"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Sem contactos visíveis"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Sem contactos"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Sem contactos visíveis"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Sem contactos marcados com uma estrela"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Sem contactos em <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Foi encontrado 1 contacto"</item>
- <item quantity="other" msgid="7752927996850263152">"Foram encontrados <xliff:g id="COUNT">%d</xliff:g> contactos"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 encontrado"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> encontrado(s)"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Contacto não encontrado"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"foram encontrados mais de <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Não encontrado"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 contacto"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> contactos"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 encontrado"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> encontrado(s)"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Contactos"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoritos"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Contactos no cartão SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Não existem contactos a apresentar. (Se acabou de adicionar uma conta, pode demorar alguns minutos a sincronizar os contactos.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Não existem contactos a apresentar."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Não tem contactos para apresentar."\n\n"Para adicionar contactos, prima "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contactos que pode sincronizar no telefone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contacto"</b></font>" para criar um novo contacto de raiz"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Não tem contactos para apresentar (se acabou de adicionar uma conta, pode demorar alguns minutos a sincronizar os contactos)."\n\n"Para adicionar contactos, prima "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com os contactos que pode sincronizar no telefone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opções de visualização"</b></font>" para alterar os contactos visíveis"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contacto"</b></font>" para criar uma nova conta de raiz"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Não tem contactos para apresentar."\n\n"Para adicionar contactos, prima "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contactos que pode sincronizar no telefone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contacto"</b></font>" para criar um novo contacto de raiz"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Não tem contactos para apresentar (se acabou de adicionar uma conta, pode demorar alguns minutos a sincronizar os contactos)."\n\n"Para adicionar contactos, prima "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com os contactos que pode sincronizar no telefone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opções de visualização"</b></font>" para alterar os contactos visíveis"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contacto"</b></font>" para criar uma nova conta de raiz"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Não tem contactos para apresentar."\n\n"Para adicionar contactos, prima "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contactos que pode sincronizar com o tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contacto"</b></font>" para criar um novo contacto de raiz"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos do SIM ou do cartão SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Não tem contactos para apresentar."\n\n"Para adicionar contactos, prima "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contactos que pode sincronizar com o telemóvel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contacto"</b></font>" para criar um novo contacto de raiz"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos do SIM ou do cartão SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Não tem contactos para apresentar (se acabou de adicionar uma conta, a sincronização dos contactos pode demorar alguns minutos)."\n\n"Para adicionar contactos, prima "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contactos que pode sincronizar com o tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opções de visualização"</b></font>" para alterar os contactos visíveis"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contacto"</b></font>" para criar um novo contacto de raiz"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos do SIM ou do cartão SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Não tem contactos para apresentar (se acabou de adicionar uma conta, a sincronização dos contactos pode demorar alguns minutos)."\n\n"Para adicionar contactos, prima "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contactos que pode sincronizar com o telemóvel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opções de visualização"</b></font>" para alterar os contactos visíveis"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contacto"</b></font>" para criar um novo contacto de raiz"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos do SIM ou do cartão SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Não tem contactos para apresentar."\n\n"Para adicionar contactos, prima "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contactos que pode sincronizar com o tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contacto"</b></font>" para criar um novo contacto de raiz"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos do cartão SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Não tem contactos para apresentar."\n\n"Para adicionar contactos, prima "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contactos que pode sincronizar com o telemóvel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contacto"</b></font>" para criar um novo contacto de raiz"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos do cartão SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Não tem contactos para apresentar (se acabou de adicionar uma conta, a sincronização dos contactos pode demorar alguns minutos)."\n\n"Para adicionar contactos, prima "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contactos que pode sincronizar com o tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opções de visualização"</b></font>" para alterar os contactos visíveis"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contacto"</b></font>" para criar um novo contacto de raiz"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos do cartão SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Não tem contactos para apresentar (se acabou de adicionar uma conta, a sincronização dos contactos pode demorar alguns minutos)."\n\n"Para adicionar contactos, prima "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contactos que pode sincronizar com o telemóvel"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opções de visualização"</b></font>" para alterar os contactos visíveis"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contacto"</b></font>" para criar um novo contacto de raiz"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contactos do cartão SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Não tem favoritos."\n\n"Para adicionar um contacto à sua lista de favoritos:"\n\n" "<li>"Toque no separador "<b>"Contactos"</b>" "\n</li>" "\n<li>"Toque no contacto que pretende adicionar aos favoritos"\n</li>" "\n<li>"Toque na estrela junto ao nome do contacto"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Todos os contactos"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Marcado com estrela"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Chamada de retorno"</string>
<string name="callAgain" msgid="3197312117049874778">"Ligar novamente"</string>
<string name="returnCall" msgid="8171961914203617813">"Devolver chamada"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min <xliff:g id="SECONDS">%2$s</xliff:g> seg"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> seg"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Contactos frequentes"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Adicionar contacto"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Adicionar \"<xliff:g id="EMAIL">%s</xliff:g>\" aos contactos?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Todos"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"um"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dois"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"três"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Falha ao analisar armazenamento USB (Motivo: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Falha na análise do cartão SD (Motivo: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Erro de E/S"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"A memória não é suficiente (o ficheiro pode ser demasiado grande)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Falha na análise do vCard por motivo inesperado"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Falha na análise do vCard embora o formato pareça válido, uma vez que não é suportado pela implementação actual"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Não foi encontrado nenhum ficheiro vCard no armazenamento USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Não foi encontrado nenhum ficheiro vCard no cartão SD"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"O formato não é suportado."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Falha ao importar vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Não foi encontrado nenhum ficheiro vCard no armazenamento USB"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Não foi encontrado nenhum ficheiro vCard no cartão SD"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"A recolha de meta informações de ficheiro(s) vCard específicado(s) falhou."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"A importação de um ou vários ficheiros falhou (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Erro desconhecido"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Seleccionar ficheiro vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"A ler vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"A ler ficheiro(s) vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Falha na leitura de dados do vCard"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contactos"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> ficheiros"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"A colocar vCard(s) em cache no armazenamento temporário local"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"O importador está a colocar vCard(s) em cache no armazenamento temporário local. A importação efectiva começará brevemente."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"A importar <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"A importar <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"A leitura dos dados vCard falhou"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"A leitura de dados vCard foi cancelada"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"A importação do vCard terminou <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"A importação de <xliff:g id="FILENAME">%s</xliff:g> foi cancelada"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> será importado em breve."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"O ficheiro será importado em breve."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"O pedido de importação do vCard foi rejeitado. Tente mais tarde."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> será exportado em breve."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"O pedido de exportação do vCard foi rejeitado. Tente mais tarde."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"contacto"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Confirmar exportação"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Tem a certeza de que pretende exportar a lista de contactos para \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Falha ao exportar dados de contacto"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Existem demasiados ficheiros vCard no armazenamento USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Demasiados ficheiros vCard no cartão SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Nome de ficheiro demasiado longo (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"A exportação do <xliff:g id="FILENAME">%s</xliff:g> terminou"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"A exportação de <xliff:g id="FILENAME">%s</xliff:g> foi cancelada"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Exportar dados do contacto"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Exportar dados do contacto para \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Não foi possível iniciar o exportador: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Erro ocorrido durante a exportação: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Falha na obtenção de informações da base de dados"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Não existe nenhum contacto exportável. Se tiver realmente contactos no telefone, a sua exportação para fora do telefone por parte de alguns fornecedores de dados pode não ser autorizada."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Não existem contactos exportáveis. Se tiver realmente contactos no tablet, a sua exportação para fora do tablet por parte de alguns fornecedores de dados pode não ser autorizada."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Não existem contactos exportáveis. Se tiver realmente contactos no telefone, a sua exportação para fora do telefone por parte de alguns fornecedores de dados pode não ser autorizada."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"O compositor vCard não foi inicializado correctamente"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Não foi possível abrir \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contactos"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Não foi possível abrir \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%s</xliff:g> contactos"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"A cancelar importação do vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Tem a certeza de que quer cancelar a importação do <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"A cancelar exportação do vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Tem a certeza de que pretende cancelar a exportação de <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Falha ao cancelar import./export. vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Nomes dos contactos"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Adicionar pausa de 2 seg."</string>
<string name="add_wait" msgid="3360818652790319634">"Adicionar espera"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Não foram encontradas aplicações para executar esta acção"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Memorizar esta escolha"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Desconhecido"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Sem dados"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Limpar predefinições"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Conjunto predefinições p/ este contacto:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Limpar"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Contas"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importar/Exportar"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importar/Exportar contactos"</string>
- <string name="menu_share" msgid="943789700636542260">"Partilhar"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Partilhar contacto"</string>
<string name="share_via" msgid="563121028023030093">"Partilhar contacto através de"</string>
<string name="share_error" msgid="4374508848981697170">"Não é possível partilhar este contacto."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Nome"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organização"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Web site"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Evento"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relacionamento"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grupos"</string>
<string name="type_short_home" msgid="7770424864090605384">"R"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"T"</string>
<string name="type_short_work" msgid="4925330752504537861">"E"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Este contacto é apenas de leitura"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Mais"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Nome principal"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Criar contacto subordinado à conta"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Remover grupo de sincronização"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Adicionar grupo de sincronização"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Todos os outros contactos"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Todos os contactos"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Ao remover \"<xliff:g id="GROUP">%s</xliff:g>\" da sincronização, removerá também quaisquer contactos não agrupados."</string>
- <string name="account_phone" msgid="3682950835276226870">"Apenas telemóvel, não sincronizado"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Apenas tablet, não sincronizado"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Apenas telemóvel, não sincronizado"</string>
<string name="call_custom" msgid="7756571794763171802">"Ligar para <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Ligar para residência"</string>
<string name="call_mobile" msgid="7502236805487609178">"Ligar para telemóvel"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chat utilizando ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chat utilizando Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Chat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Endereço"</string>
<string name="postal_street" msgid="8133143961580058972">"Rua"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Apartado"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Vizinhança"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Região"</string>
<string name="postal_postcode" msgid="572136414136673751">"Código postal"</string>
<string name="postal_country" msgid="7638264508416368690">"País"</string>
+ <string name="full_name" msgid="6602579550613988977">"Nome"</string>
<string name="name_given" msgid="1687286314106019813">"Segundo nome"</string>
<string name="name_family" msgid="3416695586119999058">"Segundo apelido"</string>
<string name="name_prefix" msgid="59756378548779822">"Título académico ou profissional"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Pesquisar contactos"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Pesquisar todos os contactos"</string>
<string name="take_photo" msgid="7496128293167402354">"Tirar fotografia"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Tirar nova fotografia"</string>
<string name="pick_photo" msgid="448886509158039462">"Seleccionar fotografia da Galeria"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"A lista de contactos está a ser actualizada para reflectir a alteração do idioma."\n\n"Aguarde..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"A lista de contactos está a ser actualizada."\n\n"Aguarde..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Os contactos estão no processo de serem actualizados. "\n\n"O processo de actualização requer aproximadamente <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB de armazenamento de telefone interno."\n\n"Escolha uma das seguintes opções:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Seleccionar nova fotografia da Galeria"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"A lista de contactos está a ser actualizada para reflectir a alteração do idioma."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"A lista de contactos está a ser actualizada."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Os contactos estão em processo de actualização. "\n\n"O processo de actualização requer aproximadamente <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB de armazenamento interno no telemóvel."\n\n"Escolha uma das seguintes opções:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Desinstalar algumas aplicações"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Repetir actualização"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Resultados de pesquisa para: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"A pesquisar..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Mostrar seleccionados"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Mostrar tudo"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Seleccionar tudo"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Desmarcar tudo"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"1 destinatário seleccionado"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g> destinatários seleccionados"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Nenhum contacto seleccionado."</string>
+ <string name="add_field" msgid="2384260056674995230">"Adicionar outro campo"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"através do <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> através do <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favorito"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Editar contacto"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"não intercalado(s)"</item>
+ <item quantity="other" msgid="425683718017380845">"intercalado a partir de <xliff:g id="COUNT">%0$d</xliff:g> origens"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Outro"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Associar contactos"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Associar o contacto actual ao contacto seleccionado?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Editar contactos seleccionados"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Alternar para edição do contacto seleccionado? A informação introduzida até agora vai ser copiada."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Copiar para os meus contactos"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Directório <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"A pesquisar todos os contactos"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Directório"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Contactos"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"A criar uma cópia pessoal"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Escolher lista de contacto"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Todos os contactos"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Marcado com estrela"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Personalizado"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Personalizar..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Contactos com números de telefone"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Contacto"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Definir vista personalizada"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Definições"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Definições"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Opções de visualização"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Localizar contactos"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Número de telefone"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Adicionar aos contactos"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Fechar"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Indique um ano"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Contacto"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"A carregar…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Criar novo contacto"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Iniciar sessão numa conta"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importar contactos de um ficheiro"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Criar novo grupo"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Criar novo grupo]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Alterar nome do grupo"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Eliminar grupo"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Tem a certeza de que pretende eliminar o grupo \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\"? (Os contactos não serão eliminados.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Introduza o nome do contacto antes de juntar a outro contacto."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Contacto associado"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Texto copiado"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Rejeitar alterações"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Pretende rejeitar as alterações?"</string>
+ <string name="discard" msgid="1234315037371251414">"Rejeitar"</string>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 7f68fb9..809adb6 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Escolha o atalho para um contato"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Escolha um número a ser chamado"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Escolha um número para enviar uma mensagem"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Selecionar um contato"</string>
<string name="starredList" msgid="4817256136413959463">"Com estrela"</string>
<string name="frequentList" msgid="7154768136473953056">"Frequente"</string>
<string name="strequentList" msgid="5640192862059373511">"Favoritos"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Excluir contato"</string>
<string name="menu_call" msgid="3992595586042260618">"Chamar contato"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Enviar SMS/MMS para o contato"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Enviar e-mail"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Endereço no mapa"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Tornar o número padrão"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Tornar o e-mail padrão"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Separar"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Contatos separados"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Renomear grupo"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Excluir grupo"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Novo"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Separar contato"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Tem certeza de que deseja separar este único contato em diversos contatos, um para cada conjunto de informações de contato unificado?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Unir"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Excluir este contato irá excluir informações de várias contas."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Este contato será excluído."</string>
<string name="menu_done" msgid="796017761764190697">"Concluído"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Reverter"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Cancelar"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Editar contato"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Novo contato"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonética"</string>
<string name="label_notes" msgid="8337354953278341042">"Observações"</string>
<string name="label_sip_address" msgid="124073911714324974">"Chamada da internet"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Toque"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Nome e sobrenome"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Nome fonético"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Empresa"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Título"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"O contato não existe."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Criar novo contato"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Selecionar marcador"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefone"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-mail"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Mensagem instantânea"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Endereço postal"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Endereço"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organização"</item>
<item msgid="7196592230748086755">"Observação"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Nenhuma imagem disponível no telefone."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Ícone de contato"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Nenhuma imagem disponível no tablet."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Nenhuma imagem disponível no telefone."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Foto do contato"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Personalizar nome do marcador"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Opções de exibição"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Opções de exibição"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Enviar chamadas diretamente para o correio de voz"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Padrão"</string>
- <string name="changePicture" msgid="2943329047610967714">"Alterar ícone"</string>
- <string name="removePicture" msgid="3041230993155966350">"Remover ícone"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Remover foto"</string>
<string name="noContacts" msgid="8579310973261953559">"Nenhum contato."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Nenhum contato correspondente foi encontrado."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nenhum contato com números de telefone."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Contato salvo."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Erro: não é possível salvar as alterações do contato."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Exibindo 1 contato com número de telefone"</item>
- <item quantity="other" msgid="6133262880804110289">"Exibindo <xliff:g id="COUNT">%d</xliff:g> contatos com números de telefone"</item>
+ <item quantity="one" msgid="3015357862286673986">"Um contato com número de telefone"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> contatos com números de telefone"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Nenhum contato visível com números de telefone"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Nenhum contato com números de telefone"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Exibindo 1 contato"</item>
- <item quantity="other" msgid="2865867557378939630">"Exibindo <xliff:g id="COUNT">%d</xliff:g> contatos"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 contato"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> contatos"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Não há contatos visíveis"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Nenhum contato"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Não há contatos visíveis"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Nenhum contato com estrela"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Nenhum contato em <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"1 contato encontrado"</item>
- <item quantity="other" msgid="7752927996850263152">"<xliff:g id="COUNT">%d</xliff:g> contatos encontrados"</item>
+ <item quantity="one" msgid="5517063038754171134">"Um encontrado"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> encontrados"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Contato não encontrado"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"mais de <xliff:g id="COUNT">%d</xliff:g> encontrados"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Não encontrado"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 contato"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> contatos"</item>
+ <item quantity="one" msgid="4826918429708286628">"Um encontrado"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> encontrados"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Contatos"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoritos"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Contatos do cartão SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Você não tem contatos para exibir. Se você acabou de adicionar uma conta, pode levar alguns minutos para sincronizar os contatos."</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Você não tem contatos para exibir."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Você não tem nenhum contato para exibir."\n\n"Para adicionar contatos, pressione "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contatos que possam ser sincronizados com o telefone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contato"</b></font>" para criar um novo contato do início"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Você não tem contatos para exibir (se acabou de adicionar uma conta, pode levar alguns minutos para sincronizar os contatos.)"\n\n"Para adicionar contatos, pressione"<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contatos que possam ser sincronizados com o telefone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opções de exibição"</b></font>" para alterar quais contatos ficam visíveis"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contato"</b></font>" para criar um novo contato do início"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Você não tem nenhum contato para exibir."\n\n"Para adicionar contatos, pressione "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contatos que possam ser sincronizados com o telefone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contato"</b></font>" para criar um novo contato do início"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Você não tem contatos para exibir (se acabou de adicionar uma conta, pode levar alguns minutos para sincronizar os contatos.)"\n\n"Para adicionar contatos, pressione"<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contatos que possam ser sincronizados com o telefone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opções de exibição"</b></font>" para alterar quais contatos ficam visíveis"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contato"</b></font>" para criar um novo contato do início"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Você não tem contatos para exibir."\n\n"Para adicionar contatos, pressione "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contatos que possam ser sincronizados com o tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contato"</b></font>" para criar um novo contato do início"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contatos do chip ou cartão SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Você não tem contatos para exibir."\n\n"Para adicionar contatos, pressione "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contatos que possam ser sincronizados com o telefone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contato"</b></font>" para criar um novo contato do início"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contatos do chip ou cartão SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Você não tem contatos para exibir. Caso tenha acabado de adicionar uma conta, pode levar alguns minutos para sincronizar os contatos."\n\n"Para adicionar contatos, pressione "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contatos que possam ser sincronizados com o tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opções de exibição"</b></font>" para alterar quais contatos ficam visíveis"\n</li>" "\n<li><font fgcolor="#ffffffff">"Novo "<b>"contato"</b></font>" para criar um novo contato do início"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contatos do chip ou cartão SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Você não tem contatos para exibir (caso tenha acabado de adicionar uma conta, pode levar alguns minutos para sincronizar os contatos)."\n\n"Para adicionar contatos, pressione "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contatos que possam ser sincronizados com o telefone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opções de exibição"</b></font>" para alterar quais contatos ficam visíveis"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contato"</b></font>" para criar um novo contato do início"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contatos do chip ou cartão SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Você não tem contatos para exibir."\n\n"Para adicionar contatos, pressione "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contatos que possam ser sincronizados com o tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contato"</b></font>" para criar um novo contato do início"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contatos do cartão SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Você não tem contatos para exibir."\n\n"Para adicionar contatos, pressione "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contatos que possam ser sincronizados com o telefone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contato"</b></font>" para criar um novo contato do início"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contatos do chip ou cartão SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Você não tem contatos para exibir. Sse acabou de adicionar uma conta, pode levar alguns minutos para sincronizar os contatos."\n\n"Para adicionar contatos, pressione "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contatos que possam ser sincronizados com o tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opções de exibição"</b></font>" para alterar quais contatos ficam visíveis"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contato"</b></font>" para criar um novo contato do início"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contatos do cartão SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Você não tem contatos para exibir (se acabou de adicionar uma conta, pode levar alguns minutos para sincronizar os contatos)."\n\n"Para adicionar contatos, pressione "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e toque em:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contas"</b></font>" para adicionar ou configurar uma conta com contatos que possam ser sincronizados com o telefone"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opções de exibição"</b></font>" para alterar quais contatos ficam visíveis"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Novo contato"</b></font>" para criar um novo contato do início"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" para importar contatos do chip ou cartão SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Você não tem nenhum favorito."\n\n"Para adicionar um contato à sua lista de favoritos:"\n\n" "<li>"Toque na guia "<b>"Contatos"</b>\n</li>" "\n<li>"Toque no contato que deseja adicionar aos seus favoritos"\n</li>" "\n<li>"Toque na estrela ao lado do nome do contato"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Todos os contatos"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Com estrela"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Retornar chamada"</string>
<string name="callAgain" msgid="3197312117049874778">"Chamar novamente"</string>
<string name="returnCall" msgid="8171961914203617813">"Retornar chamada"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min. <xliff:g id="SECONDS">%2$s</xliff:g> s"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min. <xliff:g id="SECONDS">%s</xliff:g> s"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Chamados frequentemente"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Adicionar contato"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Adicionar \"<xliff:g id="EMAIL">%s</xliff:g>\" aos contatos?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Todos"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"um"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dois"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"três"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Falha na verificação do armazenamento USB (Motivo: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Falha na verificação do cartão SD (Motivo: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Erro E/S"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Memória insuficiente (o arquivo talvez seja muito grande)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Falha ao analisar o vCard por razão inesperada"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Falha ao analisar o vCard. Embora o formato pareça ser válido, a implementação atual não oferece suporte a ele."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Nenhum arquivo de vCard encontrado no armazenamento USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Nenhum arquivo de vCard encontrado no cartão SD"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"O formato não é suportado."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Falha ao importar vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Nenhum arquivo de vCard encontrado no armazenamento USB"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Nenhum arquivo de vCard encontrado no cartão SD"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Falha ao coletar metainformações dos arquivos de vCard fornecidos."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Falha na importação de um ou mais arquivos (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Erro desconhecido"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Selecionar arquivo do vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Lendo vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Lendo os arquivos do vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"A leitura dos dados do vCard falhou"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contatos"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> arquivos"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Armazenando vCard(s) em cache no armazenamento temporário local"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"O importador está armazenando vCard(s) em cache no armazenamento temporário local. A importação real começará em breve."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importando <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importando <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Falha ao ler dados de vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"A leitura dos dados do vCard foi cancelada"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Importação de vCard concluída <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Importação do <xliff:g id="FILENAME">%s</xliff:g> cancelada"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> será importado em breve."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"O arquivo será importado em breve."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"A solicitação de importação do vCard foi rejeitada. Tente novamente mais tarde."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> será exportado em breve."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"A solicitação de exportação do vCard foi rejeitada. Tente novamente mais tarde."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"contato"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Confirmar exportação"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Tem certeza de que deseja exportar a sua lista de contatos para \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Falha ao exportar dados do contato"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Muitos arquivos do vCard no armazenamento USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Muitos arquivos do vCard no cartão SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"O nome de arquivo exigido é muito longo (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Exportação concluída <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Exportação do <xliff:g id="FILENAME">%s</xliff:g> foi cancelada"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Exportando dados do contato"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Exportando dados do contato para \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Não foi possível iniciar o exportador: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Ocorreu um erro durante a exportação: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Falha ao acessar informações do banco de dados"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Não há contatos exportáveis. Se você realmente tiver contatos no seu telefone, talvez a exportação não seja permitida por algum provedor de dados."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Não há contatos exportáveis. Se você realmente tiver contatos em seu tablet, talvez a exportação não seja permitida por alguns provedores de dados."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Não há contatos exportáveis. Se você realmente tiver contatos em seu telefone, talvez a exportação não seja permitida por alguns provedores de dados."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"O vCard não foi iniciado corretamente"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Não foi possível abrir \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contatos"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Não foi possível abrir \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> de <xliff:g id="TOTAL_NUMBER">%s</xliff:g> contatos"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Cancelando a importação do vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Tem certeza de que deseja cancelar a importação do <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Cancelando a exportação do vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Tem certeza de que deseja cancelar a exportação do <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Falha ao cancelar impor./expor. do vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Nomes dos seus contatos"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Adicionar pausa de 2 segundos"</string>
<string name="add_wait" msgid="3360818652790319634">"Adicionar espera"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Nenhum aplicativo foi encontrado para executar esta ação."</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Lembrar desta escolha"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Desconhecido"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Nenhum dado"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Limpar padrão"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Padrões definidos para este contato:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Limpar"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Contas"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importar/Exportar"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importar/Exportar contatos"</string>
- <string name="menu_share" msgid="943789700636542260">"Compartilhar"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Compartilhar contato"</string>
<string name="share_via" msgid="563121028023030093">"Compartilhar contato via"</string>
<string name="share_error" msgid="4374508848981697170">"Este contato não pode ser compartilhado."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Nome"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organização"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Site"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Evento"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relacionamento"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grupos"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"W"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Este contato é apenas leitura"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Mais"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Nome principal"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Criar contato na conta"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Remover sincronização do grupo"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Adicionar grupo sincronizado"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Todos os outros contatos"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Todos os contatos"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"A remoção de \'<xliff:g id="GROUP">%s</xliff:g>\' da sincronização também removerá todos os contatos não agrupados da sincronização."</string>
- <string name="account_phone" msgid="3682950835276226870">"Somente telefone, não sincronizado"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Somente tablet, não sincronizado"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Somente telefone, não sincronizado"</string>
<string name="call_custom" msgid="7756571794763171802">"Chamar <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Chamar residência"</string>
<string name="call_mobile" msgid="7502236805487609178">"Chamar celular"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Bater papo usando o ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Bater papo usando o Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Bate-papo"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Endereço"</string>
<string name="postal_street" msgid="8133143961580058972">"Rua"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Caixa postal"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Vizinhança"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Estado"</string>
<string name="postal_postcode" msgid="572136414136673751">"CEP"</string>
<string name="postal_country" msgid="7638264508416368690">"País"</string>
+ <string name="full_name" msgid="6602579550613988977">"Nome"</string>
<string name="name_given" msgid="1687286314106019813">"Nome"</string>
<string name="name_family" msgid="3416695586119999058">"Sobrenome"</string>
<string name="name_prefix" msgid="59756378548779822">"Prefixo do nome"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Pesquisar contatos"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Pesquisar em todos os contatos"</string>
<string name="take_photo" msgid="7496128293167402354">"Tirar foto"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Tirar outra foto"</string>
<string name="pick_photo" msgid="448886509158039462">"Selecionar foto da Galeria"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"A lista de contatos está sendo atualizada para refletir a alteração do idioma."\n\n"Aguarde..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"A lista de contatos está sendo atualizada."\n\n"Aguarde..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Os contatos estão sendo atualizados no momento. "\n\n"O processo de atualização requer aproximadamente <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB de armazenamento interno do telefone."\n\n"Escolha uma das seguintes opções:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Selecionar nova foto da Galeria"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"A lista de contatos está sendo atualizada para incorporar a alteração do idioma."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"A lista de contatos está sendo atualizada."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Os contatos estão sendo atualizados. "\n\n"O processo de atualização requer aproximadamente <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mb de memória interna. "\n\n"Escolha uma das seguintes opções:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Desinstalar alguns aplicativos"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Tentar atualizar novamente"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Resultados da pesquisa para: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Pesquisando..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Mostrar selecionados"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Mostrar todos"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Selecionar todos"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Desmarcar todos"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"1 destinatário selecionado"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g> destinatários selecionados"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Nenhum contato selecionado."</string>
+ <string name="add_field" msgid="2384260056674995230">"Adicionar outro campo"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"por meio de <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favorito"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Editar contato"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"não mesclado"</item>
+ <item quantity="other" msgid="425683718017380845">"mesclado a partir de <xliff:g id="COUNT">%0$d</xliff:g> origens"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Outros"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Unir contatos"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Unir o contato atual ao contato selecionado?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Editar contatos selecionados"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Alternar para a edição do contato selecionado? As informações inseridas até agora serão copiadas."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Copiar para meus contatos"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Diretório <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Pesquisando todos os contatos"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Diretório"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Contatos"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Como criar uma cópia pessoal"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Escolher lista de contatos"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Todos os contatos"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Com estrela"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Personalizado"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Personalizar..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Contatos com números de telefone"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Contato"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Definir exibição personalizada"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Configurações"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Configurações"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Opções de exibição"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Localizar contatos"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Número de telefone"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Adicionar aos contatos"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Fechar"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Informe um ano"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Contato"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Carregando…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Criar um novo contato"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Faça login em uma conta"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importar contatos de um arquivo"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Criar um novo grupo"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Criar um novo grupo]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Renomear grupo"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Excluir grupo"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Tem certeza de que deseja excluir o grupo \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\"? Os contatos não serão excluídos."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Insira o nome do contato antes de unificá-lo com outro contato."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Contato unificado"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Texto copiado"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Descartar alterações"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Deseja realmente descartar as alterações?"</string>
+ <string name="discard" msgid="1234315037371251414">"Descartar"</string>
</resources>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index 0e147f7..c2ea934 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -25,6 +25,8 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Tscherner in contact per la scursanida"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Tscherner il numer per telefonar"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Tscherner il numer per il messadi"</string>
+ <!-- no translation found for contactPickerActivityTitle (6886592363525235031) -->
+ <skip />
<string name="starredList" msgid="4817256136413959463">"Cun staila"</string>
<string name="frequentList" msgid="7154768136473953056">"Contacts frequents"</string>
<string name="strequentList" msgid="5640192862059373511">"Favurits"</string>
@@ -43,12 +45,14 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Stizzar il contact"</string>
<string name="menu_call" msgid="3992595586042260618">"Telefonar al contact"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Trametter in SMS al contact"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Trametter in e-mail"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Adressa sin la charta"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Numer da telefon da standard"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"E-mail predefinì"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Divider"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Contacts dividids"</string>
+ <!-- no translation found for menu_renameGroup (2798886925154156075) -->
+ <skip />
+ <!-- no translation found for menu_deleteGroup (644571524292675446) -->
+ <skip />
+ <!-- no translation found for menu_new_contact_action_bar (8887818026717394343) -->
+ <skip />
<string name="splitConfirmation_title" msgid="6716467920283502570">"Divider il contact"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Vulais Vus propi divider quest contact en plirs contacts (in per mintga detagl da contact)?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Unir"</string>
@@ -66,37 +70,36 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"\"Sche Vus stizzais quest contact, vegnan infurmaziuns en plirs contos stizzads.\""</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Quest contact vegn stizzà."</string>
<string name="menu_done" msgid="796017761764190697">"Finì"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Annullar"</string>
+ <!-- outdated translation 2174577548513895144 --> <string name="menu_doNotSave" msgid="58593876893538465">"Annullar"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Modifitgar il contact"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Nov contact"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetic"</string>
<string name="label_notes" msgid="8337354953278341042">"Notizias"</string>
<!-- outdated translation 943765439755300724 --> <string name="label_sip_address" msgid="124073911714324974">"Adressa SIP"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Tun da scalin"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Prenum e num"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Num fonetic"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Interpresa"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Titel"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Quest contact n\'exista betg."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Crear in nov contact"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Tscherni ina etichetta"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-mail"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Chat"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Adressa postala"</string>
+ <!-- outdated translation 1618078212734693682 --> <string name="postalLabelsGroup" msgid="3487738141112589324">"Adressa postala"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Firma/Organisaziun"</item>
<item msgid="7196592230748086755">"Notizia"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Nagin maletg disponibel sin il telefon."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Simbol da contact"</string>
+ <!-- outdated translation 431331662154342581 --> <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Nagin maletg disponibel sin il telefon."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Nagin maletg disponibel sin il telefon."</string>
+ <!-- no translation found for attach_photo_dialog_title (5599827035558557169) -->
+ <skip />
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Num da l\'etichetta persunalisada"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Opziuns da vista"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Opziuns da vista"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Renviar ils cloms directamain sin la mailbox"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Standard"</string>
- <string name="changePicture" msgid="2943329047610967714">"Midar il simbol"</string>
- <string name="removePicture" msgid="3041230993155966350">"Allontanar il simbol"</string>
+ <!-- no translation found for removePhoto (4898105274130284565) -->
+ <skip />
<string name="noContacts" msgid="8579310973261953559">"Nagins contacts"</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Betg chattà in contact correspundent."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nagin contact dispona d\'in numer da telefon."</string>
@@ -113,24 +116,26 @@
<string name="savingDisplayGroups" msgid="2133152192716475939">"Las opziuns da vista vegnan memorisadas…"</string>
<string name="contactSavedToast" msgid="7152589189385441091">"Contact memorisà."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Errur: impussibel da memorisar las modificaziuns dal contact"</string>
- <plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"1 contact cun numer da telefon vegn mussà"</item>
- <item quantity="other" msgid="6133262880804110289">"<xliff:g id="COUNT">%d</xliff:g> contacts cun numer da telefon vegnan mussads"</item>
- </plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Nagins contacts visibels cun numer da telefon"</string>
- <plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"1 contact vegn mussà"</item>
- <item quantity="other" msgid="2865867557378939630">"<xliff:g id="COUNT">%d</xliff:g> contacts vegnan mussads."</item>
- </plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Nagins contacts visibels"</string>
+ <!-- no translation found for listTotalPhoneContacts:other (3299954047880968205) -->
+ <!-- outdated translation 2756295259674938869 --> <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Nagins contacts visibels cun numer da telefon"</string>
+ <!-- no translation found for listTotalAllContacts:other (3578469907265375314) -->
+ <!-- outdated translation 6811347506748072822 --> <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Nagins contacts visibels"</string>
+ <!-- no translation found for listTotalAllContactsZeroCustom (4058252141420128998) -->
+ <skip />
+ <!-- no translation found for listTotalAllContactsZeroStarred (5391630590684099117) -->
+ <skip />
+ <!-- no translation found for listTotalAllContactsZeroGroup (5448979458248027615) -->
+ <skip />
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Chattà 1 contact"</item>
- <item quantity="other" msgid="7752927996850263152">"Chattà <xliff:g id="COUNT">%d</xliff:g> contacts"</item>
+ <item quantity="one" msgid="5517063038754171134">"chattà 1"</item>
+ <item quantity="other" msgid="3852668542926965042">"Chattà <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Chattà nagin contact"</string>
+ <!-- no translation found for foundTooManyContacts (2548148047461758967) -->
+ <skip />
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Betg chattà"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 contact"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> contacts"</item>
+ <item quantity="one" msgid="4826918429708286628">"chattà 1"</item>
+ <item quantity="other" msgid="7988132539476575389">"Chattà <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Contacts"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favurits"</string>
@@ -160,10 +165,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Contacts sin la carta SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Vus n\'avais nagins contacts per mussar. (I po cuzzar in pèr minutas per sincronisar ils contacts en cas che Vus avais gist agiuntà in conto.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Vus n\'avais nagins contacts per mussar."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"\"Vus n\'avais nagins contacts per mussar."\n\n"Per agiuntar contacts, smatgai sin "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e smatgai suenter sin: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Contos"</b></font>" per agiuntar u configurar in conto che cuntegna contacts che Vus pudais sincronisar cun il telefonin."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov contact"</b></font>" per cumenzar a crear in nov contact"\n</li>\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li>"\""</string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"\"Vus n\'avais nagins contacts per mussar. (I po cuzzar in pèr minutas per sincronisar ils contacts en cas che Vus avais gist agiuntà in conto.)"\n\n"Per agiuntar contacts, smatgai sin "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e smatgai suenter sin: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Contos"</b></font>" per agiuntar u configurar in conto che cuntegna contacts che Vus pudais sincronisar cun il telefonin."\n" "</li>\n<li><font fgcolor="#ffffffff"><b>"Opziuns da vista"</b></font>" per definir tge contacts che vegnan mussads"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov contact"</b></font>" per cumenzar a crear in nov contact"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li>"\""</string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"\"Vus n\'avais nagins contacts per mussar."\n\n"Per agiuntar contacts, smatgai sin "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e smatgai suenter sin: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Contos"</b></font>" per agiuntar u configurar in conto che cuntegna contacts che Vus pudais sincronisar cun il telefonin."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov contact"</b></font>" per cumenzar a crear in nov contact"\n</li>\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li>"\""</string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"\"Vus n\'avais nagins contacts per mussar. (I po cuzzar in pèr minutas per sincronisar ils contacts en cas che Vus avais gist agiuntà in conto.)"\n\n"Per agiuntar contacts, smatgai sin "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e smatgai suenter sin:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contos"</b></font>" per agiuntar u configurar in conto che cuntegna contacts che Vus pudais sincronisar cun il telefonin."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opziuns da vista"</b></font>" per definir tge contacts che vegnan mussads"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov contact"</b></font>" per cumenzar a crear in nov contact"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar."</b></font>\n</li>"\""</string>
+ <!-- outdated translation 7633826236417884130 --> <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"\"Vus n\'avais nagins contacts per mussar."\n\n"Per agiuntar contacts, smatgai sin "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e smatgai suenter sin: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Contos"</b></font>" per agiuntar u configurar in conto che cuntegna contacts che Vus pudais sincronisar cun il telefonin."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov contact"</b></font>" per cumenzar a crear in nov contact"\n</li>\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li>"\""</string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"\"Vus n\'avais nagins contacts per mussar."\n\n"Per agiuntar contacts, smatgai sin "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e smatgai suenter sin: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Contos"</b></font>" per agiuntar u configurar in conto che cuntegna contacts che Vus pudais sincronisar cun il telefonin."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov contact"</b></font>" per cumenzar a crear in nov contact"\n</li>\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>\n</li>"\""</string>
+ <!-- outdated translation 3017521127042216243 --> <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"\"Vus n\'avais nagins contacts per mussar. (I po cuzzar in pèr minutas per sincronisar ils contacts en cas che Vus avais gist agiuntà in conto.)"\n\n"Per agiuntar contacts, smatgai sin "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e smatgai suenter sin:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contos"</b></font>" per agiuntar u configurar in conto che cuntegna contacts che Vus pudais sincronisar cun il telefonin."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opziuns da vista"</b></font>" per definir tge contacts che vegnan mussads"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov contact"</b></font>" per cumenzar a crear in nov contact"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar."</b></font>" per importar contacts da Vossa carta SIM u Vossa carta SD"\n</li>"\""</string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"\"Vus n\'avais nagins contacts per mussar. (I po cuzzar in pèr minutas per sincronisar ils contacts en cas che Vus avais gist agiuntà in conto.)"\n\n"Per agiuntar contacts, smatgai sin "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e smatgai suenter sin:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contos"</b></font>" per agiuntar u configurar in conto che cuntegna contacts che Vus pudais sincronisar cun il telefonin."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opziuns da vista"</b></font>" per definir tge contacts che vegnan mussads"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov contact"</b></font>" per cumenzar a crear in nov contact"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar."</b></font>" per importar contacts da Vossa carta SIM u Vossa carta SD"\n</li>"\""</string>
+ <!-- outdated translation 467658807711582876 --> <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Vus n\'avais nagins contacts per mussar."\n\n"Per agiuntar contacts, smatgai sin "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e smatgai suenter sin:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contos"</b></font>" per agiuntar u configurar in conto che cuntegna contacts che Vus pudais sincronisar cun il telefonin"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov contact"</b></font>" per cumenzar a crear in nov contact"\n</li>\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" per importar contacts da Vossa carta SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Vus n\'avais nagins contacts per mussar."\n\n"Per agiuntar contacts, smatgai sin "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e smatgai suenter sin:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contos"</b></font>" per agiuntar u configurar in conto che cuntegna contacts che Vus pudais sincronisar cun il telefonin"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov contact"</b></font>" per cumenzar a crear in nov contact"\n</li>\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" per importar contacts da Vossa carta SD"\n</li></string>
+ <!-- outdated translation 9040060730467973050 --> <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"\"Vus n\'avais nagins contacts per mussar. (I po cuzzar in pèr minutas per sincronisar ils contacts en cas che Vus avais gist agiuntà in conto.)"\n\n"Per agiuntar contacts, smatgai sin "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e smatgai suenter sin:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contos"</b></font>" per agiuntar u configurar in conto che cuntegna contacts che Vus pudais sincronisar cun il telefonin."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opziuns da vista"</b></font>" per definir tge contacts che vegnan mussads"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov contact"</b></font>" per cumenzar a crear in nov contact"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" per importar contacts da Vossa carta SD"\n</li>"\""</string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"\"Vus n\'avais nagins contacts per mussar. (I po cuzzar in pèr minutas per sincronisar ils contacts en cas che Vus avais gist agiuntà in conto.)"\n\n"Per agiuntar contacts, smatgai sin "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" e smatgai suenter sin:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Contos"</b></font>" per agiuntar u configurar in conto che cuntegna contacts che Vus pudais sincronisar cun il telefonin."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opziuns da vista"</b></font>" per definir tge contacts che vegnan mussads"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov contact"</b></font>" per cumenzar a crear in nov contact"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importar/Exportar"</b></font>" per importar contacts da Vossa carta SD"\n</li>"\""</string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Vus n\'avais nagins favurits."\n\n"Per agiuntar in contact a la glista da favurits:"\n\n" "<li>"Smatgai sin il register "<b>"Contacts"</b>"."\n</li>" "\n<li>"Smatgai sin il contact che duai vegnir agiuntà a Voss favurits."\n</li>" "\n<li>"Smatgai sin la staila dal contact correspundent."\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Tut ils contacts"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Cun staila"</string>
@@ -180,11 +189,10 @@
<string name="callBack" msgid="5498224409038809224">"Telefonar enavos"</string>
<string name="callAgain" msgid="3197312117049874778">"Telefonar anc ina giada"</string>
<string name="returnCall" msgid="8171961914203617813">"Telefonar enavos"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min <xliff:g id="SECONDS">%2$s</xliff:g> sec"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> sec"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Contactà savens"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Agiuntar in contact"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Agiuntar «<xliff:g id="EMAIL">%s</xliff:g>» als contacts?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Tuts"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"in"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dus"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"trais"</string>
@@ -226,19 +234,43 @@
<skip />
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Errur cun leger la carta SD. Il motiv: «<xliff:g id="FAIL_REASON">%s</xliff:g>»"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Errur I/O"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Memoria insuffizienta (probablamain è la datoteca memia gronda)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"L\'analisa da las datas vCard n\'è betg reussida per motiv nunenconuschent."</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"\"Durant l\'analisa da las datas vCard è cumparida ina errur. Schebain che la vCard para dad esser d\'in format valid, na la sustegna l\'implementaziun actuala betg.\""</string>
- <!-- outdated translation 6376516175882881595 --> <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Chattà nagina datoteca vCard sin la carta SD"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Chattà nagina datoteca vCard sin la carta SD"</string>
+ <!-- no translation found for fail_reason_not_supported (294499264620201243) -->
+ <skip />
+ <!-- no translation found for vcard_import_failed (7718330063493653085) -->
+ <skip />
+ <!-- no translation found for import_failure_no_vcard_file (8809370398968655782) -->
+ <skip />
+ <!-- no translation found for import_failure_no_vcard_file (1730986357514922756) -->
+ <skip />
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Betg reussì da leger las metadatas cuntegnidas en la(s) datoteca(s) vCard."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Errur cun importar ina u pliras datotecas (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Errur nunenconuschenta"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Tscherner ina datoteca vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Leger las datas vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Leger las datotecas vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Errur cun leger las datas vCard"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> da <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contacts"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> da <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> datotecas"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Metter la(s) vCard(s) en il cache da la memoria temporara locala"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Il program d\'importaziun metta gist la(s) vCard(s) en il cache da la memoria temporara locala. Il process d\'importaziun effectiv cumenza en pauc temp"</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importar <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importar <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Errur cun leger las datas vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Interrut la lectura da datas vCard"</string>
+ <!-- no translation found for importing_vcard_finished_title (3341541727268747967) -->
+ <skip />
+ <!-- no translation found for importing_vcard_canceled_title (6367906965439777280) -->
+ <skip />
+ <!-- no translation found for vcard_import_will_start_message (2804911199145873396) -->
+ <skip />
+ <!-- no translation found for vcard_import_will_start_message_with_default_name (1022969530654129470) -->
+ <skip />
+ <!-- no translation found for vcard_import_request_rejected_message (2592424820635325951) -->
+ <skip />
+ <!-- no translation found for vcard_export_will_start_message (2210241345252081463) -->
+ <skip />
+ <!-- no translation found for vcard_export_request_rejected_message (8259494002258326330) -->
+ <skip />
+ <!-- no translation found for vcard_unknown_filename (7171709890959915954) -->
+ <skip />
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Confermar l\'export"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Vulais Vus propi exportar la glista da contacts en «<xliff:g id="VCARD_FILENAME">%s</xliff:g>»?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Errur cun exportar las datas da contact"</string>
@@ -247,15 +279,29 @@
<!-- outdated translation 7084146295639672658 --> <string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"La carta SD cuntegna memia bleras datotecas vCard"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"La carta SD cuntegna memia bleras datotecas vCard"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Il num da datoteca necessari è memia lung («<xliff:g id="FILENAME">%s</xliff:g>»)"</string>
+ <!-- no translation found for exporting_vcard_finished_title (4259736138838583213) -->
+ <skip />
+ <!-- no translation found for exporting_vcard_canceled_title (1827672399438062140) -->
+ <skip />
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Datas da contact vegnan exportadas."</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Exportaziun da las datas da contact en «<xliff:g id="FILE_NAME">%s</xliff:g>»"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Errur cun inizialisar il program d\'export: «<xliff:g id="EXACT_REASON">%s</xliff:g>»"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Errur cun exportar: «<xliff:g id="EXACT_REASON">%s</xliff:g>»"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Impussibel dad obtegnair las infurmaziuns concernent la banca da datas"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"\"Nagin contact exportabel è avant maun. Sche Vus avais memorisà contacts sin Voss telefonin, ha in purschider da datas eventualmain protegì els cunter l\'export dal telefon.\""</string>
+ <!-- outdated translation 754734132189369094 --> <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"\"Nagin contact exportabel è avant maun. Sche Vus avais memorisà contacts sin Voss telefonin, ha in purschider da datas eventualmain protegì els cunter l\'export dal telefon.\""</string>
+ <!-- outdated translation 754734132189369094 --> <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"\"Nagin contact exportabel è avant maun. Sche Vus avais memorisà contacts sin Voss telefonin, ha in purschider da datas eventualmain protegì els cunter l\'export dal telefon.\""</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Il program per crear la vCard n\'è betg inizialisà correctamain."</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Impussibel dad avrir «<xliff:g id="FILE_NAME">%1$s</xliff:g>»: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> da <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> contacts"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Impussibel dad avrir «<xliff:g id="FILE_NAME">%s</xliff:g>»: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> da <xliff:g id="TOTAL_NUMBER">%s</xliff:g> contacts"</string>
+ <!-- outdated translation 1311741984719347665 --> <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Interrumper l\'import da datas vCard"</string>
+ <!-- no translation found for cancel_import_confirmation_message (8560937090143057107) -->
+ <skip />
+ <!-- no translation found for cancel_export_confirmation_title (6516467140276768528) -->
+ <skip />
+ <!-- no translation found for cancel_export_confirmation_message (1392976902396351957) -->
+ <skip />
+ <!-- no translation found for cancel_vcard_import_or_export_failed (7096533244663846810) -->
+ <skip />
<string name="search_settings_description" msgid="2675223022992445813">"Num da Voss contacts"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Agiuntar ina pausa da 2 secundas"</string>
<string name="add_wait" msgid="3360818652790319634">"Agiuntar Spetgar"</string>
@@ -265,10 +311,17 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Betg chattà in\'applicaziun per elavurar questa acziun"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Memorisar questa tscherna"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Nunenconuschent"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Naginas datas"</string>
+ <!-- no translation found for quickcontact_clear_defaults_description (3792792870662989100) -->
+ <skip />
+ <!-- no translation found for quickcontact_clear_defaults_caption (4287306111861545753) -->
+ <skip />
+ <!-- no translation found for quickcontact_clear_defaults_button (8728754360205289059) -->
+ <skip />
<string name="menu_accounts" msgid="8499114602017077970">"Contos"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importar/exportar"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importar/exportar ils contacts"</string>
- <string name="menu_share" msgid="943789700636542260">"Cundivider"</string>
+ <!-- outdated translation 943789700636542260 --> <string name="menu_share" msgid="8746849630474240344">"Cundivider"</string>
<string name="share_via" msgid="563121028023030093">"Cundivider in contact sur"</string>
<string name="share_error" msgid="4374508848981697170">"Quest contact na po betg vegnir cundividì."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Num"</string>
@@ -276,14 +329,15 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Firma/Organisaziun"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Website"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Termin"</string>
+ <!-- no translation found for relationLabelsGroup (1854373894284572781) -->
+ <skip />
+ <!-- no translation found for groupsLabel (8573535366319059326) -->
+ <skip />
<string name="type_short_home" msgid="7770424864090605384">"P"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"F"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"A"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Quest contact è mo per lectura."</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Dapli"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Num principal"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Crear in contact sut il conto"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Allontanar la gruppa da sincronisaziun"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Agiuntar ina gruppa da sincronisaziun"</string>
@@ -291,7 +345,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Tut ils ulteriurs contacts"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Tut ils contacts"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"\"Sche «<xliff:g id="GROUP">%s</xliff:g>» vegn allontanà da la sincronisaziun, vegnan era tut ils contacts betg gruppads allontanads da la sincronisaziun.\""</string>
- <!-- outdated translation 4025734638492419713 --> <string name="account_phone" msgid="3682950835276226870">"Mo telefon (senza sincronisaziun)"</string>
+ <!-- outdated translation 4025734638492419713 --> <string name="account_phone" product="tablet" msgid="7946049152658522054">"Mo telefon (senza sincronisaziun)"</string>
+ <!-- outdated translation 4025734638492419713 --> <string name="account_phone" product="default" msgid="3682950835276226870">"Mo telefon (senza sincronisaziun)"</string>
<string name="call_custom" msgid="7756571794763171802">"Telefonar a(d) <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Telefonar (privat)"</string>
<string name="call_mobile" msgid="7502236805487609178">"Clom (telefonin)"</string>
@@ -353,6 +408,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chat sur ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chat sur Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Chat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adressa"</string>
<string name="postal_street" msgid="8133143961580058972">"Via"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Chascha postala"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Vischinanza"</string>
@@ -360,6 +416,7 @@
<string name="postal_region" msgid="6045263193478437672">"Chantun"</string>
<string name="postal_postcode" msgid="572136414136673751">"Numer postal"</string>
<string name="postal_country" msgid="7638264508416368690">"Pajais"</string>
+ <string name="full_name" msgid="6602579550613988977">"Num"</string>
<string name="name_given" msgid="1687286314106019813">"Prenum"</string>
<string name="name_family" msgid="3416695586119999058">"Num da famiglia"</string>
<string name="name_prefix" msgid="59756378548779822">"Prefix dal num"</string>
@@ -382,12 +439,129 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Retschertgar ils contacts"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Tschertgar en tut ils contacts"</string>
<string name="take_photo" msgid="7496128293167402354">"Far ina foto"</string>
+ <!-- no translation found for take_new_photo (7341354729436576304) -->
+ <skip />
<string name="pick_photo" msgid="448886509158039462">"Tscherner ina foto ord il catalog"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"La glista da contacts vegn adattada a la midada da lingua."\n\n"Spetgai per plaschair…"</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"La glista da contacts vegn actualisada. "\n\n"Spetgai per plaschair…"</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Il contacts vegnan actualmain actualisads. "\n\n"Il process d\'actualisaziun dovra radund <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>MB memoria interna dal telefon."\n\n"Tscherni ina da las suandantas opziuns:"</string>
+ <!-- no translation found for pick_new_photo (7962368009197147617) -->
+ <skip />
+ <!-- unknown placeholder BREAK_0 in locale_change_in_progress -->
+ <skip />
+ <!-- no translation found for locale_change_in_progress (7583992153091537467) -->
+ <skip />
+ <!-- unknown placeholder BREAK_0 in upgrade_in_progress -->
+ <skip />
+ <!-- no translation found for upgrade_in_progress (474511436863451061) -->
+ <skip />
+ <!-- no translation found for upgrade_out_of_memory (6153384328042175667) -->
+ <skip />
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Deinstallar in pèr applicaziuns"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Empruvar anc ina giada d\'actualisar"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Resultats da tschertga da: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Tschertgar…"</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Mussar la selecziun"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Mussar tuts"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Tscherner tut"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Deselecziunar tut"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"Tschernì in destinatur"</item>
+ <item quantity="other" msgid="4608837420986126229">"Tschernì <xliff:g id="COUNT">%d</xliff:g> destinaturs"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Betg tschernì in contact"</string>
+ <!-- outdated translation 5257149039253569615 --> <string name="add_field" msgid="2384260056674995230">"Agiuntar infurmaziuns"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"entras <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> entras <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favurit"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Modifitgar il contact"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"betg fusiunà"</item>
+ <item quantity="other" msgid="425683718017380845">"1 contact fusiunà ord <xliff:g id="COUNT">%0$d</xliff:g> funtaunas"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Auter"</string>
+ <!-- no translation found for aggregation_suggestion_join_dialog_title (5276699501316246253) -->
+ <skip />
+ <!-- no translation found for aggregation_suggestion_join_dialog_message (3842757977671434836) -->
+ <skip />
+ <!-- no translation found for aggregation_suggestion_edit_dialog_title (1064042382692091314) -->
+ <skip />
+ <!-- no translation found for aggregation_suggestion_edit_dialog_message (6549585283910518095) -->
+ <skip />
+ <!-- no translation found for menu_copyContact (4401683725471696686) -->
+ <skip />
+ <!-- no translation found for contact_directory_description (683398073603909119) -->
+ <skip />
+ <!-- no translation found for search_label (6789295859496641042) -->
+ <skip />
+ <!-- no translation found for directory_search_label (1887759056597975053) -->
+ <skip />
+ <!-- no translation found for local_search_label (1686089693064201315) -->
+ <skip />
+ <!-- no translation found for toast_making_personal_copy (7905283986345263275) -->
+ <skip />
+ <!-- no translation found for list_filter_prompt (7481426622828055116) -->
+ <skip />
+ <!-- no translation found for list_filter_all_accounts (8908683398914322369) -->
+ <skip />
+ <!-- no translation found for list_filter_all_starred (5031734941601931356) -->
+ <skip />
+ <!-- no translation found for list_filter_custom (8910173055702057002) -->
+ <skip />
+ <!-- no translation found for list_filter_customize (2035084418635775579) -->
+ <skip />
+ <!-- no translation found for list_filter_phones (7905045603593508221) -->
+ <skip />
+ <!-- no translation found for list_filter_single (5871400283515893087) -->
+ <skip />
+ <!-- no translation found for custom_list_filter (7836035257402013957) -->
+ <skip />
+ <!-- no translation found for activity_title_settings (5464130076132770781) -->
+ <skip />
+ <!-- no translation found for menu_settings (377929915873428211) -->
+ <skip />
+ <!-- no translation found for preference_displayOptions (1341720270148252393) -->
+ <skip />
+ <!-- no translation found for organization_company_and_title (6718207751363732025) -->
+ <skip />
+ <!-- no translation found for hint_findContacts (1808681193458772072) -->
+ <skip />
+ <!-- no translation found for non_phone_caption (1541655052330027380) -->
+ <skip />
+ <!-- no translation found for non_phone_add_to_contacts (6590985286250471169) -->
+ <skip />
+ <!-- no translation found for non_phone_close (7608506439725515667) -->
+ <skip />
+ <!-- no translation found for widget_name_and_phonetic (8739586586600099979) -->
+ <skip />
+ <!-- no translation found for date_year_toggle (7356532842767854606) -->
+ <skip />
+ <!-- no translation found for social_widget_label (6378905543028924592) -->
+ <skip />
+ <!-- no translation found for social_widget_loading (3697996166985327861) -->
+ <skip />
+ <!-- no translation found for contacts_unavailable_create_contact (7014525713871959208) -->
+ <skip />
+ <!-- no translation found for contacts_unavailable_add_account (7911101713860139754) -->
+ <skip />
+ <!-- no translation found for contacts_unavailable_import_contacts (4456440183590517471) -->
+ <skip />
+ <!-- no translation found for create_group_dialog_title (6874527142828424475) -->
+ <skip />
+ <!-- no translation found for create_group_item_label (5218022006186243310) -->
+ <skip />
+ <!-- no translation found for rename_group_dialog_title (3765299704290513289) -->
+ <skip />
+ <!-- no translation found for delete_group_dialog_title (7368429698398624427) -->
+ <skip />
+ <!-- no translation found for delete_group_dialog_message (295063284548750881) -->
+ <skip />
+ <!-- no translation found for toast_join_with_empty_contact (5015189525953438968) -->
+ <skip />
+ <!-- no translation found for indicator_joined_contact (3321049349627022128) -->
+ <skip />
+ <!-- no translation found for toast_text_copied (5143776250008541719) -->
+ <skip />
+ <!-- no translation found for cancel_confirmation_dialog_title (3950463632415908534) -->
+ <skip />
+ <!-- no translation found for cancel_confirmation_dialog_message (7021968394611740251) -->
+ <skip />
+ <!-- no translation found for discard (1234315037371251414) -->
+ <skip />
</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 194b7e0..6726ee5 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Alegeţi o persoană din agendă pentru a crea o comandă rapidă"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Alegeţi un număr pentru apelare"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Alegeţi un număr pentru trimiterea mesajului"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Selectaţi o persoană din agendă"</string>
<string name="starredList" msgid="4817256136413959463">"Cu stea"</string>
<string name="frequentList" msgid="7154768136473953056">"Frecvent"</string>
<string name="strequentList" msgid="5640192862059373511">"Favorite"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Ştergeţi persoana din agendă"</string>
<string name="menu_call" msgid="3992595586042260618">"Apelaţi persoana din agendă"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Trimiteţi mesaj text către o persoană din agendă"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Trimiteţi un e-mail"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Adresa pe hartă"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Setaţi ca număr prestabilit"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Setaţi ca adresă de e-mail prestabilită"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Separaţi"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Persoane din Agendă separate"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Redenumiţi grupul"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Ştergeţi grupul"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Nouă"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Separaţi persoana din agendă"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Sigur doriţi să separaţi această persoană din agendă în mai multe: câte una pentru fiecare set de informaţii care au fost reunite în aceasta?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Alăturaţi-vă"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Ştergerea acestei persoane din agendă va elimina informaţii din mai multe conturi."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Această persoană va fi ştearsă din agendă."</string>
<string name="menu_done" msgid="796017761764190697">"Terminat"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Reveniţi"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Anulaţi"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Modificaţi informaţiile despre persoana din agendă"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Persoană nouă în agendă"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetic"</string>
<string name="label_notes" msgid="8337354953278341042">"Note"</string>
<string name="label_sip_address" msgid="124073911714324974">"Apel prin internet"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Ton de apel"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Nume şi prenume"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Nume fonetic"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Companie"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Titlu"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Persoana nu există în agendă."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Creaţi o persoană nouă în agendă"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Selectaţi o etichetă"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-mail"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"IM"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Adresa poştală"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adresă"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organizaţie"</item>
<item msgid="7196592230748086755">"Notă"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Nicio fotografie disponibilă în telefon."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Pictogramă pentru persoana din agendă selectată"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Nu există imagini disponibile pe tabletă."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Nicio fotografie disponibilă în telefon."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Fotografie persoană din agendă"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Numele etichetei personalizate"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Opţiuni de afişare"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Opţiuni de afişare"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Trimiteţi apelurile direct către mesageria vocală"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Prestabilit"</string>
- <string name="changePicture" msgid="2943329047610967714">"Schimbaţi pictograma"</string>
- <string name="removePicture" msgid="3041230993155966350">"Eliminaţi pictograma"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Eliminaţi fotografia"</string>
<string name="noContacts" msgid="8579310973261953559">"Nicio persoană în agendă."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Nu a fost găsită nicio persoană potrivită în agendă."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Nicio persoană din agendă cu numere de telefon."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Persoana din agendă a fost salvată."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Eroare, modificările persoanei din agendă nu pot fi salvate."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Se afişează o persoană din agendă cu număr de telefon"</item>
- <item quantity="other" msgid="6133262880804110289">"Se afişează <xliff:g id="COUNT">%d</xliff:g> (de) persoane din agendă cu numere de telefon"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 persoană din agendă cu număr de telefon"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> (de) persoane din agendă cu numere de telefon"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Nicio persoană din agendă vizibilă cu numere de telefon"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Nicio persoană din agendă cu numere de telefon"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Se afişează o persoană din agendă"</item>
- <item quantity="other" msgid="2865867557378939630">"Se afişează <xliff:g id="COUNT">%d</xliff:g> (de) persoane din agendă"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 persoană din Agendă"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> (de) persoane din Agendă"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Nicio persoană din agendă nu este vizibilă"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Nicio persoană în agendă"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Nicio persoană din agendă nu este vizibilă"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Nicio persoană din agendă marcată cu stea"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Nicio persoană din agendă în <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"S-a găsit o persoană în agendă"</item>
- <item quantity="other" msgid="7752927996850263152">"<xliff:g id="COUNT">%d</xliff:g> persoane găsite în agendă"</item>
+ <item quantity="one" msgid="5517063038754171134">"A fost găsită 1 persoană din agendă"</item>
+ <item quantity="other" msgid="3852668542926965042">"Au fost găsite <xliff:g id="COUNT">%d</xliff:g> (de) persoane din agendă"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Persoana din agendă nu a fost găsită"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"au fost găsite mai mult de <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Nu au fost găsite persoane din agendă"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 persoană din agendă"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> (de) persoane din agendă"</item>
+ <item quantity="one" msgid="4826918429708286628">"A fost găsită 1 persoană din agendă"</item>
+ <item quantity="other" msgid="7988132539476575389">"Au fost găsite <xliff:g id="COUNT">%d</xliff:g> (de) persoane din agendă"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Agendă"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favorite"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Persoanele din agendă de pe cardul SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Nicio persoană în agendă pentru afişare. (Dacă tocmai aţi adăugat un cont, poate dura câteva minute sincronizarea persoanelor din agendă.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Nicio persoană din agendă pentru afişare."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Nicio persoană în agendă pentru afişare."\n\n"Pentru a adăuga persoane în agendă, apăsaţi pe "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" şi atingeţi:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Conturi"</b></font>" pentru a adăuga sau a configura un cont cu persoane în agendă pe care să-l puteţi sincroniza cu telefonul"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Persoană nouă în agendă"</b></font>" pentru a crea o nouă persoană în agendă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importaţi/Exportaţi"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Nicio persoană în agendă pentru afişare. (Dacă tocmai aţi adăugat un cont, poate dura câteva minute sincronizarea persoanelor din agendă.)"\n\n"Pentru a adăuga persoane în agendă, apăsaţi pe "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" şi atingeţi:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Conturi"</b></font>" pentru a adăuga sau a configura un cont cu persoane în agendă pe care să-l puteţi sincroniza cu telefonul"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opţiuni de afişare"</b></font>" pentru a selecta care persoane din agendă să fie vizibile"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Persoană nouă în agendă"</b></font>" pentru a crea o nouă persoană în agendă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importaţi/Exportaţi"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Nicio persoană în agendă pentru afişare."\n\n"Pentru a adăuga persoane în agendă, apăsaţi pe "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" şi atingeţi:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Conturi"</b></font>" pentru a adăuga sau a configura un cont cu persoane în agendă pe care să-l puteţi sincroniza cu telefonul"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Persoană nouă în agendă"</b></font>" pentru a crea o nouă persoană în agendă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importaţi/Exportaţi"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Nicio persoană în agendă pentru afişare. (Dacă tocmai aţi adăugat un cont, poate dura câteva minute sincronizarea persoanelor din agendă.)"\n\n"Pentru a adăuga persoane în agendă, apăsaţi pe "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" şi atingeţi:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Conturi"</b></font>" pentru a adăuga sau a configura un cont cu persoane în agendă pe care să-l puteţi sincroniza cu telefonul"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opţiuni de afişare"</b></font>" pentru a selecta care persoane din agendă să fie vizibile"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Persoană nouă în agendă"</b></font>" pentru a crea o nouă persoană în agendă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importaţi/Exportaţi"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Nu există persoane din agendă de afişat."\n\n"Dacă doriţi să adăugaţi persoane în agendă, apăsaţi pe "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" şi atingeţi:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Conturi"</b></font>" pentru a adăuga sau a configura un cont cu persoane din agendă, pe care le puteţi sincroniza cu cele de pe tabletă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Persoană nouă în agendă"</b></font>" pentru a crea de la zero o intrare nouă în agendă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importaţi/Exportaţi"</b></font>" pentru a importa persoane din agendă de pe cardul SIM sau SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Nu există persoane din agendă de afişat."\n\n"Dacă doriţi să adăugaţi persoane în agendă, apăsaţi pe "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" şi atingeţi:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Conturi"</b></font>" pentru a adăuga sau a configura un cont cu persoane din agendă, pe care le puteţi sincroniza cu cele de pe telefon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Persoană nouă în agendă"</b></font>" pentru a crea de la zero o intrare nouă în agendă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importaţi/Exportaţi"</b></font>" pentru a importa persoane din agendă de pe cardul SIM sau SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Nu există persoane din agendă de afişat. (Dacă aţi adăugat recent un cont, poate dura câteva minute până la finalizarea sincronizării persoanelor din agendă.)"\n\n"Dacă doriţi să adăugaţi persoane în agendă, apăsaţi pe "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" şi atingeţi:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Conturi"</b></font>" pentru a adăuga sau a configura un cont cu persoane din agendă, pe care le puteţi sincroniza cu cele de pe tabletă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opţiuni de afişare"</b></font>" pentru a modifica persoanele din agendă care sunt vizibile"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Persoană nouă în agendă"</b></font>" pentru a crea de la zero o intrare nouă în agendă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importaţi/Exportaţi"</b></font>" pentru a importa persoane din agendă de pe cardul SIM sau SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Nu există persoane din agendă de afişat. (Dacă aţi adăugat recent un cont, poate dura câteva minute până la finalizarea sincronizării persoanelor din agendă.)"\n\n"Dacă doriţi să adăugaţi persoane în agendă, apăsaţi pe "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" şi atingeţi:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Conturi"</b></font>" pentru a adăuga sau a configura un cont cu persoane din agendă, pe care le puteţi sincroniza cu cele de pe telefon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opţiuni de afişare"</b></font>" pentru a modifica persoanele din agendă care sunt vizibile"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Persoană nouă în agendă"</b></font>" pentru a crea de la zero o intrare nouă în agendă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importaţi/Exportaţi"</b></font>" pentru a importa persoane din agendă de pe cardul SIM sau SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Nu există persoane din agendă de afişat."\n\n"Dacă doriţi să adăugaţi persoane în agendă, apăsaţi pe "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" şi atingeţi:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Conturi"</b></font>" pentru a adăuga sau a configura un cont cu persoane din agendă, pe care le puteţi sincroniza cu cele de pe tabletă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Persoană nouă în agendă"</b></font>" pentru a crea de la zero o intrare nouă în agendă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importaţi/Exportaţi"</b></font>" pentru a importa persoane din agendă de pe cardul SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Nu există persoane din agendă de afişat."\n\n"Dacă doriţi să adăugaţi persoane în agendă, apăsaţi pe "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" şi atingeţi:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Conturi"</b></font>" pentru a adăuga sau a configura un cont cu persoane din agendă, pe care le puteţi sincroniza cu cele de pe telefon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Persoană nouă în agendă"</b></font>" pentru a crea de la zero o intrare nouă în agendă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importaţi/Exportaţi"</b></font>" pentru a importa persoane din agendă de pe cardul SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Nu există persoane din agendă de afişat. (Dacă aţi adăugat recent un cont, poate dura câteva minute până la finalizarea sincronizării persoanelor din agendă.)"\n\n"Dacă doriţi să adăugaţi persoane în agendă, apăsaţi pe "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" şi atingeţi:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Conturi"</b></font>" pentru a adăuga sau a configura un cont cu persoane din agendă, pe care le puteţi sincroniza cu cele de pe tabletă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opţiuni de afişare"</b></font>" pentru a modifica persoanele din agendă care sunt vizibile"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Persoană nouă în agendă"</b></font>" pentru a crea de la zero o intrare nouă în agendă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importaţi/Exportaţi"</b></font>" pentru a importa persoane din agendă de pe cardul SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Nu există persoane din agendă de afişat. (Dacă aţi adăugat recent un cont, poate dura câteva minute până la finalizarea sincronizării persoanelor din agendă.)"\n\n"Dacă doriţi să adăugaţi persoane din agendă, apăsaţi pe "<font fgcolor="#ffffffff"><b>"Meniu"</b></font>" şi atingeţi:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Conturi"</b></font>" pentru a adăuga sau a configura un cont cu persoane din agendă, pe care le puteţi sincroniza cu cele de pe telefon"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Opţiuni de afişare"</b></font>" pentru a modifica persoanele din agendă care sunt vizibile"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Persoană nouă în agendă"</b></font>" pentru a crea de la zero o intrare nouă în agendă"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importaţi/Exportaţi"</b></font>" pentru a importa persoane din agendă de pe cardul SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Nu aveţi favorite."\n\n" Pentru a adăuga o persoană în lista de favorite:"\n\n" "<li>"Apăsaţi pe fila "<b>"Agendă"</b>" "\n</li>" "\n<li>"Apăsaţi persoana pe care doriţi să o adăugaţi în lista de favorite"\n</li>" "\n<li>"Apăsaţi pe steaua de lângă numele persoanei"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Toate persoanele din agendă"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Cu stea"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Apelaţi din nou"</string>
<string name="callAgain" msgid="3197312117049874778">"Apelaţi din nou"</string>
<string name="returnCall" msgid="8171961914203617813">"Apelaţi înapoi"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> m <xliff:g id="SECONDS">%2$s</xliff:g> s"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> m <xliff:g id="SECONDS">%s</xliff:g> s"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Frecvent contactate"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Adăugaţi o persoană în agendă"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Adăugaţi „<xliff:g id="EMAIL">%s</xliff:g>” la persoanele din agendă?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Toate"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"unu"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"doi"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"trei"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Scanarea stocării USB a eşuat (Motiv: „<xliff:g id="FAIL_REASON">%s</xliff:g>”)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Scanarea cardului SD eşuată (Motiv: „<xliff:g id="FAIL_REASON">%s</xliff:g>”)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Eroare I/E"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Memoria este insuficientă (probabil fişierul este prea mare)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Analiza vCard a eşuat dintr-un motiv necunoscut"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Analiza vCard a eşuat, deşi pare a fi în format valid. Implementarea actuală nu acceptă acest format."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Nu a fost găsit niciun fişier vCard în spaţiul de stocare USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Niciun fişier vCard găsit pe cardul SD"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Formatul nu este acceptat."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Nu a reuşit să importe vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Niciun fişier vCard găsit în stocarea USB"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Niciun fişier vCard găsit pe cardul SD"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Colectarea metainformaţiilor pentru fişierele vCard indicate a eşuat."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Unul sau mai multe fişiere nu s-au putut importa (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Eroare necunoscută"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Selectaţi fişierul vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Se citeşte vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Se citeşte fişierul vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Citirea datelor de pe vCard a eşuat"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> din <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> (de) persoane din agendă"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> din <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> (de) fişiere"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Se stochează fişierele vCard într-un spaţiu de stocare local temporar"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Instrumentul de importare stochează în memoria cache fişierele vCard într-un spaţiu de stocare local temporar. Importul propriu-zis va începe în curând."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Se importă <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Se importă <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Citirea datelor vCard a eşuat"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Citirea datelor vCard a fost anulată"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"S-a finalizat importul vCard <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Importul <xliff:g id="FILENAME">%s</xliff:g> a fost anulat"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> va fi importat în curând."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Fişierul va fi importat în scurt timp."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Solicitarea de importare a fişierului vCard este respinsă. Încercaţi din nou mai târziu."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> va fi exportat în curând."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Solicitarea de exportare a fişierului vCard este respinsă. Încercaţi din nou mai târziu."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"contact"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Confirmaţi exportul"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Sigur doriţi să exportaţi lista de persoane din agendă către „<xliff:g id="VCARD_FILENAME">%s</xliff:g>”?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Exportul datelor persoanei din agendă a eşuat"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Prea multe fişiere vCard pe spaţiul de stocare USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Prea multe fişiere de tip vCard pe cardul SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Numele de fişier este prea lung („<xliff:g id="FILENAME">%s</xliff:g>”)"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"S-a finalizat exportul <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Exportul <xliff:g id="FILENAME">%s</xliff:g> a fost anulat"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Se exportă datele persoanelor din agendă"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Se exportă datele despre persoanele din agendă către „<xliff:g id="FILE_NAME">%s</xliff:g>”"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Nu se poate iniţializa instrumentul de export: „<xliff:g id="EXACT_REASON">%s</xliff:g>”"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Eroare apărută în timpul exportului: „<xliff:g id="EXACT_REASON">%s</xliff:g>”"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Obţinerea informaţiilor din baza de date a eşuat"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Nicio persoană din agendă exportabilă. Dacă în acest moment aveţi persoane în telefonul dvs., toate acestea pot fi reţinute de la exportul în afara telefonului de către un furnizor de date."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Nu există persoane din agendă care pot fi exportate. Dacă totuşi aveţi persoane din agendă pe tabletă, este posibil ca exportul acestora de pe tabletă să fie interzis de unii furnizori de date."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Nu există persoane din agendă care pot fi exportate. Dacă totuşi aveţi persoane în agenda de pe telefon, este posibil ca exportul acestora de pe telefon să fie interzis de unii furnizori de date."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Compozitorul vCard nu este corect iniţializat"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Nu poate fi deschis fişierul „<xliff:g id="FILE_NAME">%1$s</xliff:g>: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> din <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> (de) persoane din agendă"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Nu poate fi deschis fişierul „<xliff:g id="FILE_NAME">%s</xliff:g>: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> din <xliff:g id="TOTAL_NUMBER">%s</xliff:g> (de) persoane din agendă"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Se anulează importul vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Sunteţi sigur(ă) că anulaţi importul <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Se anulează exportul vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Sunteţi sigur(ă) că anulaţi exportul fişierului <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Anulare import/export vCard nereuşită"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Numele persoanelor din agenda dvs."</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Pauză 2 secunde"</string>
<string name="add_wait" msgid="3360818652790319634">"Adăugaţi interval de aşteptare"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Nicio aplicaţie pentru gestionarea acestei acţiuni"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Doresc să se reţină această alegere"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Necunoscut"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Nu există date"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Ştergeţi datele prestabilite"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Datele prestabilite pt. această intrare:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Ştergeţi"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Conturi"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importaţi/Exportaţi"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importaţi/exportaţi persoanele din agendă"</string>
- <string name="menu_share" msgid="943789700636542260">"Distribuiţi"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Distrib. persoana din agendă"</string>
<string name="share_via" msgid="563121028023030093">"Distribuiţi persoana din agendă prin"</string>
<string name="share_error" msgid="4374508848981697170">"Aceasta persoană din agendă nu poate fi distribuită."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Nume"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organizaţie"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Site web"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Eveniment"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relaţie"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grupuri"</string>
<string name="type_short_home" msgid="7770424864090605384">"D"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"Mi."</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"A"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Această persoană din agendă este numai în citire"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Mai multe"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Nume principal"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Creaţi o persoană în agendă, într-un cont"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Eliminaţi grup de sincronizare"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Adăugaţi grup de sincronizare"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Toate celelalte persoane din Agendă"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Toate persoanele din Agendă"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Eliminarea „<xliff:g id="GROUP">%s</xliff:g>” din sincronizare va elimina, de asemenea, orice persoană din agendă care nu face parte dintr-un grup."</string>
- <string name="account_phone" msgid="3682950835276226870">"Numai pe telefon, nesincronizată"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Numai pe tabletă, nesincronizată"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Numai pe telefon, nesincronizată"</string>
<string name="call_custom" msgid="7756571794763171802">"Apelaţi <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Apelaţi numărul de domiciliu"</string>
<string name="call_mobile" msgid="7502236805487609178">"Apelaţi numărul de mobil"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Conversaţi prin ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Conversaţi prin Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Conversaţi prin chat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adresă"</string>
<string name="postal_street" msgid="8133143961580058972">"Stradă"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Căsuţa poştală"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Vecinătate"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Stat"</string>
<string name="postal_postcode" msgid="572136414136673751">"Codul ZIP"</string>
<string name="postal_country" msgid="7638264508416368690">"Ţara"</string>
+ <string name="full_name" msgid="6602579550613988977">"Nume"</string>
<string name="name_given" msgid="1687286314106019813">"Prenume"</string>
<string name="name_family" msgid="3416695586119999058">"Numele de familie"</string>
<string name="name_prefix" msgid="59756378548779822">"Prefixul numelui"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Căutaţi în persoanele din agendă"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Căutare în toate persoanele din agendă"</string>
<string name="take_photo" msgid="7496128293167402354">"Fotografiaţi"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Creaţi o fotografie nouă"</string>
<string name="pick_photo" msgid="448886509158039462">"Selectaţi o fotografie din galerie"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Se actualizează lista de persoane din agendă pentru a reflecta modificarea limbii."\n\n" Aşteptaţi..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Se actualizează lista persoanelor din agendă."\n\n"Aşteptaţi..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Agenda este în proces de trecere la o versiune superioară. "\n\n"Procesul necesită aproximativ <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MO din spaţiul intern de stocare al telefonului."\n\n"Alegeţi una dintre următoarele opţiuni:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Selectaţi o fotografie nouă din Galerie"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Se actualizează lista de persoane din agendă pentru a reflecta schimbarea limbii."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Se actualizează lista de persoane din agendă."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Agenda este în proces de trecere la o versiune superioară. "\n\n"Procesul necesită aproximativ <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MO din spaţiul intern de stocare al telefonului."\n\n"Alegeţi una dintre următoarele opţiuni:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Dezinstalaţi unele aplicaţii"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Încercaţi din nou trecerea la o versiune superioară"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Rezultatele căutării pentru: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Se caută..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Afişaţi elementele selectate"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Afişaţi-le pe toate"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Selectaţi-le pe toate"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Deselectaţi-le pe toate"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Nu aţi selectat nicio persoană din Agendă."</string>
+ <string name="add_field" msgid="2384260056674995230">"Adăugaţi alt câmp"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"de pe <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"La ora <xliff:g id="DATE">%1$s</xliff:g> de pe <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"preferate"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Editaţi informaţiile despre persoana din agendă"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"nu au fost îmbinate"</item>
+ <item quantity="other" msgid="425683718017380845">"au fost îmbinate din <xliff:g id="COUNT">%0$d</xliff:g> surse"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Altul"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Asociaţi intrările din Agendă"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Asociaţi intrarea curentă din agendă cu intrarea selectată?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Editaţi persoanele din agendă selectate"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Comutaţi la funcţia de editare a persoanei din agendă selectate? Informaţiile introduse până acum vor fi copiate."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Copiaţi în Agendă"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Director <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Se caută în toată Agenda"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Director"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Agendă"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Se creează o copie personală"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Alegeţi lista cu persoane din agendă"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Toată agenda"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Cu stea"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Personalizată"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Personalizaţi..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Persoane din agendă cu numere de telefon"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Persoană din agendă"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Definiţi afişarea personalizată"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Setări"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Setări"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Opţiuni de afişare"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Găsiţi persoane din agendă"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Număr de telefon"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Adăugaţi în Agendă"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Închideţi"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Indicaţi anul"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Persoană din agendă"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Se încarcă ..."</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Creaţi o intrare nouă în Agendă"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Conectaţi-vă la un cont"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importaţi agenda din fişier"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Creaţi un grup nou"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Creaţi un grup nou]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Redenumiţi grupul"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Ştergeţi grupul"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Sunteţi sigur(ă) că doriţi să ştergeţi grupul „<xliff:g id="GROUP_LABEL">%1$s</xliff:g>”? (Agenda nu va fi ştearsă.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Introduceţi un nume pentru intrarea din agendă înainte de îmbinarea cu altă intrare."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Intrare în agendă îmbinată"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Text copiat"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Renunţaţi la modificări"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Doriţi să renunţaţi la modificări?"</string>
+ <string name="discard" msgid="1234315037371251414">"Renunţaţi"</string>
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 466c586..5431587 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Выбрать контакт для быстрого вызова"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Выбрать номер для вызова"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Выбрать номер для отправки сообщения"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Выбор контакта"</string>
<string name="starredList" msgid="4817256136413959463">"Помеченные"</string>
<string name="frequentList" msgid="7154768136473953056">"Часто вызываемые"</string>
<string name="strequentList" msgid="5640192862059373511">"Избранное"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Удалить контакт"</string>
<string name="menu_call" msgid="3992595586042260618">"Позвонить"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Отправить SMS/MMS"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Отправить письмо"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Адрес на карте"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Сделать номером по умолчанию"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Установить этот адрес по умолчанию"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Отделить"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Контакты разделены."</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Переименовать группу"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Удалить группу"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Добавить"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Разделить контакты"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Вы действительно хотите разделить этот контакт на несколько контактов: по одному для каждого набора контактной информации, содержащейся в этом контакте?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Присоединить"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Удаление этого контакта приведет к потере данных из нескольких аккаунтов."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Этот контакт будет удален."</string>
<string name="menu_done" msgid="796017761764190697">"Готово"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Отмена"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Отмена"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Изменить контакт"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Новый контакт"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Произношение"</string>
<string name="label_notes" msgid="8337354953278341042">"Примечания"</string>
<string name="label_sip_address" msgid="124073911714324974">"Интернет-вызов"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Мелодия"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Имя и фамилия"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Произношение имени"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Компания"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Название"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Контакт не существует."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Создать новый контакт"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Выбор ярлыка"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Телефон"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Адрес электронной почты"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Чат"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Почтовый адрес"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Адрес"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Организация"</item>
<item msgid="7196592230748086755">"Примечание"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"В телефоне нет картинок."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Значок контакта"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"На планшетном ПК нет изображений."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"В телефоне нет картинок."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Фотография контакта"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Название ярлыка"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Варианты отображения"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Варианты отображения"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Направлять вызовы в голосовую почту"</string>
<string name="default_ringtone" msgid="9099988849649827972">"По умолчанию"</string>
- <string name="changePicture" msgid="2943329047610967714">"Изменить значок"</string>
- <string name="removePicture" msgid="3041230993155966350">"Удалить значок"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Удалить фото"</string>
<string name="noContacts" msgid="8579310973261953559">"Нет контактов."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Подходящие контакты не найдены."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Нет контактов с номерами телефонов."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Контакт сохранен."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Ошибка. Не удалось сохранить изменения контакта."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Показан 1 контакт с номером телефона"</item>
- <item quantity="other" msgid="6133262880804110289">"Показаны контакты с номерами телефонов: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="3015357862286673986">"Контакты с номерами телефона: 1"</item>
+ <item quantity="other" msgid="3299954047880968205">"Контакты с номерами телефонов: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Нет видимых контактов с номерами телефонов."</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Нет контактов с номерами телефонов"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Показан 1 контакт"</item>
- <item quantity="other" msgid="2865867557378939630">"Показано контактов: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="3405747744700823280">"Контактов: 1"</item>
+ <item quantity="other" msgid="3578469907265375314">"Контактов: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Видимые контакты отсутствуют."</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Нет контактов"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Нет видимых контактов"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Нет помеченных контактов"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Нет контактов в: <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Найден 1 контакт"</item>
- <item quantity="other" msgid="7752927996850263152">"Найдено контактов: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="5517063038754171134">"Найдено: 1"</item>
+ <item quantity="other" msgid="3852668542926965042">"Найдено: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Контакт не найден."</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"найдено более <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Не найдено"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 контакт"</item>
- <item quantity="other" msgid="5660384247071761844">"Контактов: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="4826918429708286628">"Найдено: 1"</item>
+ <item quantity="other" msgid="7988132539476575389">"Найдено: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Контакты"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Избранное"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Контакты на SIM-карте"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Отсутствуют контакты для отображения. (Если аккаунт был только что добавлен, потребуется несколько минут для синхронизации контактов.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Отсутствуют контакты для отображения."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"У вас нет контактов."\n\n"Чтобы добавить контакты, нажмите "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и выберите:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Аккаунты"</b></font>", чтобы добавить или настроить аккаунт с контактами, которые можно будет синхронизировать с телефоном;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новый контакт"</b></font>", чтобы создать контакт \"с нуля\";"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импорт/Экспорт."</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"У вас нет контактов. (Если вы только что добавили аккаунт, синхронизация контактов может занять несколько минут.)"\n\n"Чтобы добавить контакты, нажмите "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и выберите:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Аккаунты"</b></font>", чтобы добавить или настроить аккаунт с контактами, которые можно будет синхронизировать с телефоном;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Варианты отображения"</b></font>", чтобы выбрать, какие контакты будут видны; "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новый контакт"</b></font>", чтобы создать контакт \"с нуля\";"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импорт/Экспорт."</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"У вас нет контактов."\n\n"Чтобы добавить контакты, нажмите "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и выберите:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Аккаунты"</b></font>", чтобы добавить или настроить аккаунт с контактами, которые можно будет синхронизировать с телефоном;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новый контакт"</b></font>", чтобы создать контакт \"с нуля\";"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импорт/Экспорт."</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"У вас нет контактов. (Если вы только что добавили аккаунт, синхронизация контактов может занять несколько минут.)"\n\n"Чтобы добавить контакты, нажмите "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и выберите:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Аккаунты"</b></font>", чтобы добавить или настроить аккаунт с контактами, которые можно будет синхронизировать с телефоном;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Варианты отображения"</b></font>", чтобы выбрать, какие контакты будут видны; "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новый контакт"</b></font>", чтобы создать контакт \"с нуля\";"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импорт/Экспорт."</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"У вас нет контактов."\n\n"Чтобы добавить контакты, нажмите "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и выберите:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Аккаунты"</b></font>", чтобы добавить или настроить аккаунт с контактами, которые можно будет синхронизировать с планшетным ПК;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новый контакт"</b></font>", чтобы создать новый контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импорт/Экспорт"</b></font>", чтобы импортировать контакты с SIM-карты или SD-карты."\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"У вас нет контактов."\n\n"Чтобы добавить контакты, нажмите "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и выберите:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Аккаунты,"</b></font>" чтобы добавить или настроить аккаунт с контактами, которые можно будет синхронизировать с телефоном;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новый контакт,"</b></font>" чтобы создать новый контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импорт/Экспорт,"</b></font>" чтобы импортировать контакты с SIM-карты или SD-карты."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"У вас нет контактов. Если вы только что добавили аккаунт, подождите несколько минут, пока идет синхронизация контактов с планшетным ПК. "\n\n"Чтобы добавить контакты, нажмите "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и выберите:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Аккаунты"</b></font>", чтобы добавить или настроить аккаунт с контактами, которые можно будет синхронизировать с планшетным ПК;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Варианты отображения"</b></font>", чтобы выбрать, какие контакты будут видны;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новый контакт"</b></font>", чтобы создать новый контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импорт/Экспорт"</b></font>", чтобы импортировать контакты с SIM-карты или SD-карты."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"У вас нет контактов (если вы добавили аккаунт только что, подожите несколько минут, пока идет их синхронизация с телефоном). "\n\n"Чтобы добавить контакты, нажмите "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и выберите:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Аккаунты"</b></font>", чтобы добавить или настроить аккаунт с контактами, которые можно будет синхронизировать с телефоном;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Варианты отображения"</b></font>", чтобы выбрать, какие контакты будут видны; "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новый контакт"</b></font>", чтобы создать новый контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импорт/Экспорт"</b></font>", чтобы импортировать контакты с SIM-карты или SD-карты."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"У вас нет контактов."\n\n"Чтобы добавить контакты, нажмите "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и выберите:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Аккаунты"</b></font>", чтобы добавить или настроить аккаунт с контактами, которые можно будет синхронизировать с планшетным ПК;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новый контакт"</b></font>", чтобы создать новый контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импорт/Экспорт"</b></font>", чтобы импортировать контакты с SD-карты."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"У вас нет контактов."\n\n"Чтобы добавить контакты, нажмите "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и выберите:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Аккаунты"</b></font>", чтобы добавить или настроить аккаунт с контактами, которые можно будет синхронизировать с телефоном;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новый контакт"</b></font>", чтобы создать новый контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импорт/Экспорт"</b></font>", чтобы импортировать контакты с SD-карты."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"У вас нет контактов. Если вы только что добавили аккаунт, подождите несколько минут, пока идет синхронизация контактов с планшетным ПК. "\n\n"Чтобы добавить контакты, нажмите "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и выберите:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Аккаунты"</b></font>", чтобы добавить или настроить аккаунт с контактами, которые можно будет синхронизировать с планшетным ПК;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Варианты отображения"</b></font>", чтобы выбрать, какие контакты будут видны;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новый контакт"</b></font>", чтобы создать новый контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импорт/Экспорт"</b></font>", чтобы импортировать контакты с SD-карты."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"У вас нет контактов (если вы добавили аккаунт только что, подожите несколько минут, пока идет их синхронизация с телефоном). "\n\n"Чтобы добавить контакты, нажмите "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" и выберите:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Аккаунты"</b></font>", чтобы добавить или настроить аккаунт с контактами, которые можно будет синхронизировать с телефоном;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Варианты отображения"</b></font>", чтобы выбрать, какие контакты будут видны; "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новый контакт"</b></font>", чтобы создать новый контакт;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Импорт/Экспорт"</b></font>", чтобы импортировать контакты с SD-карты."\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"У вас нет избранных контактов."\n\n"Чтобы сделать контакт избранным,"\n\n" "<li>"нажмите вкладку "<b>"Контакты"</b>";"\n</li>" "\n<li>"нажмите контакт, который нужно сделать избранным;"\n</li>" "\n<li>"нажмите звездочку возле имени контакта."\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Все контакты"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Помеченные"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Перезвонить"</string>
<string name="callAgain" msgid="3197312117049874778">"Позвонить снова"</string>
<string name="returnCall" msgid="8171961914203617813">"Перезвонить"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g>:<xliff:g id="SECONDS">%2$s</xliff:g>"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g>:<xliff:g id="SECONDS">%s</xliff:g>"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Часто набираемые"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Добавить контакт"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Добавить в контакты <xliff:g id="EMAIL">%s</xliff:g>?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Все"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"один"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"два"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"три"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Не удалось просканировать USB-накопитель (<xliff:g id="FAIL_REASON">%s</xliff:g>)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Не удалось просканировать SD-карту (причина: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Ошибка ввода-вывода"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Недостаточно памяти (возможно, файл слишком велик)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Не удалось обработать vCard по неизвестной причине."</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Не удалось обработать vCard. Файл правильного формата, но не поддерживается в текущей реализации."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"На USB-накопителе отсутствуют файлы vCard"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"На SD-карте не найдены файлы VCard"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Формат не поддерживается."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Не удалось импортировать vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"На USB-накопителе отсутствуют файлы vCard"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"На SD-карте не найдены файлы VCard"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Не удалось собрать метаинформацию файлов VCard."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Не удалось импортировать один или несколько файлов (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Неизвестная ошибка"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Выберите файл vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Чтение vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Чтение файлов VCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Не удается считать данные vCard"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"Контакт <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> из <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"Файл <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> из <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Кэширование файлов vCard в локальное временное хранилище"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Импортер кэширует файлы vCard в локальное временное хранилище. Фактический импорт начнется в ближайшее время."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Импорт <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Импорт: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Не удалось прочитать данные из файла vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Чтение данных vCard отменено"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Импорт файла vCard <xliff:g id="FILENAME">%s</xliff:g> завершен"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Импорт <xliff:g id="FILENAME">%s</xliff:g> был отменен"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"Импорт <xliff:g id="FILENAME">%s</xliff:g> начнется в ближайшее время."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Файл будет импортирован в ближайшее время."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Запрос на импорт vCard отклонен. Повторите попытку позже."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"Экспорт <xliff:g id="FILENAME">%s</xliff:g> начнется в ближайшее время."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Запрос на экспорт vCard отклонен. Повторите попытку позже."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"контакт"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Подтверждение·экспорта"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Вы действительно хотите экспортировать контакты в файл <xliff:g id="VCARD_FILENAME">%s</xliff:g>?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Не удалось экспортировать данные контакта."</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Слишком много файлов vCard на USB-накопителе"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Слишком много файлов VCard на SD-карте."</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Название файла слишком длинное (<xliff:g id="FILENAME">%s</xliff:g>)."</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Экспорт <xliff:g id="FILENAME">%s</xliff:g> завершен"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Экспорт <xliff:g id="FILENAME">%s</xliff:g> был отменен"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Экспорт данных контакта"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Экспорт данных контакта в \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Не удается запустить функцию экспорта: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Ошибка при экспорте: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Не удалось получить информацию из базы данных."</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Нет контактов для экспорта. Если в вашем телефоне имеются контакты, возможно они защищены от экспорта оператором услуг."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Нет контактов для экспорта. Если в вашем планшетном ПК имеются контакты, возможно, их экспорт был заблокирован поставщиком данных."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Нет контактов для экспорта. Если в вашем телефоне имеются контакты, возможно, их экспорт был заблокирован поставщиком данных."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Редактор vCard запущен некорректно"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Не удается открыть \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"Контакт <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> из <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g>"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Не удается открыть \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"Контакт <xliff:g id="CURRENT_NUMBER">%s</xliff:g> из <xliff:g id="TOTAL_NUMBER">%s</xliff:g>"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Отмена импорта vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Отменить импорт <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Отмена экспорта vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Вы действительно хотите отменить экспорт <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Не удалось отменить импорт/экспорт vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Имена контактов"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Добавить двухсекундную паузу"</string>
<string name="add_wait" msgid="3360818652790319634">"Добавить паузу"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Отсутствует приложение для обработки этого действия"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Запомнить выбранное"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Неизвестно"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Нет данных"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Удалить настройки по умолчанию"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Настройки этого контакта по умолчанию:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Очистить"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Аккаунты"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Импорт/Экспорт"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Импорт и экспорт контактов"</string>
- <string name="menu_share" msgid="943789700636542260">"Отправить"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Отправить контакт"</string>
<string name="share_via" msgid="563121028023030093">"Способ отправки"</string>
<string name="share_error" msgid="4374508848981697170">"Этот контакт нельзя отправить."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Имя"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Организация"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Веб-сайт"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Мероприятие"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Отношения"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Группы"</string>
<string name="type_short_home" msgid="7770424864090605384">"Д"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"М"</string>
<string name="type_short_work" msgid="4925330752504537861">"Р"</string>
<string name="type_short_pager" msgid="2613818970827594238">"П"</string>
<string name="type_short_other" msgid="5669407180177236769">"Др"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Этот контакт только для чтения"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Дополнительно"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Основное имя"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Создать контакт в аккаунте"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Удалить группу синхронизации"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Добавить группу синхронизации"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Остальные контакты"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Все контакты"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Удаление группы \"<xliff:g id="GROUP">%s</xliff:g>\" из синхронизации приведет к удалению любых несгруппированных контактов из синхронизации."</string>
- <string name="account_phone" msgid="3682950835276226870">"Только телефон, без синхронизации"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Только для планшетных ПК, без синхронизации"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Только телефон, без синхронизации"</string>
<string name="call_custom" msgid="7756571794763171802">"Позвонить (<xliff:g id="CUSTOM">%s</xliff:g>)"</string>
<string name="call_home" msgid="1990519474420545392">"Домашний тел."</string>
<string name="call_mobile" msgid="7502236805487609178">"Мобильный тел."</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Чат через ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Чат через Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Чат"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Адрес"</string>
<string name="postal_street" msgid="8133143961580058972">"Улица"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Почтовый ящик"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Окружение"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Регион"</string>
<string name="postal_postcode" msgid="572136414136673751">"Почтовый индекс"</string>
<string name="postal_country" msgid="7638264508416368690">"Страна"</string>
+ <string name="full_name" msgid="6602579550613988977">"Имя"</string>
<string name="name_given" msgid="1687286314106019813">"Имя"</string>
<string name="name_family" msgid="3416695586119999058">"Фамилия"</string>
<string name="name_prefix" msgid="59756378548779822">"Форма обращения"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Поиск контактов"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Поиск всех контактов"</string>
<string name="take_photo" msgid="7496128293167402354">"Сфотографировать"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Создать новое фото"</string>
<string name="pick_photo" msgid="448886509158039462">"Выбрать фото из галереи"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Выполняется обновление списка контактов, чтобы изменение языка вступило в силу."\n\n"Подождите..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Обновляется список контактов."\n\n"Подождите..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Контакты обновляются. "\n\n" Для обновления требуется около <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> МБ внутренней памяти телефона."\n\n"Выберите один из следующих вариантов:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Выбрать новое фото из Галереи"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Список контактов обновляется после смены языка интерфейса."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Список контактов обновляется..."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Список контактов обновляется. "\n\n"Для обновления требуется около <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> МБ встроенной памяти."\n\n"Выберите один из следующих вариантов:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Удалить некоторые приложения"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Повторить попытку обновления"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Результаты поиска для: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Поиск..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Показать выбранные"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Показать все"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Выбрать все"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Снять все выделения"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"Выбран 1 получатель"</item>
+ <item quantity="other" msgid="4608837420986126229">"Выбрано получателей: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Контакты не выбраны."</string>
+ <string name="add_field" msgid="2384260056674995230">"Добавить другое поле"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"с помощью <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> с помощью <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"избранное"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Изменить контакт"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"не объединен"</item>
+ <item quantity="other" msgid="425683718017380845">"объединено из нескольких источников (<xliff:g id="COUNT">%0$d</xliff:g>)"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Другое"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Объединить контакты"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Объединить текущий контакт с выбранным контактом?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Редактировать выбранные контакты"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Переключиться на редактирование выбранного контакта? Введенная информация будет скопирована."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Копировать в контакты"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Каталог <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Поиск всех контактов"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Каталог"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Контакты"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Создание персональной копии"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Выберите список контактов"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Все контакты"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Помеченные"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Пользовательские"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Настроить..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Контакты с номерами телефонов"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Контакт"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Настройка отображения"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Настройки"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Настройки"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Варианты отображения"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Найти контакты"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Номер телефона"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Добавить в контакты"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Закрыть"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Указать год"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Контакт"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Загрузка…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Создать новый контакт"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Войдите в аккаунт"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Импорт контактов из файла"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Создание новой группы"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Создать новую группу]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Переименование группы"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Удаление группы"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Вы действительно хотите удалить группу \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\"? Обратите внимание: при удалении группы контакты удалены не будут."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Введите имя контакта перед тем, как объединять его с другим."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Присоединенный контакт"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Текст скопирован"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Отмена изменений"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Вы действительно хотите отменить изменения?"</string>
+ <string name="discard" msgid="1234315037371251414">"Отменить"</string>
</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index cd88a5c..28ce643 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Výber skratky kontaktu"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Výber čísla pre hovor"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Výber čísla pre správu"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Výber kontaktu"</string>
<string name="starredList" msgid="4817256136413959463">"Označené hviezdičkou"</string>
<string name="frequentList" msgid="7154768136473953056">"Časté"</string>
<string name="strequentList" msgid="5640192862059373511">"Obľúbené"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Odstrániť kontakt"</string>
<string name="menu_call" msgid="3992595586042260618">"Zavolať kontaktu"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Odoslať správu kontaktu"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Poslať e-mail"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Adresa na mape"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Nastaviť číslo ako predvolenú hodnotu"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Nastaviť ako predvolený e-mail"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Oddeliť"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Kontakty boli oddelené"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Premenovať skupinu"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Odstrániť skupinu"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Nový"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Oddeliť kontakt"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Naozaj chcete tento kontakt rozdeliť do viacerých kontaktov? Pre každú pripojenú kontaktnú informáciu bude vytvorený samostatný kontakt."</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Spojiť"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Odstránením tohto kontaktu odstránite informácie z viacerých účtov."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Tento kontakt bude odstránený."</string>
<string name="menu_done" msgid="796017761764190697">"Hotovo"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Vrátiť"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Zrušiť"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Upraviť kontakt"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Nový kontakt"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Foneticky"</string>
<string name="label_notes" msgid="8337354953278341042">"Poznámky"</string>
<string name="label_sip_address" msgid="124073911714324974">"Internetový hovor"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Vyzváňací tón"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Meno a priezvisko"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Meno (foneticky)"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Spoločnosť"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Titul"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Kontakt neexistuje."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Vytvoriť nový kontakt"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Vybrať menovku"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefón"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Poslať e-mail"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Okamžité správy"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Poštová adresa"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adresa"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organizácia"</item>
<item msgid="7196592230748086755">"Poznámka"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"V telefónne nie sú žiadne fotografie."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Ikona kontaktu"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"V tablete nie sú k dispozícii žiadne fotografie."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"V telefónne nie sú žiadne fotografie."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Fotografie kontaktu"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Vlastný názov menovky"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Možnosti zobrazenia"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Možnosti zobrazenia"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Presmerovať hovory priamo do hlasovej schránky"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Predvolené"</string>
- <string name="changePicture" msgid="2943329047610967714">"Zmeniť ikonu"</string>
- <string name="removePicture" msgid="3041230993155966350">"Odstrániť ikonu"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Odstrániť fotografiu"</string>
<string name="noContacts" msgid="8579310973261953559">"Žiadne kontakty."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Nenašli sa žiadne zodpovedajúce kontakty."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Ku kontaktom nie sú priradené žiadne telefónne čísla."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Kontakt bol uložený."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Chyba, zmeny kontaktu nie je možné uložiť."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Zobrazuje sa 1 kontakt s telefónnym číslom"</item>
- <item quantity="other" msgid="6133262880804110289">"Počet zobrazených kontaktov s telefónnymi číslami: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 kontakt s telefónnym číslom"</item>
+ <item quantity="other" msgid="3299954047880968205">"Počet kontaktov s telefónnymi číslami: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Žiadne kontakty s telefónnymi číslami nie sú viditeľné"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Ku kontaktom nie sú priradené žiadne telefónne čísla"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Zobrazuje sa 1 kontakt"</item>
- <item quantity="other" msgid="2865867557378939630">"Počet zobrazených kontaktov: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="3405747744700823280">"Počet kontaktov: 1"</item>
+ <item quantity="other" msgid="3578469907265375314">"Počet kontaktov: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Žiadne kontakty nie sú viditeľné"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Žiadne kontakty"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Žiadne kontakty nie sú viditeľné"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Žiadne kontakty označené hviezdičkou"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Žiadne kontakty v položke <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Našiel sa 1 kontakt"</item>
- <item quantity="other" msgid="7752927996850263152">"Počet nájdených kontaktov: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="5517063038754171134">"Počet nájdených položiek: 1"</item>
+ <item quantity="other" msgid="3852668542926965042">"Počet nájdených položiek: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Kontakt sa nenašiel"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"nájdených viac položiek ako <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Nenájdené"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"Počet kontaktov: 1"</item>
- <item quantity="other" msgid="5660384247071761844">"Počet kontaktov: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="4826918429708286628">"Počet nájdených položiek: 1"</item>
+ <item quantity="other" msgid="7988132539476575389">"Počet nájdených položiek: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Kontakty"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Obľúbené"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Kontakty na karte SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Nemáte uložené žiadne kontakty, ktoré by bolo možné zobraziť. (Ak ste účet pridali práve teraz, môže synchronizácia kontaktov trvať niekoľko minút.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Nemáte uložené žiadne kontakty, ktoré by sa dali zobraziť."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Nemáte žiadne kontakty, ktoré by bolo možné zobraziť."\n\n"Ak chcete pridať kontakty, stlačte tlačidlo "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotknite sa položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", ak chcete pridať alebo nakonfigurovať účet a kontakty v ňom synchronizovať s telefónom;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", ak chcete vytvoriť úplne nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovať alebo exportovať"</b></font>"."\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Nemáte žiadne kontakty, ktoré by bolo možné zobraziť. (Ak ste práve pridali účet, môže synchronizácia kontaktov trvať niekoľko minút.)"\n\n"Ak chcete pridať kontakty, stlačte tlačidlo "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotknite sa položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", ak chcete pridať alebo nakonfigurovať účet a kontakty v ňom synchronizovať s telefónom;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti zobrazenia"</b></font>", ak chcete zmeniť, ktoré kontakty budú zobrazené;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", ak chcete vytvoriť úplne nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovať alebo exportovať"</b></font>"."\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Nemáte žiadne kontakty, ktoré by bolo možné zobraziť."\n\n"Ak chcete pridať kontakty, stlačte tlačidlo "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotknite sa položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", ak chcete pridať alebo nakonfigurovať účet a kontakty v ňom synchronizovať s telefónom;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", ak chcete vytvoriť úplne nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovať alebo exportovať"</b></font>"."\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Nemáte žiadne kontakty, ktoré by bolo možné zobraziť. (Ak ste práve pridali účet, môže synchronizácia kontaktov trvať niekoľko minút.)"\n\n"Ak chcete pridať kontakty, stlačte tlačidlo "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotknite sa položky:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", ak chcete pridať alebo nakonfigurovať účet a kontakty v ňom synchronizovať s telefónom;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti zobrazenia"</b></font>", ak chcete zmeniť, ktoré kontakty budú zobrazené;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", ak chcete vytvoriť úplne nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovať alebo exportovať"</b></font>"."\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Nemáte žiadne kontakty, ktoré by bolo možné zobraziť."\n\n"Ak chcete pridať kontakty, stlačte tlačidlo "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotknite sa jednej z nasledujúcich položiek:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", ak chcete pridať alebo nakonfigurovať účet a kontakty v ňom synchronizovať s tabletom;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", ak chcete vytvoriť úplne nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovať alebo exportovať"</b></font>", ak chcete importovať kontakty z karty SIM alebo SD."\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Nemáte žiadne kontakty, ktoré by bolo možné zobraziť."\n\n"Ak chcete pridať kontakty, stlačte tlačidlo "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotknite sa jednej z nasledujúcich položiek:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", ak chcete pridať alebo nakonfigurovať účet a kontakty v ňom synchronizovať s telefónom;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", ak chcete vytvoriť úplne nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovať alebo exportovať"</b></font>", ak chcete importovať kontakty z karty SIM alebo SD."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Nemáte žiadne kontakty, ktoré by bolo možné zobraziť. (Ak ste práve pridali účet, môže synchronizácia kontaktov trvať niekoľko minút.)"\n\n"Ak chcete pridať kontakty, stlačte tlačidlo "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotknite sa jednej z nasledujúcich položiek:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", ak chcete pridať alebo nakonfigurovať účet a kontakty v ňom synchronizovať s tabletom;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti zobrazenia"</b></font>", ak chcete zmeniť, ktoré kontakty budú zobrazené;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", ak chcete vytvoriť úplne nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovať alebo exportovať"</b></font>", ak chcete importovať kontakty z karty SIM alebo SD."\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Nemáte žiadne kontakty, ktoré by bolo možné zobraziť. (Ak ste práve pridali účet, môže synchronizácia kontaktov trvať niekoľko minút.)"\n\n"Ak chcete pridať kontakty, stlačte tlačidlo "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotknite sa jednej z nasledujúcich položiek:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", ak chcete pridať alebo nakonfigurovať účet a kontakty v ňom synchronizovať s telefónom;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti zobrazenia"</b></font>", ak chcete zmeniť, ktoré kontakty budú zobrazené;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", ak chcete vytvoriť úplne nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovať alebo exportovať"</b></font>", ak chcete importovať kontakty z karty SIM alebo SD."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Nemáte žiadne kontakty, ktoré by bolo možné zobraziť."\n\n"Ak chcete pridať kontakty, stlačte tlačidlo "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotknite sa jednej z nasledujúcich položiek:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", ak chcete pridať alebo nakonfigurovať účet a kontakty v ňom synchronizovať s tabletom;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", ak chcete vytvoriť úplne nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovať alebo exportovať"</b></font>", ak chcete importovať kontakty z karty SD."\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Nemáte žiadne kontakty, ktoré by bolo možné zobraziť."\n\n"Ak chcete pridať kontakty, stlačte tlačidlo "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotknite sa jednej z nasledujúcich položiek:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", ak chcete pridať alebo nakonfigurovať účet a kontakty v ňom synchronizovať s telefónom;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", ak chcete vytvoriť úplne nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovať alebo exportovať"</b></font>", ak chcete importovať kontakty z karty SD."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Nemáte žiadne kontakty, ktoré by bolo možné zobraziť. (Ak ste práve pridali účet, môže synchronizácia kontaktov trvať niekoľko minút.)"\n\n"Ak chcete pridať kontakty, stlačte tlačidlo "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotknite sa jednej z nasledujúcich položiek:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", ak chcete pridať alebo nakonfigurovať účet a kontakty v ňom synchronizovať s tabletom;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti zobrazenia"</b></font>", ak chcete zmeniť, ktoré kontakty budú zobrazené;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", ak chcete vytvoriť úplne nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovať alebo exportovať"</b></font>", ak chcete importovať kontakty zo svojej karty SD."\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Nemáte žiadne kontakty, ktoré by bolo možné zobraziť. (Ak ste práve pridali účet, môže synchronizácia kontaktov trvať niekoľko minút.)"\n\n"Ak chcete pridať kontakty, stlačte tlačidlo "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" a dotknite sa jednej z nasledujúcich položiek:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Účty"</b></font>", ak chcete pridať alebo nakonfigurovať účet a kontakty v ňom synchronizovať s telefónom;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti zobrazenia"</b></font>", ak chcete zmeniť, ktoré kontakty budú zobrazené;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nový kontakt"</b></font>", ak chcete vytvoriť úplne nový kontakt;"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importovať alebo exportovať"</b></font>", ak chcete importovať kontakty z karty SD."\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Nemáte žiadne obľúbené kontakty."\n\n"Pridanie kontaktu do zoznamu obľúbených:"\n\n" "<li>"Dotknite sa karty "<b>"Kontakty"</b>\n</li>" "\n<li>"Dotknite sa kontaktu, ktorý má byť pridaný do obľúbených"\n</li>" "\n<li>"Dotknite sa hviezdičky vedľa názvu kontaktu"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Všetky kontakty"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Označené hviezdičkou"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Zavolať späť"</string>
<string name="callAgain" msgid="3197312117049874778">"Zavolať znova"</string>
<string name="returnCall" msgid="8171961914203617813">"Zavolať späť"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min <xliff:g id="SECONDS">%2$s</xliff:g> s"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min <xliff:g id="SECONDS">%s</xliff:g> s"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Najčastejšie používané kontakty"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Pridať kontakt"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Chcete pridať „<xliff:g id="EMAIL">%s</xliff:g>“ medzi kontakty?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Všetko"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"jedna"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dva"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tri"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Vyhľadávanie v ukladacom priestore USB zlyhalo (Dôvod: „<xliff:g id="FAIL_REASON">%s</xliff:g>“)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Vyhľadávanie na karte SD zlyhalo. (Dôvod: <xliff:g id="FAIL_REASON">%s</xliff:g>)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Chyba I/O"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Nie je k dispozícii dostatok pamäte (súbor môže byť príliš veľký)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Analýza karty vCard z neznámeho dôvodu zlyhala."</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Analýza karty vCard zlyhala. Napriek tomu, že pravdepodobne ide o správny formát, aktuálna implementácia ho nepodporuje."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"V ukladacom priestore USB nebol nájdený žiadny súbor vCard"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Na karte SD sa nenašiel žiadny súbor vCard"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Formát nie je podporovaný."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Importovanie vizitky vCard zlyhalo"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"V ukladacom priestore USB nebola nájdená žiadna karta vCard"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Na karte SD nebol nájdený žiadny súbor vCard"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Zhromaždenie metainformácií zadaných súborov vCard zlyhalo."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Import jedného alebo viacerých súborov zlyhal (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Neznáma chyba"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Výber súboru vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Čítanie karty vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Prebieha čítanie súborov vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Čítanie údajov karty vCard zlyhalo"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> z <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontaktov"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> z <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> súborov"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Načítanie vizitiek vCard do vyrovnávacej pamäte miestneho dočasného ukladacieho priestoru"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Nástroj pre import načítava vizitky vCard do vyrovnávacej pamäte miestneho dočasného ukladacieho priestoru. Samotný import sa spustí o chvíľu."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Import: <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importuje sa <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Čítanie údajov vizitky vCard zlyhalo."</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Čítanie údajov vizitky vCard je zrušené."</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Import vizitky vCard bol dokončený <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Import vizitky <xliff:g id="FILENAME">%s</xliff:g> bol zrušený"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"Vizitka <xliff:g id="FILENAME">%s</xliff:g> bude čoskoro importovaná."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Súbor bude čoskoro importovaný."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Žiadosť o import vizitiek vCard bola zamietnutá. Skúste to znova neskôr."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"Vizitka <xliff:g id="FILENAME">%s</xliff:g> bude čoskoro exportovaná."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Žiadosť o export vizitiek vCard bola zamietnutá. Skúste to znova neskôr."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"kontakt"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Potvrdiť export"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Naozaj chcete exportovať zoznam kontaktov do súboru <xliff:g id="VCARD_FILENAME">%s</xliff:g>?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Export údajov kontaktov zlyhal"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"V ukladacom priestore USB je príliš veľa súborov vCard"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Na karte SD sa nachádza príliš veľa súborov vCard"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Požadovaný názov súboru (<xliff:g id="FILENAME">%s</xliff:g>) je príliš dlhý"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Export vizitky <xliff:g id="FILENAME">%s</xliff:g> bol dokončený"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Export vizitky <xliff:g id="FILENAME">%s</xliff:g> bol zrušený"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Export údajov kontaktov"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Prebieha export údajov kontaktov do súboru <xliff:g id="FILE_NAME">%s</xliff:g>"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Nie je možné spustiť exportný program: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Pri exporte sa vyskytla chyba: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Načítanie informácií z databázy zlyhalo"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Nie je možné exportovať žiadne kontakty. Ak máte v telefóne skutočne uložené kontakty, je možné, že niektorý poskytovateľ dátových služieb zakázal ich export mimo telefón."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Nie je možné exportovať žiadne kontakty. Ak máte v tablete skutočne uložené kontakty, je možné, že niektorí poskytovatelia dátových služieb zakázali ich export mimo tablet."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Nie je možné exportovať žiadne kontakty. Ak máte v telefóne skutočne uložené kontakty, je možné, že niektorí poskytovatelia dátových služieb zakázali ich export mimo telefón."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Editor karty vCard nie je správne inicializovaný"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Súbor „<xliff:g id="FILE_NAME">%1$s</xliff:g>“ nie je možné otvoriť: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> z <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontaktov"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Súbor „<xliff:g id="FILE_NAME">%s</xliff:g>“ nie je možné otvoriť: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> z <xliff:g id="TOTAL_NUMBER">%s</xliff:g> kontaktov"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Prebieha rušenie importu vizitky vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Naozaj chcete zrušiť import vizitky <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Prebieha rušenie exportu vizitky vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Naozaj chcete zrušiť export vizitky <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Zrušenie imp./exp. vizitky vCard zlyhalo"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Mená vašich kontaktov"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Pridať dvojsekundovú pauzu"</string>
<string name="add_wait" msgid="3360818652790319634">"Pridať čakanie"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Aplikácia potrebná na spracovanie tejto akcie sa nenašla"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Zapamätať túto voľbu"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Neznáme"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Žiadne údaje"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Vymazať predvolené nastavenia"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Pre kontakt boli nastav. predv. hodnoty:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Vymazať"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Účty"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Import a export"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importovať alebo exportovať kontakty"</string>
- <string name="menu_share" msgid="943789700636542260">"Zdieľať"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Zdieľať kontakt"</string>
<string name="share_via" msgid="563121028023030093">"Zdieľať kontakt pomocou"</string>
<string name="share_error" msgid="4374508848981697170">"Tento kontakt nie je možné zdieľať."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Meno"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organizácia"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Webové stránky"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Udalosť"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Vzťah"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Skupiny"</string>
<string name="type_short_home" msgid="7770424864090605384">"D"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"P"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Tento kontakt je len na čítanie"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Viac"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Primárne meno"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Vytvoriť kontakt na základe účtu"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Odstrániť synchronizovanú skupinu"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Pridať synchronizovanú skupinu"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Všetky ostatné kontakty"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Všetky kontakty"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Odstránením skupiny <xliff:g id="GROUP">%s</xliff:g> zo synchronizácie odstránite zo synchronizácie tiež všetky kontakty mimo skupinu."</string>
- <string name="account_phone" msgid="3682950835276226870">"Iba v telefóne, nesynchronizované"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Iba v tablete, nesynchronizované"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Iba v telefóne, nesynchronizované"</string>
<string name="call_custom" msgid="7756571794763171802">"Zavolať kontakt <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Zavolať domov"</string>
<string name="call_mobile" msgid="7502236805487609178">"Zavolať na mobil"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Zhovárať sa pomocou služby ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Zhovárať sa pomocou služby Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Rozhovor"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adresa"</string>
<string name="postal_street" msgid="8133143961580058972">"Ulica"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Číslo poštovej schránky"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Štvrť"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Štát"</string>
<string name="postal_postcode" msgid="572136414136673751">"PSČ"</string>
<string name="postal_country" msgid="7638264508416368690">"Krajina"</string>
+ <string name="full_name" msgid="6602579550613988977">"Meno"</string>
<string name="name_given" msgid="1687286314106019813">"Krstné meno"</string>
<string name="name_family" msgid="3416695586119999058">"Priezvisko"</string>
<string name="name_prefix" msgid="59756378548779822">"Titul pred menom"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Hľadať v kontaktoch"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Hľadať vo všetkých kontaktoch"</string>
<string name="take_photo" msgid="7496128293167402354">"Zaznamenať fotografiu"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Urobiť novú fotografiu"</string>
<string name="pick_photo" msgid="448886509158039462">"Vybrať fotografiu z galériu"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"V súvislosti so zmenou jazyka prebieha aktualizácia zoznamu kontaktov."\n\n"Čakajte..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Prebieha aktualizácia zoznamu kontaktov."\n\n"Čakajte..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Prebieha inovácia kontaktov. "\n\n"Inovácia vyžaduje približne <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB vnútornej pamäte telefónu. "\n\n"Zvoľte jednu z nasledujúcich možností:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Vybrať novú fotografiu z Galérie"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"V súvislosti so zmenou jazyka prebieha aktualizácia zoznamu kontaktov."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Prebieha aktualizácia zoznamu kontaktov."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Prebieha inovácia zoznamu kontaktov. "\n\n"Tento proces vyžaduje približne <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB voľného miesta v internom ukladacom priestore."\n\n"Vyberte jednu z týchto možností:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Odinštalovať niektoré aplikácie"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Skúsiť inovovať znova"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Výsledky hľadania pre: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Prebieha vyhľadávanie..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Zobraziť vybraté"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Zobraziť všetky"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Vybrať všetko"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Zrušiť výber všetkých"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Nie sú vybraté žiadne kontakty."</string>
+ <string name="add_field" msgid="2384260056674995230">"Pridať ďalšie pole"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">", zdroj: <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g>, zdroj: <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"zaradiť medzi obľúbené"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Upraviť kontakt"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"zlúčenie sa nepodarilo"</item>
+ <item quantity="other" msgid="425683718017380845">"zlúčené z <xliff:g id="COUNT">%0$d</xliff:g> zdrojov"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Iné"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Spojiť kontakty"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Spojiť aktuálny kontakt s vybraným kontaktom?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Upraviť vybrané kontakty"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Prepnúť do režimu úpravy vybraného kontaktu? Doposiaľ zadané informácie budú skopírované."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Skopírovať do mojich kontaktov"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Adresár <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Vyhľadávanie vo všetkých kontaktoch"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Adresár"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Kontakty"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Vytváranie osobnej kópie"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Vyberte zoznam kontaktov"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Všetky kontakty"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Označené hviezdičkou"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Vlastné"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Prispôsobiť..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Kontakty s telefónnym číslom"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kontakt"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Definícia vlastného zobrazenia"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Nastavenia"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Nastavenia"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Možnosti zobrazenia"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Nájsť kontakty"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Telefónne číslo"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Pridať medzi kontakty"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Zavrieť"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Uviesť rok"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Kontakt"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Prebieha načítavanie"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Vytvoriť nový kontakt"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Prihlásiť sa do účtu"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importovať kontakty zo súboru"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Vytvoriť novú skupinu"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Vytvoriť novú skupinu]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Premenovať skupinu"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Odstrániť skupinu"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Naozaj chcete odstrániť skupinu <xliff:g id="GROUP_LABEL">%1$s</xliff:g>? (Samotné kontakty odstránené nebudú.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Pred združením s ďalším kontaktom zadajte meno kontaktu."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Združený kontakt"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Text bol skopírovaný"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Zahodiť zmeny"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Naozaj chcete zahodiť svoje zmeny?"</string>
+ <string name="discard" msgid="1234315037371251414">"Zahodiť"</string>
</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index df53817..b44a10e 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Izberite bližnjico stika"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Izberite klicno številko"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Izberi številko za pošiljanje sporočila"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Izberite stik"</string>
<string name="starredList" msgid="4817256136413959463">"Z zvezdico"</string>
<string name="frequentList" msgid="7154768136473953056">"Pogosto"</string>
<string name="strequentList" msgid="5640192862059373511">"Priljubljene"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Izbriši stik"</string>
<string name="menu_call" msgid="3992595586042260618">"Pokliči stik"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Pošlji SMS stiku"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Pošlji e-pošto"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Naslov na zemljevidu"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Nastavi za privzeto številko"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Nastavi kot privzeti e-poštni naslov"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Razdruži"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Stiki razdruženi"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Preimenovanje skupine"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Izbriši skupino"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Nov"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Razdruži stik"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Ali ste prepričani, da želite ta stik razdružiti na več stikov: po enega za vsak niz informacij, ki jih vsebuje?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Pridruži"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Če boste izbrisali ta stik, boste izbrisali podatke iz več računov."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Ta stik bo izbrisan."</string>
<string name="menu_done" msgid="796017761764190697">"Dokončano"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Povrni"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Prekliči"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Urejanje stika"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Nov stik"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetično"</string>
<string name="label_notes" msgid="8337354953278341042">"Opombe"</string>
<string name="label_sip_address" msgid="124073911714324974">"Internetni klic"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Zvonjenje"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Prvi in zadnji"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Ime – fonetično"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Podjetje"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Naslov"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Stik ne obstaja."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Ustvari nov stik"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Izberite oznako"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-pošta"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Neposredno sporočanje"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Poštni naslov"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Naslov"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organizacija"</item>
<item msgid="7196592230748086755">"Opomba"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"V telefonu ni na voljo slik."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Ikona stika"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"V telefonu ni dostopnih slik."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"V telefonu ni na voljo slik."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Fotografija za stik"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Ime oznake po meri"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Možnosti prikaza"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Možnosti prikaza"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Klice takoj preusmeri v glasovno pošto"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Privzeto"</string>
- <string name="changePicture" msgid="2943329047610967714">"Spremeni ikono"</string>
- <string name="removePicture" msgid="3041230993155966350">"Odstrani ikono"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Odstrani fotografijo"</string>
<string name="noContacts" msgid="8579310973261953559">"Ni stikov."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Najti ni bilo mogoče nobenega ustreznega stika."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Ni stikov s telefonskimi številkami."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Stik shranjen."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Napaka, sprememb stika ni bilo mogoče shraniti."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Prikazan 1 stik s telefonsko številko"</item>
- <item quantity="other" msgid="6133262880804110289">"Prikaz <xliff:g id="COUNT">%d</xliff:g> stikov s telefonskimi številkami"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 stik s telefonsko število."</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> stikov s telefonskimi številkami"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Ni vidnih stikov s telefonskimi številkami"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Ni stikov s telefonskimi številkami"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Prikazan 1 stik"</item>
- <item quantity="other" msgid="2865867557378939630">"Prikazanih <xliff:g id="COUNT">%d</xliff:g> stikov"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 stik"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> stikov"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Ni vidnih stikov"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Ni stikov"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Ni vidnih stikov"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Ni stikov z zvezdico"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Ni stikov v: <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Najden je bil 1 stik"</item>
- <item quantity="other" msgid="7752927996850263152">"Najdenih <xliff:g id="COUNT">%d</xliff:g> stikov"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 najden"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> najdenih"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Stika ni bilo mogoče najti"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"najdenih je bilo več kot <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Ni bilo mogoče najti"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 stik"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> stikov"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 najden"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> najdenih"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Stiki"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Priljubljeno"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Stiki na kartici SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Ni stikov za prikaz. (Če ste račun pravkar dodali, utegne trajati nekaj minut, da se stiki sinhronizirajo.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Ni stikov za prikaz."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Ni stikov za prikaz."\n\n"Če želite dodati stik, pritisnite "<font fgcolor="#ffffffff"><b>"Meni"</b></font>" in nato:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>", če želite dodati ali konfigurirati račun s stiki, ki jih lahko sinhronizirate s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov stik"</b></font>", če želite ustvariti nov stik"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvozi/izvozi"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Ni stikov za prikaz. (Če ste račun pravkar dodali, utegne trajati nekaj minut, da se stiki sinhronizirajo.)"\n\n"Če želite dodati stik, pritisnite "<font fgcolor="#ffffffff"><b>"Meni"</b></font>" in nato:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>", če želite dodati ali konfigurirati račun s stiki, ki jih lahko sinhronizirate s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Prikaži možnosti"</b></font>", če želite spremeniti, kateri stiki so vidni"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov stik"</b></font>", če želite ustvariti nov stik"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvozi/izvozi"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Ni stikov za prikaz."\n\n"Če želite dodati stik, pritisnite "<font fgcolor="#ffffffff"><b>"Meni"</b></font>" in nato:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>", če želite dodati ali konfigurirati račun s stiki, ki jih lahko sinhronizirate s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov stik"</b></font>", če želite ustvariti nov stik"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvozi/izvozi"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Ni stikov za prikaz. (Če ste račun pravkar dodali, utegne trajati nekaj minut, da se stiki sinhronizirajo.)"\n\n"Če želite dodati stik, pritisnite "<font fgcolor="#ffffffff"><b>"Meni"</b></font>" in nato:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>", če želite dodati ali konfigurirati račun s stiki, ki jih lahko sinhronizirate s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Prikaži možnosti"</b></font>", če želite spremeniti, kateri stiki so vidni"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov stik"</b></font>", če želite ustvariti nov stik"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvozi/izvozi"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Nimate stikov za prikaz."\n\n"Če jih želite dodati, pritisnite "<font fgcolor="#ffffffff"><b>"Meni"</b></font>" in se dotaknite možnosti:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>", če želite dodati ali konfigurirati račun s stiki, ki jih lahko sinhronizirate s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov stik"</b></font>", če želite ustvariti nov stik"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/izvoz"</b></font>", če želite uvoziti stike s kartice SIM ali SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Nimate stikov za prikaz."\n\n"Če jih želite dodati, pritisnite "<font fgcolor="#ffffffff"><b>"Meni"</b></font>" in se dotaknite možnosti:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>", če želite dodati ali konfigurirati račun s stiki, ki jih lahko sinhronizirate s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov stik"</b></font>", če želite ustvariti nov stik"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/izvoz"</b></font>", če želite uvoziti stike s kartice SIM ali SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Nimate stikov za prikaz. (Če ste ravno dodali račun, lahko sinhronizacija stikov traja nekaj minut.)"\n\n"Če želite dodati stike, pritisnite "<font fgcolor="#ffffffff"><b>"Meni"</b></font>" in se dotaknite možnosti:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računu"</b></font>", če želite dodati ali konfigurirati račun s stiki, ki jih lahko sinhronizirate s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti prikaza"</b></font>", če želite določiti, kateri stiki so vidni"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov stik"</b></font>", če želite ustvariti nov stik"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/izvoz"</b></font>", če želite uvoziti stike s kartice SIM ali SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Nimate stikov za prikaz. (Če ste ravno dodali račun, lahko sinhronizacija stikov traja nekaj minut.)"\n\n"Če želite dodati stike, pritisnite "<font fgcolor="#ffffffff"><b>"Meni"</b></font>" in se dotaknite možnosti:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računu"</b></font>", če želite dodati ali konfigurirati račun s stiki, ki jih lahko sinhronizirate s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti prikaza"</b></font>", če želite določiti, kateri stiki so vidni"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov stik"</b></font>", če želite ustvariti nov stik"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/izvoz"</b></font>", če želite uvoziti stike s kartice SIM ali SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Nimate stikov za prikaz."\n\n"Če jih želite dodati, pritisnite "<font fgcolor="#ffffffff"><b>"Meni"</b></font>" in se dotaknite možnosti:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>", če želite dodati ali konfigurirati račun s stiki, ki jih lahko sinhronizirate s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov stik"</b></font>", če želite ustvariti nov stik"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/izvoz"</b></font>", če želite uvoziti stike s kartice SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Nimate stikov za prikaz."\n\n"Če jih želite dodati, pritisnite "<font fgcolor="#ffffffff"><b>"Meni"</b></font>" in se dotaknite možnosti:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računi"</b></font>", če želite dodati ali konfigurirati račun s stiki, ki jih lahko sinhronizirate s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov stik"</b></font>", če želite ustvariti nov stik"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/izvoz"</b></font>", če želite uvoziti stike s kartice SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Nimate stikov za prikaz. (Če ste ravno dodali račun, lahko sinhronizacija stikov traja nekaj minut.)"\n\n"Če želite dodati stike, pritisnite "<font fgcolor="#ffffffff"><b>"Meni"</b></font>" in se dotaknite možnosti:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računu"</b></font>", če želite dodati ali konfigurirati račun s stiki, ki jih lahko sinhronizirate s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti prikaza"</b></font>", če želite določiti, kateri stiki so vidni"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov stik"</b></font>", če želite ustvariti nov stik"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/izvoz"</b></font>", če želite uvoziti stike s kartice SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Nimate stikov za prikaz. (Če ste ravno dodali račun, lahko sinhronizacija stikov traja nekaj minut.)"\n\n"Če želite dodati stike, pritisnite "<font fgcolor="#ffffffff"><b>"Meni"</b></font>" in se dotaknite možnosti:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Računu"</b></font>", če želite dodati ali konfigurirati račun s stiki, ki jih lahko sinhronizirate s telefonom"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Možnosti prikaza"</b></font>", če želite določiti, kateri stiki so vidni"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nov stik"</b></font>", če želite ustvariti nov stik"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Uvoz/izvoz"</b></font>", če želite uvoziti stike s kartice SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Nimate priljubljenih."\n\n"Če želite dodati stik na seznam priljubljenih:"\n\n" "<li>"Dotaknite se zavihka "<b>"Stiki"</b>" "\n</li>" "\n<li>"Dotaknite se stika, ki ga želite dodati priljubljenim"\n</li>" "\n<li>"Dotaknite se zvezdice poleg imena stika"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Vsi stiki"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Z zvezdico"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Povratni klic"</string>
<string name="callAgain" msgid="3197312117049874778">"Ponovi klic"</string>
<string name="returnCall" msgid="8171961914203617813">"Povratni klic"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> min, <xliff:g id="SECONDS">%2$s</xliff:g> s"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> min, <xliff:g id="SECONDS">%s</xliff:g> s"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Pogosti stiki"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Dodaj stik"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Želite »<xliff:g id="EMAIL">%s</xliff:g>« dodati stikom?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Vse"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"ena"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dva"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tri"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Pregledovanje pomnilnika USB ni uspelo (Vzrok: »<xliff:g id="FAIL_REASON">%s</xliff:g>«)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Pregledovanje kartice SD ni bilo uspešno (Razlog: »<xliff:g id="FAIL_REASON">%s</xliff:g>«)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Vhodno/izhodna napaka"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Ni dovolj pomnilnika (morda je datoteka prevelika)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Iz neznanega razloga razčlenjevanje vizitke vCard ni bilo uspešno"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Razčlenjevanje vizitke vCard ni bilo uspešno, čeprav je v pravilni obliki, ker je trenutna izvedba ne podpira"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Datotek vCard ni bilo mogoče najti v pomnilniku USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Na kartici SD ni datotek z vizitko vCard"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Oblika zapisa ni podprta."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Uvoz kartice vCard ni uspel"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Na disku USB ni datotek vCard"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Na kartici SD ni datotek vCard"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Metapodatkov datotek(e) vCard ni bilo mogoče zbrati."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Ene ali več datotek ni bilo mogoče uvoziti (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Neznana napaka"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Izberite datoteko vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Branje vizitke vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Branje datotek z vizitkami vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Branje vizitke vCard ni bilo uspešno"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> od <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> stikov"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> od <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> datotek"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Medpomnjenje kartic(e) v lokalni začasni pomnilnik"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Orodje za uvoz predpomni zapise vCard v lokalni začasni pomnilnik. Dejanski uvoz se bo začel kmalu."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Uvažanje <xliff:g id="CURRENT_NUMBER">%s</xliff:g>(<xliff:g id="TOTAL_NUMBER">%s</xliff:g>:<xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Uvažanje <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Branje podatkov vCard ni uspelo"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Branje podatkov vCard je preklicano"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Končan uvoz dat. vCard <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Uvoz dat. <xliff:g id="FILENAME">%s</xliff:g> je preklican"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"Dat. <xliff:g id="FILENAME">%s</xliff:g> bo kmalu uvožena."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Datoteka bo kmalu uvožena."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Zahteva po uvozu vizitke vCard je zavrnjena. Poskusite znova pozneje."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"Dat. <xliff:g id="FILENAME">%s</xliff:g> bo kmalu izvožena."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Zahteva po izvozu vizitke vCard je zavrnjena. Poskusite znova pozneje."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"stik"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Potrdi izvoz"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Ali ste prepričani, da želite izvoziti seznam stikov v »<xliff:g id="VCARD_FILENAME">%s</xliff:g>«?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Podatkov o stiku ni bilo mogoče izvoziti"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Preveč datotek vCard v pomnilniku USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Preveč datotek vCard na kartici SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Zahtevano ime datoteke je predolgo (<xliff:g id="FILENAME">%s</xliff:g>)"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Končan izvoz datoteke <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Izvoz dat. <xliff:g id="FILENAME">%s</xliff:g> je preklican"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Izvažanje podatkov o stiku"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Izvažanje podatkov o stiku v »<xliff:g id="FILE_NAME">%s</xliff:g>«"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Funkcije za izvoz ni bilo mogoče inicializirati: »<xliff:g id="EXACT_REASON">%s</xliff:g>«"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Med izvozom je prišlo do napake: »<xliff:g id="EXACT_REASON">%s</xliff:g>«"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Informacij podatkovne zbirke ni bilo mogoče pridobiti"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Ni stikov za izvoz. Če imate v telefonu shranjene stike, je morda ponudnik podatkov onemogočil njihov izvoz iz telefona."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Ni stikov za izvoz. Če imate stike v telefonu, so morda nekateri ponudniki podatkov prepovedali izvoz vseh stikov iz telefona."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Ni stikov za izvoz. Če imate stike v telefonu, so morda nekateri ponudniki podatkov prepovedali izvoz vseh stikov iz telefona."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Urejevalnik za vCard ni pravilno inicializiran"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"»<xliff:g id="FILE_NAME">%1$s</xliff:g>« ni bilo mogoče odpreti: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> od <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> stikov"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"»<xliff:g id="FILE_NAME">%s</xliff:g>« ni bilo mogoče odpreti: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> od <xliff:g id="TOTAL_NUMBER">%s</xliff:g> stikov"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Preklic uvoza kart. vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Res želite preklicati uvoz datoteke <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Preklic izvoza kart. vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Res želite preklicati izvoz datoteke <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Preklic uvoza/izvoza kar. vCard ni uspel"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Imena stikov"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Dodaj 2 sekundi premora"</string>
<string name="add_wait" msgid="3360818652790319634">"Dodaj premor"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Najdena ni bila nobena aplikacija za izvedbo tega dejanja"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Zapomni si to izbiro"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Neznano"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Ni podatkov"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Počisti privzete nastavitve"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Privzete nastavitve za ta stik:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Počisti"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Računi"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Uvozi/izvozi"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Uvozi/izvozi stike"</string>
- <string name="menu_share" msgid="943789700636542260">"Skupna raba"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Delite stik"</string>
<string name="share_via" msgid="563121028023030093">"Deli stik z drugimi prek"</string>
<string name="share_error" msgid="4374508848981697170">"Tega stika ni mogoče deliti z drugimi"</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Ime"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organizacija"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Spletno mesto"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Dogodek"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Odnos"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Skupine"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"P"</string>
<string name="type_short_work" msgid="4925330752504537861">"S"</string>
<string name="type_short_pager" msgid="2613818970827594238">"p"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Ta stik je samo za branje"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Več"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Primarno ime"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Ustvari stik v računu"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Odstrani skupino za sinhroniziranje"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Dodaj skupino za sinhroniziranje"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Vsi drugi stiki"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Vsi stiki"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Če boste »<xliff:g id="GROUP">%s</xliff:g>« odstranili iz sinhronizacije, boste odstranili tudi vse stike, ki ne pripadajo nobeni skupini."</string>
- <string name="account_phone" msgid="3682950835276226870">"Samo v telefonu, nesinhroniziran"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Samo v telefonu, nesinhroniziran"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Samo v telefonu, nesinhroniziran"</string>
<string name="call_custom" msgid="7756571794763171802">"Pokliči <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Pokliči domov"</string>
<string name="call_mobile" msgid="7502236805487609178">"Pokliči mobilni telefon"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Klepet s storitvijo ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Klepet s storitvijo Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Klepet"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Naslov"</string>
<string name="postal_street" msgid="8133143961580058972">"Ulica"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Poštni predal"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Naselje"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Država/regija"</string>
<string name="postal_postcode" msgid="572136414136673751">"Poštna številka"</string>
<string name="postal_country" msgid="7638264508416368690">"Država"</string>
+ <string name="full_name" msgid="6602579550613988977">"Ime"</string>
<string name="name_given" msgid="1687286314106019813">"Ime"</string>
<string name="name_family" msgid="3416695586119999058">"Priimek"</string>
<string name="name_prefix" msgid="59756378548779822">"Naziv"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Iskanje stikov"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Išči vse stike"</string>
<string name="take_photo" msgid="7496128293167402354">"Posnemi fotografijo"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Posnemi novo fotografijo"</string>
<string name="pick_photo" msgid="448886509158039462">"Izberi fotografijo iz galerije"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Seznam stikov se posodablja po spremembi jezika."\n\n"Počakajte ..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Seznam stikov se posodablja."\n\n"Počakajte ..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Stiki se nadgrajujejo. "\n\n"Nadgradnja zahteva približno <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>MB notranjega pomnilnika telefona."\n\n"Izberite eno od teh možnosti:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Izberi novo fotografijo iz Galerije"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Seznam stikov se posodablja glede na izbrani jezik."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Seznam stikov se posodablja."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Stiki se nadgrajujejo. "\n\n"Za nadgradnjo je potrebno približno <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB notranjega prostora."\n\n"Izberite eno od teh možnosti:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Odstranite nekaj aplikacij"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Znova poskusi nadgraditi"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Rezultati iskanja za: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Iskanje ..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Pokaži izbrane"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Pokaži vse"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Izberi vse"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Prekliči izbor vseh"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Izbran ni noben stik."</string>
+ <string name="add_field" msgid="2384260056674995230">"Dodaj drugo polje"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"prek <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> prek <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"priljubljeno"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Uredi stik"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"ni spojeno"</item>
+ <item quantity="other" msgid="425683718017380845">"spojeno iz <xliff:g id="COUNT">%0$d</xliff:g> virov"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Drugo"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Združevanje stikov"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Želite združiti stik z izbranim stikom?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Uredi izbrane stike"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Želite urejati izbrani stik? Podatki, ki ste jih doslej vnesli, bodo kopirani."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Kopiraj v moje stike"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Imenik <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Iskanje po vseh stikih"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Imenik"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Stiki"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Ustvarjanje osebne kopije"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Izberi seznam stikov"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Vsi stiki"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Z zvezdico"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Po meri"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Prilagajanje ..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Stiki s telefonskimi številkami"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Stik"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Določite pogled po meri"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Nastavitve"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Nastavitve"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Možnosti prikaza"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Najdi stike"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Telefonska številka"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Dodaj v stike"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Zapri"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Vnesite leto"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Stik"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Nalaganje …"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Ustvarjanje novega stika"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Prijava v račun"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Uvoz stikov iz datoteke"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Ustvarjanje nove skupine"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Ustvarjanje nove skupine]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Preimenovanje skupine"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Izbris skupine"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Res želite izbrisati skupino »<xliff:g id="GROUP_LABEL">%1$s</xliff:g>«? (Stiki ne bodo izbrisani.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Vnesite ime stika, preden ga pridružite drugemu stiku."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Pridruženi stik"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Besedilo kopirano"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Zavrzi spremembe"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Želite zavreči spremembe?"</string>
+ <string name="discard" msgid="1234315037371251414">"Zavrzi"</string>
</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index fa25c21..ace95b4 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Одаберите пречицу за контакт"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Одаберите број за позив"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Одаберите број за слање порука"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Избор контакта"</string>
<string name="starredList" msgid="4817256136413959463">"Са звездицом"</string>
<string name="frequentList" msgid="7154768136473953056">"Чести"</string>
<string name="strequentList" msgid="5640192862059373511">"Омиљено"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Избриши контакт"</string>
<string name="menu_call" msgid="3992595586042260618">"Позови контакт"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Пошаљи SMS контакту"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Пошаљи поруку е-поште"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Прикажи адресу на мапи"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Постави за подразумевани број"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Постави за подразумевану адресу е-поште"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Подели"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Контакти су одвојени"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Преименуј групу"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Избриши групу"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Ново"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Раздвоји контакт"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Желите ли заиста да поделите овај контакт у више контаката: по један контакт за сваки скуп информација о контакту придружен њему?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Придружи"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Ако избришете овај контакт, биће избрисане информације са више налога."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Овај контакт ће бити избрисан."</string>
<string name="menu_done" msgid="796017761764190697">"Готово"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Врати"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Откажи"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Измена контакта"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Нови контакт"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Фонетски"</string>
<string name="label_notes" msgid="8337354953278341042">"Белешке"</string>
<string name="label_sip_address" msgid="124073911714324974">"Интернет позив"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Звук звона"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Прво и последње"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Име – фонетски"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Предузеће"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Наслов"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Контакт не постоји."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Направите нови контакт"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Изаберите ознаку"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Телефон"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Пошаљи е-поштом"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Размена тренутних порука"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Поштанска адреса"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Адреса"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Организација"</item>
<item msgid="7196592230748086755">"Напомена"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"На телефону нема доступних слика."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Икона контакта"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Слике нису доступне на таблету."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"На телефону нема доступних слика."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Фотографија контакта"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Назив прилагођене ознаке"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Опције приказа"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Опције приказа"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Шаљи позиве директно у гласовну пошту"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Подразумевано"</string>
- <string name="changePicture" msgid="2943329047610967714">"Промени икону"</string>
- <string name="removePicture" msgid="3041230993155966350">"Уклони икону"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Уклони фотографију"</string>
<string name="noContacts" msgid="8579310973261953559">"Нема контаката."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Није пронађен ниједан одговарајући контакт."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Нема контаката за које су унети бројеви телефона."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Контакт је сачуван."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Дошло је до грешке и није било могуће сачувати промене контаката."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Приказан је 1 контакт за ког је унет број телефона"</item>
- <item quantity="other" msgid="6133262880804110289">"Приказан је следећи број контаката за које су унети бројеви телефона: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 контакт са бројем телефона"</item>
+ <item quantity="other" msgid="3299954047880968205">"Бр. контаката са бројевима телефона: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Нема видљивих контаката за које су унети бројеви телефона."</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Нема контаката са бројевима телефона"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Приказан је 1 контакт"</item>
- <item quantity="other" msgid="2865867557378939630">"Приказан је следећи број контаката: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 контакт"</item>
+ <item quantity="other" msgid="3578469907265375314">"Kонтаката: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Нема видљивих контаката"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Нема контаката"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Нема видљивих контаката"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Нема контаката са звездицом"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Нема контаката у групи <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Пронађен је 1 контакт"</item>
- <item quantity="other" msgid="7752927996850263152">"Пронађен је следећи број контаката: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 пронађен"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> пронађено"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Контакт није пронађен"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"пронађено је више од <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Није пронађено"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 контакт"</item>
- <item quantity="other" msgid="5660384247071761844">"Kонтаката: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 пронађен"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> пронађено"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Контакти"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Омиљено"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Контакти на SIM картици"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Није могуће приказати контакте зато што их немате. (Ако сте управо додали налог, поступак синхронизације контаката може да потраје неколико минута.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Није могуће приказати контакте зато што их немате."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Није могуће приказати контакте зато што их немате."\n\n"Да бисте додали контакте, притисните "<font fgcolor="#ffffffff"><b>"Мени"</b></font>" и додирните:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Налози"</b></font>" да бисте додали или конфигурисали налог са контактима које можете да синхронизујете са телефоном"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нови контакт"</b></font>" да бисте направили нови контакт из почетка"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Увоз/извоз"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Није могуће приказати контакте зато што их немате. (Ако сте управо додали налог, поступак синхронизације контаката може да потраје неколико минута.)"\n\n"Да бисте додали контакт, притисните "<font fgcolor="#ffffffff"><b>"Мени"</b></font>" и додирните:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Налози"</b></font>" да бисте додали или конфигурисали налог са контактима које можете да синхронизујете са телефоном"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Опције приказа"</b></font>" да бисте променили поставке на основу којих се одређује који контакти ће бити видљиви"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нови контакт"</b></font>" да бисте направили нови контакт из почетка"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Увоз/извоз"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Није могуће приказати контакте зато што их немате."\n\n"Да бисте додали контакте, притисните "<font fgcolor="#ffffffff"><b>"Мени"</b></font>" и додирните:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Налози"</b></font>" да бисте додали или конфигурисали налог са контактима које можете да синхронизујете са телефоном"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нови контакт"</b></font>" да бисте направили нови контакт из почетка"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Увоз/извоз"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Није могуће приказати контакте зато што их немате. (Ако сте управо додали налог, поступак синхронизације контаката може да потраје неколико минута.)"\n\n"Да бисте додали контакт, притисните "<font fgcolor="#ffffffff"><b>"Мени"</b></font>" и додирните:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Налози"</b></font>" да бисте додали или конфигурисали налог са контактима које можете да синхронизујете са телефоном"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Опције приказа"</b></font>" да бисте променили поставке на основу којих се одређује који контакти ће бити видљиви"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нови контакт"</b></font>" да бисте направили нови контакт из почетка"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Увоз/извоз"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Немате контаката за приказ."\n\n"Да бисте их додали, притисните "<font fgcolor="#ffffffff"><b>"Мени"</b></font>" и додирните:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Налози"</b></font>" да бисте додали или конфигурисали налог са контактима које можете да синхронизујете са таблетом"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>" да бисте направили нов контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Увоз/извоз"</b></font>" да бисте увезли контакте са SIM или SD картице "\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Немате контаката за приказ."\n\n"Да бисте их додали, притисните "<font fgcolor="#ffffffff"><b>"Мени"</b></font>" и додирните ставку:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Налози"</b></font>" да бисте додали или конфигурисали налог са контактима које можете да синхронизујете са телефоном"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>" да бисте креирали нов контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Увоз/извоз"</b></font>" да бисте увезли контакте са SIM или SD картице"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Немате контаката за приказ. (Ако сте управо додали налог, синхронизација контаката може да потраје неколико минута.)"\n\n"Да бисте додали контакте, притисните "<font fgcolor="#ffffffff"><b>"Mени"</b></font>" и додирните:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Налози"</b></font>" да бисте додали или конфигурисали налог са контактима које можете са синхронизујете са таблетом"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Опције приказа"</b></font>" да бисте променили видљиве контакте"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нови контакти"</b></font>" да бисте направили нов контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Увоз/извоз"</b></font>" да бисте увезли контакте са SIM или SD картице"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Немате контаката за приказ. (Ако сте управо додали налог, синхронизација контаката може да потраје неколико минута.)"\n\n"Да бисте додали контакте, притисните "<font fgcolor="#ffffffff"><b>"Мени"</b></font>" и додирните ставку:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Налози"</b></font>" да бисте додали или конфигурисали налог са контактима које можете да синхронизујете са телефоном "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Опције приказа"</b></font>" да бисте променили видљиве контакте"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>" да бисте креирали нов контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Увоз/извоз"</b></font>" да бисте увезли контакте са SIM или SD картице"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Немате контаката за приказ."\n\n"Да бисте их додали, притисните "<font fgcolor="#ffffffff"><b>"Мени"</b></font>" и додирните:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Налози"</b></font>" да бисте додали или конфигурисали налог са контактима које желите да синхронизујете са таблетом"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>" да бисте направили нов контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Увоз/извоз"</b></font>" да бисте увезли контакте са SD картице"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Немате контаката за приказ."\n\n"Да бисте додали контакте, притисните "<font fgcolor="#ffffffff"><b>"Мени"</b></font>" и додирните:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Налози"</b></font>" да бисте додали или конфигурисали налог са контактима које желите да синхронизујете са телефоном"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>" да бисте креирали нов контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Увоз/извоз"</b></font>" да бисте увезли контакте са SD картице"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Немате контаката за приказ. (Ако сте управо додали налог, синхронизација контаката може да потраје неколико минута.)"\n\n"Да бисте додали контакте, притисните "<font fgcolor="#ffffffff"><b>"Мени"</b></font>" и додирните:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Налози"</b></font>" да бисте додели или конфигурисали налог са контактима које желите да синхронизујете са таблетом"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Опције приказа"</b></font>" да бисте променили видљиве контакте"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>" да бисте направили нов контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Увоз/извоз"</b></font>" да бисте увезли контакте са SD картице"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Немате контаката за приказ. (Ако сте управо додали налог, синхронизација контаката може да потраје неколико минута.)"\n\n"Да бисте додали контакте, притисните "<font fgcolor="#ffffffff"><b>"Meни"</b></font>" и додирните:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Налози"</b></font>" да бисте додали или конфигурисали налог са контактима које желите да синхронизујете са телефоном"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Опције приказа"</b></font>" да бисте променили видљиве контакте"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Нов контакт"</b></font>" да бисте креирали нов контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Увоз/извоз"</b></font>" да бисте увезли контакте са SD картице"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Немате ниједан омиљени контакт."\n\n"Да бисте додали контакт на листу омиљених контаката:"\n\n" "<li>"Додирните картицу "<b>"Контакти"</b>\n</li>" "\n<li>"Додирните контакт који желите да додате у омиљене контакте"\n</li>" "\n<li>"Додирните звездицу поред имена контакта"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Сви контакти"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Са звездицом"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Узврати позив"</string>
<string name="callAgain" msgid="3197312117049874778">"Позови поново"</string>
<string name="returnCall" msgid="8171961914203617813">"Узврати позив"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> мин <xliff:g id="SECONDS">%2$s</xliff:g> сек"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> мин <xliff:g id="SECONDS">%s</xliff:g> сек"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Често контактирани"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Додај контакт"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Желите ли да додате адресу е-поште „<xliff:g id="EMAIL">%s</xliff:g>“ у контакте?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Све"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"један"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"два"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"три"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Неуспешно скенирање USB меморије (Разлог: „<xliff:g id="FAIL_REASON">%s</xliff:g>“)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Скенирање SD картице није успело (разлог: „<xliff:g id="FAIL_REASON">%s</xliff:g>“)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"I/O грешка"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Нема довољно меморије (датотека је можда превелика)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Рашчлањивање дигиталне визиткарте није успело из неочекиваног разлога"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Рашчлањивање дигиталне визиткарте није успело иако се чини да је њен формат важећи зато што тренутна конфигурација то не подржава"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Није пронађена датотека дигиталне визиткарте у USB меморији"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"На SD картици није пронађена ниједна датотека дигиталне визиткарте"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Формат није подржан."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Увоз vCard датотеке није успео"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"У USB меморији није пронађена ниједна vCard датотека"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"На SD картици није пронађена ниједна vCard датотека"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Неуспешно прикупљање мета података за дате датотеке дигиталне визиткарте."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Увоз неких датотека није успео (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Непозната грешка"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Изаберите датотеку дигиталне визиткарте"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Читање дигиталне визиткарте је у току"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"У току је читање података са дигиталних визиткарата"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Читање података са дигиталне визиткарте није успело"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> од <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> контак(а)та"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> од <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> датотека"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Кеширање vCard датотека у локалну привремену меморију"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Програм за увоз кешира дигиталне визиткарте у локалну привремену меморију. Стварни увоз ће ускоро започети."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Увоз <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Увоз <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Неуспешно читање података са дигиталне визиткарте"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Отказано је читање података са vCard датотеке"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Увоз датотеке <xliff:g id="FILENAME">%s</xliff:g> је завршен"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Увоз датотеке <xliff:g id="FILENAME">%s</xliff:g> је отказан"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"Датотека <xliff:g id="FILENAME">%s</xliff:g> ће ускоро бити увезена."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Датотека ће ускоро бити увезена."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Одбијен је захтев за увоз дигиталне визиткарте. Покушајте касније."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"Датотека <xliff:g id="FILENAME">%s</xliff:g> ће ускоро бити извезена."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Одбијен је захтев за извоз дигиталне визиткарте. Покушајте касније."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"контакт"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Потврдите извоз"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Желите ли заиста да извезете листу контаката у датотеку „<xliff:g id="VCARD_FILENAME">%s</xliff:g>“?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Извоз података о контактима није успео"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Превише датотека дигиталне визиткарте у USB меморији"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"SD картица садржи превише датотека дигиталних визиткарата"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Захтевани назив датотеке је предугачак („<xliff:g id="FILENAME">%s</xliff:g>“)"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Извоз датотеке <xliff:g id="FILENAME">%s</xliff:g> је завршен"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Извоз визиткарте <xliff:g id="FILENAME">%s</xliff:g> је отказан"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Извоз података о контактима је у току"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"У току је извоз података о контактима у датотеку „<xliff:g id="FILE_NAME">%s</xliff:g>“"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Није било могуће покренути програм за извоз: „<xliff:g id="EXACT_REASON">%s</xliff:g>“"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Дошло је до грешке при извозу: „<xliff:g id="EXACT_REASON">%s</xliff:g>“"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Преузимање информација из базе података није успело"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Нема контаката који се могу извести. Ако заиста имате контакте на телефону, можда је неки добављач података забранио њихов извоз са телефона."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Не постоје контакти за извоз. Ако имате контакте на таблету, можда су добављачи података забранили извоз свих контаката на спољни таблет."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Не постоје контакти за извоз. Ако имате контакте на свом телефону, можда су добављачи података забранили извоз свих контаката на спољни телефон."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Програм за израду дигиталних визиткарата није исправно покренут"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Није било могуће отворити датотеку „<xliff:g id="FILE_NAME">%1$s</xliff:g>“: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> од <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> контак(а)та"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Није било могуће отворити датотеку „<xliff:g id="FILE_NAME">%s</xliff:g>“: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> од <xliff:g id="TOTAL_NUMBER">%s</xliff:g> контак(а)та"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Отказивање увоза датотеке"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Да ли заиста желите да откажете увоз датотеке <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Отказивање извоза vCard датотеке"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Да ли заиста желите да откажете извоз датотеке <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Отказ. увоза/изв. датотеке није успело"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Имена контаката"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Додај паузу од 2 секунде"</string>
<string name="add_wait" msgid="3360818652790319634">"Додај чекање"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Није пронађена ниједна апликација која би могла да изврши ову радњу"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Запамти овај избор"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Непознато"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Нема података"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Обриши подразумеване вредности"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Подразумеване вредности контакта:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Обриши"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Налози"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Увоз/извоз"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Увоз/извоз контаката"</string>
- <string name="menu_share" msgid="943789700636542260">"Дели"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Дели контакт"</string>
<string name="share_via" msgid="563121028023030093">"Дели контакт преко"</string>
<string name="share_error" msgid="4374508848981697170">"Овај контакт се не може делити."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Име"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Организација"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Веб сајт"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Догађај"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Однос"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Групе"</string>
<string name="type_short_home" msgid="7770424864090605384">"К"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"М"</string>
<string name="type_short_work" msgid="4925330752504537861">"П"</string>
<string name="type_short_pager" msgid="2613818970827594238">"П"</string>
<string name="type_short_other" msgid="5669407180177236769">"О"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Овај контакт је само за читање"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Још"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Примарно име"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Направи контакт у оквиру налога"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Уклони групу за синхронизацију"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Додај групу за синхронизацију"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Сви други контакти"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Сви контакти"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Уклањањем групе „<xliff:g id="GROUP">%s</xliff:g>“ са листе за синхронизацију уклонићете и све негруписане контакте са те листе."</string>
- <string name="account_phone" msgid="3682950835276226870">"Само телефон, није синхронизовано"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Само таблет, није синхронизовано"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Само телефон, није синхронизовано"</string>
<string name="call_custom" msgid="7756571794763171802">"Позови <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Позови кућни телефон"</string>
<string name="call_mobile" msgid="7502236805487609178">"Позови мобилни телефон"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Започни ћаскање преко ICQ-а"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Започни ћаскање преко Jabber-а"</string>
<string name="chat" msgid="9025361898797412245">"Ћаскање"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Адреса"</string>
<string name="postal_street" msgid="8133143961580058972">"Улица"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Поштанско сандуче"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Комшилук"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Држава"</string>
<string name="postal_postcode" msgid="572136414136673751">"Поштански број"</string>
<string name="postal_country" msgid="7638264508416368690">"Земља"</string>
+ <string name="full_name" msgid="6602579550613988977">"Назив"</string>
<string name="name_given" msgid="1687286314106019813">"Име"</string>
<string name="name_family" msgid="3416695586119999058">"Презиме"</string>
<string name="name_prefix" msgid="59756378548779822">"Префикс за име"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Претражи контакте"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Претражи све контакте"</string>
<string name="take_photo" msgid="7496128293167402354">"Сними фотографију"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Направи нову фотографију"</string>
<string name="pick_photo" msgid="448886509158039462">"Изаберите фотографију из галерије"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Листа контаката се тренутно ажурира како би се применила промена језика."\n\n"Сачекајте..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Ажурирање листе контаката је у току."\n\n"Сачекајте..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"У току је поступак надоградње контаката. "\n\n"Поступак надоградње захтева приближно <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB интерне меморије телефона."\n\n"Одаберите једну од следећих опција:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Изаберите нову фотографију из галерије"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Контакт листа се ажурира у складу са променом језика."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Контакт листа се ажурира."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Контакти су у процесу надоградње. "\n\n"За процес надоградње потребно је отприлике <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB интерне меморије."\n\n"Изаберите једну од следећих опција:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Деинсталирајте неке апликације"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Поново покушај надоградњу"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Резултати претраге за: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Претраживање..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Прикажи изабрано"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Прикажи све"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Изабери све"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Поништи све изборе"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Није изабран ниједан контакт."</string>
+ <string name="add_field" msgid="2384260056674995230">"Додај друго поље"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"преко <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> преко <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"омиљено"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Измени контакт"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"није обједињено"</item>
+ <item quantity="other" msgid="425683718017380845">"обједињено од <xliff:g id="COUNT">%0$d</xliff:g> извора"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Други"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Придружи контакте"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Желите ли да тренутни контакт придружите изабраном контакту?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Измена изабраних контаката"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Желите ли да пребаците на уређивање изабраног контакта? Биће копиране информације које сте унели до сада."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Копирај у моје контакте"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Каталог <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Претраживање свих контаката"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Каталог"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Контакти"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Креирање личне копије"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Одаберите листу контаката"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Сви контакти"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Са звездицом"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Прилагођено"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Прилагоди..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Контакти са бројевима телефона"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Контакт"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Дефинисање прилагођеног приказа"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Подешавања"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Подешавања"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Опције приказа"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Пронађи контакте"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Број телефона"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Додај у контакте"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Затвори"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Наведите годину"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Контакт"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Учитавање…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Направи нови контакт"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Пријавите се на налог"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Увези контакте из датотеке"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Прављење нове групе"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Направи нову групу]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Преименовање групе"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Избриши групу"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Да ли заиста желите да избришете групу „<xliff:g id="GROUP_LABEL">%1$s</xliff:g>“? (Контакти неће бити избрисани.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Унесите име контакта пре него што га придружите другом контакту."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Придружени контакт"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Текст је копиран"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Одбацивање промена"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Желите ли да одбаците промене?"</string>
+ <string name="discard" msgid="1234315037371251414">"Одбаци"</string>
</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 50c4e6e..a87ec67 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Välj genväg till kontakten"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Välj ett nummer som du vill ringa"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Välj ett nummer för meddelandet"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Markera en kontakt"</string>
<string name="starredList" msgid="4817256136413959463">"Stjärnmärkta"</string>
<string name="frequentList" msgid="7154768136473953056">"Ofta"</string>
<string name="strequentList" msgid="5640192862059373511">"Favoriter"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Ta bort kontakt"</string>
<string name="menu_call" msgid="3992595586042260618">"Ring upp kontakt"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Skicka SMS till kontakt"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Skicka e-post"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Visa adress på karta"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Använd som standardnummer"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Använd som standardadress"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Dela upp"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Kontakterna har delats upp"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Byt namn på grupp"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Ta bort grupp"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Ny"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Dela upp kontakt"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Vill du dela upp den här kontakten i flera kontakter, en för varje uppsättning kontaktinformation som kombinerats i den?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Slå ihop"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Om du tar bort den här kontakten tar du bort information från flera konton."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Kontakten kommer att tas bort."</string>
<string name="menu_done" msgid="796017761764190697">"Färdig"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Tillbaka"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Avbryt"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Redigera kontakt"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Ny kontakt"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetiskt"</string>
<string name="label_notes" msgid="8337354953278341042">"Anteckningar"</string>
<string name="label_sip_address" msgid="124073911714324974">"Internetsamtal"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Ringsignal"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"För- och efternamn"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Fonetiskt namn"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Företag"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Titel"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Kontakten finns inte."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Skapa ny kontakt"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Välj etikett"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-post"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Chatt"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Postadress"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adress"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Organisation"</item>
<item msgid="7196592230748086755">"Anteckning"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Det finns inga bilder på telefonen."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Kontaktikon"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Det finns inga bilder på pekdatorn."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Det finns inga bilder på telefonen."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Kontaktbild"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Anpassat etikettsnamn"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Visningsalternativ"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Visa alternativ"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Skicka samtal direkt till röstbrevlåda"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Standardinställning"</string>
- <string name="changePicture" msgid="2943329047610967714">"Ändra ikon"</string>
- <string name="removePicture" msgid="3041230993155966350">"Ta bort ikon"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Ta bort foto"</string>
<string name="noContacts" msgid="8579310973261953559">"Inga kontakter."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Inga matchande kontakter hittades."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Inga kontakter med telefonnummer."</string>
@@ -114,23 +111,30 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Kontakt sparad."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Fel: det går inte att spara kontakter."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Visar en kontakt med telefonnummer"</item>
- <item quantity="other" msgid="6133262880804110289">"Visar <xliff:g id="COUNT">%d</xliff:g> kontakter med telefonnummer"</item>
+ <item quantity="one" msgid="3015357862286673986">"en kontakt med telefonnummer"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> kontakter med telefonnummer"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Inga synliga kontakter med telefonnummer"</string>
+ <!-- outdated translation 3100001705005525307 --> <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Inga synliga kontakter med telefonnummer"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Visar en kontakt"</item>
- <item quantity="other" msgid="2865867557378939630">"Visar <xliff:g id="COUNT">%d</xliff:g> kontakter"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 kontakt"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> kontakter"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Det finns inga synliga kontakter"</string>
+ <!-- outdated translation 5917810721329112813 --> <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Det finns inga synliga kontakter"</string>
+ <!-- no translation found for listTotalAllContactsZeroCustom (4058252141420128998) -->
+ <skip />
+ <!-- no translation found for listTotalAllContactsZeroStarred (5391630590684099117) -->
+ <skip />
+ <!-- no translation found for listTotalAllContactsZeroGroup (5448979458248027615) -->
+ <skip />
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"En kontakt hittades"</item>
- <item quantity="other" msgid="7752927996850263152">"<xliff:g id="COUNT">%d</xliff:g> kontakter hittades"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 hittades"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> hittades"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Kontakten hittades inte"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"mer än <xliff:g id="COUNT">%d</xliff:g> hittades"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Hittades inte"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 kontakt"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> kontakter"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 hittades"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> hittades"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Kontakter"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Favoriter"</string>
@@ -160,10 +164,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Kontakter från SIM-kort"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Du har inga kontakter att visa. (Om du nyss lade till ett konto kan det ta några minuter innan kontakterna synkroniserats.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Du har inga kontakter att visa."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Det finns inga kontakter att visa."\n\n"Om du vill lägga till kontakter trycker du på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" och sedan på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konton"</b></font>" om du vill lägga till eller konfigurera ett konto med kontakter som kan synkroniseras till telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" om du vill skapa en ny kontakt från grunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importera/exportera"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Det finns inga kontakter att visa (om du nyss har lagt till ett konto kan det ta några minuter att synkronisera kontakter)."\n\n"Om du vill lägga till kontakter trycker du på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" och sedan på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konton"</b></font>" om du vill lägga till eller konfigurera ett konto med kontakter som kan synkroniseras till telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativ"</b></font>" om du vill ändra vilka kontakter som är synliga"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" om du vill skapa en ny kontakt från grunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importera/exportera"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Det finns inga kontakter att visa."\n\n"Om du vill lägga till kontakter trycker du på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" och sedan på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konton"</b></font>" om du vill lägga till eller konfigurera ett konto med kontakter som kan synkroniseras till telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" om du vill skapa en ny kontakt från grunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importera/exportera"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Det finns inga kontakter att visa (om du nyss har lagt till ett konto kan det ta några minuter att synkronisera kontakter)."\n\n"Om du vill lägga till kontakter trycker du på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" och sedan på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konton"</b></font>" om du vill lägga till eller konfigurera ett konto med kontakter som kan synkroniseras till telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativ"</b></font>" om du vill ändra vilka kontakter som är synliga"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" om du vill skapa en ny kontakt från grunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importera/exportera"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Det finns inga kontakter att visa."\n\n"Om du vill lägga till kontakter trycker du på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" och sedan på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konton"</b></font>" om du vill lägga till eller konfigurera ett konto med kontakter som kan synkroniseras till pekdatorn"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" om du vill skapa en ny kontakt från grunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importera/exportera"</b></font>" om du vill importera kontakter från SIM- eller SD-kortet"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Det finns inga kontakter att visa."\n\n"Om du vill lägga till kontakter trycker du på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" och sedan på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konton"</b></font>" om du vill lägga till eller konfigurera ett konto med kontakter som kan synkroniseras till telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" om du vill skapa en ny kontakt från grunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importera/exportera"</b></font>" om du vill importera kontakter från SIM- eller SD-kortet"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Det finns inga kontakter att visa (om du nyss har lagt till ett konto kan det ta några minuter att synkronisera kontakter)."\n\n"Om du vill lägga till kontakter trycker du på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" och sedan på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konton"</b></font>" om du vill lägga till eller konfigurera ett konto med kontakter som kan synkroniseras till pekdatorn"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativ"</b></font>" om du vill ändra vilka kontakter som är synliga"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" om du vill skapa en ny kontakt från grunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importera/exportera"</b></font>" om du vill importera kontakter från SIM-kortet eller SD-kortet"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Det finns inga kontakter att visa (om du nyss har lagt till ett konto kan det ta några minuter att synkronisera kontakter)."\n\n"Om du vill lägga till kontakter trycker du på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" och sedan på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konton"</b></font>" om du vill lägga till eller konfigurera ett konto med kontakter som kan synkroniseras till telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativ"</b></font>" om du vill ändra vilka kontakter som är synliga"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" om du vill skapa en ny kontakt från grunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importera/exportera"</b></font>" om du vill importera kontakter från SIM-kortet eller SD-kortet"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Det finns inga kontakter att visa."\n\n"Om du vill lägga till kontakter trycker du på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" och sedan på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konton"</b></font>" om du vill lägga till eller konfigurera ett konto med kontakter som kan synkroniseras till pekdatorn"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" om du vill skapa en ny kontakt från grunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importera/exportera"</b></font>" om du vill importera kontakter från SD-kortet"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Det finns inga kontakter att visa."\n\n"Om du vill lägga till kontakter trycker du på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" och sedan på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konton"</b></font>" om du vill lägga till eller konfigurera ett konto med kontakter som kan synkroniseras till telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" om du vill skapa en ny kontakt från grunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importera/exportera"</b></font>" om du vill importera kontakter från SD-kortet"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Det finns inga kontakter att visa (om du nyss har lagt till ett konto kan det ta några minuter att synkronisera kontakter)."\n\n"Om du vill lägga till kontakter trycker du på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" och sedan på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konton"</b></font>" om du vill lägga till eller konfigurera ett konto med kontakter som kan synkroniseras till pekdatorn"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativ"</b></font>" om du vill ändra vilka kontakter som är synliga"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" om du vill skapa en ny kontakt från grunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importera/exportera"</b></font>" om du vill importera kontakter från SD-kortet"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Det finns inga kontakter att visa (om du nyss har lagt till ett konto kan det ta några minuter att synkronisera kontakter)."\n\n"Om du vill lägga till kontakter trycker du på "<font fgcolor="#ffffffff"><b>"Meny"</b></font>" och sedan på:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Konton"</b></font>" om du vill lägga till eller konfigurera ett konto med kontakter som kan synkroniseras till telefonen"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Visningsalternativ"</b></font>" om du vill ändra vilka kontakter som är synliga"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Ny kontakt"</b></font>" om du vill skapa en ny kontakt från grunden"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Importera/exportera"</b></font>" om du vill importera kontakter från SD-kortet"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Du har inte några favoriter."\n\n"Så här lägger du till kontakter i favoritlistan:"\n\n" "<li>"Tryck på fliken "<b>"Kontakter"</b>\n</li>" "\n<li>"Tryck på den kontakt du vill lägga till i favoriterna"\n</li>" "\n<li>"Tryck på stjärnan bredvid kontaktens namn"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Alla kontakter"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Stjärnmärkt"</string>
@@ -180,11 +188,10 @@
<string name="callBack" msgid="5498224409038809224">"Ring upp"</string>
<string name="callAgain" msgid="3197312117049874778">"Ring igen"</string>
<string name="returnCall" msgid="8171961914203617813">"Ring upp"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> minuter <xliff:g id="SECONDS">%2$s</xliff:g> sekunder"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> minuter <xliff:g id="SECONDS">%s</xliff:g> sekunder"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Ofta kontaktade"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Lägg till kontakt"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Vill du lägga till <xliff:g id="EMAIL">%s</xliff:g> i Kontakter?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Alla"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"ett"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"två"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tre"</string>
@@ -225,19 +232,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Det gick inte att söka igenom USB-lagringsenheten (orsak: <xliff:g id="FAIL_REASON">%s</xliff:g>)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Det gick inte att skanna SD-kortet (orsak: <xliff:g id="FAIL_REASON">%s</xliff:g>)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"I/O-fel"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Det finns för lite minne (filen kan vara för stor)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Det gick inte att analysera vCard av okänd anledning"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Det gick inte att analysera vCard trots att formatet verkar vara giltigt. Den aktuella implementeringen stöder inte det."</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Det finns ingen vCard-fil i USB-lagringsenheten"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Ingen vCard-fil hittades på SD-kortet"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Formatet stöds inte."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Det gick inte att importera vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Det finns ingen vCard-fil på USB-lagringsenheten"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Det finns ingen vCard-fil på SD-kortet"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Det går inte att samla in metainformation för den eller de givna vCard-filerna."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"En eller flera filer kunde inte importeras: (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Okänt fel"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Välj vCard-fil"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Läser vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Läser vCard-filer"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Det gick inte att läsa vCard-data"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> av <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontakter"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> av <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> filer"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Cachelagrar vCard i lokalt tillfälligt utrymme"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Importfunktionen cachelagrar vCard-fil(er) i lokalt tillfälligt utrymme. Den faktiska importen börjar snart."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Importerar <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Importerar <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Det går inte att läsa vCard-data"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Läsning av vCard-data avbröts"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"vCard-filen <xliff:g id="FILENAME">%s</xliff:g> har importerats"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Importen av <xliff:g id="FILENAME">%s</xliff:g> avbröts"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> importeras snart."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Filen kommer snart att importeras."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Begäran om vCard-import avvisades. Försök igen vid ett senare tillfälle."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> exporteras snart."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Begäran om vCard-export avvisades. Försök igen vid ett senare tillfälle."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"kontakt"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Bekräfta export"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Vill du exportera kontaktlistan till \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Det gick inte att exportera kontaktdata"</string>
@@ -246,15 +265,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"För många vCard-filer på USB-lagringsenheten"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"För många vCard-filer på SD-kortet"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Det obligatoriska filnamnet är för långt (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"<xliff:g id="FILENAME">%s</xliff:g> har exporterats"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Exporten av <xliff:g id="FILENAME">%s</xliff:g> avbröts"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Exporterar kontaktuppgifter"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Exporterar kontaktuppgifter till: <xliff:g id="FILE_NAME">%s</xliff:g>"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Det gick inte att starta exportverktyget: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Ett fel uppstod under export: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Det gick inte att hämta databasinformation"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Det finns ingen kontakt som kan exporteras. Om du vet att du har kontakter i din telefon kan problemet vara att en leverantör förhindrar att kontakterna exporteras från telefonen."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Det finns inga kontakter som kan exporteras. Om du vet att du har kontakter på pekdatorn kan problemet vara att en leverantör förhindrar att kontakterna exporteras från pekdatorn."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Det finns inga kontakter som kan exporteras. Om du vet att du har kontakter i telefonen kan problemet vara att en leverantör förhindrar att kontakterna exporteras från telefonen."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"vCard-kompositören är inte korrekt initierad"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Det gick inte att öppna <xliff:g id="FILE_NAME">%1$s</xliff:g>: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> av <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kontakter"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Det gick inte att öppna <xliff:g id="FILE_NAME">%s</xliff:g>: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> av <xliff:g id="TOTAL_NUMBER">%s</xliff:g> kontakter"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Avbryter vCard-import"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Vill du avbryta importen av <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Avbryter vCard-export"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Vill du avbryta exporten av <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"vCard-import/-export kunde inte avbrytas"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Dina kontakters namn"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Lägg till en paus på 2 sek."</string>
<string name="add_wait" msgid="3360818652790319634">"Lägg till väntetid"</string>
@@ -264,10 +291,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Ingen app som kan hantera åtgärden hittades"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Kom ihåg det här valet"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Okänd"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Inga data"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Rensa info"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Standardinställningar för kontakten:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Ta bort"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Konton"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Importera/exportera"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Importera/exportera kontakter"</string>
- <string name="menu_share" msgid="943789700636542260">"Dela"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Dela kontakt"</string>
<string name="share_via" msgid="563121028023030093">"Dela kontakt via"</string>
<string name="share_error" msgid="4374508848981697170">"Den här kontakten kan inte delas."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Namn"</string>
@@ -275,14 +306,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Organisation"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Webbplats"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Händelse"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Relation"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Grupper"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"A"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"A"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Kontakten är skrivskyddad"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Mer"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Primärt namn"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Skapa kontakt under konto"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Ta bort synkgrupp"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Lägg till synkgrupp"</string>
@@ -290,7 +320,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Alla andra kontakter"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Alla kontakter"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Om du tar bort \"<xliff:g id="GROUP">%s</xliff:g>\" från synkroniseringen tas även alla kontakter som inte tillhör grupper bort från synkroniseringen."</string>
- <string name="account_phone" msgid="3682950835276226870">"Bara telefon (osynkad)"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Bara pekdator (osynkad)"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Bara telefon (osynkad)"</string>
<string name="call_custom" msgid="7756571794763171802">"Ring <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Ring hem"</string>
<string name="call_mobile" msgid="7502236805487609178">"Ring mobilen"</string>
@@ -352,6 +383,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Chatta med ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Chatta med Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Chatt"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adress"</string>
<string name="postal_street" msgid="8133143961580058972">"Gata"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Postbox"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Område"</string>
@@ -359,6 +391,7 @@
<string name="postal_region" msgid="6045263193478437672">"Delstat"</string>
<string name="postal_postcode" msgid="572136414136673751">"Postnummer"</string>
<string name="postal_country" msgid="7638264508416368690">"Land"</string>
+ <string name="full_name" msgid="6602579550613988977">"Namn"</string>
<string name="name_given" msgid="1687286314106019813">"Förnamn"</string>
<string name="name_family" msgid="3416695586119999058">"Efternamn"</string>
<string name="name_prefix" msgid="59756378548779822">"Namnprefix"</string>
@@ -381,12 +414,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Sök efter kontakter"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Sök efter alla kontakter"</string>
<string name="take_photo" msgid="7496128293167402354">"Ta en bild"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Ta ny bild"</string>
<string name="pick_photo" msgid="448886509158039462">"Välj ett foto från galleriet"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Kontaktlistan uppdateras så att språkändringen visas."\n\n"Vänta..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Kontaktlistan uppdateras."\n\n"Vänta..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Kontakter håller på att uppgraderas. "\n\n"Uppgraderingen kräver ungefär <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>MB av telefonens internminne."\n\n"Välj något av följande alternativ:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Välj nytt foto från galleriet"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Kontaktlistan uppdateras så att språkändringen visas."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Kontaktlistan uppdateras."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Kontakter håller på att uppgraderas. "\n\n"Uppgraderingsprocessen kräver ungefär <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB internt lagringsutrymme."\n\n"Välj ett av följande alternativ:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Avinstallera några program"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Försök uppgradera igen"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Sökresultat för: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Söker..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Visa markerade"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Visa alla"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Markera alla"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Avmarkera alla"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"1 mottagare har markerats"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g> mottagare har markerats"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Inga kontakter har markerats."</string>
+ <string name="add_field" msgid="2384260056674995230">"Lägg till ett fält"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"via <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> via <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favorit"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Redigera kontakt"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"inte kombinerade"</item>
+ <item quantity="other" msgid="425683718017380845">"kombinerade från <xliff:g id="COUNT">%0$d</xliff:g> källor"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Övrigt"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Kombinera kontakter"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Vill du kombinera kontakten med den markerade kontakten?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Redigera valda kontakter"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Vill du fortsätta att redigera den markerade kontakten? Information som du har angett hittills kommer att kopieras."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Kopiera till mina kontakter"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Katalog <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Söker bland alla kontakter"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Katalog"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Kontakter"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"En personlig kopia skapas"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Välj kontaktlista"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Alla kontakter"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Stjärnmärkt"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Anpassad"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Anpassa..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Kontakter med telefonnummer"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kontakt"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Definiera anpassad visning"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Inställningar"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Inställningar"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Visa alternativ"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Sök efter kontakter"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Telefonnummer"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Lägg till i Kontakter"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Stäng"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Ange ett år"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Kontakt"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Läser in..."</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Skapa en ny kontakt"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Logga in på ett konto"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Importera kontakter från en fil"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Skapa ny grupp"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Skapa ny grupp]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Byt namn på grupp"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Radera grupp"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Vill du ta bort gruppen <xliff:g id="GROUP_LABEL">%1$s</xliff:g>? Kontakterna i gruppen tas inte bort."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Ange ett namn för kontakten innan du slår ihop den med en annan kontakt."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Hopslagen kontakt"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Texten har kopierats"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Ignorera ändringar"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Vill du ignorera ändringarna?"</string>
+ <string name="discard" msgid="1234315037371251414">"Ignorera"</string>
</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 70aa9c6..64e7bde 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"เลือกทางลัดของสมุดโทรศัพท์"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"เลือกหมายเลขที่จะโทร"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"เลือกหมายเลขที่จะส่งข้อความ"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"เลือกรายชื่อติดต่อ"</string>
<string name="starredList" msgid="4817256136413959463">"ที่ติดดาว"</string>
<string name="frequentList" msgid="7154768136473953056">"บ่อยครั้ง"</string>
<string name="strequentList" msgid="5640192862059373511">"รายการโปรด"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"ลบรายชื่อ"</string>
<string name="menu_call" msgid="3992595586042260618">"โทรหารายชื่อในสมุดโทรศัพท์"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"ส่งข้อความถึงรายชื่อในสมุดโทรศัพท์"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"ส่งอีเมล"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"ที่อยู่ในแผนที่"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"ตั้งเป็นหมายเลขเริ่มต้น"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"กำหนดเป็นอีเมลค่าเริ่มต้น"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"แยก"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"แยกรายชื่อแล้ว"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"เปลี่ยนชื่อกลุ่ม"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"ลบกลุ่ม"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"ใหม่"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"แยกรายชื่อแล้ว"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"คุณแน่ใจหรือไม่ว่าต้องการแยกรายชื่อในสมุดโทรศัพท์ติดต่อนี้เป็นแบบหลายรายการ นั่นคือ หนึ่งรายการสำหรับชุดข้อมูลแต่ละชุดที่นำมารวมในรายการนี้"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"เข้าร่วม"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"การลบสมุดโทรศัพท์นี้จะลบข้อมูลจากบัญชีแบบหลายรายการ"</string>
<string name="deleteConfirmation" msgid="811706994761610640">"รายชื่อนี้จะถูกลบ"</string>
<string name="menu_done" msgid="796017761764190697">"เสร็จสิ้น"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"ย้อนกลับ"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"ยกเลิก"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"แก้ไขสมุดโทรศัพท์"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"สร้างรายชื่อใหม่"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"การออกเสียง"</string>
<string name="label_notes" msgid="8337354953278341042">"หมายเหตุ"</string>
<string name="label_sip_address" msgid="124073911714324974">"การโทรทางอินเทอร์เน็ต"</string>
<string name="label_ringtone" msgid="8833166825330686244">"เสียงเรียกเข้า"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"รายการแรกและสุดท้าย"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"ชื่อแบบออกเสียง"</string>
<string name="ghostData_company" msgid="5414421120553765775">"บริษัท"</string>
<string name="ghostData_title" msgid="7496735200318496110">"ชื่อ"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"ไม่มีรายชื่อนี้"</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"สร้างรายชื่อในสมุดโทรศัพท์ใหม่"</string>
- <string name="selectLabel" msgid="4255424123394910733">"เลือกป้าย"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"โทรศัพท์"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"อีเมล"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"IM"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"ที่อยู่ไปรษณีย์"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"ที่อยู่"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"องค์กร"</item>
<item msgid="7196592230748086755">"หมายเหตุ"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"ไม่มีรูปให้ใช้งานบนโทรศัพท์"</string>
- <string name="attachToContact" msgid="8820530304406066714">"ไอคอนรายชื่อในสมุดโทรศัพท์"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"ไม่มีรูปภาพให้ใช้งานบนแท็บเล็ต"</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"ไม่มีรูปให้ใช้งานบนโทรศัพท์"</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"ภาพของรายชื่อติดต่อ"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"ชื่อป้ายที่กำหนดเอง"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"ตัวเลือกการแสดงผล"</string>
- <string name="displayGroups" msgid="2278964020773993336">"ตัวเลือกการแสดงผล"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"ส่งการโทรไปยังข้อความเสียงโดยตรง"</string>
<string name="default_ringtone" msgid="9099988849649827972">"เริ่มต้น"</string>
- <string name="changePicture" msgid="2943329047610967714">"เปลี่ยนไอคอน"</string>
- <string name="removePicture" msgid="3041230993155966350">"นำไอคอนออก"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"นำภาพออก"</string>
<string name="noContacts" msgid="8579310973261953559">"ไม่มีรายชื่อ"</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"ไม่พบรายชื่อที่ตรงกัน"</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"ไม่รายชื่อที่มีหมายเลขโทรศัพท์"</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"บันทึกรายชื่อ"</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"ข้อผิดพลาด บันทึกการเปลี่ยนแปลงรายชื่อในสมุดโทรศัพท์ไม่ได้"</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"แสดงรายชื่อ 1 รายการที่มีหมายเลขโทรศัพท์"</item>
- <item quantity="other" msgid="6133262880804110289">"กำลังแสดงรายชื่อ <xliff:g id="COUNT">%d</xliff:g> รายการที่มีหมายเลขโทรศัพท์"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 รายชื่อติดต่อที่มีหมายเลขโทรศัพท์"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> รายชื่อติดต่อที่มีหมายเลขโทรศัพท์"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"ไม่มีรายชื่อที่แสดงซึ่งมีหมายเลขโทรศัพท์"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"ไม่มีรายชื่อติดต่อที่มีหมายเลขโทรศัพท์"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"แสดงรายชื่อ 1 รายการ"</item>
- <item quantity="other" msgid="2865867557378939630">"กำลังแสดงรายชื่อ <xliff:g id="COUNT">%d</xliff:g> รายการ"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 รายชื่อติดต่อ"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> รายชื่อติดต่อ"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"ไม่มีรายชื่อที่แสดงไว้"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"ไม่มีรายชื่อติดต่อ"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"ไม่มีรายชื่อติดต่อที่แสดงไว้"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"ไม่มีรายชื่อที่ติดดาว"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"ไม่มีรายชื่อติดต่อใน <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"พบที่รายชื่อ 1 รายการ"</item>
- <item quantity="other" msgid="7752927996850263152">"พบรายชื่อในสมุดโทรศัพท์ <xliff:g id="COUNT">%d</xliff:g> รายการ"</item>
+ <item quantity="one" msgid="5517063038754171134">"พบ 1 รายการ"</item>
+ <item quantity="other" msgid="3852668542926965042">"พบ <xliff:g id="COUNT">%d</xliff:g> รายการ"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"ไม่พบรายชื่อ"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"พบมากกว่า <xliff:g id="COUNT">%d</xliff:g> รายการ"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"ไม่พบ"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 รายการ"</item>
- <item quantity="other" msgid="5660384247071761844">"รายชื่อ <xliff:g id="COUNT">%d</xliff:g> รายการ"</item>
+ <item quantity="one" msgid="4826918429708286628">"พบ 1 รายการ"</item>
+ <item quantity="other" msgid="7988132539476575389">"พบ <xliff:g id="COUNT">%d</xliff:g> รายการ"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"รายชื่อ"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"รายการโปรด"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"รายชื่อบนซิมการ์ด"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"คุณไม่มีรายชื่อที่จะแสดง (หากคุณเพิ่งเพิ่มบัญชีลงไป อาจต้องใช้เวลาในการซิงค์รายชื่อสักครู่)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"คุณไม่มีรายชื่อให้แสดง"</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"คุณไม่มีรายชื่อที่จะแสดง"\n\n"หากต้องการเพิ่มรายชื่อ กด "<font fgcolor="#ffffffff"><b>"เมนู"</b></font>" แล้วแตะ:"\n" "\n<li><font fgcolor="#ffffffff"><b>"บัญชี"</b></font>"เพื่อเพิ่มหรือกำหนดค่าบัญชีด้วยรายชื่อที่คุณสามารถซิงค์ลงโทรศัพท์ได้"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"สร้างรายชื่อใหม่"</b></font>" เพื่อสร้างรายชื่อขึ้นใหม่หมด"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"นำเข้า/ส่งออก"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"คุณไม่มีรายชื่อที่จะแสดง (หากคุณเพิ่งเพิ่มบัญชีลงไป อาจต้องใช้เวลาในการซิงค์รายชื่อสักครู่)"\n\n"หากต้องการเพิ่มรายชื่อ กด "<font fgcolor="#ffffffff"><b>"เมนู"</b></font>" แล้วแตะ:"\n" "\n<li><font fgcolor="#ffffffff"><b>"บัญชี"</b></font>" เพื่อเพิ่มหรือกำหนดค่าบัญชีด้วยรายชื่อที่คุณสามารถซิงค์ลงโทรศัพท์ได้"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ตัวเลือกการแสดงผล"</b></font>" เพื่อเปลี่ยนว่าจะให้แสดงรายชื่อใดบ้าง"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"สร้างรายชื่อใหม่"</b></font>" เพื่อสร้างรายชื่อขึ้นใหม่หมด"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"นำเข้า/ส่งออก"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"คุณไม่มีรายชื่อที่จะแสดง"\n\n"หากต้องการเพิ่มรายชื่อ กด "<font fgcolor="#ffffffff"><b>"เมนู"</b></font>" แล้วแตะ:"\n" "\n<li><font fgcolor="#ffffffff"><b>"บัญชี"</b></font>"เพื่อเพิ่มหรือกำหนดค่าบัญชีด้วยรายชื่อที่คุณสามารถซิงค์ลงโทรศัพท์ได้"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"สร้างรายชื่อใหม่"</b></font>" เพื่อสร้างรายชื่อขึ้นใหม่หมด"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"นำเข้า/ส่งออก"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"คุณไม่มีสมุดโทรศัพท์ที่จะแสดง (หากคุณเพิ่งเพิ่มบัญชีลงไป อาจต้องใช้เวลาในการซิงค์รายชื่อสักครู่)"\n\n"หากต้องการเพิ่มรายชื่อ กด "<font fgcolor="#ffffffff"><b>"เมนู"</b></font>" แล้วแตะ:"\n" "\n<li><font fgcolor="#ffffffff"><b>"บัญชี"</b></font>" เพื่อเพิ่มหรือกำหนดค่าบัญชีด้วยรายชื่อที่คุณสามารถซิงค์ลงโทรศัพท์ได้"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ตัวเลือกการแสดงผล "</b></font>" เพื่อเปลี่ยนว่าจะให้แสดงรายชื่อใดบ้าง"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"สร้างรายชื่อใหม่"</b></font>" เพื่อสร้างรายชื่อขึ้นใหม่หมด"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"นำเข้า/ส่งออก"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"คุณไม่มีสมุดโทรศัพท์ที่จะแสดง"\n\n"หากต้องการเพิ่มรายชื่อติดต่อในสมุดโทรศัพท์ ให้กด "<font fgcolor="#ffffffff"><b>"เมนู"</b></font>" แล้วแตะ:"\n" "\n<li><font fgcolor="#ffffffff"><b>"บัญชี"</b></font>" เพื่อเพิ่มหรือกำหนดค่าบัญชีด้วยรายชื่อติดต่อที่คุณสามารถซิงค์กับแท็บเล็ตได้"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"สร้างรายชื่อติดต่อใหม่"</b></font>" เพื่อสร้างรายชื่อติดต่อใหม่ตั้งแต่ต้น"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"นำเข้า/ส่งออก"</b></font>" เพื่อนำเข้ารายชื่อติดต่อจากซิมการ์ดหรือการ์ด SD ของคุณ"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"คุณไม่มีสมุดโทรศัพท์ที่จะแสดง"\n\n"หากต้องการเพิ่มรายชื่อติดต่อในสมุดโทรศัพท์ ให้กด "<font fgcolor="#ffffffff"><b>"เมนู"</b></font>" แล้วแตะ:"\n" "\n<li><font fgcolor="#ffffffff"><b>"บัญชี"</b></font>"เพื่อเพิ่มหรือกำหนดค่าบัญชีด้วยรายชื่อติดต่อที่คุณสามารถซิงค์กับโทรศัพท์ได้"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"สร้างรายชื่อติดต่อใหม่"</b></font>" เพื่อสร้างรายชื่อติดต่อใหม่ตั้งแต่ต้น"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"นำเข้า/ส่งออก"</b></font>" เพื่อนำเข้ารายชื่อติดต่อจากซิมการ์ดหรือการ์ด SD ของคุณ"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"คุณไม่มีสมุดโทรศัพท์ที่จะแสดง (หากคุณเพิ่งเพิ่มบัญชี อาจต้องใช้เวลาสักครู่ในการซิงค์รายชื่อติดต่อ)"\n\n"หากต้องการเพิ่มรายชื่อติดต่อในสมุดโทรศัพท์ ให้กด "<font fgcolor="#ffffffff"><b>"เมนู"</b></font>" แล้วแตะ:"\n" "\n<li><font fgcolor="#ffffffff"><b>"บัญชี"</b></font>" เพื่อเพิ่มหรือกำหนดค่าบัญชีด้วยรายชื่อติดต่อที่คุณสามารถซิงค์กับแท็บเล็ตได้"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ตัวเลือกการแสดงผล"</b></font>" เพื่อเปลี่ยนว่าจะให้แสดงรายชื่อติดต่อใดบ้าง"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"สร้างรายชื่อติดต่อใหม่"</b></font>" เพื่อสร้างรายชื่อติดต่อใหม่ตั้งแต่ต้น"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"นำเข้า/ส่งออก"</b></font>" เพื่อนำเข้ารายชื่อติดต่อจากซิมการ์ดหรือการ์ด SD ของคุณ"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"คุณไม่มีสมุดโทรศัพท์ที่จะแสดง (หากคุณเพิ่งเพิ่มบัญชี อาจต้องใช้เวลาสักครู่ในการซิงค์รายชื่อติดต่อ)"\n\n"หากต้องการเพิ่มรายชื่อติดต่อในสมุดโทรศัพท์ ให้กด "<font fgcolor="#ffffffff"><b>"เมนู"</b></font>" แล้วแตะ:"\n" "\n<li><font fgcolor="#ffffffff"><b>"บัญชี"</b></font>" เพื่อเพิ่มหรือกำหนดค่าบัญชีด้วยรายชื่อติดต่อที่คุณสามารถซิงค์กับโทรศัพท์ได้"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ตัวเลือกการแสดงผล"</b></font>" เพื่อเปลี่ยนว่าจะให้แสดงรายชื่อติดต่อใดบ้าง"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"สร้างรายชื่อติดต่อใหม่"</b></font>" เพื่อสร้างรายชื่อติดต่อใหม่ตั้งแต่ต้น"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"นำเข้า/ส่งออก"</b></font>" เพื่อนำเข้ารายชื่อติดต่อจากซิมการ์ดหรือการ์ด SD ของคุณ"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"คุณไม่มีสมุดโทรศัพท์ที่จะแสดง"\n\n"หากต้องการเพิ่มรายชื่อติดต่อในสมุดโทรศัพท์ ให้กด "<font fgcolor="#ffffffff"><b>"เมนู"</b></font>" แล้วแตะ:"\n" "\n<li><font fgcolor="#ffffffff"><b>"บัญชี"</b></font>" เพื่อเพิ่มหรือกำหนดค่าบัญชีด้วยรายชื่อติดต่อที่คุณสามารถซิงค์กับแท็บเล็ตได้"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"สร้างรายชื่อติดต่อใหม่"</b></font>" เพื่อสร้างรายชื่อติดต่อใหม่ตั้งแต่ต้น"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"นำเข้า/ส่งออก"</b></font>" เพื่อนำเข้ารายชื่อติดต่อจากการ์ด SD ของคุณ"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"คุณไม่มีสมุดโทรศัพท์ที่จะแสดง"\n\n"หากต้องการเพิ่มรายชื่อติดต่อในสมุดโทรศัพท์ ให้กด "<font fgcolor="#ffffffff"><b>"เมนู"</b></font>" แล้วแตะ:"\n" "\n<li><font fgcolor="#ffffffff"><b>"บัญชี"</b></font>" เพื่อเพิ่มหรือกำหนดค่าบัญชีด้วยรายชื่อติดต่อที่คุณสามารถซิงค์กับโทรศัพท์ได้"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"สร้างรายชื่อติดต่อใหม่"</b></font>" เพื่อสร้างรายชื่อติดต่อใหม่ตั้งแต่ต้น"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"นำเข้า/ส่งออก"</b></font>" เพื่อนำเข้ารายชื่อติดต่อจากการ์ด SD ของคุณ"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"คุณไม่มีสมุดโทรศัพท์ที่จะแสดง (หากคุณเพิ่งเพิ่มบัญชี อาจต้องใช้เวลาสักครู่ในการซิงค์รายชื่อติดต่อ)"\n\n"หากต้องการเพิ่มรายชื่อติดต่อในสมุดโทรศัพท์ ให้กด "<font fgcolor="#ffffffff"><b>"เมนู"</b></font>" แล้วแตะ:"\n" "\n<li><font fgcolor="#ffffffff"><b>"บัญชี"</b></font>" เพื่อเพิ่มหรือกำหนดค่าบัญชีด้วยรายชื่อติดต่อที่คุณสามารถซิงค์กับแท็บเล็ตได้"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ตัวเลือกการแสดงผล"</b></font>" เพื่อเปลี่ยนว่าจะให้แสดงรายชื่อติดต่อใดบ้าง"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"สร้างรายชื่อติดต่อใหม่"</b></font>" เพื่อสร้างรายชื่อติดต่อใหม่ตั้งแต่ต้น"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"นำเข้า/ส่งออก"</b></font>" เพื่อนำเข้ารายชื่อติดต่อจากการ์ด SD ของคุณ"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"คุณไม่มีสมุดโทรศัพท์ที่จะแสดง (หากคุณเพิ่งเพิ่มบัญชี อาจต้องใช้เวลาสักครู่ในการซิงค์รายชื่อติดต่อ)"\n\n"หากต้องการเพิ่มรายชื่อติดต่อในสมุดโทรศัพท์ ให้กด "<font fgcolor="#ffffffff"><b>"เมนู"</b></font>" แล้วแตะ:"\n" "\n<li><font fgcolor="#ffffffff"><b>"บัญชี"</b></font>" เพื่อเพิ่มหรือกำหนดค่าบัญชีด้วยรายชื่อติดต่อที่คุณสามารถซิงค์กับโทรศัพท์ได้"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"ตัวเลือกการแสดงผล"</b></font>" เพื่อเปลี่ยนว่าจะให้แสดงรายชื่อติดต่อใดบ้าง"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"สร้างรายชื่อติดต่อใหม่"</b></font>" เพื่อสร้างรายชื่อติดต่อใหม่ตั้งแต่ต้น"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"นำเข้า/ส่งออก"</b></font>" เพื่อนำเข้ารายชื่อติดต่อจากการ์ด SD ของคุณ"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"คุณไม่มีรายการโปรดใดๆ "\n\n"หากต้องการเพิ่มรายชื่อในรายการโปรด:"\n\n" "<li>"แตะที่แท็บ "<b>"สมุดโทรศัพท์"</b>" "\n</li>" "\n<li>"แตะรายชื่อที่คุณต้องการเพิ่มลงในรายการโปรด"\n</li>" "\n<li>"แตะรูปดาวที่อยู่ติดกับชื่อ"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"รายชื่อในสมุดโทรศัพท์ทั้งหมด"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"ที่ติดดาว"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"ติดต่อกลับ"</string>
<string name="callAgain" msgid="3197312117049874778">"โทรอีกครั้ง"</string>
<string name="returnCall" msgid="8171961914203617813">"โทรกลับ"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> นาที <xliff:g id="SECONDS">%2$s</xliff:g> วินาที"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> นาที <xliff:g id="SECONDS">%s</xliff:g> วินาที"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"ที่ติดต่อบ่อยครั้ง"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"เพิ่มรายชื่อในสมุดโทรศัพท์"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"เพิ่ม \"<xliff:g id="EMAIL">%s</xliff:g>\" ในสมุดโทรศัพท์หรือไม่"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"ทั้งหมด"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"หนึ่ง"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"สอง"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"สาม"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"การสแกนที่เก็บข้อมูล USB ล้มเหลว (เหตุผล: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"การสแกนการ์ด SD ล้มเหลว (เหตุผล: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"ข้อผิดพลาด I/O"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"หน่วยความจำไม่เพียงพอ (ไฟล์อาจใหญ่เกินไป)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"แยกวิเคราะห์ vCard ไม่สำเร็จด้วยเหตุผลที่ไม่คาดคิด"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"แยกวิเคราะห์ vCard ไม่สำเร็จแม้รูปแบบจะไม่ถูกต้อง เนื่องจากการปฏิบัติงานปัจจุบันไม่สนับสนุน"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"ไม่พบไฟล์ vCard ในที่เก็บข้อมูล USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"ไม่พบไฟล์ vCard บนการ์ด SD"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"รูปแบบไม่ได้รับการสนับสนุน"</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"ไม่สามารถนำเข้า vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"ไม่พบไฟล์ vCard ในที่เก็บข้อมูล USB"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"ไม่พบไฟล์ vCard บนการ์ด SD"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"รวบรวมข้อมูลเมตาของไฟล์ vCard ที่ระบุไม่สำเร็จ"</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"นำเข้าไฟล์ตั้งแต่หนึ่งไฟล์ขึ้นไปไม่สำเร็จ (%s)"</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"เกิดข้อผิดพลาดที่ไม่ทราบสาเหตุ"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"เลือกไฟล์ vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"กำลังอ่าน vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"กำลังอ่านไฟล์ vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"การอ่านข้อมูล vCard ล้มเหลว"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"รายชื่อในสมุดโทรศัพท์ <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> จาก <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> รายการ"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> จาก <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> ไฟล์"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"กำลังแคช vCard ไปยังที่เก็บข้อมูลชั่วคราวในตัวเครื่อง"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"ตัวนำเข้ากำลังแคช vCard ไปยังที่เก็บข้อมูลชั่วคราวในตัวเครื่อง การนำเข้าจริงจะเริ่มต้นในอีกสักครู่"</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"กำลังนำเข้า <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"กำลังนำเข้า <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"อ่านข้อมูล vCard ไม่สำเร็จ"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"ยกเลิกการอ่านข้อมูล vCard แล้ว"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"นำเข้า vCard <xliff:g id="FILENAME">%s</xliff:g> เรียบร้อยแล้ว"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"ยกเลิกการนำเข้า <xliff:g id="FILENAME">%s</xliff:g> แล้ว"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"การนำเข้า <xliff:g id="FILENAME">%s</xliff:g> จะเกิดขึ้นในไม่ช้า"</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"ไฟล์จะถููกนำเข้าในไม่ช้า"</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"คำขอนำเข้า vCard ถูกปฏิเสธ โปรดลองใหม่ภายหลัง"</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"การส่งออก <xliff:g id="FILENAME">%s</xliff:g> จะเกิดขึ้นในไม่ช้า"</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"คำขอส่งออก vCard ถูกปฏิเสธ โปรดลองใหม่ภายหลัง"</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"สมุดโทรศัพท์"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"ยืนยันการส่งออก"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"คุณแน่ใจหรือไม่ว่าต้องการส่งรายการชื่อให้ \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\""</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"ส่งออกข้อมูลรายชื่อในสมุดโทรศัพท์ไม่สำเร็จ"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"มีไฟล์ vCard มากเกินไปในที่เก็บข้อมูล USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"มีไฟล์ vCard บนการ์ด SD มากเกินไป"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"ชื่อไฟล์ที่ต้องใช้ยาวเกินไป (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"ส่งออก <xliff:g id="FILENAME">%s</xliff:g> เรียบร้อยแล้ว"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"ยกเลิกการส่งออก <xliff:g id="FILENAME">%s</xliff:g> แล้ว"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"กำลังส่งออกข้อมูลสมุดโทรศัพท์"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"กำลังส่งออกข้อมูลรายชื่อในสมุดโทรศัพท์ไปยัง \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"เริ่มใช้งานโปรแกรมส่งออกไม่ได้: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"เกิดข้อผิดพลาดระหว่างส่งออก: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"เรียกข้อมูลจากฐานข้อมูลไม่สำเร็จ"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"ไม่มีรายชื่อที่ส่งออกได้ หากคุณมีรายชื่อในโทรศัพท์ รายชื่อทั้งหมดอาจถูกห้ามส่งออกไปยังโทรศัพท์ภายนอกโดยผู้ให้บริการข้อมูลบางราย"</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"ไม่มีรายชื่อติดต่อที่สามารถส่งออกได้ หากคุณมีรายชื่อติดต่ออยู่ในแท็บเล็ตของคุณจริงๆ รายชื่อติดต่อทั้งหมดอาจถูกห้ามไม่ให้ส่งออกไปภายนอกแท็บเล็ตโดยผู้ให้บริการข้อมูลบางราย"</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"ไม่มีรายชื่อติดต่อที่สามารถส่งออกได้ หากคุณมีรายชื่อติดต่ออยู่ในโทรศัพท์ของคุณจริงๆ รายชื่อติดต่อทั้งหมดอาจถูกห้ามไม่ให้ส่งออกไปภายนอกโทรศัพท์โดยผู้ให้บริการข้อมูลบางราย"</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"โปรแกรมเขียนข้อความ vCard เริ่มการทำงานไม่ถูกต้อง"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"ไม่สามารถเปิด \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"รายชื่อในสมุดโทรศัพท์ <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> จาก <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> รายการ"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"ไม่สามารถเปิด \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"รายชื่อในสมุดโทรศัพท์ <xliff:g id="CURRENT_NUMBER">%s</xliff:g> จาก <xliff:g id="TOTAL_NUMBER">%s</xliff:g> รายการ"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"การยกเลิกการนำเข้า vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"คุณต้องการยกเลิกการนำเข้า <xliff:g id="FILENAME">%s</xliff:g> หรือไม่"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"การยกเลิกการส่งออก vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"คุณต้องการยกเลิกการส่งออก <xliff:g id="FILENAME">%s</xliff:g> หรือไม่"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"ยกเลิกการนำเข้า/ส่งออก vCard ไม่สำเร็จ"</string>
<string name="search_settings_description" msgid="2675223022992445813">"ชื่อของรายชื่อในสมุดโทรศัพท์ของคุณ"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"เพิ่มช่วงคั่น 2 วินาที"</string>
<string name="add_wait" msgid="3360818652790319634">"เพิ่มการรอ"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"ไม่พบแอปพลิเคชันสำหรับการทำงานนี้"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"จำตัวเลือกนี้"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"ไม่ทราบ"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"ไม่มีข้อมูล"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"ล้างค่าเริ่มต้น"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"ค่าเริ่มต้นที่กำหนดไว้สำหรับรายชื่อนี้:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"ล้าง"</string>
<string name="menu_accounts" msgid="8499114602017077970">"บัญชี"</string>
<string name="menu_import_export" msgid="3765725645491577190">"นำเข้า/ส่งออก"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"นำเข้า/ส่งออกรายชื่อในสมุดโทรศัพท์"</string>
- <string name="menu_share" msgid="943789700636542260">"ใช้ร่วมกัน"</string>
+ <string name="menu_share" msgid="8746849630474240344">"แบ่งปันที่อยู่ติดต่อ"</string>
<string name="share_via" msgid="563121028023030093">"ใช้สมุดโทรศัพท์ร่วมกันทาง"</string>
<string name="share_error" msgid="4374508848981697170">"รายชื่อนี้ไม่สามารถแบ่งปันได้"</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"ชื่อ"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"องค์กร"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"เว็บไซต์"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"กิจกรรม"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"ความสัมพันธ์"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"กลุ่ม"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"W"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"รายชื่อนี้เป็นแบบอ่านอย่างเดียว"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"เพิ่มเติม"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"ชื่อหลัก"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"สร้างรายชื่อภายในบัญชี"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"นำกลุ่มที่ซิงค์ออก"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"เพิ่มกลุ่มที่ซิงค์"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"รายชื่ออื่นๆ ทั้งหมด"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"รายชื่อในสมุดโทรศัพท์ทั้งหมด"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"การนำ \"<xliff:g id="GROUP">%s</xliff:g>\" ออกจากการซิงค์จะนำรายชื่อในสมุดโทรศัพท์ที่ไม่ได้จัดกลุ่มออกจากการซิงค์ด้วย"</string>
- <string name="account_phone" msgid="3682950835276226870">"โทรศัพท์เท่านั้น ไม่ซิงค์"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"แท็บเล็ตเท่านั้น ไม่ซิงค์"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"โทรศัพท์เท่านั้น ไม่ซิงค์"</string>
<string name="call_custom" msgid="7756571794763171802">"โทรหา <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"โทรเข้าบ้าน"</string>
<string name="call_mobile" msgid="7502236805487609178">"โทรเข้ามือถือ"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"แชทโดยใช้ ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"แชทโดยใช้ Jabber"</string>
<string name="chat" msgid="9025361898797412245">"แชท"</string>
+ <string name="postal_address" msgid="8765560217149624536">"ที่อยู่"</string>
<string name="postal_street" msgid="8133143961580058972">"ถนน"</string>
<string name="postal_pobox" msgid="4431938829180269821">"ตู้ ปณ."</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"ย่านใกล้เคียง"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"รัฐ"</string>
<string name="postal_postcode" msgid="572136414136673751">"รหัสไปรษณีย์"</string>
<string name="postal_country" msgid="7638264508416368690">"ประเทศ"</string>
+ <string name="full_name" msgid="6602579550613988977">"ชื่อ"</string>
<string name="name_given" msgid="1687286314106019813">"ชื่อ"</string>
<string name="name_family" msgid="3416695586119999058">"นามสกุล"</string>
<string name="name_prefix" msgid="59756378548779822">"คำนำหน้าชื่อ"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"ค้นหารายชื่อในสมุดโทรศัพท์"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"ค้นหารายชื่อทั้งหมด"</string>
<string name="take_photo" msgid="7496128293167402354">"ถ่ายภาพ"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"ถ่ายภาพใหม่"</string>
<string name="pick_photo" msgid="448886509158039462">"เลือกภาพจากแกลเลอรี"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"กำลังอัปเดตรายการสมุดโทรศัพท์เพื่อแสดงการเปลี่ยนภาษา"\n\n"โปรดรอสักครู่..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"กำลังอัปเดตรายชื่อในสมุดโทรศัพท์"\n\n"โปรดรอสักครู่..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"กำลังอัปเกรดรายชื่อ"\n\n"การอัปเกรดต้องใช้พื้นที่จัดเก็บภายในโทรศัพท์ประมาณ <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB"\n\n"เลือกตัวเลือกใดต่อไปนี้:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"เลือกภาพใหม่จากแกลเลอรี"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"กำลังอัปเดตรายการที่อยู่ติดต่อตามการเปลี่ยนภาษา"</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"กำลังอัปเดตรายการที่อยู่ติดต่อ"</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"กำลังปรับปรุงที่อยู่ติดต่ออยู่ในขณะนี้ "\n\n"การอัปเกรดต้องใช้พื้นที่จัดเก็บภายในประมาณ <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g>MB "\n\n"ให้เลือกตัวเลือกใดตัวเลือกหนึ่งดังต่อไปนี้:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"ถอนการติดตั้งแอปพลิเคชันบางอย่าง"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"ลองอัปเกรดซ้ำ"</string>
- <string name="search_results_for" msgid="8705490885073188513">"ผลการค้นหาสำหรับ: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"กำลังค้นหา..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"แสดงรายการที่เลือก"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"แสดงทั้งหมด"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"เลือกทั้งหมด"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"ยกเลิกการเลือกทั้งหมด"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"ไม่ได้เลือกรายชื่อติดต่อไว้"</string>
+ <string name="add_field" msgid="2384260056674995230">"เพิ่มฟิลด์อื่น"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"ผ่านทาง <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> ผ่านทาง <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"รายการโปรด"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"แก้ไขรายชื่อติดต่อ"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"ไม่รวม"</item>
+ <item quantity="other" msgid="425683718017380845">"รวมจากแหล่งที่มา <xliff:g id="COUNT">%0$d</xliff:g> แหล่ง"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"อื่นๆ"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"รวมรายชื่อติดต่อ"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"รวมรายชื่อติดต่อปัจจุบันกับรายชื่อติดต่อที่เลือกหรือไม่"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"แก้ไขรายชื่อติดต่อที่เลือก"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"เปลี่ยนไปแก้ไขรายชื่อติดต่อที่เลือกหรือไม่ ข้อมูลที่คุณป้อนไว้จนถึงขณะนี้จะถูกคัดลอก"</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"คัดลอกไปยังสมุดโทรศัพท์ของฉัน"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"ไดเรกทอรี <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"กำลังค้นหารายชื่อติดต่อทั้งหมด"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"ไดเรกทอรี"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"สมุดโทรศัพท์"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"กำลังสร้างสำเนาส่วนบุคคล"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"เลือกรายชื่อติดต่อ"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"รายชื่อติดต่อทั้งหมด"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"ที่ติดดาว"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"กำหนดเอง"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"กำหนดค่า..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"รายชื่อติดต่อที่มีหมายเลขโทรศัพท์"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"รายชื่อติดต่อ"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"กำหนดมุมมองที่กำหนดเอง"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"การตั้งค่า"</string>
+ <string name="menu_settings" msgid="377929915873428211">"การตั้งค่า"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"ตัวเลือกการแสดงผล"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"ค้นหารายชื่อติดต่อ"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"หมายเลขโทรศัพท์"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"เพิ่มลงในสมุดโทรศัพท์"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"ปิด"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"ระบุปี"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"รายชื่อติดต่อ"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"กำลังโหลด…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"สร้างที่อยู่ติดต่อใหม่"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"ลงชื่อเข้าใช้บัญชี"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"นำเข้าที่อยู่ติดต่อจากไฟล์"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"สร้างกลุ่มใหม่"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[สร้างกลุ่มใหม่]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"เปลี่ยนชื่อกลุ่ม"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"ลบกลุ่ม"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"คุณต้องการลบกลุ่ม \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\" หรือไม่ (ตัวที่อยู่ติดต่อเองจะไม่ถูกลบ)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"โปรดป้อนชื่อบุคคลติดต่อก่อนนำไปรวมกับรายชื่อติดต่ออื่น"</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"รายชื่อติดต่อที่รวมกัน"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"คัดลอกข้อความแล้ว"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"ยกเลิกการเปลี่ยนแปลง"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"คุณต้องการยกเลิกการเปลี่ยนแปลงของคุณหรือไม่"</string>
+ <string name="discard" msgid="1234315037371251414">"ยกเลิก"</string>
</resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 9810138..db502e9 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Pumili ng shortcut sa contact"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Pumili ng tatawagang numero"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Pumili ng numero na padadalhan ng mensahe"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Pumili ng contact"</string>
<string name="starredList" msgid="4817256136413959463">"Naka-star"</string>
<string name="frequentList" msgid="7154768136473953056">"Madalas"</string>
<string name="strequentList" msgid="5640192862059373511">"Mga Paborito"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Tanggalin ang contact"</string>
<string name="menu_call" msgid="3992595586042260618">"Tawagan ang contact"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Mag-text sa contact"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Ipadala ang email"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Address ng mapa"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Gawing default na numero"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Gumawa ng default na email"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Hiwalay"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Hiniwalay ang mga contact"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Muling pangalanan ang pangkat"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Magtanggal ng pangkat"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Bago"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Hiwalay na Contact"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Sigurado ka bang gusto mong ihiwalay ang isang contact na ito sa maraming contact: isa sa bawat lupon ng impormasyon sa pakikipag-ugnay na isinama dito?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Sumali"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Tatanggalin ng pagtanggal sa account na ito ang impormasyon mula sa maramihang account."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Tatanggalin ang contact na ito."</string>
<string name="menu_done" msgid="796017761764190697">"Tapos na"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Ibalik"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Kanselahin"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"I-edit ang contact"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Bagong contact"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Phonetic"</string>
<string name="label_notes" msgid="8337354953278341042">"Mga Tala"</string>
<string name="label_sip_address" msgid="124073911714324974">"Tawag sa internet"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Ringtone"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Una at Huli"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Phonetic na pangalan"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Kumpanya"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Pamagat"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Hindi umiiral ang contact."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Lumikha ng bagong contact"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Pumili ng label"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telepono"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Email"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"IM"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Postal address"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Address"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Samahan"</item>
<item msgid="7196592230748086755">"Tandaan"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Walang mga larawan ang available sa telepono."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Icon ng contact"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Walang available na mga larawan sa tablet."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Walang mga larawan ang available sa telepono."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Larawan ng contact"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Custom na pangalan ng label"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Mga pagpipilian sa pagpapakita"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Mga pagpipilian sa pagpapakita"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Ipadala ang mga tawag nang direkta sa voicemail"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Default"</string>
- <string name="changePicture" msgid="2943329047610967714">"Icon na baguhin"</string>
- <string name="removePicture" msgid="3041230993155966350">"Icon sa pag-alis"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Alisin ang larawan"</string>
<string name="noContacts" msgid="8579310973261953559">"Walang mga contact."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Walang nakitang magkakatugmang mga contact."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Walang mga contact na may mga numero ng telepono."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Na-save ang contact."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Error, hindi nagawang i-save ang mga pagbabago sa contact."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Pagpapakita ng 1 contact na may numero ng telepono"</item>
- <item quantity="other" msgid="6133262880804110289">"Pagpapakita ng <xliff:g id="COUNT">%d</xliff:g> (na) contact na may mga numero ng telepono"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 contact na may numero ng telepono"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> (na) contact na may mga numero ng telepono"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Walang mga nakikitang contact na may mga numero ng telepono"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Walang mga contact na may mga numero ng telepono"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Ipinapakita ang 1 contact"</item>
- <item quantity="other" msgid="2865867557378939630">"Pagpapakita ng <xliff:g id="COUNT">%d</xliff:g> (na) contact"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 contact"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> (na) contact"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Walang nakikitang mga contact"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Walang mga contact"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Walang nakikitang mga contact"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Walang mga naka-star na contact"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Walang mga contact sa <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Nakita ang 1 contact"</item>
- <item quantity="other" msgid="7752927996850263152">"Nakita <xliff:g id="COUNT">%d</xliff:g> (na) contact"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 ang nakita"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> ang nakita"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Hindi nakita ang contact"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"higit pa sa <xliff:g id="COUNT">%d</xliff:g> ang nakita"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Hindi Nakita"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 contact"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> (na) contact"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 ang nakita"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> ang nakita"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Mga Contact"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Mga Paborito"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Mga contact sa SIM card"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Wala kang ipapakitang anumang mga contact. (Kung nagdagdag ka lang ng account, maaaring tumagal ng ilang minuto upang ma-sync ang mga contact.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Wala kang maipapakitang anumang mga contact."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Wala kang maipapakitang anumang mga contact."\n\n"Upang magdagdag ng mga contact, pindutin ang "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" at galawin ang:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Mga Account"</b></font>" upang magdagdag o mag-configure ng account na may mga contact na maaari mong ma-sync sa telepono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Bagong contact"</b></font>" upang lumikha ng bagong contact mula sa scratch"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mag-import/Mag-export"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Wala kang maipapakitang anumang mga contact. (Kung nagdagdag ka ng account, tatagal ito ng ilang minuto upang ma-sync ang mga contact.)"\n\n"Upang magdagdag ng mga contact, pindutin ang "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" at galawin ang:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Mga Account"</b></font>" upang magdagdag o mag-configure ng account na may mga contact na maaari mong ma-sync sa telepono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mga pagpipilian ng display"</b></font>" upang mabago kung aling mga contact ang makikita"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Bagong contact"</b></font>" upang lumikha ng bagong contact mula sa scratch"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mag-import/Mag-export"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Wala kang maipapakitang anumang mga contact."\n\n"Upang magdagdag ng mga contact, pindutin ang "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" at galawin ang:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Mga Account"</b></font>" upang magdagdag o mag-configure ng account na may mga contact na maaari mong ma-sync sa telepono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Bagong contact"</b></font>" upang lumikha ng bagong contact mula sa scratch"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mag-import/Mag-export"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Wala kang maipapakitang anumang mga contact. (Kung nagdagdag ka ng account, tatagal ito ng ilang minuto upang ma-sync ang mga contact.)"\n\n"Upang magdagdag ng mga contact, pindutin ang "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" at galawin ang:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Mga Account"</b></font>" upang magdagdag o mag-configure ng account na may mga contact na maaari mong ma-sync sa telepono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mga pagpipilian ng display"</b></font>" upang mabago kung aling mga contact ang makikita"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Bagong contact"</b></font>" upang lumikha ng bagong contact mula sa scratch"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mag-import/Mag-export"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Wala kang anumang mga contact na ipapakita."\n\n"Upang magdagdag ng mga contact, pindutin ang "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" at pindutin ang:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Mga Account"</b></font>" upang magdagdag o mag-configure ng account na may mga contact na masi-sync mo sa tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Bagong contact"</b></font>" upang lumikha ng bagong contact mula sa scratch"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>" upang makapag-import ng mga contact mula sa iyong SIM o SD card"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Wala kang anumang mga contact na ipapakita."\n\n"Upang magdagdag ng mga contact, pindutin ang "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" at pindutin ang:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Mga Account"</b></font>" upang magdagdag o mag-configure ng account na may mga contact na masi-sync mo sa telepono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Bagong contact"</b></font>" upang lumikha ng bagong contact mula sa scratch"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>" upang makapag-import ng mga contact mula sa iyong SIM o SD card"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Wala kang anumang mga contact na ipapakita. (Kung nagdagdag ka lang ng account, maaaring magtagal ng ilang minuto upang i-sync ang mga contact.)"\n\n"Upang magdagdag ng mga contact, pindutin ang "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" at pindutin ang:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Mga Account"</b></font>" upang magdagdag o mag-configure ng isang account na may mga contact na masi-sync mo sa tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mga pagpipilian ng display"</b></font>" upang baguhin kung anong mga contact ang nakikita"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Bagong contact"</b></font>" upang lumikha ng bagong contact mula sa scratch"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>" upang mag-import ng mga contact mula sa iyong SIM o SD card"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Wala kang anumang mga contact na ipapakita. (Kung nagdagdag ka lang ng account, maaaring magtagal ng ilang minuto upang i-sync ang mga contact.)"\n\n"Upang magdagdag ng mga contact, pindutin ang "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" at pindutin ang:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Mga Account"</b></font>" upang magdagdag o mag-configure ng isang account na may mga contact na masi-sync mo sa telepono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mga pagpipilian sa pagpapakita"</b></font>" upang baguhin kung anong mga contact ang nakikita"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Bagong contact"</b></font>" upang lumikha ng bagong contact mula sa scratch"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>" upang mag-import ng mga contact mula sa iyong SIM o SD card"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Wala kang anumang mga contact na ipapakita."\n\n"Upang magdagdag ng mga contact, pindutin ang "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" at pindutin ang:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Mga Account"</b></font>" upang magdagdag o mag-configure ng account na may mga contact na masi-sync mo sa tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Bagong contact"</b></font>" upang lumikha ng bagong contact mula sa scratch"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>" upang makapag-import ng mga contact mula sa iyong SD card"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Wala kang anumang mga contact na ipapakita."\n\n"Upang magdagdag ng mga contact, pindutin ang "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" at pindutin ang:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Mga Account"</b></font>" upang magdagdag o mag-configure ng account na may mga contact na masi-sync mo sa telepono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Bagong contact"</b></font>" upang lumikha ng bagong contact mula sa scratch"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>" upang makapag-import ng mga contact mula sa iyong SD card"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Wala kang anumang mga contact na ipapakita. (Kung nagdagdag ka lang ng account, maaaring magtagal ng ilang minuto upang i-sync ang mga contact.)"\n\n"Upang magdagdag ng mga contact, pindutin ang "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" at pindutin ang:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Mga Account"</b></font>" upang magdagdag o mag-configure ng isang account na may mga contact na masi-sync mo sa tablet"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mga pagpipilian sa pagpapakita"</b></font>" upang baguhin kung anong mga contact ang nakikita"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Bagong contact"</b></font>" upang lumikha ng bagong contact mula sa scratch"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>" upang mag-import ng mga contact mula sa iyong SIM o SD card"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Wala kang anumang mga contact na ipapakita. (Kung nagdagdag ka lang ng account, maaaring magtagal ng ilang minuto upang i-sync ang mga contact.)"\n\n"Upang magdagdag ng mga contact, pindutin ang "<font fgcolor="#ffffffff"><b>"Menu"</b></font>" at pindutin ang:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Mga Account"</b></font>" upang magdagdag o mag-configure ng isang account na may mga contact na masi-sync mo sa telepono"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Mga pagpipilian sa pagpapakita"</b></font>" upang baguhin kung anong mga contact ang nakikita"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Bagong contact"</b></font>" upang lumikha ng bagong contact mula sa scratch"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Import/Export"</b></font>" upang mag-import ng mga contact mula sa iyong SD card"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Wala kang anumang mga paborito."\n\n"Upang magdagdag ng contact sa iyong listahan ng mga paborito:"\n\n" "<li>"Galawin ang tab na "<b>"Mga Contact"</b>\n</li>" "\n<li>"Galawin ang contact na gusto mong idagdag sa iyong mga paborito"\n</li>" "\n<li>"Galawin ang bituin sa tabi ng pangalan ng contact"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Lahat ng mga contact"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Naka-star"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Tumawag pabalik"</string>
<string name="callAgain" msgid="3197312117049874778">"Tawagan muli"</string>
<string name="returnCall" msgid="8171961914203617813">"Pabalik na tawag"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> (na) min <xliff:g id="SECONDS">%2$s</xliff:g> (na) seg"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> (na) min <xliff:g id="SECONDS">%s</xliff:g> (na) seg"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Madalas na tinatawagan"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Magdagdag ng contact"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Idagdag ang \"<xliff:g id="EMAIL">%s</xliff:g>\" sa mga contact?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Lahat"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"isa"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"dalawa"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"tatlo"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Nabigo ang pag-scan ng imbakan na USB (Dahilan: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Nabigo ang pag-scan sa SD card (Dahilan: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Error na I/O"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Hindi sapat ang memorya (maaaring masyadong malaki ang file)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Nabigong i-parse ang vCard para sa di-inaasahang dahilan"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Nabigong i-parse ang vCard kahit na mukhang nasa wastong format na ito, dahil hindi ito sinusuportahan ng kasalukuyang pagpapatupad"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Walang nakitang vCard file sa imbakan na USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Walang nakitang vCard file sa SD card"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Hindi suportado ang format."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Nabigong i-import ang vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Walang nakitang vCard file sa imabakan ng USB"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Walang nakitang vCard file sa SD card"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Nabigong kumolekta ng impormasyon na meta ng ibinigay na (mga) vCard."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Nabigo ang isa o higit pang file upang ma-import (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Hindi kilalang error"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Pumili ng vCard file"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Pagbabasa ng vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Pagbabasa ng (mga) file ng vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Nabigo ang pagbabasa ng data ng vCard"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> ng <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> (na) contact"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> ng <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> (na) file"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Kina-cache ang (mga) vCard sa lokal na pansamantalang imbakan"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Kina-cache ng taga-import ang (mga) vCard sa lokal na pansamantalang imbakan. Malapit nang magsimula ang aktwal na pag-import."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Nag-i-import <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Nag-iimport <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Nabigong Basahin ang vCard data"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Kinansela ang pagbabasa ng vCard"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Tapos na ang pag-import ng vCard <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Kinansela ang pag-import ng <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"Mai-import ang <xliff:g id="FILENAME">%s</xliff:g> sa ilang saglit."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Sa ilang sandali ay mai-import na ang file."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Tinanggihan ang kahilingang pag-import ng vCard. Pakisubukang muli sa ibang pagkakataon."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"Mae-export ang <xliff:g id="FILENAME">%s</xliff:g> sa ilang saglit."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Tinanggihan ang kahilingang pag-export ng vCard. Pakisubukang muli sa ibang pagkakataon."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"contact"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Kumpirmahin ang pag-export"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Sigurado ka bang gusto mong i-export ang listahan ng iyong contact sa \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Nabigong i-export ang data ng contact"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Masyadong maraming vCard file sa imbakan na USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Masyadong maraming vCard file sa SD card"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Ang kinakailangang filename ay masyadong mahaba (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Tapos na ang pag-export ng <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Kinansela ang pag-export ng <xliff:g id="FILENAME">%s</xliff:g>"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Pag-e-export ng data ng contact"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Pag-export ng data ng contact sa \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Hindi mapasimulan ang taga-export: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Naganap ang error sa panahon ng pag-export: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Nabigong kumuha ng impormasyon ng database"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Walang nae-export na contact. Kung mayroon kang mga contact sa iyong telepono, maaaring mapagbawalan ang lahat ng mga contact mula sa pag-export sa labas ng telepono ng ilang provider ng data."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Walang mga nae-export na mga contact. Kung may mga contact ka sa iyong tablet, maaaring ipinagbabawal ang lahat ng mga contact na ma-export sa labas ng tablet ng ilang tagapagbigay ng data."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Walang nae-export na mga contact. Kung may mga contact ka sa iyong telepono, maaaring ipinagbabawal ang lahat ng mga contact na ma-export sa labas ng telepono ng ilang tagapagbigay ng data."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Hindi tamang nasimulan ang vCard composer"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Hindi mabuksan ang \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> ng <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> (na) contact"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Hindi mabuksan ang \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> ng <xliff:g id="TOTAL_NUMBER">%s</xliff:g> (na) contact"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Pagkansela ng pag-import ng vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Sigurado ka bang kakanselahin ang pag-import ng <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Pagkansela ng pag-export ng vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Sigurado ka bang kakanselahin ang pag-export ng <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Di nakansela ang vCard import/export"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Mga pangalan ng iyong mga contact"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Magdagdag ng pag-pause na 2-seg"</string>
<string name="add_wait" msgid="3360818652790319634">"Magdagdag ng paghihintay"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Walang nakitang application upang pangasiwaan ang pagkilos na ito"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Tandaan ang pagpipiliang ito"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Di-kilala"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Walang data"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"I-clear ang mga default"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Default na itinakda sa contact na ito."</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"I-clear"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Mga Account"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Mag-import/Mag-export"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Mag-import/Mag-export ng mga contact"</string>
- <string name="menu_share" msgid="943789700636542260">"Ibahagi"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Magbahagi ng contact"</string>
<string name="share_via" msgid="563121028023030093">"Ibahagi ang contact sa pamamagitan ng"</string>
<string name="share_error" msgid="4374508848981697170">"Hindi maibabahagi ang contact na ito."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Pangalan"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Samahan"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Website"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Kaganapan"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Kaugnayan"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Mga Pangkat"</string>
<string name="type_short_home" msgid="7770424864090605384">"H"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"W"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Read-only ang contact na ito"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Higit pa"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Pangunahing pangalan"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Lumikha ng contact sa ilalim ng account"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Alisin ang pangkat sa pag-sync"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Magdagdag ng pangkat sa pag-sync"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Lahat ng Iba Pang Mga Contact"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Lahat ng mga Contact"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Aalisin rin ng pag-alis ng \'<xliff:g id="GROUP">%s</xliff:g>\' mula sa sync ang anumang mga di-nakapangkat na contact mula sa sync."</string>
- <string name="account_phone" msgid="3682950835276226870">"Telepono lamang, hindi naka-sync"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Tablet lang, hindi naka-sync"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Telepono lamang, hindi naka-sync"</string>
<string name="call_custom" msgid="7756571794763171802">"Tawagan si <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Tawagan ang tahanan"</string>
<string name="call_mobile" msgid="7502236805487609178">"Tawagan ang mobile"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Makipag-chat gamit ang ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Makipag-chat gamit ang Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Makipag-chat"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Address"</string>
<string name="postal_street" msgid="8133143961580058972">"Kalye"</string>
<string name="postal_pobox" msgid="4431938829180269821">"PO box"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Kapitbahayan"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Katayuan"</string>
<string name="postal_postcode" msgid="572136414136673751">"ZIP code"</string>
<string name="postal_country" msgid="7638264508416368690">"Bansa"</string>
+ <string name="full_name" msgid="6602579550613988977">"Pangalan"</string>
<string name="name_given" msgid="1687286314106019813">"Pangalan"</string>
<string name="name_family" msgid="3416695586119999058">"Apelyido"</string>
<string name="name_prefix" msgid="59756378548779822">"Prefix ng pangalan"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Maghanap ng mga contact"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Hanapan ang lahat ng mga contact"</string>
<string name="take_photo" msgid="7496128293167402354">"Kumuha ng larawan"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Kumuha ng bagong larawan"</string>
<string name="pick_photo" msgid="448886509158039462">"Pumili ng larawan mula sa Gallery"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Ina-update ang listahan ng contact upang ipakita ang pagbabago ng wika."\n\n"Pakihintay..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Na-update ang listahan ng contact."\n\n"Pakihintay..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Ang mga contact ay nasa proseso ng pag-upgrade. "\n\n"Nangangailangan ang proseso ng pag-upgrade ng tinatayang <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>Mb ng panloob na imbakan ng telepono."\n\n"Piliin ang isa sa mga sumusunod na pagpipilian:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Pumili ng bagong larawan mula sa Gallery"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Ina-update ang listahan ng contact upang maipakita ang pagbabago ng wika."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Ina-update ang listahan ng contact."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Ang mga contact ay ina-upgrade. "\n\n"Nangangailangan ng tinatayang <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mb ng panloob na imbakan ang proseso sa pag-upgrade."\n\n"Pumili ng isa sa mga sumusunod na opsyon:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"I-uninstall ang ilang application"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Muling subukan ang i-upgrade"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Mga resulta ng paghahanap para sa: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Naghahanap..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Ipakita ang napili"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Ipakita lahat"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Piliin lahat"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Alisin sa pagkakapili ang lahat"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Walang mga napiling kontrata."</string>
+ <string name="add_field" msgid="2384260056674995230">"Magdagdag ng ibang field"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"sa pamamagitan ng <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> sa pamamagitan ng <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"paborito"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"I-edit ang Contact"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"hindi pinagsama"</item>
+ <item quantity="other" msgid="425683718017380845">"pinagsama mula sa <xliff:g id="COUNT">%0$d</xliff:g> (na) pinagmumulan"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Iba pa"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Sumali sa mga contact"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Isama ang kasalukuyang contact sa piniling contact?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"I-edit ang napiling mga contact"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Lumipat sa pag-edit ng napiling contact? Kokopyahin ang impormasyong ipinasok mo sa ngayon."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Kopyahin sa aking mga contact"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Direktoryo na <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Hinahanap ang lahat ng mga contact"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Direktoryo"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Mga Contact"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Lumilikha ng personal na kopya"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Pumili ng listahan ng contact"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Lahat ng mga contact"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Naka-star"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Custom"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"I-customize..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Mga contact na may mga numero ng telepono"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Contact"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Tukuyin ang custom na pagtingin"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Mga Setting"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Mga Setting"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Mga pagpipilian sa pagpapakita"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Maghanap ng mga contact"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Numero ng telepono"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Idagdag sa mga contact"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Isara"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Magbigay ng taon"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Contact"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Naglo-load…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Lumikha ng bagong contact"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Mag-sign in sa isang account"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Pag-import ng mga contact mula sa isang file"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Lumikha ng bagong pangkat"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Lumikha ng bagong pangkat]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Palitan ng pangalan ang pangkat"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Tanggalin ang pangkat"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Sigurado ka bang gusto mong tanggalin ang pangkat na \'<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\'? (Hindi tatanggalin ang mga mismong contact.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Mangyaring magpasok ng pangalan ng contact bago sumali sa ibang contact."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Sumaling contact"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Kinopya ang teksto"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Itapon ang mga pagbabago"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Nais mo ba talagang itapon ang iyong mga pagbabago?"</string>
+ <string name="discard" msgid="1234315037371251414">"Itapon"</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index d942949..299d36d 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Bir kişi kısayolu seçin"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Aranacak numarayı seçin"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"İleti gönderilecek bir numara seçin"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Kişi seçin"</string>
<string name="starredList" msgid="4817256136413959463">"Yıldızlı"</string>
<string name="frequentList" msgid="7154768136473953056">"Sık sık"</string>
<string name="strequentList" msgid="5640192862059373511">"Sık Kullanılanlar"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Kişiyi sil"</string>
<string name="menu_call" msgid="3992595586042260618">"Çağrı yap"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Kısa mesaj gönder"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"E-posta gönder"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Adresi haritada göster"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Varsayılan numara yap"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Varsayılan e-posta yap"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Ayır"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Kişi ayrıldı"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Grubu yeniden adlandır"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Grubu sil"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Yeni"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Kişiyi Ayır"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Bu tek kişiyi birden fazla kişiye ayırmak istediğinizden emin misiniz? Bu kişiye eklenen kişi bilgileri setinin her biri için bir kişi oluşturulsun mu?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Birleştir"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Bu kişiyi silmek, birden fazla hesaba ait bilgileri de siler."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Bu kişi silinecek."</string>
<string name="menu_done" msgid="796017761764190697">"Bitti"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Geri Döndür"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"İptal"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Kişiyi düzenle"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Yeni kişi"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Fonetik"</string>
<string name="label_notes" msgid="8337354953278341042">"Notlar"</string>
<string name="label_sip_address" msgid="124073911714324974">"İnternet çağrısı"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Zil sesi"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Adı ve Soyadı"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Fonetik adı"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Şirket"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Başlık"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Kişi mevcut değil."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Yeni kişi oluştur"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Etiketi seç"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Telefon"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"E-posta"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"IM"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Posta adresi"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Adres"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Kuruluş"</item>
<item msgid="7196592230748086755">"Not"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Telefonda hiçbir resim yok."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Kişi simgesi"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Tablette kullanılabilir resim yok."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Telefonda hiçbir resim yok."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Kişi fotoğrafı"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Özel etiket adı"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Seçenekleri görüntüle"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Seçenekleri görüntüle"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Çağrıları doğrudan sesli mesaja gönder"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Varsayılan"</string>
- <string name="changePicture" msgid="2943329047610967714">"Simgeyi değiştir"</string>
- <string name="removePicture" msgid="3041230993155966350">"Simgeyi kaldır"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Fotoğrafı kaldır"</string>
<string name="noContacts" msgid="8579310973261953559">"Hiçbir kişi yok."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Eşleşen kişi bulunamadı."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Telefon numarası olan hiçbir kişi yok."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Kişi kaydedildi."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Hata, kişi değişiklikleri kaydedilemiyor."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Telefon numarası olan 1 kişi görüntüleniyor"</item>
- <item quantity="other" msgid="6133262880804110289">"Telefon numarası olan <xliff:g id="COUNT">%d</xliff:g> kişi görüntüleniyor"</item>
+ <item quantity="one" msgid="3015357862286673986">"Telefon numarası olan 1 kişi"</item>
+ <item quantity="other" msgid="3299954047880968205">"Telefon numarası olan <xliff:g id="COUNT">%d</xliff:g> kişi"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Telefon numarası olan görülebilir kişi yok"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Telefon numarası olan hiçbir kişi yok"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"1 kişi görüntüleniyor"</item>
- <item quantity="other" msgid="2865867557378939630">"<xliff:g id="COUNT">%d</xliff:g> kişi görüntüleniyor"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 kişi"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> kişi"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Görülebilir kişi yok"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Hiçbir kişi yok"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Görülebilir kişi yok"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Yıldız işaretli hiçbir kişi yok"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"<xliff:g id="NAME">%s</xliff:g> içinde hiçbir kişi yok"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"1 kişi bulundu"</item>
- <item quantity="other" msgid="7752927996850263152">"<xliff:g id="COUNT">%d</xliff:g> kişi bulundu"</item>
+ <item quantity="one" msgid="5517063038754171134">"1 kişi bulundu"</item>
+ <item quantity="other" msgid="3852668542926965042">"<xliff:g id="COUNT">%d</xliff:g> kişi bulundu"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Kişi bulunamadı"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"<xliff:g id="COUNT">%d</xliff:g> kişiden fazla bulundu"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Bulunamadı"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 kişi"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> kişi"</item>
+ <item quantity="one" msgid="4826918429708286628">"1 kişi bulundu"</item>
+ <item quantity="other" msgid="7988132539476575389">"<xliff:g id="COUNT">%d</xliff:g> kişi bulundu"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Kişiler"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Sık Kullanılanlar"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"SIM kart kişileri"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Görüntülenecek kişiniz yok. (Hesabı yeni eklediyseniz, kişilerin senkronize edilmesi birkaç dakika sürebilir.)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Görüntülenecek kişiniz yok."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Görüntülenecek hiçbir kişiniz yok."\n\n"Kişi eklemek için, "<font fgcolor="#ffffffff"><b>"Menü"</b></font>"\'ye basın ve şunlara dokunun:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Hesaplar"</b></font>" (telefon ile senkronize edebileceğiniz kişilere sahip olan bir hesap eklemek veya yapılandırmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Yeni kişi"</b></font>" (en baştan yeni bir kişi eklemek için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"İçe/Dışa Aktar"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Görüntülenecek hiçbir kişiniz yok. (Kısa süre önce bir hesap eklediyseniz, kişileri senkronize etmek birkaç dakika sürebilir.)"\n\n"Kişi eklemek için, "<font fgcolor="#ffffffff"><b>"Menü"</b></font>"\'ye basın ve şunlara dokunun:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Hesaplar"</b></font>" (telefon ile senkronize edebileceğiniz kişilere sahip olan bir hesap eklemek veya yapılandırmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Seçenekleri görüntüle"</b></font>" (görülebilir kişileri değiştirmek için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Yeni kişi"</b></font>" (en baştan yeni bir kişi oluşturmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"İçe/Dışa Aktar"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Görüntülenecek hiçbir kişiniz yok."\n\n"Kişi eklemek için, "<font fgcolor="#ffffffff"><b>"Menü"</b></font>"\'ye basın ve şunlara dokunun:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Hesaplar"</b></font>" (telefon ile senkronize edebileceğiniz kişilere sahip olan bir hesap eklemek veya yapılandırmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Yeni kişi"</b></font>" (en baştan yeni bir kişi eklemek için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"İçe/Dışa Aktar"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Görüntülenecek hiçbir kişiniz yok. (Kısa süre önce bir hesap eklediyseniz, kişileri senkronize etmek birkaç dakika sürebilir.)"\n\n"Kişi eklemek için, "<font fgcolor="#ffffffff"><b>"Menü"</b></font>"\'ye basın ve şunlara dokunun:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Hesaplar"</b></font>" (telefon ile senkronize edebileceğiniz kişilere sahip olan bir hesap eklemek veya yapılandırmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Seçenekleri görüntüle"</b></font>" (görülebilir kişileri değiştirmek için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Yeni kişi"</b></font>" (en baştan yeni bir kişi oluşturmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"İçe/Dışa Aktar"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Görüntülenecek kişiniz yok."\n\n"Kişi eklemek için "<font fgcolor="#ffffffff"><b>"Menü"</b></font>"\'ye basın ve şunlara dokunun:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Hesaplar"</b></font>" (tabletle senkronize edebileceğiniz kişilere sahip bir hesap eklemek veya yapılandırmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Yeni kişi"</b></font>" (en baştan yeni kişi oluşturmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"İçe/Dışa Aktar"</b></font>" (kişileri SIM veya SD kartınızdan içe aktarmak için)"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Görüntülenecek kişiniz yok."\n\n"Kişi eklemek için, "<font fgcolor="#ffffffff"><b>"Menü"</b></font>"\'ye basın ve şunlara dokunun:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Hesaplar"</b></font>" (telefon ile senkronize edebileceğiniz kişilere sahip bir hesap eklemek veya yapılandırmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Yeni kişi"</b></font>" (en baştan yeni bir kişi eklemek için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"İçe/Dışa Aktar"</b></font>\n</li>"(SIM veya SD kartınızdaki kişileri aktarmak için)"</string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Görüntülenecek kişiniz yok. (Kısa bir süre önce hesap eklediyseniz, kişileri senkronize etmek birkaç dakika sürebilir.)"\n\n"Kişi eklemek için, "<font fgcolor="#ffffffff"><b>"Menü"</b></font>"\'ye basın ve şunlara dokunun:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Hesaplar"</b></font>" (tabletle senkronize edebileceğiniz kişilere sahip bir hesap eklemek veya yapılandırmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Görüntüleme seçenekleri"</b></font>" (görülebilir kişileri değiştirmek için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Yeni kişi"</b></font>" (en baştan yeni bir kişi oluşturmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"İçe/Dışa Aktar"</b></font>" (SIM veya SD kartınızdaki kişileri aktarmak için)"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Görüntülenecek kişiniz yok. (Kısa bir süre önce hesap eklediyseniz, kişileri senkronize etmek birkaç dakika sürebilir.)"\n\n"Kişi eklemek için, "<font fgcolor="#ffffffff"><b>"Menü"</b></font>"\'ye basın ve şunlara dokunun:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Hesaplar"</b></font>" (telefon ile senkronize edebileceğiniz kişilere sahip olan bir hesap eklemek veya yapılandırmak için"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Seçenekleri görüntüle"</b></font>" (görülebilir kişileri değiştirmek için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Yeni kişi"</b></font>" (en baştan yeni bir kişi oluşturmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"İçe/Dışa Aktar"</b></font>" (SIM veya SD kartınızdaki kişileri aktarmak için)"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Görüntülenecek kişiniz yok."\n\n"Kişi eklemek için "<font fgcolor="#ffffffff"><b>"Menü"</b></font>"\'ye basın ve şunlara dokunun:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Hesaplar"</b></font>" (tabletle senkronize edebileceğiniz kişilere sahip bir hesap eklemek veya yapılandırmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Yeni kişi"</b></font>" (en baştan yeni kişi oluşturmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"İçe/Dışa Aktar"</b></font>" (kişileri SD kartınızdan içe aktarmak için)"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Görüntülenecek kişiniz yok."\n\n"Kişi eklemek için, "<font fgcolor="#ffffffff"><b>"Menü"</b></font>"\'ye basın ve şunlara dokunun:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Hesaplar"</b></font>" (telefon ile senkronize edebileceğiniz kişilere sahip olan bir hesap eklemek veya yapılandırmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Yeni kişi"</b></font>" (en baştan yeni bir kişi eklemek için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"İçe/Dışa Aktar"</b></font>" (kişileri SD kartınızdan içe aktarmak için)"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Görüntülenecek kişiniz yok. (Kısa süre önce bir hesap eklediyseniz kişileri senkronize etmek birkaç dakika sürebilir.)"\n\n"Kişi eklemek için, "<font fgcolor="#ffffffff"><b>"Menü"</b></font>"\'ye basın ve şunlara dokunun:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Hesaplar"</b></font>" (tabletle senkronize edebileceğiniz kişilere sahip bir hesap eklemek veya yapılandırmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Görüntüleme seçenekleri"</b></font>" (görülebilir kişileri değiştirmek için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Yeni kişi"</b></font>" (en baştan yeni bir kişi oluşturmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"İçe/Dışa Aktar"</b></font>\n</li>"(SIM veya SD kartınızdaki kişileri içe aktarmak için)"</string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Görüntülenecek kişiniz yok. (Kısa süre önce bir hesap eklediyseniz kişileri senkronize etmek birkaç dakika sürebilir.)"\n\n"Kişi eklemek için, "<font fgcolor="#ffffffff"><b>"Menü"</b></font>"\'ye basın ve şunlara dokunun:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Hesaplar"</b></font>" (telefon ile senkronize edebileceğiniz kişilere sahip bir hesap eklemek veya yapılandırmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Seçenekleri görüntüle"</b></font>" (görülebilir kişileri değiştirmek için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Yeni kişi"</b></font>" (en baştan yeni bir kişi oluşturmak için)"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"İçe/Dışa Aktar"</b></font>\n</li>"(SIM veya SD kartınızdaki kişileri içe aktarmak için)"</string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Favoriniz bulunmuyor."\n\n"Favoriler listenize kişi eklemek için:"\n\n" "<li><b>"Kişiler"</b>" sekmesine dokunun"\n</li>" "\n<li>"Favorilerinize eklemek istediğiniz kişiye dokunun"\n</li>" "\n<li>"Kişi adının yanındaki yıldız simgesine dokunun"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Tüm kişiler"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Yıldızlı"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Geri ara"</string>
<string name="callAgain" msgid="3197312117049874778">"Tekrar çağrı yap"</string>
<string name="returnCall" msgid="8171961914203617813">"Geri ara"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> dak <xliff:g id="SECONDS">%2$s</xliff:g> sn"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> dak <xliff:g id="SECONDS">%s</xliff:g> sn"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Sık iletişim kurulanlar"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Kişi ekle"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"\"<xliff:g id="EMAIL">%s</xliff:g>\" adresi kişilere eklensin mi?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Tümü"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"bir"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"iki"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"üç"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"USB depolama birimi taraması başarısız oldu (Nedeni: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"SD kart taraması başarısız oldu (Neden: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"G/Ç Hatası"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Bellek yetersiz (dosya çok büyük olabilir)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Beklenmedik bir nedenden dolayı vCard ayrıştırılamadı"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Geçerli biçimde görünmesine rağmen vCard ayrıştırılamadı, çünkü şu anki uygulama vCard\'ı desteklemiyor"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"USB depolama biriminde herhangi bir vCard dosyası bulunamadı"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"SD kartta hiçbir vCard dosyası bulunamadı"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Biçim desteklenmiyor."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"vCard içe aktarılamadı"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"USB depolama biriminde herhangi bir vCard dosyası bulunamadı"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"SD kartta hiçbir vCard dosyası bulunamadı"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Belirtilen vCard dosyalarının meta bilgileri toplanamıyor."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"İçe aktarılamayan bir veya birden fazla dosya (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Bilinmeyen hata"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"vCard dosyasını seç"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"vCard okunuyor"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"vCard dosyaları okunuyor"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"vCard verileri okunamadı"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"Toplam <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kişiden <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> kişi"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"toplam <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> dosyadan <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g>. dosya"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"vCard\'lar yerel geçici depolamada önbelleğe kaydediliyor"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"İçe aktarma aracı vCard\'ları yerel geçici depolama alanına önbelleğe kaydediyor. Asıl içe aktarma işlemi kısa süre içinde başlayacak."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"İçe aktarılıyor <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"<xliff:g id="NAME">%s</xliff:g> içe aktarılıyor"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"vCard verileri Okunamadı"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"vCard verilerini okuma işlemi iptal edildi."</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"vCard <xliff:g id="FILENAME">%s</xliff:g> dosysn içe aktarma tamamlandı"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"<xliff:g id="FILENAME">%s</xliff:g> dosyasını içe aktarma iptal edildi"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> kısa bir süre içinde içe aktarılacak."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Dosya kısa bir süre sonra içe aktarılacaktır."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"vCard\'ı içe aktarma isteği reddedildi. Lütfen daha sonra tekrar deneyin."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> kısa bir süre içinde dışa aktarılacak."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"vCard\'ı dışa aktarma isteği reddedildi. Lütfen daha sonra tekrar deneyin."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"kişi"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Dışa aktarımı onayla"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Kişi listenizi \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\" için dışa aktarmak istediğinizden emin misiniz?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Kişi verileri dışa aktarılamadı"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"USB depolama biriminde çok fazla vCard dosyası var"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"SD kartta çok fazla vCard dosyası var"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Gereken dosya adı çok uzun (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"<xliff:g id="FILENAME">%s</xliff:g> dosyasını dışa aktarma tamamlandı"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"<xliff:g id="FILENAME">%s</xliff:g> dosyasını dışa aktarma iptal edildi"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Kişi verileri dışa aktarılıyor"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Kişi verileri \"<xliff:g id="FILE_NAME">%s</xliff:g>\" dosyasına dışa aktarılıyor"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Dışa aktarıcı başlatılamadı: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Dışa aktarma sırasında hata oluştu: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Veri tabanı bilgileri alınamadı"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Dışa aktarılabilir kişi yok. Telefonunuzda aslında kişi kayıtları varsa, veri sağlayıcısı tarafından tüm kişilerin telefon dışına aktarılması yasaklanmış olabilir."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Dışa aktarılabilir kişi yok. Gerçekte tabletinizde kişi kayıtları varsa da, veri sağlayıcısı tarafından tüm kişilerin telefon dışına aktarılması yasaklanmış olabilir."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Dışa aktarılabilir kişi yok. Gerçekte telefonunuzda kişi kayıtları varsa da, veri sağlayıcısı tarafından tüm kişilerin telefon dışına aktarılması yasaklanmış olabilir."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"vCard oluşturucu doğru bir şekilde başlatılmamış"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"\"<xliff:g id="FILE_NAME">%1$s</xliff:g>\" dosyası açılamadı: <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"Toplam <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> kişiden <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> kişi"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"\"<xliff:g id="FILE_NAME">%s</xliff:g>\" dosyası açılamadı: <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"Toplam <xliff:g id="TOTAL_NUMBER">%s</xliff:g> kişiden <xliff:g id="CURRENT_NUMBER">%s</xliff:g> kişi"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"vCard içe aktarmayı iptal etme"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"<xliff:g id="FILENAME">%s</xliff:g> dosyasını içe aktarma işlemini iptal etmek istediğinizden emin misiniz?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"vCard dışa aktarmayı iptal etme"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"<xliff:g id="FILENAME">%s</xliff:g> dosyasını dışa aktarmayı iptal etmek istediğinizden emin misiniz?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"vCard içe/dışa aktarma iptal edilemedi"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Kişilerinizin adları"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"2 saniyelik duraklama ekle"</string>
<string name="add_wait" msgid="3360818652790319634">"Bekleme ekle"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Bu işlemi gerçekleştirecek bir uygulama bulunamadı"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Bu tercihi anımsa"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Bilinmiyor"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Veri yok"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Varsayılanları temizle"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Bu kişi için varsayılan ayarlar:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Temizle"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Hesaplar"</string>
<string name="menu_import_export" msgid="3765725645491577190">"İçe/Dışa Aktar"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Kişileri içe/dışa aktar"</string>
- <string name="menu_share" msgid="943789700636542260">"Paylaş"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Kişiyi paylaş"</string>
<string name="share_via" msgid="563121028023030093">"Şunu kullanarak kişi paylaş:"</string>
<string name="share_error" msgid="4374508848981697170">"Bu kişi paylaşılamıyor."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Ad"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Kuruluş"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Web sitesi"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Etkinlik"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"İlişki"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Gruplar"</string>
<string name="type_short_home" msgid="7770424864090605384">"E"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"İ"</string>
<string name="type_short_pager" msgid="2613818970827594238">"Ç"</string>
<string name="type_short_other" msgid="5669407180177236769">"D"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Bu kişi salt okunur"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Diğer"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Birincil ad"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Hesap altında kişi oluştur"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Senkronize grubu kaldır"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Senk. grup ekle"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Diğer Tüm Kişiler"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Tüm Kişiler"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"\'<xliff:g id="GROUP">%s</xliff:g>\' grubunu senkronizasyondan kaldırmak, gruplandırılmamış tüm kişileri de senkronizasyondan kaldırır."</string>
- <string name="account_phone" msgid="3682950835276226870">"Yalnızca telefon, senkronize edilmemiş"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Yalnızca tablette, senkronize değil"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Yalnızca telefon, senkronize edilmemiş"</string>
<string name="call_custom" msgid="7756571794763171802">"Ara (<xliff:g id="CUSTOM">%s</xliff:g>)"</string>
<string name="call_home" msgid="1990519474420545392">"Çağrı yap (ev)"</string>
<string name="call_mobile" msgid="7502236805487609178">"Çağrı yap (mobil)"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"ICQ kullanarak sohbet et"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Jabber kullanarak sohbet et"</string>
<string name="chat" msgid="9025361898797412245">"Sohbet"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Adres"</string>
<string name="postal_street" msgid="8133143961580058972">"Cadde"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Posta kutusu"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Mahalle"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Eyalet"</string>
<string name="postal_postcode" msgid="572136414136673751">"Alan kodu"</string>
<string name="postal_country" msgid="7638264508416368690">"Ülke"</string>
+ <string name="full_name" msgid="6602579550613988977">"Ad"</string>
<string name="name_given" msgid="1687286314106019813">"Adı"</string>
<string name="name_family" msgid="3416695586119999058">"Soyadı"</string>
<string name="name_prefix" msgid="59756378548779822">"Ad öneki"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Kişileri ara"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Tüm kişilerde ara"</string>
<string name="take_photo" msgid="7496128293167402354">"Fotoğraf çek"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Yeni fotoğraf çek"</string>
<string name="pick_photo" msgid="448886509158039462">"Galeri\'den fotoğraf seç"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Kişi listesi, dil değişikliğini yansıtmak üzere güncelleniyor."\n\n"Lütfen bekleyin..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Kişi listesi güncelleniyor."\n\n"Lütfen bekleyin..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Kişiler yeni sürüme geçiriliyor. "\n\n"Yeni sürüme geçirme işlemi yaklaşık <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>Mb dahili telefon depolama alanı gerektirir."\n\n"Aşağıdaki seçeneklerden birini belirleyin:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Galeri\'den yeni fotoğraf seç"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Kişi listesi, dil değişikliğini yansıtmak üzere güncelleniyor."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Kişi listesi güncelleniyor."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Kişilerin yeni sürüme geçirilmesi devam ediyor. "\n\n"Yeni sürüme geçirme işlemi yaklaşık <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mb dahili depolama alanı gerektirir."\n\n"Aşağıdaki seçeneklerden birini belirleyin:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Bazı uygulamaları kaldır"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Yeni sürüme geçmeyi tekrar dene"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Şunun için arama sonuçları: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Aranıyor..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"Seçileni göster"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"Tümünü göster"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"Tümünü seç"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"Tümünün seçimini kaldır"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"1 alıcı seçildi"</item>
+ <item quantity="other" msgid="4608837420986126229">"<xliff:g id="COUNT">%d</xliff:g> alıcı seçildi"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"Seçili kişi yok."</string>
+ <string name="add_field" msgid="2384260056674995230">"Başka alan ekle"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"<xliff:g id="SOURCE">%1$s</xliff:g> aracılığıyla"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="SOURCE">%2$s</xliff:g> üzerinden şu saatte: <xliff:g id="DATE">%1$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"favori"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Kişiyi düzenle"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"birleştirilmemiş"</item>
+ <item quantity="other" msgid="425683718017380845">"<xliff:g id="COUNT">%0$d</xliff:g> kaynaktan birleştirildi"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Diğer"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Kişileri birleştir"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Mevcut kişi, seçili kişiyle birleştirilsin mi?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Seçili kişileri düzenle"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Seçili kişiyi düzenlemeye geçilsin mi? Şimdiye kadar girdiğiniz bilgiler kopyalanacak."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Kişilerime kopyala"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"<xliff:g id="TYPE">%1$s</xliff:g> dizini"</string>
+ <string name="search_label" msgid="6789295859496641042">"Tüm kişilerde aranıyor"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Dizin"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Kişiler"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Kişisel kopya oluşturuluyor"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Kişi listesi seç"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Tüm kişiler"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Yıldızlı"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Özel"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Özelleştir..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Telefon numarası olan kişiler"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Kişi"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Özel görünüm tanımla"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Ayarlar"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Ayarlar"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Görüntüleme seçenekleri"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Kişileri bul"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Telefon numarası"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Kişilere ekle"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Kapat"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Yıl girin"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Kişi"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Yükleniyor …"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Yeni kişi oluştur"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Bir hesapta oturum açın"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Bir dosyadaki kişileri içe aktarma"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Yeni grup oluştur"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Yeni grup oluştur]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Grubu yeniden adlandırma"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Grubu sil"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"\'<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\' grubunu silmek istediğinizden emin misiniz? (Kişilerin kendileri silinmeyecektir.)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Başka bir kişiyle birleştirmeden önce kişi adını girin."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Birleştirilmiş kişi"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Metin kopyalandı"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Değişiklikleri sil"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Yaptığınız değişiklikleri silmek istiyor musunuz?"</string>
+ <string name="discard" msgid="1234315037371251414">"Sil"</string>
</resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index cdc9dec..5cc6575 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Вибрати ярлик контакту"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Вибір номера для виклику"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Вибір номера для надс. повід."</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Виберіть контакт"</string>
<string name="starredList" msgid="4817256136413959463">"Із зіроч."</string>
<string name="frequentList" msgid="7154768136473953056">"Найчастіші"</string>
<string name="strequentList" msgid="5640192862059373511">"Вибране"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Видалити контакт"</string>
<string name="menu_call" msgid="3992595586042260618">"Набрати конт."</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Повід. контакт"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Надісл. ел.лист"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Позн. адр. на карті"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Устан. номер за умовч."</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Зроб. ел.адр. за умовч."</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Розділити"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Контакти розділено"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Перейменувати групу"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Видалити групу"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Новий"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Розділити контакт"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Дійсно розділити цей єдиний контакт на декілька контактів: один для кожного набору доданої об\'єднаної контактної інформації?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Приєдн."</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Якщо видал. цей контакт, буде видалено інф-ю з декількох обл. записів."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Цей контакт буде видалено."</string>
<string name="menu_done" msgid="796017761764190697">"Готово"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Відмінити"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Скасувати"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Редаг. контакт"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Новий контакт"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Вимова"</string>
<string name="label_notes" msgid="8337354953278341042">"Примітки"</string>
<string name="label_sip_address" msgid="124073911714324974">"Інтернет-дзвінок"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Мелодія"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Ім\'я, прізвище"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Вимова імені"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Компанія"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Назва"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Контакт не існує."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Створ. новий контакт"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Вибрати мітку"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Телеф."</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Ел.адр."</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"Чат"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Поштова адреса"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Адреса"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Організація"</item>
<item msgid="7196592230748086755">"Прим."</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"У телефоні немає доступних зображень."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Піктогр. конт."</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"У пристрої немає доступних зображень."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"У телефоні немає доступних зображень."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Фото контакта"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Назва спец. мітки"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Парам. відображ."</string>
- <string name="displayGroups" msgid="2278964020773993336">"Парам. відображ."</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Надсилати дзвінки на голос. пошту"</string>
<string name="default_ringtone" msgid="9099988849649827972">"За умовч."</string>
- <string name="changePicture" msgid="2943329047610967714">"Змін. піктогр."</string>
- <string name="removePicture" msgid="3041230993155966350">"Видал. піктогр."</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Видалити фото"</string>
<string name="noContacts" msgid="8579310973261953559">"Немає контакт."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Не знайдено відпов. контактів."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Немає контактів з номерами тел."</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Контакт збережено"</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Помилка. Неможл. збер. зміни до контакту."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Відображ. 1 контакт з номером телефону"</item>
- <item quantity="other" msgid="6133262880804110289">"Відображ. контактів із номерами телефону: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 контакт із номером телефону"</item>
+ <item quantity="other" msgid="3299954047880968205">"Контактів із номерами тел.: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Немає видимих контактів із номерами тел."</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Немає контактів з номерами телефону"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Відображ. 1 контакт"</item>
- <item quantity="other" msgid="2865867557378939630">"Відображ. контактів: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 контакт"</item>
+ <item quantity="other" msgid="3578469907265375314">"Контактів: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Нема видимих контактів"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Немає контактів"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Немає видимих контактів"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Немає контактів із зірочкою"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Немає контактів у <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Знайдено 1 контакт"</item>
- <item quantity="other" msgid="7752927996850263152">"Знайдено <xliff:g id="COUNT">%d</xliff:g> контакт."</item>
+ <item quantity="one" msgid="5517063038754171134">"Знайдено 1"</item>
+ <item quantity="other" msgid="3852668542926965042">"Знайдено <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Контакт не знайдено"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"знайдено більше <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Не знайдено"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 контакт"</item>
- <item quantity="other" msgid="5660384247071761844">"Контактів: <xliff:g id="COUNT">%d</xliff:g>"</item>
+ <item quantity="one" msgid="4826918429708286628">"Знайдено 1"</item>
+ <item quantity="other" msgid="7988132539476575389">"Знайдено <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Контакти"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Вибране"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Контакти SIM-карти"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Немає контактів для показу. (Якщо ви щойно додали облік. запис, синхронізація контактів може зайняти кілька хвилин)."</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"У вас немає контактів для показу."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Немає конт. для відобр."\n\n"Щоб дод. контакти, натис. "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" і торкн. пунктів:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Обл. записи"</b></font>", щоб дод. або налашт. обл. запис із контакт., які можна синхроніз. з тел."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новий контакт"</b></font>", щоб створ. зовсім новий контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Імпорт/експорт"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Немає контактів для відобр. (Якщо ви щойно дод. обл. запис, синхроніз. контактів займе кілька хв.)."\n\n"Щоб додати контакти, натис. "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" і торкн. пунктів:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Обл. записи"</b></font>", щоб дод. або налашт. обл. запис із контакт., які можна синхроніз. з тел."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Параметри відображ."</b></font>", щоб змін. парам. видимості контактів"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новий контакт"</b></font>", щоб створ. зовсім новий контакт "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Іморт/Експорт"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Немає конт. для відобр."\n\n"Щоб дод. контакти, натис. "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" і торкн. пунктів:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Обл. записи"</b></font>", щоб дод. або налашт. обл. запис із контакт., які можна синхроніз. з тел."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новий контакт"</b></font>", щоб створ. зовсім новий контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Імпорт/експорт"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Немає контактів для відобр. (Якщо ви щойно дод. обл. запис, синхроніз. контактів займе кілька хв.)."\n\n"Щоб додати контакти, натис. "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" і торкн. пунктів:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Обл. записи"</b></font>", щоб дод. або налашт. обл. запис із контакт., які можна синхроніз. з тел."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Параметри відображ."</b></font>", щоб змін. парам. видимості контактів"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новий контакт"</b></font>", щоб створ. зовсім новий контакт "\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Іморт/Експорт"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Немає контактів для відобр."\n\n"Щоб додати конт., натис. "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" й торкн. опцій:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Обл. записи"</b></font>", щоб додати чи налашт. обл. запис із контакт., які можна синхроніз. з пристр."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новий контакт"</b></font>", щоб створ. зовсім новий конт."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Імпорт/експорт"</b></font>", щоб імпорт. конт. із SIM-карти чи карти SD"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Немає контактів для відобр."\n\n"Щоб додати конт., натис. "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" й торкн. опцій:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Обл. записи"</b></font>", щоб додати чи налашт. обл. запис із контакт., які можна синхроніз. з тел."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новий контакт"</b></font>", щоб створ. зовсім новий контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Імпорт/експорт"</b></font>", щоб імпорт. конт. із SIM-карти чи карти SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Немає контактів для відобр. (Якщо ви щойно дод. обл. запис, синхроніз. контактів займе кілька хв.)."\n\n"Щоб дод. конт., натис. "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" й торкн. опцій:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Обл. записи"</b></font>", щоб додати чи налашт. обл. запис із контакт., які можна синхроніз. з пристр."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Параметри відображ."</b></font>", щоб змінити парам. видимості контактів"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новий контакт"</b></font>", щоб створ. зовсім новий конт."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Імпорт/експорт"</b></font>", щоб імпорт. конт. із SIM-карти чи карти SD"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Немає контактів для відобр. (Якщо ви щойно дод. обл. запис, синхроніз. контактів займе кілька хв.)."\n\n"Щоб додати конт., натис."<font fgcolor="#ffffffff"><b>"Меню"</b></font>" й торкн. опцій:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Обл. записи"</b></font>", щоб додати чи налашт. обл. запис із контакт., які можна синхроніз. з тел."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Параметри відображ."</b></font>", щоб змін. парам. видимості контактів"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новий контакт"</b></font>", щоб створ. зовсім новий контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Імпорт/експорт"</b></font>", щоб імпорт. конт. із SIM-карти чи карти SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Немає контактів для відобр."\n\n"Щоб додати контакти, натис. "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" й торкн. опцій: "\n" "\n<li><font fgcolor="#ffffffff"><b>"Обл. записи"</b></font>", щоб додати чи налашт. обл. запис із контакт., які можна синхроніз. з пристр."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новий контакт"</b></font>", щоб створ. зовсім новий контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Імпорт/експорт"</b></font>", щоб імпорт. контакти з карти SD"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Немає контактів для відобр."\n\n"Щоб додати контакти, натис. "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" й торкн. опцій:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Обл. записи"</b></font>", щоб додати чи налашт. обл. запис із контакт., які можна синхроніз. з тел."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новий контакт"</b></font>", щоб створити зовсім новий контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Імпорт/експорт"</b></font>", щоб імпорт. контакти з карти SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Немає контактів для відобр. (Якщо ви щойно дод. обл. запис, синхроніз. контактів займе кілька хв.)."\n\n"Щоб додати конт., натис."<font fgcolor="#ffffffff"><b>"Меню"</b></font>" й торкн. опцій:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Обл. записи"</b></font>", щоб додати чи налашт. обл. запис із контакт., які можна синхроніз. з пристроєм"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Параметри відображ."</b></font>", щоб змінити парам. видимості контактів"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новий контакт"</b></font>", щоб створ. зовсім новий контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Імпорт/експорт"</b></font>", щоб імпорт. контакти з карти SD"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Немає контактів для відобр. (Якщо ви щойно дод. обл. запис, синхроніз. контактів займе кілька хв.)."\n\n"Щоб додати контакти, натис. "<font fgcolor="#ffffffff"><b>"Меню"</b></font>" й торкн. опцій:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Обл. записи"</b></font>", щоб додати чи налашт. обл. запис із контакт., які можна синхроніз. з тел."\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Параметри відображ."</b></font>", щоб змінити парам. видимості контактів"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Новий контакт"</b></font>", щоб створ. зовсім новий контакт"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Імпорт/експорт"</b></font>", щоб імпорт. контакти з карти SD"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"У вас немає вибраного."\n\n"Щоб додати контакт до списку вибраних:"\n\n" "<li>"Торкніться вкладки "<b>"Контакти"</b>\n</li>" "\n<li>"Торкніться контакту, який хочете додати до вибраного"\n</li>" "\n<li>"Торкніться зірочки поруч з іменем контакту"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Усі контакти"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Із зіроч."</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Передзвонити"</string>
<string name="callAgain" msgid="3197312117049874778">"Набрати знову"</string>
<string name="returnCall" msgid="8171961914203617813">"Зворот. виклик"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> хв. <xliff:g id="SECONDS">%2$s</xliff:g> сек."</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> хв. <xliff:g id="SECONDS">%s</xliff:g> сек."</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Часті контакти"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Додати контакт"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Дод.\" <xliff:g id="EMAIL">%s</xliff:g>\" до контактів?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Усе"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"один"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"два"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"три"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Помилка сканування носія USB (причина: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Помилка сканув. SD карти (Причина: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Помилка I/O"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Недостатньо пам\'яті (можливо, файл завеликий)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Не вдал. проаналіз. vCard через неочік. прич."</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Не вдалося проаналізувати vCard, хоч вона має дійсний формат, оскільки поточне застосування не підтримує її"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"На носії USB не знайдено файлів vCard"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"На карті SD не знайдено файлів vCard"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Формат не підтримується."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Помилка імпорту vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"На носії USB не знайдено файлів vCard"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"На карті SD не знайдено файлів vCard"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Помилка отримання мета-інформації файлів даної vCard."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Не вдалося імпорт. один чи кілька файлів (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Невідома помилка"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Вибрати файл vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Читання vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Читання файлів vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Не вдалося прочитати дані vCard"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> із <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> контактів"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> з <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> файл."</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Кешування vCard до локальної тимчас. пам\'яті"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Імпортер кешує vCard до локальної тимчасової пам\'яті. Незабаром почнеться власне імпорт."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Імпорт <xliff:g id="CURRENT_NUMBER">%s</xliff:g> з <xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Імпорт <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Помилка читання даних vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Читання даних vCard скасовано"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Імпорт файлу <xliff:g id="FILENAME">%s</xliff:g> vCard завершено"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Імпорт файлу <xliff:g id="FILENAME">%s</xliff:g> скасовано"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"Файл <xliff:g id="FILENAME">%s</xliff:g> незабаром буде імпортовано."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Незабаром файл буде імпортовано."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Запит на імпорт vCard відхилено. Спробуйте пізніше."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"Файл <xliff:g id="FILENAME">%s</xliff:g> незабаром буде експортовано."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Запит на експорт vCard відхилено. Спробуйте пізніше."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"контакт"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Підтверд. експорт"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Дійсно експортувати список контактів до \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\"?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Не вдал. експорт. контактні дані"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Забагато файлів vCard на носії USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"На карті SD забагато файлів vCard"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Потрібна назва файлу задовга (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Експорт файлу <xliff:g id="FILENAME">%s</xliff:g> завершено"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Експорт файлу <xliff:g id="FILENAME">%s</xliff:g> скасовано"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Експорт. контактні дані"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Експорт. контактні дані до \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Не вдалося ініціаліз-ти експортер: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Під час експорту стал. помилка: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Не вдалося отримати інфо з бази даних"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Немає доступу для експорту контактів. Якщо у вас дійсно є контакти на телефоні, можливо, якийсь постачальник даних заборонив експортування всіх контактів на зовнішні пристрої."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Немає контактів, які можна експортувати. Якщо у вашому пристрої дійсно є контакти, можливо, якийсь постачальник даних заборонив експорт усіх контактів на зовнішні пристрої."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Немає контактів, які можна експортувати. Якщо у вашому телефоні дійсно є контакти, можливо, якийсь постачальник даних заборонив експорт усіх контактів на зовнішні пристрої."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Майстер vCard не ініціалізовано належним чином"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Неможл. відкрити \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> із <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> контактів"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Неможл. відкрити \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> із <xliff:g id="TOTAL_NUMBER">%s</xliff:g> контактів"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Скасування імпорту vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Дійсно скасувати імпорт файлу <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Скасування експорту vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Дійсно скасувати експорт <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Помилка скасув. імпорту/експорту vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Імена ваших контактів"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Дод. 2-сек. паузу"</string>
<string name="add_wait" msgid="3360818652790319634">"Дод. очікув."</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Не знайдено програму для обробки цієї дії"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Пам\'ятати цей вибір"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Невідомий"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Нема даних"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Очистити налаштування за умовчанням"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Налашт-ня за умовч. для цього контакта:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Очистити"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Обл. записи"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Імпорт/експорт"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Імпорт/експорт контактів"</string>
- <string name="menu_share" msgid="943789700636542260">"Надісл."</string>
+ <string name="menu_share" msgid="8746849630474240344">"Надіслати контакт"</string>
<string name="share_via" msgid="563121028023030093">"Надісл. контакт через"</string>
<string name="share_error" msgid="4374508848981697170">"Цей контакт неможл. надіслати."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Ім\'я"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Організація"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Веб-сайт"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Подія"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Стосунки"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Групи"</string>
<string name="type_short_home" msgid="7770424864090605384">"Д"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"М"</string>
<string name="type_short_work" msgid="4925330752504537861">"Р"</string>
<string name="type_short_pager" msgid="2613818970827594238">"П"</string>
<string name="type_short_other" msgid="5669407180177236769">"І"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Цей контакт лише для читання"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Більше"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Основне ім\'я"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Створ. контакт в обл. записі"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Видал. синхр. групу"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Дод. синхр. групу"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Усі інші контакти"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Усі контакти"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Видал-ня &quot;<xliff:g id="GROUP">%s</xliff:g>&quot; із синхрон-ції видалить контакти поза групою із синхрон-ції."</string>
- <string name="account_phone" msgid="3682950835276226870">"Лише в тел., несинхр."</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Лише в пристр., несинхр."</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Лише в тел., несинхр."</string>
<string name="call_custom" msgid="7756571794763171802">"Набрати <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Набрати дом. ном."</string>
<string name="call_mobile" msgid="7502236805487609178">"Набрати моб."</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Чат через ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Чат через Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Чат"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Адреса"</string>
<string name="postal_street" msgid="8133143961580058972">"Вулиця"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Пошт. скр."</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"У районі"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Область"</string>
<string name="postal_postcode" msgid="572136414136673751">"Пошт. код"</string>
<string name="postal_country" msgid="7638264508416368690">"Країна"</string>
+ <string name="full_name" msgid="6602579550613988977">"Ім\'я"</string>
<string name="name_given" msgid="1687286314106019813">"Ім\'я"</string>
<string name="name_family" msgid="3416695586119999058">"Прізвище"</string>
<string name="name_prefix" msgid="59756378548779822">"Префікс імені"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Пошук контактів"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Пошук усіх контактів"</string>
<string name="take_photo" msgid="7496128293167402354">"Зробити фото"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Зробити нове фото"</string>
<string name="pick_photo" msgid="448886509158039462">"Вибрати фото з Галереї"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Список контактів оновлено для відображення зміни мови."\n\n"Зачекайте..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Список контактів оновлюється."\n\n"Зачекайте..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Контакти проходять процес оновлення. "\n\n"Для процесу оновлення треба приблизно <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>Мб внутрішньої пам\'яті телефону."\n\n"Виберіть один із поданих нижче параметрів."</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Вибрати нове фото з Галереї"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Список контактів оновлено для відображення зміни мови."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Список контактів оновлюється."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Контакти перебувають у процесі оновлення. "\n\n"Для процесу оновлення потрібно приблизно <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Мб внутрішньої пам\'яті."\n\n"Виберіть один із поданих нижче параметрів."</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Видалити деякі програми"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Повтор. оновл."</string>
- <string name="search_results_for" msgid="8705490885073188513">"Результ. пошуку для: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Пошук..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Показати вибране"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Показати все"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Вибрати все"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Відмінити все"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Контакти не вибрано."</string>
+ <string name="add_field" msgid="2384260056674995230">"Додати ще одне поле"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"через <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> через <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"вибране"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Редаг. контакта"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"не об\'єднано"</item>
+ <item quantity="other" msgid="425683718017380845">"об\'єднано з джерел: <xliff:g id="COUNT">%0$d</xliff:g>"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Інші"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Об\'єднати контакти"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Об\'єднати поточний контакт із вибраним контактом?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Редагувати вибрані контакти"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Перейти до редагування вибраного контакта? Введену досі інформацію буде скопійовано."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Копіювати до моїх контактів"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Каталог <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Пошук усіх контактів"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Каталог"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Контакти"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Створення особистої копії"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Виберіть список контактів"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Усі контакти"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Із зірочкою"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Спеціальні"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Налаштувати..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Контакти з номерами телефону"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Контакт"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Налаштувати параметри перегляду"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Налаштування"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Налаштування"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Параметри відображення"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Пошук контактів"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Номер телефону"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Додати до контактів"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Закрити"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Указати рік"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Контакт"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Завантаження…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Створити новий контакт"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Увійти в обліковий запис"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Імпортувати контакти з файлу"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Створити нову групу"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Створити нову групу]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Перейменувати групу"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Видалити групу"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Дійсно видалити групу \"<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\"? (Самі контакти не буде видалено)."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Введіть ім\'я контакта перед тим, як об\'єднати з іншим контактом."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Об\'єднаний контакт"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Текст скопійовано"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Відхилити зміни"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Дійсно відхилити ці зміни?"</string>
+ <string name="discard" msgid="1234315037371251414">"Відхилити"</string>
</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index a3424d3..32002de 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"Chọn lối tắt cho liên hệ"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"Chọn một số để gọi"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"Chọn một số để gửi tin nhắn"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"Chọn địa chỉ liên hệ"</string>
<string name="starredList" msgid="4817256136413959463">"Được gắn dấu sao"</string>
<string name="frequentList" msgid="7154768136473953056">"Thường xuyên"</string>
<string name="strequentList" msgid="5640192862059373511">"Mục ưa thích"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"Xoá liên hệ"</string>
<string name="menu_call" msgid="3992595586042260618">"Gọi liên hệ"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"Nhắn tin tới liên hệ"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"Gửi email"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"Địa chỉ trên bản đồ"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"Đặt làm số mặc định"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"Đặt làm email mặc định"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"Tách"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"Danh bạ đã được tách"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"Đổi tên nhóm"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"Xóa nhóm"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"Mới"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"Tách Liên hệ"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"Bạn có chắc chắn muốn tách liên hệ duy nhất này thành nhiều liên hệ: một liên hệ cho mỗi tập thông tin liên hệ đã được nhập vào liên hệ không?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"Kết hợp"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"Xoá liên hệ này sẽ xoá thông tin khỏi nhiều tài khoản."</string>
<string name="deleteConfirmation" msgid="811706994761610640">"Liên hệ này sẽ bị xoá."</string>
<string name="menu_done" msgid="796017761764190697">"Xong"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"Hoàn nguyên"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"Hủy"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"Chỉnh sửa liên hệ"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"Liên hệ mới"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"Đúng phát âm"</string>
<string name="label_notes" msgid="8337354953278341042">"Ghi chú"</string>
<string name="label_sip_address" msgid="124073911714324974">"Cuộc gọi qua internet"</string>
<string name="label_ringtone" msgid="8833166825330686244">"Nhạc chuông"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"Đầu tiên và Cuối cùng"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"Tên đúng phát âm"</string>
<string name="ghostData_company" msgid="5414421120553765775">"Công ty"</string>
<string name="ghostData_title" msgid="7496735200318496110">"Tiêu đề"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"Liên hệ không tồn tại."</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"Tạo liên hệ mới"</string>
- <string name="selectLabel" msgid="4255424123394910733">"Chọn nhãn"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"Điện thoại"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"Email"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"IM"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"Địa chỉ bưu điện"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"Địa chỉ"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"Tổ chức"</item>
<item msgid="7196592230748086755">"Ghi chú"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"Không có ảnh nào trong điện thoại."</string>
- <string name="attachToContact" msgid="8820530304406066714">"Biểu tượng của liên hệ"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"Không có ảnh nào trong máy tính bảng."</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"Không có ảnh nào trong điện thoại."</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"Ảnh của liên hệ"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"Tên nhãn tuỳ chỉnh"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"Tuỳ chọn hiển thị"</string>
- <string name="displayGroups" msgid="2278964020773993336">"Tuỳ chọn hiển thị"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"Gửi cuộc gọi trực tiếp tới thư thoại"</string>
<string name="default_ringtone" msgid="9099988849649827972">"Mặc định"</string>
- <string name="changePicture" msgid="2943329047610967714">"Thay đổi biểu tượng"</string>
- <string name="removePicture" msgid="3041230993155966350">"Xoá biểu tượng"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"Xóa ảnh"</string>
<string name="noContacts" msgid="8579310973261953559">"Không có liên hệ nào."</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"Không tìm thấy liên hệ nào phù hợp."</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"Không có liên hệ nào có số điện thoại"</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"Đã lưu liên hệ."</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"Lỗi, không thể lưu thay đổi liên hệ."</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"Hiển thị 1 liên hệ có số điện thoại"</item>
- <item quantity="other" msgid="6133262880804110289">"Hiển thị <xliff:g id="COUNT">%d</xliff:g> liên hệ có số điện thoại"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 liên hệ có số điện thoại"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> liên hệ có số điện thoại"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"Không có liên hệ hiển thị nào có số điện thoại"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"Không có liên hệ nào có số điện thoại"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"Hiển thị 1 liên hệ"</item>
- <item quantity="other" msgid="2865867557378939630">"Hiển thị <xliff:g id="COUNT">%d</xliff:g> liên hệ"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 địa chỉ liên hệ"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> địa chỉ liên hệ"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"Không có liên hệ hiển thị nào"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"Không có liên hệ nào"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"Không có liên hệ hiển thị nào"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"Không liên hệ nào được gắn dấu sao"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"Không có liên hệ nào trong <xliff:g id="NAME">%s</xliff:g>"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"Đã tìm thấy 1 liên hệ"</item>
- <item quantity="other" msgid="7752927996850263152">"Đã tìm thấy <xliff:g id="COUNT">%d</xliff:g> liên hệ"</item>
+ <item quantity="one" msgid="5517063038754171134">"Đã tìm thấy 1"</item>
+ <item quantity="other" msgid="3852668542926965042">"Đã tìm thấy <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"Không tìm thấy liên hệ"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"đã tìm thấy hơn <xliff:g id="COUNT">%d</xliff:g>"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"Không tìm thấy"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 liên hệ"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> liên hệ"</item>
+ <item quantity="one" msgid="4826918429708286628">"Đã tìm thấy 1"</item>
+ <item quantity="other" msgid="7988132539476575389">"Đã tìm thấy <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"Danh bạ"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"Mục ưa thích"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"Danh bạ trên thẻ SIM"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"Bạn không có liên hệ nào để hiển thị. (Nếu bạn vừa thêm tài khoản, bạn có thể mất vài phút để đồng bộ hoá danh bạ)."</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"Bạn không có liên hệ nào để hiển thị."</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"Bạn không có liên hệ nào để hiển thị."\n\n"Để thêm liên hệ, nhấn "<font fgcolor="#ffffffff"><b>"Trình đơn"</b></font>" và chạm:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tài khoản"</b></font>" để thêm hoặc định cấu hình tài khoản có danh bạ bạn có thể đồng bộ hoá với điện thoại"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Liên hệ mới"</b></font>" để tạo liên hệ mới từ đầu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nhập/Xuất"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"Bạn không có liên hệ nào để hiển thị. (Nếu bạn vừa thêm tài khoản, bạn có thể mất vài phút để đồng bộ hoá danh bạ)."\n\n"Để thêm liên hệ, nhấn "<font fgcolor="#ffffffff"><b>"Trình đơn"</b></font>" và chạm:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tài khoản"</b></font>" để thêm hoặc định cấu hình tài khoản có danh bạ bạn có thể đồng bộ hoá với điện thoại"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuỳ chọn hiển thị"</b></font>" để thay đổi liên hệ nào có thể hiển thị"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Liên hệ mới"</b></font>" để tạo liên hệ mới từ đầu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nhập/Xuất"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"Bạn không có liên hệ nào để hiển thị."\n\n"Để thêm liên hệ, nhấn "<font fgcolor="#ffffffff"><b>"Trình đơn"</b></font>" và chạm:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tài khoản"</b></font>" để thêm hoặc định cấu hình tài khoản có danh bạ bạn có thể đồng bộ hoá với điện thoại"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Liên hệ mới"</b></font>" để tạo liên hệ mới từ đầu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nhập/Xuất"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"Bạn không có liên hệ nào để hiển thị. (Nếu bạn vừa thêm tài khoản, bạn có thể mất vài phút để đồng bộ hoá danh bạ)."\n\n"Để thêm liên hệ, nhấn "<font fgcolor="#ffffffff"><b>"Trình đơn"</b></font>" và chạm:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tài khoản"</b></font>" để thêm hoặc định cấu hình tài khoản có danh bạ bạn có thể đồng hoá với điện thoại"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tuỳ chọn hiển thị"</b></font>" để thay đổi liên hệ nào có thể hiển thị"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Liên hệ mới"</b></font>" để tạo liên hệ mới từ đầu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nhập/Xuất"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"Bạn chưa có địa chỉ liên hệ nào để hiển thị."\n\n"Để thêm địa chỉ liên hệ, nhấn "<font fgcolor="#ffffffff"><b>"Trình đơn"</b></font>" và chạm:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tài khoản"</b></font>" để thêm hoặc cấu hình tài khoản có địa chỉ liên hệ bạn có thể đồng bộ hóa với máy tính bảng"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Địa chỉ liên hệ mới"</b></font>" để tạo địa chỉ liên hệ mới từ đầu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nhập/Xuất"</b></font>" để nhập địa chỉ liên hệ từ SIM hoặc thẻ SD của bạn"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"Bạn không có địa chỉ liên hệ nào để hiển thị."\n\n"Để thêm địa chỉ liên hệ, nhấn "<font fgcolor="#ffffffff"><b>"Trình đơn"</b></font>" và chạm:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tài khoản"</b></font>" để thêm hoặc cấu hình tài khoản có địa chỉ liên hệ bạn có thể đồng bộ hóa với điện thoại"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Địa chỉ liên hệ mới"</b></font>" để tạo địa chỉ liên hệ mới từ đầu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nhập/Xuất"</b></font>" để nhập danh bạ từ SIM hoặc thẻ SD của bạn"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"Bạn chưa có địa chỉ liên hệ nào để hiển thị. (Nếu bạn vừa thêm tài khoản, có thể mất vài phút để đồng bộ hóa địa chỉ liên hệ.)"\n\n"Để thêm địa chỉ liên hệ, nhấn "<font fgcolor="#ffffffff"><b>"Trình đơn"</b></font>" và chạm:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tài khoản"</b></font>" để thêm hoặc cấu hình tài khoản có địa chỉ liên hệ bạn có thể đồng bộ hóa với máy tính bảng"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tùy chọn hiển thị"</b></font>" để thay đổi địa chỉ liên hệ nào được hiển thị"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Địa chỉ liên hệ mới"</b></font>" để tạo địa chỉ liên hệ mới từ đầu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nhập/Xuất"</b></font>" để nhập địa chỉ liên hệ từ SIM hoặc thẻ SD của bạn"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"Bạn chưa có địa chỉ liên hệ nào để hiển thị. (Nếu bạn vừa thêm tài khoản, có thể mất vài phút để đồng bộ hóa địa chỉ liên hệ.)"\n\n"Để thêm địa chỉ liên hệ, nhấn "<font fgcolor="#ffffffff"><b>"Trình đơn"</b></font>" và chạm:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tài khoản"</b></font>" để thêm hoặc cấu hình tài khoản có địa chỉ liên hệ bạn có thể đồng bộ hóa với điện thoại"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tùy chọn hiển thị"</b></font>" để thay đổi địa chỉ liên hệ nào được hiển thị"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Địa chỉ liên hệ mới"</b></font>" để tạo địa chỉ liên hệ mới từ đầu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nhập/Xuất"</b></font>" để nhập địa chỉ liên hệ từ SIM hoặc thẻ SD của bạn"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"Bạn chưa có địa chỉ liên hệ nào để hiển thị."\n\n"Để thêm địa chỉ liên hệ, nhấn "<font fgcolor="#ffffffff"><b>"Trình đơn"</b></font>" và chạm:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tài khoản"</b></font>" để thêm hoặc cấu hình tài khoản có địa chỉ liên hệ bạn có thể đồng bộ hóa với máy tính bảng"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Địa chỉ liên hệ mới"</b></font>" để tạo địa chỉ liên hệ mới từ đầu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nhập/Xuất"</b></font>" để nhập địa chỉ liên hệ từ thẻ SD của bạn"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"Bạn chưa có địa chỉ liên hệ nào để hiển thị."\n\n"Để thêm địa chỉ liên hệ, nhấn "<font fgcolor="#ffffffff"><b>"Trình đơn"</b></font>" và chạm:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tài khoản"</b></font>" để thêm hoặc cấu hình tài khoản có địa chỉ liên hệ bạn có thể đồng bộ hóa với điện thoại"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Địa chỉ liên hệ mới"</b></font>" để tạo địa chỉ liên hệ mới từ đầu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nhập/Xuất"</b></font>" để nhập địa chỉ liên hệ từ thẻ SD của bạn"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"Bạn chưa có địa chỉ liên hệ nào để hiển thị. (Nếu bạn đã thêm tài khoản, có thể mất vài phút để đồng bộ hóa địa chỉ liên hệ.)"\n\n"Để thêm địa chỉ liên hệ, nhấn "<font fgcolor="#ffffffff"><b>"Trình đơn"</b></font>" và chạm:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tài khoản"</b></font>" để thêm hoặc cấu hình tài khoản có địa chỉ liên hệ bạn có thể đồng bộ hóa với máy tính bảng"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tùy chọn hiển thị"</b></font>" để thay đổi tài khoản nào được hiển thị"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Địa chỉ liên hệ mới"</b></font>" để tạo địa chỉ liên hệ mới từ đầu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nhập/Xuất"</b></font>" để nhập địa chỉ liên hệ từ thẻ SD của bạn"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"Bạn chưa có địa chỉ liên hệ nào để hiển thị. (Nếu bạn vừa thêm tài khoản, có thể mất vài phút để đồng bộ hóa địa chỉ liên hệ.)"\n\n"Để thêm địa chỉ liên hệ, nhấn "<font fgcolor="#ffffffff"><b>"Trình đơn"</b></font>" và chạm:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Tài khoản"</b></font>" để thêm hoặc cấu hình tài khoản có địa chỉ liên hệ bạn có thể đồng bộ hóa với điện thoại"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Tùy chọn hiển thị"</b></font>" để thay đổi địa chỉ liên hệ nào được hiển thị"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Địa chỉ liên hệ mới"</b></font>" để tạo địa chỉ liên hệ mới từ đầu"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"Nhập/Xuất"</b></font>" để nhập địa chỉ liên hệ từ thẻ SD của bạn"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"Bạn không có mục ưa thích nào."\n\n"Để thêm liên hệ vào danh sách mục ưa thích của bạn:"\n\n" "<li>"Chạm vào tab "<b>"Danh bạ"</b>\n</li>" "\n<li>"Chạm vào liên hệ bạn muốn thêm vào mục ưa thích của mình"\n</li>" "\n<li>"Chạm vào dấu sao bên cạnh tên của liên hệ"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"Tất cả liên hệ"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"Được gắn dấu sao"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"Gọi lại"</string>
<string name="callAgain" msgid="3197312117049874778">"Gọi lại"</string>
<string name="returnCall" msgid="8171961914203617813">"Gọi lại"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> phút <xliff:g id="SECONDS">%2$s</xliff:g> giây"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> phút <xliff:g id="SECONDS">%s</xliff:g> giây"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"Thường xuyên được liên hệ"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"Thêm liên hệ"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"Thêm \"<xliff:g id="EMAIL">%s</xliff:g>\" vào danh bạ?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"Tất cả"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"một"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"hai"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"ba"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"Quét bộ nhớ USB không thành công (Lý do: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"Quét thẻ SD không thành công (Lý do: \"<xliff:g id="FAIL_REASON">%s</xliff:g>\")"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"Lỗi I/O"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"Bộ nhớ không đủ (tệp có thể quá lớn)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"Không thể phân tích cú pháp vCard vì lý do không mong muốn"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"Không thể phân tích cú pháp vCard mặc dù thẻ có vẻ có định dạng hợp lệ vì quá trình triển khai hiện tại không hỗ trợ"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"Không tìm thấy tệp vCard nào trong bộ nhớ USB"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"Không tìm thấy tệp vCard nào trên thẻ SD"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"Định dạng không được hỗ trợ."</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"Không thể nhập vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"Không tìm thấy tệp vCard trong bộ lưu trữ USB"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"Không tìm thấy tệp vCard trên thẻ SD"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"Không thể thu thập siêu thông tin của (các) tệp vCard cho trước."</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"Không thể nhập một hoặc nhiều tệp (%s)."</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"Lỗi không xác định"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"Chọn tệp vCard"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"Đang đọc vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"Đang đọc tệp vCard"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"Đọc dữ liệu vCard không thành công"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> trong tổng số <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> liên hệ"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> trong tổng số <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> tệp"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"Đang chuyển vCard trong bộ nhớ cache vào bộ nhớ cục bộ tạm thời"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"Trình nhập đang chuyển vCard trong bộ nhớ cache vào bộ nhớ cục bộ tạm thời. Thao tác nhập thực sự sẽ bắt đầu sớm."</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"Đang nhập <xliff:g id="CURRENT_NUMBER">%s</xliff:g>/<xliff:g id="TOTAL_NUMBER">%s</xliff:g>: <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"Đang nhập <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"Không thể Đọc dữ liệu vCard"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"Thao tác đọc dữ liệu vCard bị hủy"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"Đã nhập xong vCard <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"Quá trình nhập <xliff:g id="FILENAME">%s</xliff:g> bị hủy"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> sẽ sớm được nhập."</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"Tệp sẽ sớm được nhập."</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"Yêu cầu nhập vCard bị từ chối. Vui lòng thử lại sau."</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> sẽ sớm được xuất."</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"Yêu cầu xuất vCard bị từ chối. Vui lòng thử lại sau."</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"địa chỉ liên hệ"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"Xác nhận xuất"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"Bạn có chắc chắn muốn xuất danh sách liên hệ của mình sang \"<xliff:g id="VCARD_FILENAME">%s</xliff:g>\" không?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"Không thể xuất dữ liệu liên hệ"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"Quá nhiều tệp vCard trong bộ nhớ USB"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"Quá nhiều tệp vCard trên thẻ SD"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"Tên tệp yêu cầu quá dài (\"<xliff:g id="FILENAME">%s</xliff:g>\")"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"Đã xuất xong <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"Quá trình xuất <xliff:g id="FILENAME">%s</xliff:g> bị hủy"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"Xuất dữ liệu liên hệ"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"Xuất dữ liệu liên hệ sang \"<xliff:g id="FILE_NAME">%s</xliff:g>\""</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"Không thể khởi chạy trình xuất: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"Đã xảy ra lỗi khi xuất: \"<xliff:g id="EXACT_REASON">%s</xliff:g>\""</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"Không thể tải xuống thông tin cơ sở dữ liệu"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"Không có liên hệ nào có thể xuất. Nếu thực sự bạn có liên hệ trên điện thoại của mình, thì tất cả liên hệ đó có thể đã bị một số nhà cung cấp dữ liệu chặn xuất ra bên ngoài điện thoại."</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"Không có liên hệ nào có thể xuất. Nếu thực sự bạn có liên hệ trên máy tính bảng của mình, thì tất cả liên hệ đó có thể đã bị một số nhà cung cấp dữ liệu chặn xuất ra bên ngoài máy tính bảng."</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"Không có địa chỉ liên hệ có thể xuất nào. Nếu bạn thực sự có địa chỉ liên hệ trong điện thoại, tất cả địa chỉ liên hệ có thể bị cấm xuất ra ngoài điện thoại do một số nhà cung cấp dữ liệu."</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"Trình soạn vCard không được khởi chạy đúng cách"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Không thể mở \"<xliff:g id="FILE_NAME">%1$s</xliff:g>\": <xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> trong tổng số <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> liên hệ"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"Không thể mở \"<xliff:g id="FILE_NAME">%s</xliff:g>\": <xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"<xliff:g id="CURRENT_NUMBER">%s</xliff:g> trong tổng số <xliff:g id="TOTAL_NUMBER">%s</xliff:g> liên hệ"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"Hủy nhập vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"Bạn có chắc chắn muốn hủy nhập <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"Hủy xuất vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"Bạn có chắc chắn muốn hủy xuất <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"Không thể hủy nhập/xuất vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"Tên danh bạ của bạn"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"Thêm 2 giây dừng"</string>
<string name="add_wait" msgid="3360818652790319634">"Thêm chờ"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"Không tìm thấy ứng dụng nào để xử lý tác vụ này"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"Nhớ lựa chọn này"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"Không xác định"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"Không có dữ liệu"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"Xóa mặc định"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"Mặc định được đặt cho địa chỉ liên hệ này:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"Xóa"</string>
<string name="menu_accounts" msgid="8499114602017077970">"Tài khoản"</string>
<string name="menu_import_export" msgid="3765725645491577190">"Nhập/Xuất"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"Nhập/Xuất danh bạ"</string>
- <string name="menu_share" msgid="943789700636542260">"Chia sẻ"</string>
+ <string name="menu_share" msgid="8746849630474240344">"Chia sẻ địa chỉ liên hệ"</string>
<string name="share_via" msgid="563121028023030093">"Chia sẻ liên hệ qua"</string>
<string name="share_error" msgid="4374508848981697170">"Không thể chia sẻ liên hệ này."</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"Tên"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"Tổ chức"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"Trang web"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"Sự kiện"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"Mối quan hệ"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"Nhóm"</string>
<string name="type_short_home" msgid="7770424864090605384">"Nhà riêng"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"M"</string>
<string name="type_short_work" msgid="4925330752504537861">"W"</string>
<string name="type_short_pager" msgid="2613818970827594238">"P"</string>
<string name="type_short_other" msgid="5669407180177236769">"O"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"Liên hệ này ở chế độ chỉ đọc"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"Khác"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"Tên chính"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"Tạo liên hệ trong tài khoản"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"Xoá nhóm đồng bộ hoá"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"Thêm nhóm đồng bộ hoá"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"Tất cả Liên hệ Khác"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"Tất cả Liên hệ"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"Xoá \'<xliff:g id="GROUP">%s</xliff:g>\' khỏi đồng bộ hoá cũng sẽ xoá bất kỳ liên hệ nào đã được tách nhóm khỏi đồng bộ hoá."</string>
- <string name="account_phone" msgid="3682950835276226870">"Chỉ trên điện thoại, chưa đồng bộ hóa"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"Chỉ trên máy tính bảng, chưa được đồng bộ hóa"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"Chỉ trên điện thoại, chưa đồng bộ hóa"</string>
<string name="call_custom" msgid="7756571794763171802">"Gọi <xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"Gọi số điện thoại nhà riêng"</string>
<string name="call_mobile" msgid="7502236805487609178">"Gọi số điện thoại di động"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"Trò chuyện sử dụng ICQ"</string>
<string name="chat_jabber" msgid="7561444230307829609">"Trò chuyện sử dụng Jabber"</string>
<string name="chat" msgid="9025361898797412245">"Trò chuyện"</string>
+ <string name="postal_address" msgid="8765560217149624536">"Địa chỉ"</string>
<string name="postal_street" msgid="8133143961580058972">"Đường phố"</string>
<string name="postal_pobox" msgid="4431938829180269821">"Hộp thư bưu điện"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"Vùng lân cận"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"Tiểu bang"</string>
<string name="postal_postcode" msgid="572136414136673751">"Mã ZIP"</string>
<string name="postal_country" msgid="7638264508416368690">"Quốc gia"</string>
+ <string name="full_name" msgid="6602579550613988977">"Tên"</string>
<string name="name_given" msgid="1687286314106019813">"Tên"</string>
<string name="name_family" msgid="3416695586119999058">"Họ"</string>
<string name="name_prefix" msgid="59756378548779822">"Tiền tố tên"</string>
@@ -381,12 +411,73 @@
<string name="search_bar_hint" msgid="1012756309632856553">"Tìm kiếm trong danh bạ"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"Tìm kiếm tất cả liên hệ"</string>
<string name="take_photo" msgid="7496128293167402354">"Chụp ảnh"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"Chụp ảnh mới"</string>
<string name="pick_photo" msgid="448886509158039462">"Chọn ảnh từ Thư viện"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"Danh sách liên hệ đang được cập nhật để phản ánh thay đổi ngôn ngữ."\n\n"Vui lòng đợi..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"Danh sách liên hệ đang được cập nhật."\n\n"Vui lòng đợi..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"Danh bạ đang được nâng cấp. "\n\n"Quá trình nâng cấp yêu cầu khoảng <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g>Mb bộ nhớ trong của điện thoại."\n\n"Chọn một trong các tuỳ chọn sau:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"Chọn ảnh mới từ Thư viện"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"Danh sách liên hệ đang được cập nhật để phản ánh sự thay đổi ngôn ngữ."</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"Danh sách liên hệ đang được cập nhật."</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"Danh sách liên hệ đang trong quá trình nâng cấp. "\n\n"Quá trình nâng cấp cần khoảng <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> Mb bộ nhớ trong."\n\n"Chọn một trong các tùy chọn sau:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"Gỡ cài đặt một số ứng dụng"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"Thử lại nâng cấp"</string>
- <string name="search_results_for" msgid="8705490885073188513">"Kết quả tìm kiếm cho: <xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"Đang tìm kiếm..."</string>
+ <!-- outdated translation 7509416164257469826 --> <string name="menu_display_selected" msgid="6470001164297969034">"Hiển thị mục đã chọn"</string>
+ <!-- outdated translation 6157266246378155002 --> <string name="menu_display_all" msgid="8887488642609786198">"Hiển thị tất cả"</string>
+ <!-- outdated translation 6071138984118728586 --> <string name="menu_select_all" msgid="621719255150713545">"Chọn tất cả"</string>
+ <!-- outdated translation 6876179536556771017 --> <string name="menu_select_none" msgid="7093222469852132345">"Bỏ chọn tất cả"</string>
+ <!-- no translation found for multiple_picker_title:other (4608837420986126229) -->
+ <!-- outdated translation 2762557778532289842 --> <string name="no_contacts_selected" msgid="5877803471037324613">"Không có địa chỉ liên hệ nào được chọn."</string>
+ <string name="add_field" msgid="2384260056674995230">"Thêm trường khác"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">" qua <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> qua <xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"Yêu thích"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"Chỉnh sửa liên hệ"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"chưa được hợp nhất"</item>
+ <item quantity="other" msgid="425683718017380845">"được hợp nhất từ <xliff:g id="COUNT">%0$d</xliff:g> nguồn"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"Khác"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"Kết hợp danh bạ"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"Kết hợp địa chỉ liên hệ hiện tại với địa chỉ liên hệ đã chọn?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"Chỉnh sửa địa chỉ liên hệ đã chọn"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"Chuyển sang chỉnh sửa liên hệ đã chọn? Thông tin bạn đã nhập đến giờ sẽ được sao chép."</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"Sao chép vào danh bạ của tôi"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"Thư mục <xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"Đang tìm kiếm tất cả địa chỉ liên hệ"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"Thư mục"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"Danh bạ"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"Đang tạo bản sao cá nhân"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"Chọn danh sách địa chỉ liên hệ"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"Tất cả địa chỉ liên hệ"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"Được gắn dấu sao"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"Tùy chỉnh"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"Tùy chỉnh..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"Liên hệ có số điện thoại"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"Địa chỉ liên hệ"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"Xác định chế độ xem tùy chỉnh"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"Cài đặt"</string>
+ <string name="menu_settings" msgid="377929915873428211">"Cài đặt"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"Tùy chọn hiển thị"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>, <xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"Tìm địa chỉ liên hệ"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"Số điện thoại"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"Thêm vào danh bạ"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"Đóng"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"Cho biết năm"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"Địa chỉ liên hệ"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"Đang tải…"</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"Tạo địa chỉ liên hệ mới"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"Đăng nhập vào tài khoản"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"Nhập danh bạ từ một tệp"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"Tạo nhóm mới"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[Tạo nhóm mới]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"Đổi tên nhóm"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"Xóa nhóm"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"Bạn có chắc chắn muốn xóa nhóm \'<xliff:g id="GROUP_LABEL">%1$s</xliff:g>\'? (Danh bạ sẽ không bị xóa)."</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"Vui lòng nhập tên liên hệ trước khi kết hợp với một địa chỉ liên hệ khác."</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"Địa chỉ liên hệ đã kết hợp"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"Đã sao chép văn bản"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"Hủy thay đổi"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"Bạn muốn hủy thay đổi của mình?"</string>
+ <string name="discard" msgid="1234315037371251414">"Hủy"</string>
</resources>
diff --git a/res/values-xlarge-land/dimens.xml b/res/values-xlarge-land/dimens.xml
new file mode 100644
index 0000000..52c521e
--- /dev/null
+++ b/res/values-xlarge-land/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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>
+ <dimen name="quick_contact_width">452dip</dimen>
+ <dimen name="action_bar_filter_min_width">220dip</dimen>
+ <dimen name="action_bar_filter_max_width">300dip</dimen>
+ <dimen name="action_bar_search_max_width">336dip</dimen>
+ <dimen name="action_bar_search_spacing">32dip</dimen>
+</resources>
diff --git a/res/values-xlarge/colors.xml b/res/values-xlarge/colors.xml
new file mode 100644
index 0000000..0f8f8de
--- /dev/null
+++ b/res/values-xlarge/colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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>
+
+ <!-- Color used for the letter in the A-Z section header -->
+ <color name="section_header_text_color">#ff000000</color>
+
+</resources>
diff --git a/res/values-xlarge/dimens.xml b/res/values-xlarge/dimens.xml
new file mode 100644
index 0000000..6ffbb31
--- /dev/null
+++ b/res/values-xlarge/dimens.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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>
+ <dimen name="edit_photo_size">96dip</dimen>
+ <dimen name="aggregation_suggestion_icon_size">64dip</dimen>
+ <dimen name="quick_contact_width">356dip</dimen>
+ <dimen name="contact_name_text_size">26sp</dimen>
+ <dimen name="action_bar_filter_min_width">120dip</dimen>
+ <dimen name="action_bar_filter_max_width">120dip</dimen>
+ <dimen name="action_bar_search_max_width">300dip</dimen>
+ <dimen name="action_bar_search_spacing">12dip</dimen>
+</resources>
diff --git a/res/values-xlarge/styles.xml b/res/values-xlarge/styles.xml
new file mode 100644
index 0000000..e601590
--- /dev/null
+++ b/res/values-xlarge/styles.xml
@@ -0,0 +1,108 @@
+<?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="ContactBrowserTheme" parent="@android:Theme.Holo.Light">
+ <item name="list_item_height">66dip</item>
+ <item name="activated_background">@drawable/list_item_activated_background</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:actionBarStyle">@style/TransparentActionBarStyle</item>
+ <item name="section_header_background">@drawable/list_title_holo</item>
+ <item name="list_item_divider">?android:attr/listDivider</item>
+ <item name="list_item_padding_top">0dip</item>
+ <item name="list_item_padding_right">24dip</item>
+ <item name="list_item_padding_bottom">0dip</item>
+ <item name="list_item_padding_left">0dip</item>
+ <item name="list_item_gap_between_image_and_text">16dip</item>
+ <item name="list_item_gap_between_label_and_data">5dip</item>
+ <item name="list_item_call_button_padding">14dip</item>
+ <item name="list_item_vertical_divider_margin">5dip</item>
+ <item name="list_item_presence_icon_margin">30dip</item>
+ <item name="list_item_photo_size">64dip</item>
+ <item name="list_item_prefix_highlight_color">#729a27</item>
+ <item name="list_item_header_text_indent">77dip</item>
+ <item name="list_item_header_text_color">?color/section_header_text_color</item>
+ <item name="list_item_header_text_size">14sp</item>
+ <item name="contact_filter_popup_width">320dip</item>
+ </style>
+
+ <style name="ContactPickerTheme" parent="@android:Theme.Holo.Light.Dialog">
+ <item name="list_item_height">66dip</item>
+ <item name="section_header_background">@drawable/list_title_holo</item>
+ <item name="list_item_divider">?android:attr/listDivider</item>
+ <item name="list_item_padding_top">0dip</item>
+ <item name="list_item_padding_right">24dip</item>
+ <item name="list_item_padding_bottom">0dip</item>
+ <item name="list_item_padding_left">0dip</item>
+ <item name="list_item_gap_between_image_and_text">16dip</item>
+ <item name="list_item_gap_between_label_and_data">5dip</item>
+ <item name="list_item_call_button_padding">14dip</item>
+ <item name="list_item_vertical_divider_margin">5dip</item>
+ <item name="list_item_presence_icon_margin">30dip</item>
+ <item name="list_item_photo_size">64dip</item>
+ <item name="list_item_header_text_indent">77dip</item>
+ <item name="list_item_header_text_color">?color/section_header_text_color</item>
+ <item name="list_item_header_text_size">14sp</item>
+ </style>
+
+ <style name="ContactsPreferencesTheme" parent="@android:Theme.Holo.Light">
+ </style>
+
+ <style name="CustomContactListFilterTheme" parent="@android:Theme.Holo.Light.Dialog">
+ </style>
+
+ <style name="CustomContactListFilterView" parent="CustomContactListFilterTheme">
+ <item name="android:layout_width">400dip</item>
+ <item name="android:layout_height">600dip</item>
+ </style>
+
+ <style name="ContactPickerLayout" parent="ContactPickerTheme">
+ <item name="android:layout_width">480dip</item>
+ <item name="android:layout_height">match_parent</item>
+ </style>
+
+ <style name="CallDetailActivityTheme" parent="@android:Theme.Dialog">
+ <item name="android:windowContentOverlay">@null</item>
+ </style>
+
+ <style name="ContactDetailActivityTheme" parent="@android:Theme.Dialog">
+ <item name="android:windowContentOverlay">@null</item>
+ </style>
+
+ <style name="ContactEditorActivityTheme" parent="@android:Theme.Holo.Light">
+ <item name="android:actionBarStyle">@style/TransparentActionBarStyle</item>
+ <item name="android:windowContentOverlay">@null</item>
+ </style>
+
+ <style name="TransparentActionBarStyle" parent="android:Widget.Holo.Light.ActionBar">
+ <item name="android:background">@null</item>
+ </style>
+
+ <style name="DirectoryHeader" parent="ContactBrowserTheme">
+ <item name="android:background">@drawable/directory_bg_holo</item>
+ </style>
+
+ <style name="NonPhoneDialogTheme" parent="@android:Theme.Holo.Light.Dialog.Alert">
+ </style>
+
+ <style name="BackgroundOnly" parent="@android:Theme.Holo.Light">
+ <item name="android:windowBackground">@null</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowNoDisplay">true</item>
+ <item name="android:windowIsFloating">true</item>
+ </style>
+</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 142245c..1bfed1d 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"选择联系人快捷方式"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"选择一个可直接拨号的号码"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"选择一个可直接向其发送短信的号码"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"选择联系人"</string>
<string name="starredList" msgid="4817256136413959463">"已加星标的内容"</string>
<string name="frequentList" msgid="7154768136473953056">"经常联系"</string>
<string name="strequentList" msgid="5640192862059373511">"收藏"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"删除联系人"</string>
<string name="menu_call" msgid="3992595586042260618">"呼叫联系人"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"向联系人发送短信"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"发送电子邮件"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"地图地址"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"设置默认号码"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"设置为默认电子邮件"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"拆分"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"已拆分联系人"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"重命名群组"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"删除群组"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"新建"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"拆分联系人"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"您确定要将此单一联系人拆分为多位联系人(即,其中包含的每条联系信息分别对应一位联系人)吗?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"合并"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"删除该联系人将会删除多个帐户中的信息。"</string>
<string name="deleteConfirmation" msgid="811706994761610640">"将会删除此联系人。"</string>
<string name="menu_done" msgid="796017761764190697">"完成"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"取消"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"取消"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"编辑联系人"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"新建联系人"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"拼音"</string>
<string name="label_notes" msgid="8337354953278341042">"备注"</string>
<string name="label_sip_address" msgid="124073911714324974">"互联网通话"</string>
<string name="label_ringtone" msgid="8833166825330686244">"铃声"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"姓名"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"姓名拼音"</string>
<string name="ghostData_company" msgid="5414421120553765775">"公司"</string>
<string name="ghostData_title" msgid="7496735200318496110">"职位"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"该联系人不存在。"</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"新建联系人"</string>
- <string name="selectLabel" msgid="4255424123394910733">"选择标签"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"电话"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"发送电子邮件"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"即时消息"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"通信地址"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"地址"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"组织"</item>
<item msgid="7196592230748086755">"备注"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"手机上没有照片。"</string>
- <string name="attachToContact" msgid="8820530304406066714">"联系人图标"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"平板电脑上没有照片可供使用。"</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"手机上没有照片。"</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"联系人照片"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"自定义标签名称"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"显示选项"</string>
- <string name="displayGroups" msgid="2278964020773993336">"显示选项"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"直接将来电转到语音信箱"</string>
<string name="default_ringtone" msgid="9099988849649827972">"默认日历"</string>
- <string name="changePicture" msgid="2943329047610967714">"更改图标"</string>
- <string name="removePicture" msgid="3041230993155966350">"删除图标"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"删除照片"</string>
<string name="noContacts" msgid="8579310973261953559">"没有联系人。"</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"未找到匹配的联系人。"</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"没有联系人拥有电话号码。"</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"此联系人已保存。"</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"发生错误,无法保存联系人更改。"</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"显示 1 位有电话号码的联系人"</item>
- <item quantity="other" msgid="6133262880804110289">"显示 <xliff:g id="COUNT">%d</xliff:g> 位有电话号码的联系人"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 位联系人有电话号码"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> 位联系人有电话号码"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"显示的联系人都没有电话号码。"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"没有联系人拥有电话号码"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"显示 1 位联系人"</item>
- <item quantity="other" msgid="2865867557378939630">"显示 <xliff:g id="COUNT">%d</xliff:g> 位联系人"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 位联系人"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> 位联系人"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"未显示任何联系人"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"没有联系人"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"未显示任何联系人"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"没有已加星标的联系人"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"“<xliff:g id="NAME">%s</xliff:g>”中没有联系人"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"找到 1 位联系人"</item>
- <item quantity="other" msgid="7752927996850263152">"找到 <xliff:g id="COUNT">%d</xliff:g> 位联系人"</item>
+ <item quantity="one" msgid="5517063038754171134">"找到 1 个联系人"</item>
+ <item quantity="other" msgid="3852668542926965042">"找到 <xliff:g id="COUNT">%d</xliff:g> 个联系人"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"未找到联系人"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"找到的联系人超过 <xliff:g id="COUNT">%d</xliff:g> 位"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"找不到"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 位联系人"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> 位联系人"</item>
+ <item quantity="one" msgid="4826918429708286628">"找到 1 个联系人"</item>
+ <item quantity="other" msgid="7988132539476575389">"找到 <xliff:g id="COUNT">%d</xliff:g> 个联系人"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"通讯录"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"收藏"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"SIM 卡联系人"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"没有任何联系人可供显示。(如果您刚刚添加了一个帐户,则系统需要几分钟的时间同步联系人。)"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"没有任何联系人可供显示。"</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"您没有可显示的联系人。"\n\n"要添加联系人,请按"<font fgcolor="#ffffffff"><b>"菜单"</b></font>",然后触摸:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Google 帐户"</b></font>"以添加或配置可同步到手机的联系人的帐户"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"新建联系人"</b></font>"以从头开始创建新联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"导入/导出"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"您没有可显示的联系人。(如果您刚刚添加了一个帐户,则需要几分钟时间同步联系人。)"\n\n"要添加联系人,请按"<font fgcolor="#ffffffff"><b>"菜单"</b></font>",然后触摸:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Google 帐户"</b></font>"以添加或配置可同步到手机的联系人的帐户"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"显示选项"</b></font>"以更改可见的联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"新建联系人"</b></font>"以从头开始创建新联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"导入/导出"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"您没有可显示的联系人。"\n\n"要添加联系人,请按"<font fgcolor="#ffffffff"><b>"菜单"</b></font>",然后触摸:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Google 帐户"</b></font>"以添加或配置可同步到手机的联系人的帐户"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"新建联系人"</b></font>"以从头开始创建新联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"导入/导出"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"您没有可显示的联系人。(如果您刚刚添加了一个帐户,则需要几分钟时间同步联系人。)"\n\n"要添加联系人,请按"<font fgcolor="#ffffffff"><b>"菜单"</b></font>",然后触摸:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Google 帐户"</b></font>"以添加或配置可同步到手机的联系人的帐户"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"显示选项"</b></font>"以更改可见的联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"新建联系人"</b></font>"以从头开始创建新联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"导入/导出"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"您没有可显示的联系人。"\n\n"要添加联系人,请按"<font fgcolor="#ffffffff"><b>"菜单"</b></font>",然后触摸:"\n\n<li><font fgcolor="#ffffffff"><b>"Google 帐户"</b></font>"以添加或配置帐户,您可以将该帐户中包含的联系人同步到平板电脑"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"新建联系人"</b></font>"以从头开始创建新联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"导入/导出"</b></font>"以导入 SIM 卡或 SD 卡上的联系人"\n</li></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"您没有可显示的联系人。"\n\n"要添加联系人,请按"<font fgcolor="#ffffffff"><b>"菜单"</b></font>",然后触摸以下按钮执行操作:"\n\n<li><font fgcolor="#ffffffff"><b>"Google 帐户"</b></font>"以添加或配置帐户,您可以将该帐户中包含的联系人同步到手机"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"新建联系人"</b></font>"以从头开始创建新联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"导入/导出"</b></font>"以导入 SIM 卡或 SD 卡上的联系人"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"您没有可显示的联系人。(如果您刚刚添加了一个帐户,则需要几分钟时间同步联系人。)"\n\n"要添加联系人,请按"<font fgcolor="#ffffffff"><b>"菜单"</b></font>",然后触摸:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Google 帐户"</b></font>"以添加或配置帐户,您可以将该帐户中包含的联系人同步到平板电脑"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"显示选项"</b></font>"以更改可见的联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"新建联系人"</b></font>"以从头开始创建新联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"导入/导出"</b></font>"以导入 SIM 卡或 SD 卡上的联系人"\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"您没有可显示的联系人。(如果您刚刚添加了一个帐户,则需要几分钟时间同步联系人。)"\n\n"要添加联系人,请按"<font fgcolor="#ffffffff"><b>"菜单"</b></font>",然后触摸:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Google 帐户"</b></font>"以添加或配置帐户,您可以将该帐户中包含的联系人同步到手机"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"显示选项"</b></font>"以更改可见的联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"新建联系人"</b></font>"以从头开始创建新联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"导入/导出"</b></font>"以导入 SIM 卡或 SD 卡上的联系人"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"您没有可显示的联系人。"\n\n"要添加联系人,请按"<font fgcolor="#ffffffff"><b>"菜单"</b></font>",然后触摸:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Google 帐户"</b></font>"以添加或配置帐户,您可以将该帐户中包含的联系人同步到平板电脑"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"新建联系人"</b></font>"以从头开始创建新联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"导入/导出"</b></font>"以导入 SD 卡上的联系人"\n</li></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"您没有可显示的联系人。"\n\n"要添加联系人,请按"<font fgcolor="#ffffffff"><b>"菜单"</b></font>",然后触摸:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Google 帐户"</b></font>"以添加或配置帐户,您可以将该帐户中包含的联系人同步到手机"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"新建联系人"</b></font>"以从头开始创建新联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"导入/导出"</b></font>"以导入 SIM 卡或 SD 卡上的联系人"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"您没有可显示的联系人。(如果您刚刚添加了一个帐户,则需要几分钟时间同步联系人。)"\n\n"要添加联系人,请按"<font fgcolor="#ffffffff"><b>"菜单"</b></font>",然后触摸:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Google 帐户"</b></font>"以添加或配置帐户,您可以将该帐户中包含的联系人同步到平板电脑"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"显示选项"</b></font>"以更改可见的联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"新建联系人"</b></font>"以从头开始创建新联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"导入/导出"</b></font>"以导入 SD 卡上的联系人"\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"您没有可显示的联系人。(如果您刚刚添加了一个帐户,则需要几分钟时间同步联系人。)"\n\n"要添加联系人,请按"<font fgcolor="#ffffffff"><b>"菜单"</b></font>",然后触摸:"\n" "\n<li><font fgcolor="#ffffffff"><b>"Google 帐户"</b></font>"以添加或配置帐户,您可以将该帐户中包含的联系人同步到手机"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"显示选项"</b></font>"以更改可见的联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"新建联系人"</b></font>"以从头开始创建新联系人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"导入/导出"</b></font>"以导入 SD 卡上的联系人"\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"您未收藏任何联系人。"\n\n"要向您的收藏列表添加联系人,请执行以下操作:"\n\n" "<li>"触摸"<b>"联系人"</b>"标签"\n</li>\n<li>"触摸您要加入收藏的联系人"\n</li>\n<li>"触摸联系人姓名旁边的星标"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"所有联系人"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"已加星标的内容"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"回电"</string>
<string name="callAgain" msgid="3197312117049874778">"重拨"</string>
<string name="returnCall" msgid="8171961914203617813">"回拨"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> 分 <xliff:g id="SECONDS">%2$s</xliff:g> 秒"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> 分 <xliff:g id="SECONDS">%s</xliff:g> 秒"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"常用联系人"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"添加联系人"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"将“<xliff:g id="EMAIL">%s</xliff:g>”添加到联系人?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"全部"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"一"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"二"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"三"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"扫描 USB 存储设备时失败(原因:“<xliff:g id="FAIL_REASON">%s</xliff:g>”)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"扫描 SD 卡失败(原因:“<xliff:g id="FAIL_REASON">%s</xliff:g>”)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"I/O 错误"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"存储器空间不足(文件可能过大)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"由于意外原因而无法解析 vCard"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"虽然 vCard 似乎使用了有效的格式,但系统无法对其进行解析,因为当前的实现方案不支持该格式。"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"未在 USB 存储设备中找到 vCard 文件"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"未在 SD 卡上找到 vCard 文件"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"不支持此格式。"</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"未能导入 vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"未在 USB 存储设备中找到 vCard 文件"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"未在 SD 卡上找到 vCard 文件"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"无法收集指定的 vCard 文件的元信息。"</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"一个或多个文件导入失败 (%s)。"</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"未知错误"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"选择 vCard 文件"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"正在读取 vCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"正在读取 vCard 文件"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"读取 vCard 数据失败"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"第 <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> 个联系人(共 <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> 个)"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"第 <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> 个,共 <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> 个文件"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"正在将 vCard 缓存到本地临时存储"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"导入程序正在将 vCard 缓存到本地临时存储。很快将会开始真正的导入。"</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"正在导入第 <xliff:g id="CURRENT_NUMBER">%s</xliff:g> 个(共 <xliff:g id="TOTAL_NUMBER">%s</xliff:g> 个)联系人:<xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"正在导入<xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"无法读取 vCard 数据"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"已取消读取 vCard 数据"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"导入 vCard <xliff:g id="FILENAME">%s</xliff:g> 已完成"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"已取消导入 <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> 将在稍后导入。"</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"系统稍后就会导入该文件。"</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"vCard 导入请求已遭到拒绝。请稍后再试。"</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> 将在稍后导出。"</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"vCard 导出请求已遭到拒绝。请稍后再试。"</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"联系人"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"确认导出"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"确定要将您的联系人列表导出到“<xliff:g id="VCARD_FILENAME">%s</xliff:g>”中吗?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"导出联系人数据失败"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"USB 存储设备中的 vCard 文件太多"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"SD 卡中的 vCard 文件太多"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"所需文件名太长(“<xliff:g id="FILENAME">%s</xliff:g>”)"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"导出 <xliff:g id="FILENAME">%s</xliff:g> 已完成"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"已取消导出 <xliff:g id="FILENAME">%s</xliff:g>"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"正在导出联系人数据"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"正在向“<xliff:g id="FILE_NAME">%s</xliff:g>”导出联系人数据"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"无法初始化导出程序:“<xliff:g id="EXACT_REASON">%s</xliff:g>”"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"导出时出错:“<xliff:g id="EXACT_REASON">%s</xliff:g>”"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"获取数据库信息失败"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"没有可导出的联系人。如果您的手机中确实有联系人,则可能是某个数据提供程序禁止您从手机中导出任何联系人。"</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"没有可导出的联系人。如果您的平板电脑中确实有联系人,则可能是某些数据提供程序禁止您从平板电脑中导出任何联系人。"</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"没有可导出的联系人。如果您的手机中确实有联系人,则可能是某些数据提供程序禁止您从手机中导出任何联系人。"</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"vCard 制作程序未正确初始化"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"无法打开“<xliff:g id="FILE_NAME">%1$s</xliff:g>”:<xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"第 <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> 个联系人(共 <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> 个)"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"无法打开“<xliff:g id="FILE_NAME">%s</xliff:g>”:<xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"第 <xliff:g id="CURRENT_NUMBER">%s</xliff:g> 个联系人(共 <xliff:g id="TOTAL_NUMBER">%s</xliff:g> 个)"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"取消导入 vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"您确定要取消导入 <xliff:g id="FILENAME">%s</xliff:g> 吗?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"取消导出 vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"您确定要取消导出 <xliff:g id="FILENAME">%s</xliff:g> 吗?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"取消导入/导出 vCard 失败"</string>
<string name="search_settings_description" msgid="2675223022992445813">"联系人姓名"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"暂停时间延长 2 秒"</string>
<string name="add_wait" msgid="3360818652790319634">"延长等待时间"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"未找到可处理此操作的应用程序"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"记住此选择"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"未知"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"无数据"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"清除默认设置"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"该联系人的默认设置:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"清除"</string>
<string name="menu_accounts" msgid="8499114602017077970">"帐户"</string>
<string name="menu_import_export" msgid="3765725645491577190">"导入/导出"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"导入/导出联系人"</string>
- <string name="menu_share" msgid="943789700636542260">"分享"</string>
+ <string name="menu_share" msgid="8746849630474240344">"分享联系人"</string>
<string name="share_via" msgid="563121028023030093">"联系人分享方式"</string>
<string name="share_error" msgid="4374508848981697170">"无法分享此联系人。"</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"名称"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"组织"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"网站"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"活动"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"关系"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"群组"</string>
<string name="type_short_home" msgid="7770424864090605384">"住宅"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"手机"</string>
<string name="type_short_work" msgid="4925330752504537861">"单位"</string>
<string name="type_short_pager" msgid="2613818970827594238">"寻呼"</string>
<string name="type_short_other" msgid="5669407180177236769">"其他"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"该联系人的状态为只读"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"更多"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"主名称"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"在帐户下创建联系人"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"删除同步群组"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"添加同步群组"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"其他所有联系人"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"所有联系人"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"如果从同步中删除群组“<xliff:g id="GROUP">%s</xliff:g>”,则会同时删除其中所有未分组的联系人。"</string>
- <string name="account_phone" msgid="3682950835276226870">"仅保存在手机中,不同步"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"只限平板电脑,不同步"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"仅保存在手机中,不同步"</string>
<string name="call_custom" msgid="7756571794763171802">"呼叫<xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"呼叫住宅电话"</string>
<string name="call_mobile" msgid="7502236805487609178">"呼叫手机"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"使用 ICQ 聊天"</string>
<string name="chat_jabber" msgid="7561444230307829609">"使用 Jabber 聊天"</string>
<string name="chat" msgid="9025361898797412245">"聊天"</string>
+ <string name="postal_address" msgid="8765560217149624536">"地址"</string>
<string name="postal_street" msgid="8133143961580058972">"街道"</string>
<string name="postal_pobox" msgid="4431938829180269821">"信箱"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"街区"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"省/直辖市/自治区"</string>
<string name="postal_postcode" msgid="572136414136673751">"邮政编码"</string>
<string name="postal_country" msgid="7638264508416368690">"国家/地区"</string>
+ <string name="full_name" msgid="6602579550613988977">"姓名"</string>
<string name="name_given" msgid="1687286314106019813">"名字"</string>
<string name="name_family" msgid="3416695586119999058">"姓氏"</string>
<string name="name_prefix" msgid="59756378548779822">"名称前缀"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"搜索联系人"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"搜索所有联系人"</string>
<string name="take_photo" msgid="7496128293167402354">"拍照"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"拍摄新照片"</string>
<string name="pick_photo" msgid="448886509158039462">"从图库中选择照片"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"正在更新联系人列表,以反映语言的变更。"\n\n"请稍候..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"正在更新联系人列表。"\n\n"请稍候..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"正在升级联系人。"\n\n"升级过程需要约 <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> Mb 的手机内存。"\n\n"请选择以下选项之一:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"从图库中选择新照片"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"正在更新联系人列表,以反映语言的变更。"</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"正在更新联系人列表。"</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"正在升级联系人。"\n\n"升级过程需要约 <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB 的内存。"\n\n"请选择以下选项之一:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"卸载一些应用程序"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"重新尝试升级"</string>
- <string name="search_results_for" msgid="8705490885073188513">"“<xliff:g id="QUERY">%s</xliff:g>”的搜索结果"</string>
<string name="search_results_searching" msgid="7755623475227227314">"正在搜索..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"已选收件人"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"全部收件人"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"全选"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"取消全选"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"已选择 1 个收件人"</item>
+ <item quantity="other" msgid="4608837420986126229">"已选择 <xliff:g id="COUNT">%d</xliff:g> 个收件人"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"未选择联系人。"</string>
+ <string name="add_field" msgid="2384260056674995230">"添加其他字段"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"来源:<xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"时间:<xliff:g id="DATE">%1$s</xliff:g>,来源:<xliff:g id="SOURCE">%2$s</xliff:g>"</string>
+ <string name="description_star" msgid="2605854427360036550">"收藏"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"编辑联系人"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"未合并"</item>
+ <item quantity="other" msgid="425683718017380845">"合并自 <xliff:g id="COUNT">%0$d</xliff:g> 个来源"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"其他"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"合并联系人"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"要合并当前联系人与所选联系人吗?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"编辑所选联系人"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"要切换至编辑所选联系人吗?系统会复制您到目前为止输入的所有信息。"</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"复制到我的联系人"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"目录类型:<xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"正在搜索所有联系人"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"目录"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"联系人"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"创建个人副本"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"选择联系人列表"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"所有联系人"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"已加星标"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"自定义"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"自定义..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"有电话号码的联系人"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"联系人"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"定义自定义视图"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"设置"</string>
+ <string name="menu_settings" msgid="377929915873428211">"设置"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"显示选项"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_0">%2$s</xliff:g>,<xliff:g id="COMPANY_1">%1$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"查找联系人"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"电话号码"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"添加到联系人"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"关闭"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"输入年份"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"联系人"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"正在载入..."</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"创建新联系人"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"登录帐户"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"从文件导入联系人"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"创建新群组"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[创建新群组]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"重命名群组"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"删除群组"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"您确定要删除群组“<xliff:g id="GROUP_LABEL">%1$s</xliff:g>”吗?(不会删除联系人自身。)"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"请先输入联系人姓名,然后再与其他联系人合并。"</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"已合并的联系人"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"文本已复制"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"舍弃更改"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"要舍弃更改吗?"</string>
+ <string name="discard" msgid="1234315037371251414">"舍弃"</string>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 8332890..2683e50 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -25,6 +25,7 @@
<string name="shortcutActivityTitle" msgid="6642877210643565436">"選擇聯絡人捷徑"</string>
<string name="callShortcutActivityTitle" msgid="6065749861423648991">"選擇去電號碼"</string>
<string name="messageShortcutActivityTitle" msgid="3084542316620335911">"選擇傳訊號碼"</string>
+ <string name="contactPickerActivityTitle" msgid="6886592363525235031">"選取聯絡人"</string>
<string name="starredList" msgid="4817256136413959463">"已加星號"</string>
<string name="frequentList" msgid="7154768136473953056">"經常聯絡"</string>
<string name="strequentList" msgid="5640192862059373511">"我的最愛"</string>
@@ -43,12 +44,11 @@
<string name="menu_deleteContact" msgid="1916555454274101750">"刪除聯絡人"</string>
<string name="menu_call" msgid="3992595586042260618">"去電聯絡人"</string>
<string name="menu_sendSMS" msgid="5535886767547006515">"傳送簡訊至聯絡人"</string>
- <string name="menu_sendEmail" msgid="7293508859242926187">"傳送電子郵件"</string>
- <string name="menu_viewAddress" msgid="1814744325763202024">"在地圖上顯示地址"</string>
- <string name="menu_makeDefaultNumber" msgid="4838759253316649534">"設為預設號碼"</string>
<string name="menu_makeDefaultEmail" msgid="2599044610375789994">"設為預設電子郵件"</string>
<string name="menu_splitAggregate" msgid="8368636463748691868">"分割"</string>
- <string name="contactsSplitMessage" msgid="5253490235863170269">"已分割的聯絡人"</string>
+ <string name="menu_renameGroup" msgid="2798886925154156075">"重新命名群組"</string>
+ <string name="menu_deleteGroup" msgid="644571524292675446">"刪除群組"</string>
+ <string name="menu_new_contact_action_bar" msgid="8887818026717394343">"新增"</string>
<string name="splitConfirmation_title" msgid="6716467920283502570">"分割聯絡人"</string>
<string name="splitConfirmation" msgid="1150797297503944823">"您確定要將這個聯絡人分割成多組聯絡資訊嗎 (個別分到相關的聯絡資訊分類)?"</string>
<string name="menu_joinAggregate" msgid="5027981918265667970">"合併"</string>
@@ -66,37 +66,34 @@
<string name="multipleContactDeleteConfirmation" msgid="938900978442960800">"刪除此聯絡人將會刪除多個帳戶的資訊。"</string>
<string name="deleteConfirmation" msgid="811706994761610640">"刪除此聯絡人?"</string>
<string name="menu_done" msgid="796017761764190697">"完成"</string>
- <string name="menu_doNotSave" msgid="2174577548513895144">"取消"</string>
+ <string name="menu_doNotSave" msgid="58593876893538465">"取消"</string>
<string name="editContact_title_edit" msgid="7678695190666836093">"編輯聯絡人"</string>
<string name="editContact_title_insert" msgid="9125600232291405757">"新增聯絡人"</string>
<string name="label_phonetic_name" msgid="2288082649573927286">"拼音"</string>
<string name="label_notes" msgid="8337354953278341042">"附註"</string>
<string name="label_sip_address" msgid="124073911714324974">"網路電話"</string>
<string name="label_ringtone" msgid="8833166825330686244">"鈴聲"</string>
- <string name="ghostData_name" msgid="6490954238641157585">"姓名"</string>
<string name="ghostData_phonetic_name" msgid="7852749081984070902">"姓名拼音"</string>
<string name="ghostData_company" msgid="5414421120553765775">"公司"</string>
<string name="ghostData_title" msgid="7496735200318496110">"標題"</string>
<string name="invalidContactMessage" msgid="5816991830260044593">"聯絡人不存在。"</string>
<string name="pickerNewContactHeader" msgid="7750705279843568147">"建立新聯絡人"</string>
- <string name="selectLabel" msgid="4255424123394910733">"選取標籤"</string>
<string name="phoneLabelsGroup" msgid="6468091477851199285">"電話"</string>
<string name="emailLabelsGroup" msgid="8389931313045344406">"電子郵件"</string>
<string name="imLabelsGroup" msgid="3898238486262614027">"即時訊息"</string>
- <string name="postalLabelsGroup" msgid="1618078212734693682">"郵寄地址"</string>
+ <string name="postalLabelsGroup" msgid="3487738141112589324">"地址"</string>
<string-array name="otherLabels">
<item msgid="8287841928119937597">"機構"</item>
<item msgid="7196592230748086755">"附註"</item>
</string-array>
- <string name="photoPickerNotFoundText" msgid="431331662154342581">"手機上沒有相片 。"</string>
- <string name="attachToContact" msgid="8820530304406066714">"聯絡人圖示"</string>
+ <string name="photoPickerNotFoundText" product="tablet" msgid="6247290728908599701">"平板電腦中沒有相片可供選用。"</string>
+ <string name="photoPickerNotFoundText" product="default" msgid="431331662154342581">"手機上沒有相片 。"</string>
+ <string name="attach_photo_dialog_title" msgid="5599827035558557169">"聯絡人相片"</string>
<string name="customLabelPickerTitle" msgid="1081475101983255212">"自訂標籤名稱"</string>
<string name="menu_displayGroup" msgid="5655505437727616553">"顯示選項"</string>
- <string name="displayGroups" msgid="2278964020773993336">"顯示選項"</string>
<string name="send_to_voicemail_checkbox" msgid="9001686764070676353">"直接將來電轉到語音信箱"</string>
<string name="default_ringtone" msgid="9099988849649827972">"預設值"</string>
- <string name="changePicture" msgid="2943329047610967714">"變更圖示"</string>
- <string name="removePicture" msgid="3041230993155966350">"移除圖示"</string>
+ <string name="removePhoto" msgid="4898105274130284565">"移除相片"</string>
<string name="noContacts" msgid="8579310973261953559">"沒有聯絡人。"</string>
<string name="noMatchingContacts" msgid="4266283206853990471">"找不到符合的聯絡人。"</string>
<string name="noContactsWithPhoneNumbers" msgid="1605457050218824269">"沒有包含電話號碼的聯絡人資訊。"</string>
@@ -114,23 +111,27 @@
<string name="contactSavedToast" msgid="7152589189385441091">"聯絡人已儲存。"</string>
<string name="contactSavedErrorToast" msgid="9189098776225004666">"發生錯誤,無法儲存聯絡人變更。"</string>
<plurals name="listTotalPhoneContacts">
- <item quantity="one" msgid="8721111084815668845">"顯示 1 位附有電話號碼的聯絡人"</item>
- <item quantity="other" msgid="6133262880804110289">"顯示 <xliff:g id="COUNT">%d</xliff:g> 位附有電話號碼的聯絡人"</item>
+ <item quantity="one" msgid="3015357862286673986">"1 位有電話號碼的聯絡人"</item>
+ <item quantity="other" msgid="3299954047880968205">"<xliff:g id="COUNT">%d</xliff:g> 位有電話號碼的聯絡人"</item>
</plurals>
- <string name="listTotalPhoneContactsZero" msgid="2756295259674938869">"在可顯示的聯絡人中,找不到附有電話號碼的聯絡人"</string>
+ <string name="listTotalPhoneContactsZero" msgid="6968813857632984319">"沒有包含電話號碼的聯絡人"</string>
<plurals name="listTotalAllContacts">
- <item quantity="one" msgid="1096068709488455155">"顯示 1 位聯絡人"</item>
- <item quantity="other" msgid="2865867557378939630">"顯示 <xliff:g id="COUNT">%d</xliff:g> 位聯絡人"</item>
+ <item quantity="one" msgid="3405747744700823280">"1 位聯絡人"</item>
+ <item quantity="other" msgid="3578469907265375314">"<xliff:g id="COUNT">%d</xliff:g> 位聯絡人"</item>
</plurals>
- <string name="listTotalAllContactsZero" msgid="6811347506748072822">"沒有可顯示的聯絡人"</string>
+ <string name="listTotalAllContactsZero" msgid="1889349925514589304">"沒有聯絡人"</string>
+ <string name="listTotalAllContactsZeroCustom" msgid="4058252141420128998">"沒有可顯示的聯絡人"</string>
+ <string name="listTotalAllContactsZeroStarred" msgid="5391630590684099117">"沒有已加星號的聯絡人"</string>
+ <string name="listTotalAllContactsZeroGroup" msgid="5448979458248027615">"<xliff:g id="NAME">%s</xliff:g>中沒有聯絡人"</string>
<plurals name="listFoundAllContacts">
- <item quantity="one" msgid="2830107332033967280">"已找到 1 位聯絡人"</item>
- <item quantity="other" msgid="7752927996850263152">"已找到 <xliff:g id="COUNT">%d</xliff:g> 位聯絡人"</item>
+ <item quantity="one" msgid="5517063038754171134">"找到 1 位聯絡人"</item>
+ <item quantity="other" msgid="3852668542926965042">"找到 <xliff:g id="COUNT">%d</xliff:g> 位聯絡人"</item>
</plurals>
- <string name="listFoundAllContactsZero" msgid="5554368784319460828">"找不到聯絡人"</string>
+ <string name="foundTooManyContacts" msgid="2548148047461758967">"找到超過 <xliff:g id="COUNT">%d</xliff:g> 位聯絡人"</string>
+ <string name="listFoundAllContactsZero" msgid="777952841930508289">"找不到"</string>
<plurals name="searchFoundContacts">
- <item quantity="one" msgid="916648718690661777">"1 位聯絡人"</item>
- <item quantity="other" msgid="5660384247071761844">"<xliff:g id="COUNT">%d</xliff:g> 位聯絡人"</item>
+ <item quantity="one" msgid="4826918429708286628">"找到 1 位聯絡人"</item>
+ <item quantity="other" msgid="7988132539476575389">"找到 <xliff:g id="COUNT">%d</xliff:g> 位聯絡人"</item>
</plurals>
<string name="contactsIconLabel" msgid="7666609097606552806">"聯絡人"</string>
<string name="contactsFavoritesLabel" msgid="8417039765586853670">"我的最愛"</string>
@@ -160,10 +161,14 @@
<string name="simContacts_title" msgid="27341688347689769">"SIM 卡聯絡人"</string>
<string name="noContactsHelpTextWithSyncForCreateShortcut" msgid="801504710275614594">"您沒有任何聯絡人可供顯示 (如果您剛剛才新增帳戶,系統需要幾分鐘的時間才能同步聯絡人資訊)。"</string>
<string name="noContactsHelpTextForCreateShortcut" msgid="3081286388667108335">"您沒有任何聯絡人可供顯示。"</string>
- <string name="noContactsHelpText" msgid="6788487368878712350">"您沒有任何聯絡人。"\n\n"如要新增聯絡人,請按下 ["<font fgcolor="#ffffffff">"選單"<b></b></font>"] 並輕觸:"\n" "\n<li>"["<font fgcolor="#ffffffff">"帳戶"<b></b></font>" 來新增或設定您可以同步處理手機資料的聯絡人帳戶"\n</li>" "\n<li>"["<font fgcolor="#ffffffff">"新增聯絡人"<b></b></font>"] 從頭開始建立聯絡人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"匯入/匯出"</b></font>\n</li></string>
- <string name="noContactsHelpTextWithSync" msgid="3734101165712848179">"您沒有任何聯絡人 (如果您剛剛才新增帳戶,系統需要幾分鐘的時間才能同步聯絡人資訊)。"\n\n"如要新增聯絡人,請按下 ["<font fgcolor="#ffffffff">"選單"</font><b></b>"] 並輕觸:"\n\n"["<li><font fgcolor="#ffffffff">"帳戶"<b></b></font>"] 來新增或設定您可以同步處理手機資料的聯絡人帳戶"\n</li>" "\n<li>"["<font fgcolor="#ffffffff">"顯示選項"<b></b></font>"] 來變更聯絡人的顯示狀態"\n</li>" "\n<li>"["<font fgcolor="#ffffffff">"新增聯絡人"<b></b></font>"] 從頭開始建立聯絡人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"匯入/匯出"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpText" msgid="6553845386917463292">"您沒有任何聯絡人。"\n\n"如要新增聯絡人,請按下 ["<font fgcolor="#ffffffff">"選單"<b></b></font>"] 並輕觸:"\n" "\n<li>"["<font fgcolor="#ffffffff">"帳戶"<b></b></font>" 來新增或設定您可以同步處理手機資料的聯絡人帳戶"\n</li>" "\n<li>"["<font fgcolor="#ffffffff">"新增聯絡人"<b></b></font>"] 從頭開始建立聯絡人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"匯入/匯出"</b></font>\n</li></string>
- <string name="noContactsNoSimHelpTextWithSync" msgid="1122296298361373488">"您沒有任何聯絡人 (如果您剛剛才新增帳戶,系統需要幾分鐘的時間才能同步聯絡人資訊)。"\n\n"如要新增聯絡人,請按下 ["<font fgcolor="#ffffffff">"選單"</font><b></b>"] 並輕觸:"\n\n"["<li><font fgcolor="#ffffffff">"帳戶"<b></b></font>"] 來新增或設定您可以同步處理手機資料的聯絡人帳戶"\n</li>" "\n<li>"["<font fgcolor="#ffffffff">"顯示選項"<b></b></font>"] 來變更聯絡人的顯示狀態"\n</li>" "\n<li>"["<font fgcolor="#ffffffff">"新增聯絡人"<b></b></font>"] 從頭開始建立聯絡人"\n</li>" "\n<li><font fgcolor="#ffffffff"><b>"匯入/匯出"</b></font>\n</li></string>
+ <string name="noContactsHelpText" product="tablet" msgid="6450346791169710787">"您沒有任何聯絡人。"\n\n"如要新增聯絡人,請按下選單鍵並輕觸:"\n" "\n<li>"[帳戶] 以新增或設定含有聯絡人的帳戶,您可以將其聯絡人資料同步傳送到平板電腦"\n</li>" "\n<li>"[新增聯絡人] 以從頭開始建立新的聯絡人"\n</li>" "\n<li>"[匯入/匯出] 以從 SIM 卡或 SD 卡匯入聯絡人"\n</li><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font></string>
+ <string name="noContactsHelpText" product="default" msgid="7633826236417884130">"您沒有任何聯絡人可以顯示。"\n\n"如要新增聯絡人,請按下 [選單] 並輕觸:"\n\n"[帳戶] 以新增或設定含有聯絡人的帳戶,您可以將其聯絡人資料同步傳送到手機"\n" "\n"[新增聯絡人] 以從頭開始建立聯絡人"\n" "\n"[匯入/匯出] 以從 SIM 卡或 SD 卡匯入聯絡人"<font fgcolor="#ffffffff"><b></b></font><li><font fgcolor="#ffffffff"><b></b></font></li><li><font fgcolor="#ffffffff"><b></b></font></li><li><font fgcolor="#ffffffff"><b></b></font>\n</li></string>
+ <string name="noContactsHelpTextWithSync" product="tablet" msgid="2364665535969139880">"您沒有任何聯絡人 (如果您剛剛才新增帳戶,系統需要幾分鐘的時間才能同步處理聯絡人資訊)。"\n\n"如要新增聯絡人,請按下選單鍵並輕觸:"\n" "\n<li>"[帳戶] 以新增或設定含有聯絡人的帳戶,您可以將其聯絡人資料同步傳送到平板電腦"\n</li>" "\n<li>"[顯示選項] 以變更聯絡人的顯示狀態"\n</li>" "\n<li>"[新增聯絡人] 以從頭開始建立新的聯絡人"\n</li>" "\n<li>"[匯入/匯出] 以從 SIM 卡或 SD 卡匯入聯絡人"\n</li><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font></string>
+ <string name="noContactsHelpTextWithSync" product="default" msgid="3017521127042216243">"您沒有任何聯絡人 (如果您剛剛才新增帳戶,系統需要幾分鐘的時間才能同步處理聯絡人資訊)。"\n\n"如要新增聯絡人,請按下 [選單] 並輕觸:"\n\n"[帳戶] 以新增或設定含有聯絡人的帳戶,您可以將其聯絡人資料同步傳送到手機"\n" "\n"[顯示選項] 以變更聯絡人的顯示狀態"\n" "\n"[新增聯絡人] 以從頭開始建立聯絡人"\n" "\n"[匯入/匯出] 以從 SIM 卡或 SD 卡匯入聯絡人"<font fgcolor="#ffffffff"><b></b></font><li><font fgcolor="#ffffffff"><b></b></font></li><li><font fgcolor="#ffffffff"><b></b></font></li><li><font fgcolor="#ffffffff"><b></b></font></li><li><font fgcolor="#ffffffff"><b></b></font>\n</li></string>
+ <string name="noContactsNoSimHelpText" product="tablet" msgid="6031363021287849874">"您沒有任何聯絡人。"\n\n"如要新增聯絡人,請按下選單鍵並輕觸:"\n\n<li>"[帳戶] 以新增或設定含有聯絡人的帳戶,您可以將其聯絡人資料同步傳送到平板電腦"\n</li>" "\n<li>"[新增聯絡人] 以從頭開始建立新的聯絡人"\n</li>" "\n<li>"[匯入/匯出] 以從 SD 卡匯入聯絡人"\n</li><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font></string>
+ <string name="noContactsNoSimHelpText" product="default" msgid="467658807711582876">"您沒有任何聯絡人。"\n\n"如要新增聯絡人,請按下 [選單] 並輕觸:"\n" "\n"[帳戶] 以新增或設定含有聯絡人的帳戶,您可以將其聯絡人資料同步傳送到手機帳戶"\n" "\n"[新增聯絡人] 從頭開始建立聯絡人"\n" "\n"[匯入/匯出] 從 SD 卡匯入聯絡人"<font fgcolor="#ffffffff"><b></b></font><li><font fgcolor="#ffffffff"><b></b></font></li><li><font fgcolor="#ffffffff"><b></b></font></li><li><font fgcolor="#ffffffff"><b></b></font>\n</li></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet" msgid="6222739731808897565">"您沒有任何聯絡人 (如果您剛剛才新增帳戶,系統需要幾分鐘的時間才能同步處理聯絡人資訊)。"\n\n"如要新增聯絡人,請按下選單鍵並輕觸:"\n" "\n<li>"[帳戶] 以新增或設定含有聯絡人的帳戶,您可以將其聯絡人資料同步傳送到平板電腦"\n</li>" "\n<li>"[顯示選項] 以變更聯絡人的顯示狀態"\n</li>" "\n<li>"[新增聯絡人] 以從頭開始建立新的聯絡人"\n</li>" "\n<li>"[匯入/匯出] 以從 SD 卡匯入聯絡人"\n</li><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font><font fgcolor="#ffffffff"><b></b></font></string>
+ <string name="noContactsNoSimHelpTextWithSync" product="default" msgid="9040060730467973050">"您沒有任何聯絡人 (如果您剛剛才新增帳戶,系統需要幾分鐘的時間才能同步處理聯絡人資訊)。"\n\n"如要新增聯絡人,請按下 [選單] 並輕觸:"\n\n"[帳戶] 以新增或設定含有聯絡人的帳戶,您可以將其聯絡人資料同步傳送到手機"\n" "\n"[顯示選項] 以變更聯絡人的顯示狀態"\n" "\n"[新增聯絡人] 以從頭開始建立聯絡人"\n" "\n"[匯入/匯出] 以從 SD 卡匯入聯絡人"<font fgcolor="#ffffffff"><b></b></font><li><font fgcolor="#ffffffff"><b></b></font></li><li><font fgcolor="#ffffffff"><b></b></font></li><li><font fgcolor="#ffffffff"><b></b></font></li><li><font fgcolor="#ffffffff"><b></b></font>\n</li></string>
<string name="noFavoritesHelpText" msgid="3744655776704833277">"您沒有任何最愛。"\n\n"如要將聯絡人新增至最愛清單:"\n\n<li>"輕觸 [聯絡人"<b></b>"] 標籤"\n</li>" "\n<li>"輕觸要新增至最愛的聯絡人"\n</li>" "\n<li>"輕觸聯絡人名稱旁邊的星號圖示"\n</li></string>
<string name="liveFolder_all_label" msgid="5961411940473276616">"所有聯絡人"</string>
<string name="liveFolder_favorites_label" msgid="2674341514070517105">"已加星號"</string>
@@ -180,11 +185,10 @@
<string name="callBack" msgid="5498224409038809224">"回播電話"</string>
<string name="callAgain" msgid="3197312117049874778">"重撥"</string>
<string name="returnCall" msgid="8171961914203617813">"回電"</string>
- <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%1$s</xliff:g> 分 <xliff:g id="SECONDS">%2$s</xliff:g> 秒"</string>
+ <string name="callDetailsDurationFormat" msgid="8157706382818184268">"<xliff:g id="MINUTES">%s</xliff:g> 分 <xliff:g id="SECONDS">%s</xliff:g> 秒"</string>
<string name="favoritesFrquentSeparator" msgid="8107518433381283736">"常用聯絡人"</string>
<string name="add_contact_dlg_title" msgid="2896685845822146494">"新增聯絡人"</string>
<string name="add_contact_dlg_message_fmt" msgid="7986472669444326576">"要將「<xliff:g id="EMAIL">%s</xliff:g>」新增為聯絡人嗎?"</string>
- <string name="all_tab_label" msgid="4003124364397916826">"全部"</string>
<string name="description_image_button_one" msgid="1740638037139856139">"1"</string>
<string name="description_image_button_two" msgid="5882638439003731308">"2"</string>
<string name="description_image_button_three" msgid="8709731759376015180">"3"</string>
@@ -225,19 +229,31 @@
<string name="scanning_sdcard_failed_message" product="nosdcard" msgid="5645544323676912703">"掃描 USB 儲存裝置失敗 (原因:「<xliff:g id="FAIL_REASON">%s</xliff:g>」)"</string>
<string name="scanning_sdcard_failed_message" product="default" msgid="3761992500690182922">"無法掃描 SD 卡 (原因:「<xliff:g id="FAIL_REASON">%s</xliff:g>」)"</string>
<string name="fail_reason_io_error" msgid="5922864781066136340">"I/O 錯誤"</string>
+ <string name="fail_reason_low_memory_during_import" msgid="7514918659342886381">"記憶體不足 (檔案可能過大)"</string>
<string name="fail_reason_vcard_parse_error" msgid="1201233722762680214">"因未預期原因,無法剖析 VCard"</string>
- <string name="fail_reason_vcard_not_supported_error" msgid="655208100451286027">"VCard 格式正確,但目前的實作系統不支援此格式,因此無法剖析"</string>
- <string name="fail_reason_no_vcard_file" product="nosdcard" msgid="4231528180481838813">"在 USB 儲存裝置中找不到 vCard 檔案"</string>
- <string name="fail_reason_no_vcard_file" product="default" msgid="6376516175882881595">"SD 卡上沒有 vCard 檔案"</string>
+ <string name="fail_reason_not_supported" msgid="294499264620201243">"不支援這種格式。"</string>
+ <string name="vcard_import_failed" msgid="7718330063493653085">"無法匯入 vCard"</string>
+ <string name="import_failure_no_vcard_file" product="nosdcard" msgid="8809370398968655782">"在 USB 儲存裝置中找不到 vCard 檔案"</string>
+ <string name="import_failure_no_vcard_file" product="default" msgid="1730986357514922756">"在 SD 卡上找不到 vCard 檔案"</string>
+ <string name="fail_reason_failed_to_collect_vcard_meta_info" msgid="4154492282316067754">"無法從指定的 vCard 檔案收集中繼資料。"</string>
<string name="fail_reason_failed_to_read_files" msgid="3659521123567134029">"無法匯入一或多個檔案 (%s)。"</string>
<string name="fail_reason_unknown" msgid="999034019513096768">"未知的錯誤"</string>
<string name="select_vcard_title" msgid="3968948173786172468">"選取 VCard 檔案"</string>
- <string name="progress_shower_message" msgid="5636525578293752526">"<xliff:g id="ACTION">%1$s</xliff:g>"\n"<xliff:g id="FILENAME">%2$s</xliff:g>"</string>
- <string name="reading_vcard_title" msgid="4723433501579653199">"正在讀取 VCard"</string>
- <string name="reading_vcard_message" msgid="6381368920030748743">"正在讀取 VCard 檔案"</string>
- <string name="reading_vcard_failed_title" msgid="4923008144735294994">"無法讀取 VCard 資料"</string>
- <string name="reading_vcard_contacts" msgid="3066834102042012868">"第 <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> 位聯絡人,共 <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> 位"</string>
- <string name="reading_vcard_files" msgid="34180143726972661">"第 <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> 個檔案,共 <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> 個"</string>
+ <string name="caching_vcard_title" msgid="5009556022082659780">"正在將 vCard 資料快取至本機暫存空間"</string>
+ <string name="caching_vcard_message" msgid="2380844718093378900">"匯入工具正在將 vCard 的資料快取至本機暫存空間,隨即將啟動實際的匯入作業。"</string>
+ <string name="progress_notifier_message" msgid="2311011466908220528">"正在匯入第 <xliff:g id="CURRENT_NUMBER">%s</xliff:g> 筆資料:<xliff:g id="NAME">%s</xliff:g>,共 <xliff:g id="TOTAL_NUMBER">%s</xliff:g> 筆資料"</string>
+ <string name="importing_vcard_description" msgid="4245275224298571351">"正在匯入 <xliff:g id="NAME">%s</xliff:g>"</string>
+ <string name="reading_vcard_failed_title" msgid="2162610359561887043">"無法讀取 vCard 資料"</string>
+ <string name="reading_vcard_canceled_title" msgid="1770608329958463131">"已取消讀取 vCard 資料"</string>
+ <string name="importing_vcard_finished_title" msgid="3341541727268747967">"已完成匯入 vCard 的 <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="importing_vcard_canceled_title" msgid="6367906965439777280">"已取消匯入 <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="vcard_import_will_start_message" msgid="2804911199145873396">"<xliff:g id="FILENAME">%s</xliff:g> 將在稍後匯入。"</string>
+ <string name="vcard_import_will_start_message_with_default_name" msgid="1022969530654129470">"稍候即將匯入該檔案。"</string>
+ <string name="vcard_import_request_rejected_message" msgid="2592424820635325951">"vCard 匯入要求遭到拒絕,請稍後再試。"</string>
+ <string name="vcard_export_will_start_message" msgid="2210241345252081463">"<xliff:g id="FILENAME">%s</xliff:g> 將在稍後匯出。"</string>
+ <string name="vcard_export_request_rejected_message" msgid="8259494002258326330">"vCard 匯出要求遭到拒絕,請稍後再試。"</string>
+ <string name="vcard_unknown_filename" msgid="7171709890959915954">"聯絡人"</string>
+ <string name="percentage" msgid="34897865327092209">"%s%%"</string>
<string name="confirm_export_title" msgid="7648747763127442983">"確認匯出"</string>
<string name="confirm_export_message" msgid="3875683519257829750">"確定要將聯絡人清單匯出至「<xliff:g id="VCARD_FILENAME">%s</xliff:g>」嗎?"</string>
<string name="exporting_contact_failed_title" msgid="585823094820602526">"無法匯出聯絡人資料"</string>
@@ -246,15 +262,23 @@
<string name="fail_reason_too_many_vcard" product="nosdcard" msgid="2638638826954895225">"USB 儲存裝置中的 vCard 檔案過多"</string>
<string name="fail_reason_too_many_vcard" product="default" msgid="7084146295639672658">"SD 卡中的 VCard 檔案過多"</string>
<string name="fail_reason_too_long_filename" msgid="1915716071321839166">"要求的檔案名稱過長 (「<xliff:g id="FILENAME">%s</xliff:g>」)"</string>
+ <string name="exporting_vcard_finished_title" msgid="4259736138838583213">"已完成匯出 <xliff:g id="FILENAME">%s</xliff:g>"</string>
+ <string name="exporting_vcard_canceled_title" msgid="1827672399438062140">"已取消匯出 <xliff:g id="FILENAME">%s</xliff:g>"</string>
<string name="exporting_contact_list_title" msgid="9072240631534457415">"正在匯出聯絡人資料"</string>
<string name="exporting_contact_list_message" msgid="5640326540405486055">"正在將聯絡人資料匯出至「<xliff:g id="FILE_NAME">%s</xliff:g>」"</string>
<string name="fail_reason_could_not_initialize_exporter" msgid="4943708332700987376">"無法初始化匯出程式:「<xliff:g id="EXACT_REASON">%s</xliff:g>」"</string>
<string name="fail_reason_error_occurred_during_export" msgid="2151165129433831202">"匯出時發生錯誤:「<xliff:g id="EXACT_REASON">%s</xliff:g>」"</string>
<string name="composer_failed_to_get_database_infomation" msgid="3723109558155169053">"無法取得資料庫資訊"</string>
- <string name="composer_has_no_exportable_contact" msgid="754734132189369094">"沒有可匯出的聯絡人。如果您的手機中確實存有聯絡人資料,這可能是因為某些資料提供者禁止您將所有聯絡人匯出手機。"</string>
+ <string name="composer_has_no_exportable_contact" product="tablet" msgid="5161491059051198932">"沒有可匯出的聯絡人。如果您的平板電腦中確實有聯絡人資料,則可能是有些資料提供者禁止您將所有聯絡人匯出平板電腦。"</string>
+ <string name="composer_has_no_exportable_contact" product="default" msgid="322344221706924358">"沒有可匯出的聯絡人。如果您的手機中確實存有聯絡人資料,則可能是有些資料提供者禁止您將所有聯絡人匯出手機。"</string>
<string name="composer_not_initialized" msgid="8041534450748388843">"VCard 編輯器並未正確初始化"</string>
- <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"無法開啟「<xliff:g id="FILE_NAME">%1$s</xliff:g>」:<xliff:g id="EXACT_REASON">%2$s</xliff:g>"</string>
- <string name="exporting_contact_list_progress" msgid="560522409559101193">"第 <xliff:g id="CURRENT_NUMBER">%1$s</xliff:g> 位聯絡人,共 <xliff:g id="TOTAL_NUMBER">%2$s</xliff:g> 位"</string>
+ <string name="fail_reason_could_not_open_file" msgid="4013520943128739511">"無法開啟「<xliff:g id="FILE_NAME">%s</xliff:g>」:<xliff:g id="EXACT_REASON">%s</xliff:g>"</string>
+ <string name="exporting_contact_list_progress" msgid="560522409559101193">"第 <xliff:g id="CURRENT_NUMBER">%s</xliff:g> 位聯絡人,共 <xliff:g id="TOTAL_NUMBER">%s</xliff:g> 位"</string>
+ <string name="cancel_import_confirmation_title" msgid="5578683596010294836">"取消匯入 vCard"</string>
+ <string name="cancel_import_confirmation_message" msgid="8560937090143057107">"您確定要取消匯入 <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_export_confirmation_title" msgid="6516467140276768528">"取消匯出 vCard"</string>
+ <string name="cancel_export_confirmation_message" msgid="1392976902396351957">"您確定要取消匯出 <xliff:g id="FILENAME">%s</xliff:g>?"</string>
+ <string name="cancel_vcard_import_or_export_failed" msgid="7096533244663846810">"無法取消匯入/匯出 vCard"</string>
<string name="search_settings_description" msgid="2675223022992445813">"您的聯絡人姓名"</string>
<string name="add_2sec_pause" msgid="9214012315201040129">"新增 2 秒暫停功能"</string>
<string name="add_wait" msgid="3360818652790319634">"新增插播功能"</string>
@@ -264,10 +288,14 @@
<string name="quickcontact_missing_app" msgid="4600366393134289038">"找不到可以處理這個動作的應用程式"</string>
<string name="quickcontact_remember_choice" msgid="5964536411579749424">"記住這個選擇"</string>
<string name="quickcontact_missing_name" msgid="5590266114306996632">"不明"</string>
+ <string name="quickcontact_no_data" msgid="2098000859125253675">"無資料"</string>
+ <string name="quickcontact_clear_defaults_description" msgid="3792792870662989100">"清除預設值"</string>
+ <string name="quickcontact_clear_defaults_caption" msgid="4287306111861545753">"這位聯絡人的預設值:"</string>
+ <string name="quickcontact_clear_defaults_button" msgid="8728754360205289059">"清除"</string>
<string name="menu_accounts" msgid="8499114602017077970">"帳戶"</string>
<string name="menu_import_export" msgid="3765725645491577190">"匯入/匯出"</string>
<string name="dialog_import_export" msgid="4771877268244096596">"匯入/匯出聯絡人"</string>
- <string name="menu_share" msgid="943789700636542260">"分享"</string>
+ <string name="menu_share" msgid="8746849630474240344">"分享聯絡人"</string>
<string name="share_via" msgid="563121028023030093">"使用下列應用程式分享聯絡人資訊:"</string>
<string name="share_error" msgid="4374508848981697170">"無法分享此聯絡人。"</string>
<string name="nameLabelsGroup" msgid="2034640839640477827">"姓名"</string>
@@ -275,14 +303,13 @@
<string name="organizationLabelsGroup" msgid="2478611760751832035">"機構"</string>
<string name="websiteLabelsGroup" msgid="4202998982804009261">"網站"</string>
<string name="eventLabelsGroup" msgid="8069912895912714412">"活動"</string>
+ <string name="relationLabelsGroup" msgid="1854373894284572781">"關係"</string>
+ <string name="groupsLabel" msgid="8573535366319059326">"群組"</string>
<string name="type_short_home" msgid="7770424864090605384">"住家"</string>
<string name="type_short_mobile" msgid="1655473281466676216">"行動"</string>
<string name="type_short_work" msgid="4925330752504537861">"工作"</string>
<string name="type_short_pager" msgid="2613818970827594238">"呼叫器"</string>
<string name="type_short_other" msgid="5669407180177236769">"其他"</string>
- <string name="edit_read_only" msgid="8158629550655830981">"此聯絡人為唯讀"</string>
- <string name="edit_secondary_collapse" msgid="5371618426594477103">"更多"</string>
- <string name="dialog_primary_name" msgid="5521591005692614833">"主要名稱"</string>
<string name="dialog_new_contact_account" msgid="9044704073286262197">"在帳戶下建立聯絡人"</string>
<string name="menu_sync_remove" msgid="3266725887008450161">"移除同步處理群組"</string>
<string name="dialog_sync_add" msgid="8267045393119375803">"新增同步處理群組"</string>
@@ -290,7 +317,8 @@
<string name="display_ungrouped" msgid="4602580795576261158">"所有其他聯絡人"</string>
<string name="display_all_contacts" msgid="6846131371214707956">"所有聯絡人"</string>
<string name="display_warn_remove_ungrouped" msgid="2314043155909167610">"如果從同步處理群組中移除「<xliff:g id="GROUP">%s</xliff:g>」群組,系統也會從同步處理群組中移除所有未分組的聯絡人。"</string>
- <string name="account_phone" msgid="3682950835276226870">"僅儲存於手機 (不會同步處理)"</string>
+ <string name="account_phone" product="tablet" msgid="7946049152658522054">"僅限平板電腦,不會同步處理"</string>
+ <string name="account_phone" product="default" msgid="3682950835276226870">"僅儲存於手機 (不會同步處理)"</string>
<string name="call_custom" msgid="7756571794763171802">"去電<xliff:g id="CUSTOM">%s</xliff:g>"</string>
<string name="call_home" msgid="1990519474420545392">"去電住家電話"</string>
<string name="call_mobile" msgid="7502236805487609178">"去電行動裝置"</string>
@@ -352,6 +380,7 @@
<string name="chat_icq" msgid="8438405386153745775">"使用 ICQ 進行即時通訊"</string>
<string name="chat_jabber" msgid="7561444230307829609">"使用 Jabber 進行即時通訊"</string>
<string name="chat" msgid="9025361898797412245">"即時通訊"</string>
+ <string name="postal_address" msgid="8765560217149624536">"地址"</string>
<string name="postal_street" msgid="8133143961580058972">"街道"</string>
<string name="postal_pobox" msgid="4431938829180269821">"郵政信箱"</string>
<string name="postal_neighborhood" msgid="1450783874558956739">"鄰"</string>
@@ -359,6 +388,7 @@
<string name="postal_region" msgid="6045263193478437672">"州/省"</string>
<string name="postal_postcode" msgid="572136414136673751">"郵遞區號"</string>
<string name="postal_country" msgid="7638264508416368690">"國家/地區"</string>
+ <string name="full_name" msgid="6602579550613988977">"姓名"</string>
<string name="name_given" msgid="1687286314106019813">"名字"</string>
<string name="name_family" msgid="3416695586119999058">"姓氏"</string>
<string name="name_prefix" msgid="59756378548779822">"姓名前稱銜"</string>
@@ -381,12 +411,76 @@
<string name="search_bar_hint" msgid="1012756309632856553">"搜尋聯絡人"</string>
<string name="search_for_all_contacts" msgid="6644963335787294131">"搜尋所有聯絡人"</string>
<string name="take_photo" msgid="7496128293167402354">"拍照"</string>
+ <string name="take_new_photo" msgid="7341354729436576304">"拍攝新相片"</string>
<string name="pick_photo" msgid="448886509158039462">"從圖片庫選取相片"</string>
- <string name="locale_change_in_progress" msgid="1124266507671178413">"正在更新聯絡人清單以反映語言變更。"\n\n"請稍候..."</string>
- <string name="upgrade_in_progress" msgid="7530893673211750223">"正在更新聯絡人清單。"\n\n"請稍候..."</string>
- <string name="upgrade_out_of_memory" msgid="492151063059824962">"正在進行聯絡人升級。"\n\n"升級程序需要大約 <xliff:g id="SIZE_IN_MEGABYTES">%d</xliff:g> MB 的手機內部儲存空間。"\n\n"請選擇下列其中一個選項:"</string>
+ <string name="pick_new_photo" msgid="7962368009197147617">"從圖片庫選取新相片"</string>
+ <string name="locale_change_in_progress" msgid="7583992153091537467">"正在更新聯絡人清單以反映語言變更。"</string>
+ <string name="upgrade_in_progress" msgid="474511436863451061">"正在更新聯絡人清單。"</string>
+ <string name="upgrade_out_of_memory" msgid="6153384328042175667">"正在進行聯絡人升級。"\n\n"升級程序需要大約 <xliff:g id="SIZE_IN_MEGABYTES">%s</xliff:g> MB 的內部儲存空間。"\n\n"請選擇下列其中一個選項:"</string>
<string name="upgrade_out_of_memory_uninstall" msgid="1721798828992091432">"解除安裝一些應用程式"</string>
<string name="upgrade_out_of_memory_retry" msgid="8431289830472724609">"重試升級"</string>
- <string name="search_results_for" msgid="8705490885073188513">"此項目之搜尋結果:<xliff:g id="QUERY">%s</xliff:g>"</string>
<string name="search_results_searching" msgid="7755623475227227314">"搜尋中..."</string>
+ <string name="menu_display_selected" msgid="6470001164297969034">"顯示已選取的項目"</string>
+ <string name="menu_display_all" msgid="8887488642609786198">"全部顯示"</string>
+ <string name="menu_select_all" msgid="621719255150713545">"全選"</string>
+ <string name="menu_select_none" msgid="7093222469852132345">"全部取消選取"</string>
+ <plurals name="multiple_picker_title">
+ <item quantity="one" msgid="4761009734586319101">"已選取 1 位收件者"</item>
+ <item quantity="other" msgid="4608837420986126229">"已選取 <xliff:g id="COUNT">%d</xliff:g> 位收件者"</item>
+ </plurals>
+ <string name="no_contacts_selected" msgid="5877803471037324613">"未選取任何聯絡人。"</string>
+ <string name="add_field" msgid="2384260056674995230">"新增其他欄位"</string>
+ <string name="contact_status_update_attribution" msgid="752179367353018597">"透過 <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
+ <string name="contact_status_update_attribution_with_date" msgid="7358045508107825068">"<xliff:g id="DATE">%1$s</xliff:g> (透過 <xliff:g id="SOURCE">%2$s</xliff:g>)"</string>
+ <string name="description_star" msgid="2605854427360036550">"我的最愛"</string>
+ <string name="edit_contact" msgid="7529281274005689512">"編輯聯絡人"</string>
+ <plurals name="merge_info">
+ <item quantity="one" msgid="148365587896371969">"未合併"</item>
+ <item quantity="other" msgid="425683718017380845">"從 <xliff:g id="COUNT">%0$d</xliff:g> 個來源合併"</item>
+ </plurals>
+ <string name="local_invisible_directory" msgid="6046691709127661065">"其他"</string>
+ <string name="aggregation_suggestion_join_dialog_title" msgid="5276699501316246253">"合併聯絡人"</string>
+ <string name="aggregation_suggestion_join_dialog_message" msgid="3842757977671434836">"要將目前聯絡人與所選聯絡人合併嗎?"</string>
+ <string name="aggregation_suggestion_edit_dialog_title" msgid="1064042382692091314">"編輯所選聯絡人"</string>
+ <string name="aggregation_suggestion_edit_dialog_message" msgid="6549585283910518095">"要切換至編輯所選聯絡人嗎?系統會為您複製目前已輸入的資訊。"</string>
+ <string name="menu_copyContact" msgid="4401683725471696686">"複製到我的聯絡人"</string>
+ <string name="contact_directory_description" msgid="683398073603909119">"目錄:<xliff:g id="TYPE">%1$s</xliff:g>"</string>
+ <string name="search_label" msgid="6789295859496641042">"正在搜尋所有聯絡人"</string>
+ <string name="directory_search_label" msgid="1887759056597975053">"目錄"</string>
+ <string name="local_search_label" msgid="1686089693064201315">"聯絡人"</string>
+ <string name="toast_making_personal_copy" msgid="7905283986345263275">"建立個人副本"</string>
+ <string name="list_filter_prompt" msgid="7481426622828055116">"選擇聯絡人清單"</string>
+ <string name="list_filter_all_accounts" msgid="8908683398914322369">"所有聯絡人"</string>
+ <string name="list_filter_all_starred" msgid="5031734941601931356">"已加星號"</string>
+ <string name="list_filter_custom" msgid="8910173055702057002">"自訂"</string>
+ <string name="list_filter_customize" msgid="2035084418635775579">"自訂..."</string>
+ <string name="list_filter_phones" msgid="7905045603593508221">"包含電話號碼的聯絡人資訊"</string>
+ <string name="list_filter_single" msgid="5871400283515893087">"聯絡人"</string>
+ <string name="custom_list_filter" msgid="7836035257402013957">"定義自訂檢視"</string>
+ <string name="activity_title_settings" msgid="5464130076132770781">"設定"</string>
+ <string name="menu_settings" msgid="377929915873428211">"設定"</string>
+ <string name="preference_displayOptions" msgid="1341720270148252393">"顯示選項"</string>
+ <string name="organization_company_and_title" msgid="6718207751363732025">"<xliff:g id="COMPANY_1">%1$s</xliff:g> <xliff:g id="COMPANY_0">%2$s</xliff:g>"</string>
+ <string name="hint_findContacts" msgid="1808681193458772072">"尋找聯絡人"</string>
+ <string name="non_phone_caption" msgid="1541655052330027380">"電話號碼"</string>
+ <string name="non_phone_add_to_contacts" msgid="6590985286250471169">"新增至通訊錄"</string>
+ <string name="non_phone_close" msgid="7608506439725515667">"關閉"</string>
+ <string name="widget_name_and_phonetic" msgid="8739586586600099979">"<xliff:g id="DISPLAY_NAME">%1$s</xliff:g> (<xliff:g id="PHONETIC_NAME">%2$s</xliff:g>)"</string>
+ <string name="date_year_toggle" msgid="7356532842767854606">"提供年份"</string>
+ <string name="social_widget_label" msgid="6378905543028924592">"聯絡人"</string>
+ <string name="social_widget_loading" msgid="3697996166985327861">"載入中..."</string>
+ <string name="contacts_unavailable_create_contact" msgid="7014525713871959208">"建立新聯絡人"</string>
+ <string name="contacts_unavailable_add_account" msgid="7911101713860139754">"登入帳戶"</string>
+ <string name="contacts_unavailable_import_contacts" msgid="4456440183590517471">"從檔案匯入聯絡人"</string>
+ <string name="create_group_dialog_title" msgid="6874527142828424475">"建立新群組"</string>
+ <string name="create_group_item_label" msgid="5218022006186243310">"[建立新群組]"</string>
+ <string name="rename_group_dialog_title" msgid="3765299704290513289">"重新命名群組"</string>
+ <string name="delete_group_dialog_title" msgid="7368429698398624427">"刪除群組"</string>
+ <string name="delete_group_dialog_message" msgid="295063284548750881">"您確定要刪除「<xliff:g id="GROUP_LABEL">%1$s</xliff:g>」群組?這並不會刪除聯絡人本身。"</string>
+ <string name="toast_join_with_empty_contact" msgid="5015189525953438968">"請先輸入聯絡人姓名,然後才能與其他聯絡人合併。"</string>
+ <string name="indicator_joined_contact" msgid="3321049349627022128">"已合併的聯絡人"</string>
+ <string name="toast_text_copied" msgid="5143776250008541719">"文字已複製"</string>
+ <string name="cancel_confirmation_dialog_title" msgid="3950463632415908534">"捨棄變更"</string>
+ <string name="cancel_confirmation_dialog_message" msgid="7021968394611740251">"您要捨棄變更嗎?"</string>
+ <string name="discard" msgid="1234315037371251414">"捨棄"</string>
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 5f39532..69f92ad 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -13,17 +13,30 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<resources>
<color name="textColorIconOverlay">#fff</color>
<color name="textColorIconOverlayShadow">#000</color>
- <color name="sect_secondary">#4fff</color>
+
<color name="quickcontact_disambig">#f2f2f2</color>
<color name="quickcontact_disambig_divider">#afafaf</color>
<color name="edit_divider">#ff666666</color>
<color name="background_secondary">#ff202020</color>
- <color name="pinned_header_background">#ff202020</color>
<color name="translucent_search_background">#cc000000</color>
+
+ <!-- Color used for the letter in the A-Z section header -->
+ <color name="section_header_text_color">#ff999999</color>
+
+ <!-- Color used as a selected background for the account/group selection widget -->
+ <color name="filter_selector_selected_background">#66ffffff</color>
+
+ <!-- Color used for the label (type) in the editor -->
+ <color name="editor_label_text_color">#FF7F7F7F</color>
+
+ <!-- Color of the line above the contact photo in the contact detail header -->
+ <color name="contact_detail_header_divider_color">#FF999999</color>
+
+ <!-- Color of the text indicating the type of entry (e.g. Home, Work etc) -->
+ <color name="detail_item_type_color">#B4B4B4</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 4a7a743..0b2cffd 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -13,30 +13,64 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<resources>
- <dimen name="quickcontact_shadow_horiz">30dip</dimen>
- <dimen name="quickcontact_shadow_vert">37dip</dimen>
- <dimen name="quickcontact_shadow_touch">20dip</dimen>
-
<dimen name="edit_photo_size">76dip</dimen>
<!-- The height of the ScrollingTabWidget -->
<dimen name="tab_height">40dip</dimen>
<dimen name="account_name_height">25dip</dimen>
+
+ <dimen name="contact_shortcut_frame_width">50dip</dimen>
+ <dimen name="contact_shortcut_frame_height">56dip</dimen>
+
+ <dimen name="aggregation_suggestion_icon_size">40dip</dimen>
+
+ <dimen name="account_selector_popup_width">400dip</dimen>
+
+ <dimen name="photo_action_popup_width">400dip</dimen>
+
+ <!-- Width of the quick contact popup. This size is chosen so that the last icon is clipped
+ to indicate horizontal scrollability. Also, this size is the same as the widget to make them
+ aligned -->
+ <dimen name="quick_contact_width">352dip</dimen>
+
+ <!-- Padding of the rounded plus/minus/expand/collapse buttons in the editor -->
+ <dimen name="editor_round_button_padding_left">8dip</dimen>
+ <dimen name="editor_round_button_padding_right">8dip</dimen>
+ <dimen name="editor_round_button_padding_top">8dip</dimen>
+ <dimen name="editor_round_button_padding_bottom">8dip</dimen>
+
+ <!-- Width of the Type-Label in the Editor -->
+ <dimen name="editor_type_label_width">180dip</dimen>
- <dimen name="contact_shortcut_frame_width">50dip</dimen>
- <dimen name="contact_shortcut_frame_height">56dip</dimen>
-
- <!-- Dimensions for a list item -->
- <dimen name="list_item_padding_top">4dip</dimen>
- <dimen name="list_item_padding_right">11dip</dimen>
- <dimen name="list_item_padding_bottom">4dip</dimen>
- <dimen name="list_item_padding_left">4dip</dimen>
- <dimen name="list_item_gap_between_image_and_text">8dip</dimen>
- <dimen name="list_item_gap_between_label_and_data">5dip</dimen>
- <dimen name="list_item_call_button_padding">14dip</dimen>
- <dimen name="list_item_vertical_divider_margin">5dip</dimen>
- <dimen name="list_item_presence_icon_margin">5dip</dimen>
- <dimen name="list_item_header_text_width">56dip</dimen>
+ <!-- Minimum height of a row in the Editor -->
+ <dimen name="editor_min_line_item_height">48dip</dimen>
+
+ <!-- Minimum height of a row in the contact detail -->
+ <dimen name="detail_min_line_item_height">48dip</dimen>
+
+ <!-- Padding to be used between a visible scrollbar and the contact list -->
+ <dimen name="list_visible_scrollbar_padding">40dip</dimen>
+
+ <!-- Font size used for the contact name in the widget -->
+ <dimen name="widget_text_size_name">14sp</dimen>
+
+ <!-- Font size used for the social status in the widget -->
+ <dimen name="widget_text_size_snippet">13sp</dimen>
+
+ <!-- Font size used for the contact name in the detail and the editor -->
+ <dimen name="contact_name_text_size">18sp</dimen>
+
+ <!-- Minimum width of the filter selector in the action bar -->
+ <dimen name="action_bar_filter_min_width">100dip</dimen>
+
+ <!-- Maximum width of the filter selector in the action bar -->
+ <dimen name="action_bar_filter_max_width">100dip</dimen>
+
+ <!-- Maximum width of the search field in the action bar -->
+ <dimen name="action_bar_search_max_width">100dip</dimen>
+
+ <!-- Spacing on the left the search field in the action bar -->
+ <dimen name="action_bar_search_spacing">12dip</dimen>
+
</resources>
diff --git a/res/values/donottranslate_config.xml b/res/values/donottranslate_config.xml
index f1c6951..cad2a63 100644
--- a/res/values/donottranslate_config.xml
+++ b/res/values/donottranslate_config.xml
@@ -53,7 +53,7 @@
<string name="config_export_vcard_type" translatable="false">default</string>
<!-- Directory in which exported VCard file is stored -->
- <string name="config_export_dir" translatable="false">/sdcard</string>
+ <string name="config_export_dir" translatable="false">/mnt/sdcard</string>
<!-- Prefix of exported VCard file -->
<string name="config_export_file_prefix" translatable="false"></string>
diff --git a/res/values/ids.xml b/res/values/ids.xml
index ceb10f8..1a553d1 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -14,32 +14,55 @@
limitations under the License.
-->
<resources>
- <!-- The EditText for entries in the EditContactActivity -->
+ <!-- The EditText for entries in the ContactEditFragment -->
<item type="id" name="data"/>
<item type="id" name="header_phones"/>
<item type="id" name="dialog_sync_add"/>
<item type="id" name="dialog_import_export"/>
- <!-- For ImportVCardActivity -->
+ <!-- For vcard.ImportVCardActivity -->
<item type="id" name="dialog_searching_vcard"/>
<item type="id" name="dialog_sdcard_not_found"/>
<item type="id" name="dialog_vcard_not_found"/>
<item type="id" name="dialog_select_import_type"/>
<item type="id" name="dialog_select_one_vcard"/>
<item type="id" name="dialog_select_multiple_vcard"/>
- <item type="id" name="dialog_reading_vcard"/>
+ <item type="id" name="dialog_cache_vcard"/>
<item type="id" name="dialog_io_exception"/>
<item type="id" name="dialog_error_with_message"/>
- <!-- For ContactsListActivity -->
- <item type="id" name="dialog_delete_contact_confirmation"/>
- <item type="id" name="dialog_readonly_contact_hide_confirmation"/>
- <item type="id" name="dialog_multiple_contact_delete_confirmation"/>
- <item type="id" name="dialog_readonly_contact_delete_confirmation"/>
+ <!-- For vcard.CancelActivity -->
+ <item type="id" name="dialog_cancel_confirmation"/>
+ <item type="id" name="dialog_cancel_failed"/>
- <!-- For ExportVCard -->
+ <!-- For ContactDeletionInteraction -->
+ <item type="id" name="dialog_delete_contact_confirmation"/>
+ <item type="id" name="dialog_delete_contact_loader_id" />
+
+ <!-- For ImportExportInteraction -->
+ <item type="id" name="dialog_import_export_options"/>
+
+ <!-- For ExportVCardActivity -->
<item type="id" name="dialog_export_confirmation"/>
<item type="id" name="dialog_exporting_vcard"/>
<item type="id" name="dialog_fail_to_export_with_reason"/>
+ <!-- For PhoneNumberInteraction -->
+ <item type="id" name="dialog_phone_number_call_disambiguation"/>
+
+ <!-- For PhoneNumberMessageSendInteraction -->
+ <item type="id" name="dialog_phone_number_message_disambiguation"/>
+
+ <!-- Dialog Manager Ids -->
+ <item type="id" name="dialog_manager_id_1"/>
+ <item type="id" name="dialog_manager_id_2"/>
+
+ <!-- Loader ID for contact filters -->
+ <item type="id" name="contact_list_filter_loader" />
+
+ <!-- Dialog ID for the date picker in event (birthday) editors -->
+ <item type="id" name="dialog_event_date_picker" />
+
+ <!-- An ID to be used for contents of a custom dialog so that its state be preserved -->
+ <item type="id" name="custom_dialog_content" />
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5d275aa..d09904a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -47,6 +47,9 @@
<!-- Activity title when the user is selecting a contact for a direct message shortcut. -->
<string name="messageShortcutActivityTitle">Choose a number to message</string>
+ <!-- Activity title when the user is selecting a contact. [CHAR LIMIT=128] -->
+ <string name="contactPickerActivityTitle">Select a contact</string>
+
<!-- Title for the activity that shows only starred contacts -->
<string name="starredList">Starred</string>
@@ -105,16 +108,6 @@
<!-- 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">Text contact</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>
-
<!-- Menu item that makes an email address the default for a contact. The default email used
when you try to email a contact without specifying a specific address. -->
<string name="menu_makeDefaultEmail">Make default email</string>
@@ -122,8 +115,14 @@
<!-- Menu item that splits an item from the contact detail into a separate aggregate -->
<string name="menu_splitAggregate">Separate</string>
- <!-- Toast shown after a contact has been split from an aggregate by a user action -->
- <string name="contactsSplitMessage">Contacts separated</string>
+ <!-- Menu item that renames the currently selected group [CHAR LIMIT=30] -->
+ <string name="menu_renameGroup">Rename group</string>
+
+ <!-- Menu item that deletes the currently selected group [CHAR LIMIT=30] -->
+ <string name="menu_deleteGroup">Delete group</string>
+
+ <!-- Menu item (in the action bar) that creates a new contacts [CHAR LIMIT=12] -->
+ <string name="menu_new_contact_action_bar">New</string>
<!-- Title of the confirmation dialog for separating contacts into multiple instances -->
<string name="splitConfirmation_title">Separate Contact</string>
@@ -178,8 +177,8 @@
<!-- 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>
+ <!-- Menu item to indicate you want to stop editing a contact and NOT save the changes you've made [CHAR LIMIT=12] -->
+ <string name="menu_doNotSave">Cancel</string>
<!-- The title of the activity that edits and existing contact -->
<string name="editContact_title_edit">Edit contact</string>
@@ -187,7 +186,7 @@
<!-- The title of the activity that creates a new contact -->
<string name="editContact_title_insert">New contact</string>
- <!-- The label describing the phonetic pronunciation/reading of a contact name -->
+ <!-- The label describing the phonetic pronunciation/reading of a contact name [CHAR LIMIT=20] -->
<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 -->
@@ -199,9 +198,6 @@
<!-- 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>
@@ -219,24 +215,21 @@
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 -->
+ <!-- Header that expands to list all of the types of phone numbers when editing or creating a phone number for a contact [CHAR LIMIT=20] -->
<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 -->
+ <!-- Header that expands to list all of the types of email addresses when editing or creating an email address for a contact [CHAR LIMIT=20] -->
<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 -->
+ <!-- Header that expands to list all of the types of IM account when editing or creating an IM account for a contact [CHAR LIMIT=20] -->
<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 all of the types of postal addresses when editing or creating an postal address for a contact [CHAR LIMIT=20] -->
+ <string name="postalLabelsGroup">Address</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. -->
+ <!-- The labels that are under the otherLabelsGroup when editing a contact. [CHAR LIMIT=20] -->
<string-array name="otherLabels">
<!-- An organization associated with a contact -->
<item>Organization</item>
@@ -245,10 +238,12 @@
</string-array>
<!-- 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>
+ <string name="photoPickerNotFoundText" product="tablet">No pictures are available on the tablet.</string>
+ <!-- Description in the dialog that appears if there are no pictures from which to create an icon for a contact -->
+ <string name="photoPickerNotFoundText" product="default">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>
+ <!-- Description used in the attach photo Intent from third party apps [CHAR LIMIT=50] -->
+ <string name="attach_photo_dialog_title">Contact photo</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" -->
@@ -257,20 +252,14 @@
<!-- The menu item to open the list of groups to display -->
<string name="menu_displayGroup">Display options</string>
- <!-- Title of activity that lets user pick which contact groups to display -->
- <string name="displayGroups">Display options</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 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 change an existing contact picture -->
- <string name="changePicture">Change icon</string>
-
- <!-- The menu item that allows you to remove a picture from a contact -->
- <string name="removePicture">Remove icon</string>
+ <!-- The menu item that allows you to remove a photo from a contact [CHAR LIMIT=50] -->
+ <string name="removePhoto">Remove photo</string>
<!-- The text displayed when the contacts list is empty while displaying all contacts -->
<string name="noContacts">No contacts.</string>
@@ -314,35 +303,47 @@
<!-- Displayed at the top of the contacts showing the total number of contacts visible when "Only contacts with phones" is selected -->
<plurals name="listTotalPhoneContacts">
- <item quantity="one">Displaying 1 contact with phone number</item>
- <item quantity="other">Displaying <xliff:g id="count">%d</xliff:g> contacts with phone numbers</item>
+ <item quantity="one">1 contact with phone number</item>
+ <item quantity="other"><xliff:g id="count">%d</xliff:g> contacts with phone numbers</item>
</plurals>
- <!-- Displayed at the top of the contacts showing the zero as total number of contacts visible when "Only contacts with phones" is selected -->
- <string name="listTotalPhoneContactsZero">No visible contacts with phone numbers</string>
+ <!-- Displayed at the top of the contacts showing the zero as total number of contacts visible when "Only contacts with phones" is selected [CHAR LIMIT=64]-->
+ <string name="listTotalPhoneContactsZero">No contacts with phone numbers</string>
<!-- Displayed at the top of the contacts showing the total number of contacts visible when "Only contacts with phones" not selected -->
<plurals name="listTotalAllContacts">
- <item quantity="one">Displaying 1 contact</item>
- <item quantity="other">Displaying <xliff:g id="count">%d</xliff:g> contacts</item>
+ <item quantity="one">1 contact</item>
+ <item quantity="other"><xliff:g id="count">%d</xliff:g> contacts</item>
</plurals>
- <!-- Displayed at the top of the contacts showing the zero total number of contacts visible when "Only contacts with phones" not selected -->
- <string name="listTotalAllContactsZero">No visible contacts</string>
+ <!-- Displayed at the top of the contacts showing the zero total number of contacts visible when "All contacts" is selected [CHAR LIMIT=64]-->
+ <string name="listTotalAllContactsZero">No contacts</string>
+
+ <!-- Displayed at the top of the contacts showing the zero total number of contacts visible when "Custom" is selected [CHAR LIMIT=64]-->
+ <string name="listTotalAllContactsZeroCustom">No visible contacts</string>
+
+ <!-- Displayed at the top of the contacts showing the zero total number of contacts visible when starred contact list is selected [CHAR LIMIT=64]-->
+ <string name="listTotalAllContactsZeroStarred">No starred contacts</string>
+
+ <!-- Displayed at the top of the contacts showing the zero total number of contacts visible when a group or account is selected [CHAR LIMIT=64]-->
+ <string name="listTotalAllContactsZeroGroup">No contacts in <xliff:g id="name" example="Friends">%s</xliff:g></string>
<!-- Displayed at the top of the contacts showing the total number of contacts found when "Only contacts with phones" not selected -->
<plurals name="listFoundAllContacts">
- <item quantity="one">Found 1 contact</item>
- <item quantity="other">Found <xliff:g id="count">%d</xliff:g> contacts</item>
+ <item quantity="one">1 found</item>
+ <item quantity="other"><xliff:g id="count">%d</xliff:g> found</item>
</plurals>
+ <!-- Displayed at the top of search results indicating that more contacts were found than shown [CHAR LIMIT=64] -->
+ <string name="foundTooManyContacts">more than <xliff:g id="count">%d</xliff:g> found</string>
+
<!-- Displayed at the top of the contacts showing the zero total number of contacts found when "Only contacts with phones" not selected -->
- <string name="listFoundAllContactsZero">Contact not found</string>
+ <string name="listFoundAllContactsZero">Not found</string>
<!-- Displayed at the top of the contacts showing the total number of contacts found when typing search query -->
<plurals name="searchFoundContacts">
- <item quantity="one">1 contact</item>
- <item quantity="other"><xliff:g id="count">%d</xliff:g> contacts</item>
+ <item quantity="one">1 found</item>
+ <item quantity="other"><xliff:g id="count">%d</xliff:g> found</item>
</plurals>
<!-- The description text for the contacts tab. Space is limited for this string, so the shorter the better -->
@@ -428,33 +429,59 @@
<string name="noContactsHelpTextForCreateShortcut">"You don't have any contacts to display."</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 to display.\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and touch:\n
+ <string name="noContactsHelpText" product="tablet">"You don't have any contacts to display.\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and touch:\n
+ \n<li><font fgcolor="#ffffffff"><b>Accounts</b></font> to add or configure an account with contacts you can sync to the tablet\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/Export</b></font> to import contacts from your SIM or SD 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 disabled -->
+ <string name="noContactsHelpText" product="default">"You don't have any contacts to display.\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and touch:\n
\n<li><font fgcolor="#ffffffff"><b>Accounts</b></font> to add or configure an account with contacts you can sync to the phone\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/Export</b></font>\n</li>"
+ \n<li><font fgcolor="#ffffffff"><b>Import/Export</b></font> to import contacts from your SIM or SD 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 to display. (If you just added an account, it can take a few minutes to sync contacts.)\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and touch:\n
+ <string name="noContactsHelpTextWithSync" product="tablet">"You don't have any contacts to display. (If you just added an account, it can take a few minutes to sync contacts.)\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and touch:\n
+ \n<li><font fgcolor="#ffffffff"><b>Accounts</b></font> to add or configure an account with contacts you can sync to the tablet\n</li>
+ \n<li><font fgcolor="#ffffffff"><b>Display options</b></font> to change which contacts are visible\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/Export</b></font> to import contacts from your SIM or SD 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" product="default">"You don't have any contacts to display. (If you just added an account, it can take a few minutes to sync contacts.)\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and touch:\n
\n<li><font fgcolor="#ffffffff"><b>Accounts</b></font> to add or configure an account with contacts you can sync to the phone\n</li>
\n<li><font fgcolor="#ffffffff"><b>Display options</b></font> to change which contacts are visible\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/Export</b></font>\n</li>"
+ \n<li><font fgcolor="#ffffffff"><b>Import/Export</b></font> to import contacts from your SIM or SD 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 disabled, and there is no sim card (cdma)-->
- <string name="noContactsNoSimHelpText">"You don't have any contacts to display.\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and touch:\n
+ <string name="noContactsNoSimHelpText" product="tablet">"You don't have any contacts to display.\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and touch:\n
+ \n<li><font fgcolor="#ffffffff"><b>Accounts</b></font> to add or configure an account with contacts you can sync to the tablet\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/Export</b></font> to import contacts from your SD 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 disabled, and there is no sim card (cdma)-->
+ <string name="noContactsNoSimHelpText" product="default">"You don't have any contacts to display.\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and touch:\n
\n<li><font fgcolor="#ffffffff"><b>Accounts</b></font> to add or configure an account with contacts you can sync to the phone\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/Export</b></font>\n</li>"
+ \n<li><font fgcolor="#ffffffff"><b>Import/Export</b></font> to import contacts from your SD 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, and there is no sim card (cdma) -->
- <string name="noContactsNoSimHelpTextWithSync">"You don't have any contacts to display. (If you just added an account, it can take a few minutes to sync contacts.)\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and touch:\n
+ <string name="noContactsNoSimHelpTextWithSync" product="tablet">"You don't have any contacts to display. (If you just added an account, it can take a few minutes to sync contacts.)\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and touch:\n
+ \n<li><font fgcolor="#ffffffff"><b>Accounts</b></font> to add or configure an account with contacts you can sync to the tablet\n</li>
+ \n<li><font fgcolor="#ffffffff"><b>Display options</b></font> to change which contacts are visible\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/Export</b></font> to import contacts from your SD 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, and there is no sim card (cdma) -->
+ <string name="noContactsNoSimHelpTextWithSync" product="default">"You don't have any contacts to display. (If you just added an account, it can take a few minutes to sync contacts.)\n\nTo add contacts, press <font fgcolor="#ffffffff"><b>Menu</b></font> and touch:\n
\n<li><font fgcolor="#ffffffff"><b>Accounts</b></font> to add or configure an account with contacts you can sync to the phone\n</li>
\n<li><font fgcolor="#ffffffff"><b>Display options</b></font> to change which contacts are visible\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/Export</b></font>\n</li>"
+ \n<li><font fgcolor="#ffffffff"><b>Import/Export</b></font> to import contacts from your SD card\n</li>"
</string>
<!-- Displayed full screen when the user has no favorites and they are displaying the favorites tab -->
@@ -464,9 +491,6 @@
\n<li>Touch the star next to the contact\'s name\n</li>"
</string>
- <!-- Activity title for the activity that lets the user choose which groups of contacts to sync from the server -->
-
-
<!-- Live folder label for all contacts -->
<string name="liveFolder_all_label">All contacts</string>
@@ -519,7 +543,7 @@
<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">%1$s</xliff:g> mins <xliff:g id="seconds" example="28">%2$s</xliff:g> secs</string>
+ <string name="callDetailsDurationFormat"><xliff:g id="minutes" example="42">%s</xliff:g> mins <xliff:g id="seconds" example="28">%s</xliff:g> secs</string>
<!-- A list separator for the Favorites tab indicating that items below it are frequently contacted contacts rather than starred contacts -->
<string name="favoritesFrquentSeparator">Frequently contacted</string>
@@ -530,9 +554,6 @@
the email address, e.g. "Add xyz@foo.com to contacts?" -->
<string name="add_contact_dlg_message_fmt">Add \"<xliff:g id="email">%s</xliff:g>\" to contacts?</string>
- <!-- Label for the all data tab in the view and edit card -->
- <string name="all_tab_label">All</string>
-
<!-- String describing the image on ImageButton one
Note: AccessibilityServices use this attribute to announce what the view represents.
@@ -669,7 +690,7 @@
<!-- Dialog message shown when SDcard does not exist -->
<string name="no_sdcard_message" product="default">No SD card detected</string>
- <!-- Dialog title shown when searching VCard data from SD Card -->
+ <!-- Dialog title shown when searching vCard data from SD Card -->
<string name="searching_vcard_title">Searching for vCard</string>
<!-- Action string for selecting SIM for importing contacts -->
@@ -724,23 +745,33 @@
emitted some I/O error. Exact reason will be appended by the system. -->
<string name="fail_reason_io_error">I/O Error</string>
+ <!-- Failure reason show when Contacts app (especially vCard importer) encountered
+ low memory problem and could not proceed its import procedure. -->
+ <string name="fail_reason_low_memory_during_import">Memory is insufficient (the file may be too large)</string>
+
<!-- The failed reason shown when vCard parser was not able to be parsed by the current vCard
implementation. This might happen even when the input vCard is completely valid, though
we believe it is rather rare in the actual world. -->
<string name="fail_reason_vcard_parse_error">Failed to parse vCard for unexpected reason</string>
- <!-- The failed reason shown when the current vCard importer cannot parse the file since the
- parser is incomplete (actually, there's missing part in the current vCard parser, though
- our understanding is that the functionality missed by the current vCard parser
- is rarely needed in the actual vCard...). -->
- <string name="fail_reason_vcard_not_supported_error">Failed to parse vCard though it seems in valid format, since the current implementation does not support it</string>
+ <!-- The failed reason shown when vCard importer doesn't support the format.
+ This may be shown when the vCard is corrupted [CHAR LIMIT=40] -->
+ <string name="fail_reason_not_supported">The format is not supported.</string>
- <!-- The failed reason shown when the system could not find any vCard file
- (with extension ".vcf" in SDCard.) [CHAR LIMIT=NONE] -->
- <string name="fail_reason_no_vcard_file" product="nosdcard">No vCard file found in the USB storage</string>
- <!-- The failed reason shown when the system could not find any vCard file
- (with extension ".vcf" in SDCard.) -->
- <string name="fail_reason_no_vcard_file" product="default">No vCard file found on the SD card</string>
+ <!-- Message used when vCard import has failed. [CHAR LIMIT=40] -->
+ <string name="vcard_import_failed">Failed to import vCard</string>
+
+ <!-- The failure message shown when the system could not find any vCard file.
+ (with extension ".vcf" in USB storage.)
+ [CHAR LIMIT=128] -->
+ <string name="import_failure_no_vcard_file" product="nosdcard">No vCard file found in the USB storage</string>
+ <!-- The failure message shown when the system could not find any vCard file.
+ (with extension ".vcf" in SDCard.)
+ [CHAR LIMIT=128] -->
+ <string name="import_failure_no_vcard_file" product="default">No vCard file found on the SD card</string>
+
+ <!-- Fail reason shown when vCard importer failed to look over meta information stored in vCard file(s). -->
+ <string name="fail_reason_failed_to_collect_vcard_meta_info">Failed to collect meta information of given vCard file(s).</string>
<!-- The failed reason shown when the import of some of vCard files failed during multiple vCard
files import. It includes the case where all files were failed to be imported. -->
@@ -749,28 +780,70 @@
<!-- The failed reason which should not be shown but it may in some buggy condition. -->
<string name="fail_reason_unknown">Unknown error</string>
- <!-- Dialog title shown when a user is asked to select VCard file -->
+ <!-- Dialog title shown when a user is asked to select vCard file -->
<string name="select_vcard_title">Select vCard file</string>
- <!-- The message shown while reading a vCard file/entry. The first argument is like
- "Reading VCard" or "Reading VCard files" and the second is the display name of the current
- data being parsed -->
- <string name="progress_shower_message"><xliff:g id="action" example="Reading VCard">%1$s</xliff:g>\n<xliff:g id="filename" example="foo.vcf">%2$s</xliff:g></string>
+ <!-- The title shown when vCard importer is caching files to be imported into local temporary
+ data storage. -->
+ <string name="caching_vcard_title">Caching vCard(s) to local temporary storage</string>
- <!-- Dialog title shown when reading VCard data -->
- <string name="reading_vcard_title">Reading vCard</string>
+ <!-- The message shown when vCard importer is caching files to be imported into local temporary
+ data storage. -->
+ <string name="caching_vcard_message">Importer is caching vCard(s) to local temporary storage. Actual import will start soon.</string>
- <!-- Dialog message shown when reading a VCard file -->
- <string name="reading_vcard_message">Reading vCard file(s)</string>
+ <!-- The message shown while importing vCard(s).
+ First argument is current index of contacts to be imported.
+ Second argument is the total number of contacts.
+ Third argument is the name of a contact which is being read.
+ [CHAR LIMIT=20] -->
+ <string name="progress_notifier_message">Importing <xliff:g id="current_number">%s</xliff:g>/<xliff:g id="total_number">%s</xliff:g>: <xliff:g id="name" example="Joe Due">%s</xliff:g></string>
- <!-- Dialog title shown when reading VCard data failed -->
- <string name="reading_vcard_failed_title">Reading of vCard data has failed</string>
+ <!-- Description shown when importing vCard data.
+ The argument is the name of a contact which is being read.
+ [CHAR LIMIT=20] -->
+ <string name="importing_vcard_description">Importing <xliff:g id="name" example="Joe Due">%s</xliff:g></string>
- <!-- Message while reading one vCard file "(current number) of (total number) contacts" The order of "current number" and "total number" cannot be changed (like "total: (total number), current: (current number)")-->
- <string name="reading_vcard_contacts"><xliff:g id="current_number">%1$s</xliff:g> of <xliff:g id="total_number">%2$s</xliff:g> contacts</string>
+ <!-- Dialog title shown when reading vCard data failed [CHAR LIMIT=40] -->
+ <string name="reading_vcard_failed_title">Failed to Read vCard data</string>
- <!-- Message while reading multiple vCard files "(current number) of (total number) files" The order of "current number" and "total number" cannot be changed (like "total: (total number), current: (current number)")-->
- <string name="reading_vcard_files"><xliff:g id="current_number">%1$s</xliff:g> of <xliff:g id="total_number">%2$s</xliff:g> files</string>
+ <!-- The title shown when reading vCard is canceled (probably by a user)
+ [CHAR LIMIT=40] -->
+ <string name="reading_vcard_canceled_title">Reading vCard data was canceled</string>
+
+ <!-- The title shown when reading vCard finished
+ The argument is file name the user imported.
+ [CHAR LIMIT=40] -->
+ <string name="importing_vcard_finished_title">Finished importing vCard <xliff:g id="filename" example="import.vcf">%s</xliff:g></string>
+
+ <!-- The title shown when importing vCard is canceled (probably by a user)
+ The argument is file name the user canceled importing.
+ [CHAR LIMIT=40] -->
+ <string name="importing_vcard_canceled_title">Importing <xliff:g id="filename" example="import.vcf">%s</xliff:g> was canceled</string>
+
+ <!-- The message shown when vCard import request is accepted. The system may start that work soon, or do it later
+ when there are already other import/export requests.
+ The argument is file name the user imported.
+ [CHAR LIMIT=40] -->
+ <string name="vcard_import_will_start_message"><xliff:g id="filename" example="import.vcf">%s</xliff:g> will be imported shortly.</string>
+ <!-- The message shown when vCard import request is accepted. The system may start that work soon, or do it later when there are already other import/export requests.
+ "The file" is what a user selected for importing.
+ [CHAR LIMIT=40] -->
+ <string name="vcard_import_will_start_message_with_default_name">The file will be imported shortly.</string>
+ <!-- The message shown when a given vCard import request is rejected by the system. [CHAR LIMIT=NONE] -->
+ <string name="vcard_import_request_rejected_message">vCard import request is rejected. Please try later.</string>
+ <!-- The message shown when vCard export request is accepted. The system may start that work soon, or do it later
+ when there are already other import/export requests.
+ The argument is file name the user exported.
+ [CHAR LIMIT=40] -->
+ <string name="vcard_export_will_start_message"><xliff:g id="filename" example="import.vcf">%s</xliff:g> will be exported shortly.</string>
+ <!-- The message shown when a given vCard export request is rejected by the system. [CHAR LIMIT=NONE] -->
+ <string name="vcard_export_request_rejected_message">vCard export request is rejected. Please try later.</string>
+ <!-- Used when file name is unknown in vCard processing. It typically happens
+ when the file is given outside the Contacts app. [CHAR LIMIT=30] -->
+ <string name="vcard_unknown_filename">contact</string>
+
+ <!-- The percentage, used for expressing the progress of vCard import/export. -->
+ <string name="percentage">%s%%</string>
<!-- Dialog title shown when a user confirms whether he/she export Contact data -->
<string name="confirm_export_title">Confirm export</string>
@@ -801,6 +874,14 @@
mention it here. -->
<string name="fail_reason_too_long_filename">Required filename is too long (\"<xliff:g id="filename">%s</xliff:g>\")</string>
+ <!-- The title shown when exporting vCard is successfuly finished [CHAR LIMIT=40] -->
+ <string name="exporting_vcard_finished_title">Finished exporting <xliff:g id="filename" example="export.vcf">%s</xliff:g></string>
+
+ <!-- The title shown when exporting vCard is canceled (probably by a user)
+ The argument is file name the user canceled importing.
+ [CHAR LIMIT=40] -->
+ <string name="exporting_vcard_canceled_title">Exporting <xliff:g id="filename" example="export.vcf">%s</xliff:g> was canceled</string>
+
<!-- Dialog title shown when the application is exporting contact data outside -->
<string name="exporting_contact_list_title">Exporting contact data</string>
@@ -822,7 +903,11 @@
<!-- This error message shown when the user actually have no contact
(e.g. just after data-wiping), or, data providers of the contact list prohibits their
contacts from being exported to outside world via vcard exporter, etc. -->
- <string name="composer_has_no_exportable_contact">There is no exportable contact. If you have actualy have contacts on your phone, all the contacts may be prohibited from being exported to outside the phone by some data provider.</string>
+ <string name="composer_has_no_exportable_contact" product="tablet">There are no exportable contacts. If you actually have contacts on your tablet, all the contacts may be prohibited from being exported to outside the tablet by some data providers.</string>
+ <!-- This error message shown when the user actually have no contact
+ (e.g. just after data-wiping), or, data providers of the contact list prohibits their
+ contacts from being exported to outside world via vcard exporter, etc. -->
+ <string name="composer_has_no_exportable_contact" product="default">There are no exportable contacts. If you actually have contacts on your phone, all the contacts may be prohibited from being exported to outside the phone by some data providers.</string>
<!-- The error reason the vCard composer may emit when vCard composer is not initialized
even when needed.
@@ -831,10 +916,29 @@
<!-- The failed reason shown when vCard importer/exporter could not open the file
specified by a user. The file name should be in the message. -->
- <string name="fail_reason_could_not_open_file">Could not open \"<xliff:g id="file_name">%1$s</xliff:g>\": <xliff:g id="exact_reason">%2$s</xliff:g></string>
+ <string name="fail_reason_could_not_open_file">Could not open \"<xliff:g id="file_name">%s</xliff:g>\": <xliff:g id="exact_reason">%s</xliff:g></string>
<!-- Message in progress bar while exporting contact list to a file "(current number) of (total number) contacts" The order of "current number" and "total number" cannot be changed (like "total: (total number), current: (current number)")-->
- <string name="exporting_contact_list_progress"><xliff:g id="current_number">%1$s</xliff:g> of <xliff:g id="total_number">%2$s</xliff:g> contacts</string>
+ <string name="exporting_contact_list_progress"><xliff:g id="current_number">%s</xliff:g> of <xliff:g id="total_number">%s</xliff:g> contacts</string>
+
+ <!-- Title shown in a Dialog confirming a user's cancel request toward existing vCard import. [CHAR LIMIT=40] -->
+ <string name="cancel_import_confirmation_title">Canceling vCard import</string>
+
+ <!-- Message shown in a Dialog confirming a user's cancel request toward existing vCard import.
+ The argument is file name for the vCard import the user wants to cancel.
+ [CHAR LIMIT=128] -->
+ <string name="cancel_import_confirmation_message">Are you sure to cancel importing <xliff:g id="filename" example="import.vcf">%s</xliff:g>?</string>
+
+ <!-- Title shown in a Dialog confirming a user's cancel request toward existing vCard export. [CHAR LIMIT=128] -->
+ <string name="cancel_export_confirmation_title">Canceling vCard export</string>
+
+ <!-- Message shown in a Dialog confirming a user's cancel request toward existing vCard export.
+ The argument is file name for the vCard export the user wants to cancel.
+ [CHAR LIMIT=128] -->
+ <string name="cancel_export_confirmation_message">Are you sure to cancel exporting <xliff:g id="filename" example="export.vcf">%s</xliff:g>?</string>
+
+ <!-- Title shown in a Dialog telling users cancel vCard import/export operation is failed. [CHAR LIMIT=40] -->
+ <string name="cancel_vcard_import_or_export_failed">Failed to cancel vCard import/export</string>
<!-- The string used to describe Contacts as a searchable item within system search settings. -->
<string name="search_settings_description">Names of your contacts</string>
@@ -867,6 +971,17 @@
<!-- Text that is shown in the Badge, when there is no data to display -->
<string name="quickcontact_no_data">No data</string>
+ <!-- Accessibility description for the button in QuickContacts that allows the user to clear
+ defaults of a contact (like primary email). [CHAR LIMIT=100] -->
+ <string name="quickcontact_clear_defaults_description">Clear defaults</string>
+
+ <!-- Caption that shows above the list of defaults (like primary email). [CHAR LIMIT=40] -->
+ <string name="quickcontact_clear_defaults_caption">Defaults set for this contact:</string>
+
+ <!-- Button caption that allows the user to clear the defaults (like primary email) of one
+ contact. [CHAR LIMIT=15] -->
+ <string name="quickcontact_clear_defaults_button">Clear</string>
+
<!-- The menu item to open the list of accounts -->
<string name="menu_accounts">Accounts</string>
@@ -876,8 +991,8 @@
<!-- Dialog title when selecting the bulk operation to perform from a list. -->
<string name="dialog_import_export">Import/Export contacts</string>
- <!-- The menu item to share the currently viewed contact -->
- <string name="menu_share">Share</string>
+ <!-- The menu item to share the currently viewed contact [CHAR LIMIT=30] -->
+ <string name="menu_share">Share contact</string>
<!-- Dialog title when picking the application to share a contact with. -->
<string name="share_via">Share contact via</string>
@@ -885,16 +1000,20 @@
<!-- Toast indicating that sharing a contact has failed. -->
<string name="share_error">This contact cannot be shared.</string>
- <!-- Header that expands to list all name types when editing a structured name of a contact -->
+ <!-- Header that expands to list all name types when editing a structured name of a contact [CHAR LIMIT=20] -->
<string name="nameLabelsGroup">Name</string>
- <!-- Header that expands to list all nickname types when editing a nickname of a contact -->
+ <!-- Header that expands to list all nickname types when editing a nickname of a contact [CHAR LIMIT=20] -->
<string name="nicknameLabelsGroup">Nickname</string>
- <!-- Header that expands to list all organization types when editing an organization of a contact -->
+ <!-- Header that expands to list all organization types when editing an organization of a contact [CHAR LIMIT=20] -->
<string name="organizationLabelsGroup">Organization</string>
- <!-- Header that expands to list all website types when editing a website of a contact -->
+ <!-- Header that expands to list all website types when editing a website of a contact [CHAR LIMIT=20] -->
<string name="websiteLabelsGroup">Website</string>
- <!-- Header that expands to list all event types when editing an event of a contact -->
+ <!-- Header that expands to list all event types when editing an event of a contact [CHAR LIMIT=20] -->
<string name="eventLabelsGroup">Event</string>
+ <!-- Header for the list of all relationships for a contact [CHAR LIMIT=20] -->
+ <string name="relationLabelsGroup">Relationship</string>
+ <!-- Header for the list of all groups for a contact [CHAR LIMIT=20] -->
+ <string name="groupsLabel">Groups</string>
<!-- Single-character overlay for home phone numbers when creating desktop shortcuts -->
<string name="type_short_home">H</string>
@@ -907,14 +1026,6 @@
<!-- Single-character overlay for other phone numbers when creating desktop shortcuts -->
<string name="type_short_other">O</string>
- <!-- In edit dialog, shown if the contact is marked as being read-only -->
- <string name="edit_read_only">This contact is read-only</string>
-
- <!-- Shown as the header title over a collapsible section that, by default, hides
- secondary contact detail edit fields, such as birthday. -->
- <string name="edit_secondary_collapse">More</string>
-
- <string name="dialog_primary_name">Primary name</string>
<string name="dialog_new_contact_account">Create contact under account</string>
<string name="menu_sync_remove">Remove sync group</string>
@@ -935,7 +1046,11 @@
<!-- Title for data source when creating or editing a contact that doesn't
belong to a specific account. This contact will only exist on the phone
and will not be synced. -->
- <string name="account_phone">Phone-only, unsynced</string>
+ <string name="account_phone" product="tablet">Tablet-only, unsynced</string>
+ <!-- Title for data source when creating or editing a contact that doesn't
+ belong to a specific account. This contact will only exist on the phone
+ and will not be synced. -->
+ <string name="account_phone" product="default">Phone-only, unsynced</string>
<!-- Action string for calling a custom phone number -->
<string name="call_custom">Call <xliff:g id="custom">%s</xliff:g></string>
@@ -1066,6 +1181,8 @@
<!-- Generic action string for starting an IM chat -->
<string name="chat">Chat</string>
+ <!-- Field title for the full postal address of a contact [CHAR LIMIT=64]-->
+ <string name="postal_address">Address</string>
<!-- Field title for the street of a structured postal address of a contact -->
<string name="postal_street">Street</string>
<!-- Field title for the PO box of a structured postal address of a contact -->
@@ -1081,6 +1198,8 @@
<!-- Field title for the country of a structured postal address of a contact -->
<string name="postal_country">Country</string>
+ <!-- Field title for the full name of a contact [CHAR LIMIT=64]-->
+ <string name="full_name">Name</string>
<!-- Field title for the given name of a contact -->
<string name="name_given">Given name</string>
<!-- Field title for the family name of a contact -->
@@ -1137,32 +1256,234 @@
<!-- Button displayed underneath the list of filtered visible contacts -->
<string name="search_for_all_contacts">Search for all contacts</string>
- <!-- An option in the 'change photo' or 'pick photo' dialog -->
+ <!-- An option in the 'Contact photo' dialog, if there is no photo yet [CHAR LIMIT=50] -->
<string name="take_photo">Take photo</string>
- <!-- An option in the 'change photo' or 'pick photo' dialog -->
+ <!-- An option in the 'Contact photo' dialog, if there is already a photo [CHAR LIMIT=50] -->
+ <string name="take_new_photo">Take new photo</string>
+
+ <!-- An option in the 'Contact photo' dialog, if there is no photo yet [CHAR LIMIT=50] -->
<string name="pick_photo">Select photo from Gallery</string>
- <!-- Text shown in the contacts app while the background process updates contacts after a locale change -->
- <string name="locale_change_in_progress">Contact list is being updated to reflect the change of language.\n\nPlease wait...</string>
+ <!-- An option in the 'Contact photo' dialog, if there is already a photo [CHAR LIMIT=50] -->
+ <string name="pick_new_photo">Select new photo from Gallery</string>
- <!-- Text shown in the contacts app while the background process updates contacts after a system upgrade -->
- <string name="upgrade_in_progress">Contact list is being updated.\n\nPlease wait...</string>
+ <!-- Text shown in the contacts app while the background process updates contacts after a locale change [CHAR LIMIT=300] -->
+ <string name="locale_change_in_progress">Contact list is being updated to reflect the change of language.</string>
- <!-- Text shown in the contacts app if the background process updating contacts fails because of memory shortage -->
+ <!-- Text shown in the contacts app while the background process updates contacts after a system upgrade [CHAR LIMIT=300] -->
+ <string name="upgrade_in_progress">Contact list is being updated.</string>
+
+ <!-- Text shown in the contacts app if the background process updating contacts fails because of memory shortage [CHAR LIMIT=300] -->
<string name="upgrade_out_of_memory">Contacts are in the process of being upgraded.
- \n\nThe upgrade process requires approximately <xliff:g id="size_in_megabytes">%d</xliff:g>
- Mb of internal phone storage.\n\nChoose one of the following options:</string>
+ \n\nThe upgrade process requires approximately <xliff:g id="size_in_megabytes">%s</xliff:g>
+ Mb of internal storage.\n\nChoose one of the following options:</string>
- <!-- Button shown in the contacts app if the background process updating contacts fails because of memory shortage -->
+ <!-- Button shown in the contacts app if the background process updating contacts fails because of memory shortage [CHAR LIMIT=50] -->
<string name="upgrade_out_of_memory_uninstall">Uninstall some applications</string>
- <!-- Button shown in the contacts app if the background process updating contacts fails because of memory shortage -->
+ <!-- Button shown in the contacts app if the background process updating contacts fails because of memory shortage [CHAR LIMIT=50] -->
<string name="upgrade_out_of_memory_retry">Retry upgrade</string>
- <!-- Title shown in the search result activity of contacts app -->
- <string name="search_results_for">Search results for: <xliff:g id="query">%s</xliff:g></string>
-
<!-- Title shown in the search result activity of contacts app while searching -->
<string name="search_results_searching">Searching...</string>
+
+ <!-- Label to display only selection in multiple picker -->
+ <string name="menu_display_selected">"Show selected"</string>
+
+ <!-- Label to display all recipients in multiple picker -->
+ <string name="menu_display_all">"Show all"</string>
+
+ <!-- Label to select all contacts in multiple picker -->
+ <string name="menu_select_all">"Select all"</string>
+
+ <!-- Label to clear all selection in multiple picker -->
+ <string name="menu_select_none">"Unselect all"</string>
+
+ <!-- Label to display how many selected in multiple picker -->
+ <plurals name="multiple_picker_title">
+ <!-- number of selected recipients is one -->
+ <item quantity="one">"1 recipient selected"</item>
+ <!-- number of selected recipients is not equal to one -->
+ <item quantity="other"><xliff:g id="count">%d</xliff:g>" recipients selected"</item>
+ </plurals>
+
+ <!-- The text displayed when the contacts list is empty while displaying only selected contacts in multiple picker -->
+ <string name="no_contacts_selected">"No contacts selected."</string>
+
+ <!-- The add field button shown in the editor under each editable Raw Contact [CHAR LIMIT=30] -->
+ <string name="add_field">Add another field</string>
+
+ <!-- Attbution of a contact status update, when the time of update is unknown -->
+ <string name="contact_status_update_attribution">via <xliff:g id="source" example="Google Talk">%1$s</xliff:g></string>
+
+ <!-- Attbution of a contact status update, when the time of update is known -->
+ <string name="contact_status_update_attribution_with_date"><xliff:g id="date" example="3 hours ago">%1$s</xliff:g> via <xliff:g id="source" example="Google Talk">%2$s</xliff:g></string>
+
+ <!-- String describing the Star/Favorite checkbox
+
+ Used by AccessibilityService to announce the purpose of the view.
+ -->
+ <string name="description_star">favorite</string>
+
+ <!-- The title of the Edit-Contact screen -->
+ <string name="edit_contact">Edit contact</string>
+
+ <!-- Shows how many contacts have been merged. The value 1 is not shown but should be translated
+ anyway if we change our mind later -->
+ <plurals name="merge_info">
+ <item quantity="one">not merged</item>
+ <item quantity="other">merged from <xliff:g id="count">%0$d</xliff:g> sources</item>
+ </plurals>
+
+ <!-- The name of the invisible local contact directory -->
+ <string name="local_invisible_directory">Other</string>
+
+ <!-- The title of a confirmation dialog shown when the user selects a
+ contact aggregation suggestion in Contact editor. [CHAR LIMIT=128]-->
+ <string name="aggregation_suggestion_join_dialog_title">Join contacts</string>
+
+ <!-- The message in a confirmation dialog shown when the user selects a
+ contact aggregation suggestion in Contact editor. [CHAR LIMIT=512]-->
+ <string name="aggregation_suggestion_join_dialog_message">Join
+ the current contact with the selected contact?</string>
+
+ <!-- The title of a confirmation dialog shown when the user selects a
+ contact aggregation suggestion in Contact editor. [CHAR LIMIT=128]-->
+ <string name="aggregation_suggestion_edit_dialog_title">Edit selected contacts</string>
+
+ <!-- The message in a confirmation dialog shown when the user selects a
+ contact aggregation suggestion in Contact editor. [CHAR LIMIT=512]-->
+ <string name="aggregation_suggestion_edit_dialog_message">Switch to editing
+ the selected contact? Information you entered so far will be copied.</string>
+
+ <!-- The menu item (or button) that creates a local copy of a corporate contact. [CHAR LIMIT=40]-->
+ <string name="menu_copyContact">Copy to my contacts</string>
+
+ <!-- The description of the directory where the contact was found [CHAR LIMIT=100]-->
+ <string name="contact_directory_description">Directory <xliff:g id="type" example="Corporate Directory">%1$s</xliff:g></string>
+
+ <!-- The label displayed in the Contacts action bar when in search mode [CHAR LIMIT=64] -->
+ <string name="search_label">Searching all contacts</string>
+
+ <!-- The label in section header in the contact list for a contact directory [CHAR LIMIT=128] -->
+ <string name="directory_search_label">Directory</string>
+
+ <!-- The label in section header in the contact list for a local contacts [CHAR LIMIT=128] -->
+ <string name="local_search_label">Contacts</string>
+
+ <!-- Toast shown when creating a personal copy of a contact [CHAR LIMIT=100] -->
+ <string name="toast_making_personal_copy">Creating a personal copy</string>
+
+ <!-- Prompt for selection of a contact list filter [CHAR LIMIT=64] -->
+ <string name="list_filter_prompt">Choose contact list</string>
+
+ <!-- Contact list filter label indicating that the list is showing all available accounts [CHAR LIMIT=64] -->
+ <string name="list_filter_all_accounts">All contacts</string>
+
+ <!-- Contact list filter label indicating that the list is showing all starred contacts [CHAR LIMIT=64] -->
+ <string name="list_filter_all_starred">Starred</string>
+
+ <!-- Contact list filter indicating that the list shows groups chosen by the user [CHAR LIMIT=64] -->
+ <string name="list_filter_custom">Custom</string>
+
+ <!-- Contact list filter selection indicating that the list shows groups chosen by the user [CHAR LIMIT=64] -->
+ <string name="list_filter_customize">Customize...</string>
+
+ <!-- Contact list filter selection indicating that the list shows all contacts with phone numbers [CHAR LIMIT=64] -->
+ <string name="list_filter_phones">Contacts with phone numbers</string>
+
+ <!-- Contact list filter selection indicating that the list shows only the selected contact [CHAR LIMIT=64] -->
+ <string name="list_filter_single">Contact</string>
+
+ <!-- Title of the activity that allows the user to customize filtering of contact list [CHAR LIMIT=128] -->
+ <string name="custom_list_filter">Define custom view</string>
+
+ <!-- Title of the settings activity [CHAR LIMIT=64] -->
+ <string name="activity_title_settings">Settings</string>
+
+ <!-- Menu item for the settings activity [CHAR LIMIT=64] -->
+ <string name="menu_settings">Settings</string>
+
+ <!-- The preference section title for contact display options [CHAR LIMIT=128] -->
+ <string name="preference_displayOptions">Display options</string>
+
+ <!-- Text used to show a organization that has both a company and title. This is used in the Detail-View
+ of a Contact. This is mostly about the formatting of the two elements, so it should be kept small [CHAR LIMIT=79] -->
+ <string name="organization_company_and_title"><xliff:g id="company" example="Technical Program Manager">%2$s</xliff:g>, <xliff:g id="company" example="Google Inc.">%1$s</xliff:g></string>
+
+ <!-- Query hint displayed inside the search field [CHAR LIMIT=64] -->
+ <string name="hint_findContacts">Find contacts</string>
+
+ <!-- Title shown for the phone number when the number tries to call on a device that it not a phone [CHAR LIMIT=30] -->
+ <string name="non_phone_caption">Phone number</string>
+
+ <!-- Button to add a phone number to contacts [CHAR LIMIT=25] -->
+ <string name="non_phone_add_to_contacts">Add to contacts</string>
+
+ <!-- Button to close without add a phone number to contacts [CHAR LIMIT=25] -->
+ <string name="non_phone_close">Close</string>
+
+ <!-- Format string that combines the name and the phonetic name for the widget. if the phonetic name is empty, only the display name is used instead [CHAR LIMIT=25] -->
+ <string name="widget_name_and_phonetic"><xliff:g id="display_name" example="John Huber">%1$s</xliff:g> (<xliff:g id="phonetic_name">%2$s</xliff:g>)</string>
+
+ <!-- Checkbox whether to provide a year for a birthday [CHAR LIMIT=30] -->
+ <string name="date_year_toggle">Provide a year</string>
+
+ <!-- Label for the widget that shows picture and social status of a contact [CHAR LIMIT=20] -->
+ <string name="social_widget_label">Contact</string>
+
+ <!-- Message of widget while it is loading data [CHAR LIMIT=20] -->
+ <string name="social_widget_loading">Loading \u2026</string>
+
+ <!-- Button shown on the main contacts screen when there are no contacts on the device.
+ Creates a new contact. [CHAR LIMIT=128] -->
+ <string name="contacts_unavailable_create_contact">Create a new contact</string>
+
+ <!-- Button shown on the main contacts screen when there are no contacts on the device.
+ Navigates to account setup [CHAR LIMIT=128] -->
+ <string name="contacts_unavailable_add_account">Sign in to an account</string>
+
+ <!-- Button shown on the main contacts screen when there are no contacts on the device.
+ Initiates a contact import dialog [CHAR LIMIT=128] -->
+ <string name="contacts_unavailable_import_contacts">Import contacts from a file</string>
+
+ <!-- Title of the dialog that allows creation of a contact group [CHAR LIMIT=128] -->
+ <string name="create_group_dialog_title">Create new group</string>
+
+ <!-- An item in the popup list of groups that triggers creation of a contact group [CHAR LIMIT=128] -->
+ <string name="create_group_item_label">[Create new group]</string>
+
+ <!-- Title of the dialog that allows renaming of a contact group [CHAR LIMIT=128] -->
+ <string name="rename_group_dialog_title">Rename group</string>
+
+ <!-- Title of the dialog that allows deletion of a contact group [CHAR LIMIT=128] -->
+ <string name="delete_group_dialog_title">Delete group</string>
+
+ <!-- Confirmation message of the dialog that allows deletion of a contact group [CHAR LIMIT=256] -->
+ <string name="delete_group_dialog_message">Are you sure you want to delete the group
+ \'<xliff:g id="group_label" example="Friends">%1$s</xliff:g>\'?
+ (Contacts themselves will not be deleted.)
+ </string>
+
+ <!-- Toast displayed when the user creates a new contact and attempts to join it
+ with another before entering any data [CHAR LIMIT=256] -->
+ <string name="toast_join_with_empty_contact">Please enter contact name before joining
+ with another contact.
+ </string>
+
+ <!-- Joined contact indicator displayed in the contact detail [CHAR LIMIT=64] -->
+ <string name="indicator_joined_contact">Joined contact</string>
+
+ <!-- Toast shown when text is copied to the clipboard [CHAR LIMIT=64] -->
+ <string name="toast_text_copied">Text copied</string>
+
+ <!-- Title of the alert dialog when the user hits the Cancel button in the editor [CHAR LIMIT=64] -->
+ <string name="cancel_confirmation_dialog_title">Discard changes</string>
+
+ <!-- Contents of the alert dialog when the user hits the Cancel button in the editor [CHAR LIMIT=128] -->
+ <string name="cancel_confirmation_dialog_message">Do you want to discard your changes?</string>
+
+ <!-- Button in the alert dialog when the user hits the Cancel button in the editor [CHAR LIMIT=20] -->
+ <string name="discard">Discard</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index ad4f4f6..db50204 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -13,40 +13,19 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<resources>
<style name="DialtactsTheme" parent="@android:Theme">
<item name="android:windowNoTitle">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
- <style name="ContactsSearchTheme" parent="@android:Theme.Translucent.NoTitleBar">
- <item name="android:windowAnimationStyle">@style/ContactsSearchAnimation</item>
+ <style name="CallDetailActivityTheme" parent="android:Theme.NoTitleBar">
+ <item name="android:windowContentOverlay">@null</item>
</style>
-
- <style name="MinusButton">
- <item name="android:background">@drawable/btn_circle</item>
- <item name="android:src">@drawable/ic_btn_round_minus</item>
- <item name="android:contentDescription">@string/description_minus_button</item>
+ <style name="ContactDetailActivityTheme" parent="android:Theme.NoTitleBar">
+ <item name="android:windowContentOverlay">@null</item>
</style>
-
- <style name="PlusButton">
- <item name="android:background">@drawable/btn_circle</item>
- <item name="android:src">@drawable/ic_btn_round_plus</item>
- <item name="android:contentDescription">@string/description_plus_button</item>
- </style>
-
- <style name="MoreButton">
- <item name="android:background">@drawable/btn_circle</item>
- <item name="android:src">@drawable/ic_btn_round_more</item>
- </style>
-
- <style name="LessButton">
- <item name="android:background">@drawable/btn_circle</item>
- <item name="android:src">@drawable/ic_btn_round_less</item>
- </style>
-
- <style name="TallTitleBarTheme" parent="android:Theme.NoTitleBar">
+ <style name="ContactEditorActivityTheme" parent="android:Theme.NoTitleBar">
<item name="android:windowContentOverlay">@null</item>
</style>
@@ -71,7 +50,8 @@
<item name="android:windowAnimationStyle">@style/DummyAnimation</item>
</style>
- <style name="QuickContact">
+ <style name="QuickContact" parent="@android:Theme.Holo.Light">
+ <item name="android:windowNoTitle">true</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsFloating">true</item>
@@ -100,4 +80,132 @@
<style name="DummyAnimation">
<item name="android:windowExitAnimation">@anim/dummy_animation</item>
</style>
+
+ <declare-styleable name="ContactBrowser">
+ <attr name="contact_filter_popup_width" format="dimension"/>
+ </declare-styleable>
+
+ <declare-styleable name="ContactListItemView">
+ <attr name="list_item_height" format="dimension"/>
+ <attr name="list_section_header_height" format="dimension"/>
+ <attr name="activated_background" format="reference"/>
+ <attr name="section_header_background" format="reference"/>
+ <attr name="list_item_divider" format="reference"/>
+ <attr name="list_item_padding_top" format="dimension"/>
+ <attr name="list_item_padding_right" format="dimension"/>
+ <attr name="list_item_padding_bottom" format="dimension"/>
+ <attr name="list_item_padding_left" format="dimension"/>
+ <attr name="list_item_gap_between_image_and_text" format="dimension"/>
+ <attr name="list_item_gap_between_label_and_data" format="dimension"/>
+ <attr name="list_item_call_button_padding" format="dimension"/>
+ <attr name="list_item_vertical_divider_margin" format="dimension"/>
+ <attr name="list_item_presence_icon_margin" format="dimension"/>
+ <attr name="list_item_photo_size" format="dimension"/>
+ <attr name="list_item_prefix_highlight_color" format="color"/>
+ <attr name="list_item_header_text_indent" format="dimension" />
+ <attr name="list_item_header_text_color" format="color" />
+ <attr name="list_item_header_text_size" format="dimension" />
+ </declare-styleable>
+
+ <style name="ContactBrowserTheme" parent="@android:Theme">
+ <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
+ <item name="activated_background">@drawable/list_item_activated_background</item>
+ <item name="section_header_background">@drawable/section_header</item>
+ <item name="list_section_header_height">32dip</item>
+ <item name="list_item_divider">@drawable/list_item_divider</item>
+ <item name="list_item_padding_top">4dip</item>
+ <item name="list_item_padding_right">11dip</item>
+ <item name="list_item_padding_bottom">4dip</item>
+ <item name="list_item_padding_left">4dip</item>
+ <item name="list_item_gap_between_image_and_text">8dip</item>
+ <item name="list_item_gap_between_label_and_data">5dip</item>
+ <item name="list_item_call_button_padding">14dip</item>
+ <item name="list_item_vertical_divider_margin">5dip</item>
+ <item name="list_item_presence_icon_margin">5dip</item>
+ <item name="list_item_photo_size">56dip</item>
+ <item name="list_item_prefix_highlight_color">#729a27</item>
+ <item name="list_item_header_text_indent">56dip</item>
+ <item name="list_item_header_text_color">?color/section_header_text_color</item>
+ <item name="list_item_header_text_size">14sp</item>
+ <item name="contact_filter_popup_width">320dip</item>
+ </style>
+
+ <style name="ContactPickerTheme" parent="@android:Theme">
+ <item name="section_header_background">@drawable/section_header</item>
+ <item name="list_item_divider">@drawable/list_item_divider</item>
+ <item name="list_item_padding_top">4dip</item>
+ <item name="list_item_padding_right">11dip</item>
+ <item name="list_item_padding_bottom">4dip</item>
+ <item name="list_item_padding_left">4dip</item>
+ <item name="list_item_gap_between_image_and_text">8dip</item>
+ <item name="list_item_gap_between_label_and_data">5dip</item>
+ <item name="list_item_call_button_padding">14dip</item>
+ <item name="list_item_vertical_divider_margin">5dip</item>
+ <item name="list_item_presence_icon_margin">5dip</item>
+ <item name="list_item_photo_size">56dip</item>
+ <item name="list_item_prefix_highlight_color">#729a27</item>
+ <item name="list_item_header_text_indent">56dip</item>
+ <item name="list_item_header_text_color">?color/section_header_text_color</item>
+ <item name="list_item_header_text_size">14sp</item>
+ </style>
+
+ <style name="JoinContactActivityTheme" parent="ContactPickerTheme" >
+ </style>
+
+ <style name="ContactsPreferencesTheme" parent="@android:Theme">
+ </style>
+
+ <style name="CustomContactListFilterTheme" parent="@android:Theme">
+ </style>
+
+ <style name="ContactPickerLayout" parent="ContactPickerTheme">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">match_parent</item>
+ </style>
+
+ <style name="CustomContactListFilterView" parent="CustomContactListFilterTheme">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">match_parent</item>
+ </style>
+
+ <declare-styleable name="InterpolatingLayout_Layout">
+ <attr name="layout_narrowParentWidth" format="dimension"/>
+ <attr name="layout_narrowWidth" format="dimension"/>
+ <attr name="layout_narrowMarginLeft" format="dimension"/>
+ <attr name="layout_narrowMarginRight" format="dimension"/>
+ <attr name="layout_narrowPaddingLeft" format="dimension"/>
+ <attr name="layout_narrowPaddingRight" format="dimension"/>
+ <attr name="layout_wideParentWidth" format="dimension"/>
+ <attr name="layout_wideWidth" format="dimension"/>
+ <attr name="layout_wideMarginLeft" format="dimension"/>
+ <attr name="layout_wideMarginRight" format="dimension"/>
+ <attr name="layout_widePaddingLeft" format="dimension"/>
+ <attr name="layout_widePaddingRight" format="dimension"/>
+ </declare-styleable>
+
+ <declare-styleable name="TransitionAnimationView">
+ <attr name="clipMarginLeft" format="dimension"/>
+ <attr name="clipMarginRight" format="dimension"/>
+ <attr name="clipMarginTop" format="dimension"/>
+ <attr name="clipMarginBottom" format="dimension"/>
+ <attr name="enterAnimation" format="reference"/>
+ <attr name="exitAnimation" format="reference"/>
+ <attr name="animationDuration" format="integer"/>
+ </declare-styleable>
+
+ <style name="DirectoryHeader" parent="ContactBrowserTheme">
+ <item name="android:background">@drawable/directory_bg</item>
+ </style>
+
+ <style name="NonPhoneActivityTheme" parent="@android:Theme.Translucent">
+ </style>
+
+ <style name="NonPhoneDialogTheme" parent="@android:Theme.Dialog">
+ </style>
+
+ <style name="SectionDivider">
+ <item name="android:background">#7e7e87</item>
+ <item name="android:layout_height">2dip</item>
+ <item name="android:layout_width">match_parent</item>
+ </style>
</resources>
diff --git a/res/xml/preference_display_options.xml b/res/xml/preference_display_options.xml
new file mode 100644
index 0000000..a65010d
--- /dev/null
+++ b/res/xml/preference_display_options.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+ <PreferenceCategory
+ android:title="@string/preference_displayOptions">
+ <com.android.contacts.preference.SortOrderPreference
+ android:key="sortOrder"
+ android:title="@string/display_options_sort_list_by"
+ android:dialogTitle="@string/display_options_sort_list_by" />
+
+ <com.android.contacts.preference.DisplayOrderPreference
+ android:key="displayOrder"
+ android:title="@string/display_options_view_names_as"
+ android:dialogTitle="@string/display_options_view_names_as" />
+ </PreferenceCategory>
+</PreferenceScreen>
diff --git a/res/xml/preference_headers.xml b/res/xml/preference_headers.xml
new file mode 100644
index 0000000..ed709fc
--- /dev/null
+++ b/res/xml/preference_headers.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<preference-headers
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <header
+ android:fragment="com.android.contacts.preference.DisplayOptionsPreferenceFragment"
+ android:title="@string/preference_displayOptions" />
+
+</preference-headers>
diff --git a/res/xml/social_widget_info.xml b/res/xml/social_widget_info.xml
new file mode 100644
index 0000000..a7bece6
--- /dev/null
+++ b/res/xml/social_widget_info.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+ <!-- It is enough to update once per day, as the widget watches the database for changes -->
+
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="294dip"
+ android:minHeight="72dip"
+ android:updatePeriodMillis="86400000"
+ android:initialLayout="@layout/social_widget"
+ android:previewImage="@drawable/contacts_widget_preview"
+ android:configure="com.android.contacts.socialwidget.SocialWidgetConfigureActivity" >
+</appwidget-provider>
diff --git a/src/com/android/contacts/AttachImage.java b/src/com/android/contacts/AttachImage.java
deleted file mode 100644
index 599ab4f..0000000
--- a/src/com/android/contacts/AttachImage.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * 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.google.android.collect.Maps;
-
-import android.app.Activity;
-import android.content.ContentProviderOperation;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.widget.Toast;
-
-import com.android.contacts.model.ExchangeSource;
-import com.android.contacts.model.GoogleSource;
-
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * 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 RAW_CONTACT_URIS_KEY = "raw_contact_uris";
-
- public AttachImage() {
-
- }
-
- private Long[] mRawContactIds;
-
- private ContentResolver mContentResolver;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- if (icicle != null) {
- mRawContactIds = toClassArray(icicle.getLongArray(RAW_CONTACT_URIS_KEY));
- } else {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.setType(Contacts.CONTENT_ITEM_TYPE);
- startActivityForResult(intent, REQUEST_PICK_CONTACT);
- }
-
- mContentResolver = getContentResolver();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
-
- if (mRawContactIds != null && mRawContactIds.length != 0) {
- outState.putLongArray(RAW_CONTACT_URIS_KEY, toPrimativeArray(mRawContactIds));
- }
- }
-
- private static long[] toPrimativeArray(Long[] in) {
- if (in == null) {
- return null;
- }
- long[] out = new long[in.length];
- for (int i = 0; i < in.length; i++) {
- out[i] = in[i];
- }
- return out;
- }
-
- private static Long[] toClassArray(long[] in) {
- if (in == null) {
- return null;
- }
- Long[] out = new Long[in.length];
- for (int i = 0; i < in.length; i++) {
- out[i] = in[i];
- }
- return out;
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent result) {
- if (resultCode != RESULT_OK) {
- finish();
- return;
- }
-
- if (requestCode == REQUEST_PICK_CONTACT) {
- // 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);
-
- // while they're cropping, convert the contact into a raw_contact
- final long contactId = ContentUris.parseId(result.getData());
- final ArrayList<Long> rawContactIdsList = ContactsUtils.queryForAllRawContactIds(
- mContentResolver, contactId);
- mRawContactIds = new Long[rawContactIdsList.size()];
- mRawContactIds = rawContactIdsList.toArray(mRawContactIds);
-
- if (mRawContactIds == null || rawContactIdsList.isEmpty()) {
- Toast.makeText(this, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
- }
- } else if (requestCode == REQUEST_CROP_PHOTO) {
- final Bundle extras = result.getExtras();
- if (extras != null && mRawContactIds != null) {
- Bitmap photo = extras.getParcelable("data");
- if (photo != null) {
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- photo.compress(Bitmap.CompressFormat.JPEG, 75, stream);
-
- final ContentValues imageValues = new ContentValues();
- imageValues.put(Photo.PHOTO, stream.toByteArray());
- imageValues.put(RawContacts.Data.IS_SUPER_PRIMARY, 1);
-
- // attach the photo to every raw contact
- for (Long rawContactId : mRawContactIds) {
-
- // exchange and google only allow one image, so do an update rather than insert
- boolean shouldUpdate = false;
-
- final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
- rawContactId);
- final Uri rawContactDataUri = Uri.withAppendedPath(rawContactUri,
- RawContacts.Data.CONTENT_DIRECTORY);
- insertPhoto(imageValues, rawContactDataUri, true);
- }
- }
- }
- finish();
- }
- }
-
- /**
- * Inserts a photo on the raw contact.
- * @param values the photo values
- * @param assertAccount if true, will check to verify that no photos exist for Google,
- * Exchange and unsynced phone account types. These account types only take one picture,
- * so if one exists, the account will be updated with the new photo.
- */
- private void insertPhoto(ContentValues values, Uri rawContactDataUri,
- boolean assertAccount) {
-
- ArrayList<ContentProviderOperation> operations =
- new ArrayList<ContentProviderOperation>();
-
- if (assertAccount) {
- // Make sure no pictures exist for Google, Exchange and unsynced phone accounts.
- operations.add(ContentProviderOperation.newAssertQuery(rawContactDataUri)
- .withSelection(Photo.MIMETYPE + "=? AND ("
- + RawContacts.ACCOUNT_TYPE + " IN (?,?) OR "
- + RawContacts.ACCOUNT_TYPE + " IS NULL)",
- new String[] {Photo.CONTENT_ITEM_TYPE, GoogleSource.ACCOUNT_TYPE,
- ExchangeSource.ACCOUNT_TYPE})
- .withExpectedCount(0).build());
- }
-
- // insert the photo
- values.put(Photo.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
- operations.add(ContentProviderOperation.newInsert(rawContactDataUri)
- .withValues(values).build());
-
- try {
- mContentResolver.applyBatch(ContactsContract.AUTHORITY, operations);
- } catch (RemoteException e) {
- throw new IllegalStateException("Problem querying raw_contacts/data", e);
- } catch (OperationApplicationException e) {
- // the account doesn't allow multiple photos, so update
- if (assertAccount) {
- updatePhoto(values, rawContactDataUri, false);
- } else {
- throw new IllegalStateException("Problem inserting photo into raw_contacts/data", e);
- }
- }
- }
-
- /**
- * Tries to update the photo on the raw_contact. If no photo exists, and allowInsert == true,
- * then will try to {@link #updatePhoto(ContentValues, boolean)}
- */
- private void updatePhoto(ContentValues values, Uri rawContactDataUri,
- boolean allowInsert) {
- ArrayList<ContentProviderOperation> operations =
- new ArrayList<ContentProviderOperation>();
-
- values.remove(Photo.MIMETYPE);
-
- // check that a photo exists
- operations.add(ContentProviderOperation.newAssertQuery(rawContactDataUri)
- .withSelection(Photo.MIMETYPE + "=?", new String[] {
- Photo.CONTENT_ITEM_TYPE
- }).withExpectedCount(1).build());
-
- // update that photo
- operations.add(ContentProviderOperation.newUpdate(rawContactDataUri).withSelection(Photo.MIMETYPE + "=?", new String[] {
- Photo.CONTENT_ITEM_TYPE}).withValues(values).build());
-
- try {
- mContentResolver.applyBatch(ContactsContract.AUTHORITY, operations);
- } catch (RemoteException e) {
- throw new IllegalStateException("Problem querying raw_contacts/data", e);
- } catch (OperationApplicationException e) {
- if (allowInsert) {
- // they deleted the photo between insert and update, so insert one
- insertPhoto(values, rawContactDataUri, false);
- } else {
- throw new IllegalStateException("Problem inserting photo raw_contacts/data", e);
- }
- }
- }
-}
diff --git a/src/com/android/contacts/CallContactActivity.java b/src/com/android/contacts/CallContactActivity.java
new file mode 100644
index 0000000..38e890d
--- /dev/null
+++ b/src/com/android/contacts/CallContactActivity.java
@@ -0,0 +1,75 @@
+/*
+ * 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 com.android.contacts.interactions.PhoneNumberInteraction;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+
+/**
+ * An interstitial activity used when the user selects a QSB search suggestion using
+ * a call button.
+ */
+public class CallContactActivity extends ContactsActivity implements OnDismissListener {
+
+ private PhoneNumberInteraction mPhoneNumberInteraction;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mPhoneNumberInteraction = new PhoneNumberInteraction(this, false, this);
+
+ Uri contactUri = getIntent().getData();
+ if (contactUri == null) {
+ finish();
+ }
+
+ // If we are being invoked with a saved state, rely on Activity to restore it
+ if (savedInstanceState != null) {
+ return;
+ }
+
+ if (Contacts.CONTENT_ITEM_TYPE.equals(getContentResolver().getType(contactUri))) {
+ mPhoneNumberInteraction.startInteraction(contactUri);
+ } else {
+ startActivity(new Intent(Intent.ACTION_CALL_PRIVILEGED, contactUri));
+ finish();
+ }
+ }
+
+ public void onDismiss(DialogInterface dialog) {
+ if (!isChangingConfigurations()) {
+ finish();
+ }
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id, Bundle args) {
+ return mPhoneNumberInteraction.onCreateDialog(id, args);
+ }
+
+ @Override
+ protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
+ mPhoneNumberInteraction.onPrepareDialog(id, dialog, args);
+ }
+}
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 9f027bb..b3c68ef 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -63,6 +63,7 @@
private TextView mCallDuration;
private String mNumber = null;
+ private String mDefaultCountryIso;
/* package */ LayoutInflater mInflater;
/* package */ Resources mResources;
@@ -72,12 +73,14 @@
CallLog.Calls.DURATION,
CallLog.Calls.NUMBER,
CallLog.Calls.TYPE,
+ CallLog.Calls.COUNTRY_ISO,
};
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 int COUNTRY_ISO_COLUMN_INDEX = 4;
static final String[] PHONES_PROJECTION = new String[] {
PhoneLookup._ID,
@@ -85,12 +88,14 @@
PhoneLookup.TYPE,
PhoneLookup.LABEL,
PhoneLookup.NUMBER,
+ PhoneLookup.NORMALIZED_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;
+ static final int COLUMN_INDEX_NORMALIZED_NUMBER = 5;
@Override
protected void onCreate(Bundle icicle) {
@@ -105,6 +110,7 @@
mCallTypeIcon = (ImageView) findViewById(R.id.icon);
mCallTime = (TextView) findViewById(R.id.time);
mCallDuration = (TextView) findViewById(R.id.duration);
+ mDefaultCountryIso = ContactsUtils.getCurrentCountryIso(this);
getListView().setOnItemClickListener(this);
}
@@ -126,7 +132,6 @@
Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Uri.fromParts("tel", mNumber, null));
startActivity(callIntent);
- StickyTabs.saveTab(this, getIntent());
return true;
}
}
@@ -150,7 +155,10 @@
long date = callCursor.getLong(DATE_COLUMN_INDEX);
long duration = callCursor.getLong(DURATION_COLUMN_INDEX);
int callType = callCursor.getInt(CALL_TYPE_COLUMN_INDEX);
-
+ String countryIso = callCursor.getString(COUNTRY_ISO_COLUMN_INDEX);
+ if (TextUtils.isEmpty(countryIso)) {
+ countryIso = mDefaultCountryIso;
+ }
// Pull out string in format [relative], [date]
CharSequence dateClause = DateUtils.formatDateRange(this, date, date,
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE |
@@ -210,12 +218,14 @@
callText = getString(R.string.recentCalls_callNumber,
phonesCursor.getString(COLUMN_INDEX_NAME));
mNumber = PhoneNumberUtils.formatNumber(
- phonesCursor.getString(COLUMN_INDEX_NUMBER));
+ phonesCursor.getString(COLUMN_INDEX_NUMBER),
+ phonesCursor.getString(COLUMN_INDEX_NORMALIZED_NUMBER),
+ countryIso);
callLabel = Phone.getDisplayLabel(this,
phonesCursor.getInt(COLUMN_INDEX_TYPE),
phonesCursor.getString(COLUMN_INDEX_LABEL)).toString();
} else {
- mNumber = PhoneNumberUtils.formatNumber(mNumber);
+ mNumber = PhoneNumberUtils.formatNumber(mNumber, countryIso);
}
} finally {
if (phonesCursor != null) phonesCursor.close();
@@ -241,7 +251,6 @@
// to create new contact from this number.
if (personUri != null) {
Intent viewIntent = new Intent(Intent.ACTION_VIEW, personUri);
- StickyTabs.setTab(viewIntent, getIntent());
actions.add(new ViewEntry(R.drawable.sym_action_view_contact,
getString(R.string.menu_viewContact), viewIntent));
} else {
@@ -364,9 +373,6 @@
if (view.getTag() instanceof ViewEntry) {
ViewEntry entry = (ViewEntry) view.getTag();
if (entry.intent != null) {
- if (Intent.ACTION_CALL_PRIVILEGED.equals(entry.intent.getAction())) {
- StickyTabs.saveTab(this, getIntent());
- }
startActivity(entry.intent);
}
}
diff --git a/src/com/android/contacts/Collapser.java b/src/com/android/contacts/Collapser.java
index 3872dfd..5b5d5a0 100644
--- a/src/com/android/contacts/Collapser.java
+++ b/src/com/android/contacts/Collapser.java
@@ -16,9 +16,8 @@
package com.android.contacts;
-import java.util.HashMap;
import java.util.Iterator;
-import java.util.ArrayList;
+import java.util.List;
/**
* Class used for collapsing data items into groups of similar items. The data items that should be
@@ -44,12 +43,12 @@
/**
* Collapses a list of Collapsible items into a list of collapsed items. Items are collapsed
- * if {@link Collapsible#shouldCollapseWith(Object) return strue, and are collapsed
+ * if {@link Collapsible#shouldCollapseWith(Object)} returns strue, and are collapsed
* through the {@Link Collapsible#collapseWith(Object)} function implemented by the data item.
*
- * @param list ArrayList of Objects of type <T extends Collapsible<T>> to be collapsed.
+ * @param list List of Objects of type <T extends Collapsible<T>> to be collapsed.
*/
- public static <T extends Collapsible<T>> void collapseList(ArrayList<T> list) {
+ public static <T extends Collapsible<T>> void collapseList(List<T> list) {
int listSize = list.size();
diff --git a/src/com/android/contacts/ContactEntryAdapter.java b/src/com/android/contacts/ContactEntryAdapter.java
deleted file mode 100644
index 34ee505..0000000
--- a/src/com/android/contacts/ContactEntryAdapter.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * 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.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-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 {
-
- protected ArrayList<ArrayList<E>> mSections;
- protected LayoutInflater mInflater;
- protected Context mContext;
- protected boolean mSeparators;
-
- /**
- * Base class for adapter entries.
- */
- public static class Entry {
- public int type = -1;
- public String label;
- public String data;
- public Uri uri;
- public long id = 0;
- public long contactId;
- public int maxLines = 1;
- public String mimetype;
-
- /**
- * Helper for making subclasses parcelable.
- */
- protected void writeToParcel(Parcel p) {
- p.writeInt(type);
- p.writeString(label);
- p.writeString(data);
- p.writeParcelable(uri, 0);
- p.writeLong(id);
- p.writeInt(maxLines);
- p.writeString(mimetype);
- }
-
- /**
- * Helper for making subclasses parcelable.
- */
- protected void readFromParcel(Parcel p) {
- final ClassLoader loader = getClass().getClassLoader();
- type = p.readInt();
- label = p.readString();
- data = p.readString();
- uri = p.readParcelable(loader);
- id = p.readLong();
- maxLines = p.readInt();
- mimetype = p.readString();
- }
- }
-
- 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/ContactListEmptyView.java b/src/com/android/contacts/ContactListEmptyView.java
new file mode 100644
index 0000000..40d5152
--- /dev/null
+++ b/src/com/android/contacts/ContactListEmptyView.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2010 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.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IContentService;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.telephony.TelephonyManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+/**
+ * Displays a message when there is nothing to display in a contact list.
+ */
+public class ContactListEmptyView extends ScrollView {
+
+ private static final String TAG = "ContactListEmptyView";
+
+ public ContactListEmptyView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void hide() {
+ TextView empty = (TextView) findViewById(R.id.emptyText);
+ empty.setVisibility(GONE);
+ }
+
+ public void show(boolean searchMode, boolean displayOnlyPhones,
+ boolean isFavoritesMode, boolean isQueryMode, boolean isShortcutAction,
+ boolean isMultipleSelectionEnabled, boolean showSelectedOnly) {
+ if (searchMode) {
+ return;
+ }
+
+ TextView empty = (TextView) findViewById(R.id.emptyText);
+ Context context = getContext();
+ if (displayOnlyPhones) {
+ empty.setText(context.getText(R.string.noContactsWithPhoneNumbers));
+ } else if (isFavoritesMode) {
+ empty.setText(context.getText(R.string.noFavoritesHelpText));
+ } else if (isQueryMode) {
+ empty.setText(context.getText(R.string.noMatchingContacts));
+ } if (isMultipleSelectionEnabled) {
+ if (showSelectedOnly) {
+ empty.setText(context.getText(R.string.no_contacts_selected));
+ } else {
+ empty.setText(context.getText(R.string.noContactsWithPhoneNumbers));
+ }
+ } else {
+ TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ boolean hasSim = telephonyManager.hasIccCard();
+ if (isSyncActive()) {
+ if (isShortcutAction) {
+ // Help text is the same no matter whether there is SIM or not.
+ empty.setText(
+ context.getText(R.string.noContactsHelpTextWithSyncForCreateShortcut));
+ } else if (hasSim) {
+ empty.setText(context.getText(R.string.noContactsHelpTextWithSync));
+ } else {
+ empty.setText(context.getText(R.string.noContactsNoSimHelpTextWithSync));
+ }
+ } else {
+ if (isShortcutAction) {
+ // Help text is the same no matter whether there is SIM or not.
+ empty.setText(context.getText(R.string.noContactsHelpTextForCreateShortcut));
+ } else if (hasSim) {
+ empty.setText(context.getText(R.string.noContactsHelpText));
+ } else {
+ empty.setText(context.getText(R.string.noContactsNoSimHelpText));
+ }
+ }
+ }
+ empty.setVisibility(VISIBLE);
+ }
+
+ private boolean isSyncActive() {
+ Account[] accounts = AccountManager.get(getContext()).getAccounts();
+ if (accounts != null && accounts.length > 0) {
+ IContentService contentService = ContentResolver.getContentService();
+ for (Account account : accounts) {
+ try {
+ if (contentService.isSyncActive(account, ContactsContract.AUTHORITY)) {
+ return true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not get the sync status");
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/contacts/ContactListItemView.java b/src/com/android/contacts/ContactListItemView.java
deleted file mode 100644
index 89e4265..0000000
--- a/src/com/android/contacts/ContactListItemView.java
+++ /dev/null
@@ -1,595 +0,0 @@
-/*
- * Copyright (C) 2010 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.contacts.ui.widget.DontPressWithParentImageView;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Typeface;
-import android.graphics.drawable.Drawable;
-import android.provider.ContactsContract.Contacts;
-import android.text.TextUtils;
-import android.text.TextUtils.TruncateAt;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.QuickContactBadge;
-import android.widget.TextView;
-import android.widget.ImageView.ScaleType;
-
-/**
- * A custom view for an item in the contact list.
- */
-public class ContactListItemView extends ViewGroup {
-
- private static final int QUICK_CONTACT_BADGE_STYLE =
- com.android.internal.R.attr.quickContactBadgeStyleWindowMedium;
-
- private final Context mContext;
-
- private final int mPreferredHeight;
- private final int mVerticalDividerMargin;
- private final int mPaddingTop;
- private final int mPaddingRight;
- private final int mPaddingBottom;
- private final int mPaddingLeft;
- private final int mGapBetweenImageAndText;
- private final int mGapBetweenLabelAndData;
- private final int mCallButtonPadding;
- private final int mPresenceIconMargin;
- private final int mHeaderTextWidth;
-
- private boolean mHorizontalDividerVisible;
- private Drawable mHorizontalDividerDrawable;
- private int mHorizontalDividerHeight;
-
- private boolean mVerticalDividerVisible;
- private Drawable mVerticalDividerDrawable;
- private int mVerticalDividerWidth;
-
- private boolean mHeaderVisible;
- private Drawable mHeaderBackgroundDrawable;
- private int mHeaderBackgroundHeight;
- private TextView mHeaderTextView;
-
- private QuickContactBadge mQuickContact;
- private ImageView mPhotoView;
- private TextView mNameTextView;
- private DontPressWithParentImageView mCallButton;
- private TextView mLabelView;
- private TextView mDataView;
- private TextView mSnippetView;
- private ImageView mPresenceIcon;
-
- private int mPhotoViewWidth;
- private int mPhotoViewHeight;
- private int mLine1Height;
- private int mLine2Height;
- private int mLine3Height;
-
- private OnClickListener mCallButtonClickListener;
-
- public ContactListItemView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mContext = context;
-
- // Obtain preferred item height from the current theme
- TypedArray a = context.obtainStyledAttributes(null, com.android.internal.R.styleable.Theme);
- mPreferredHeight =
- a.getDimensionPixelSize(android.R.styleable.Theme_listPreferredItemHeight, 0);
- a.recycle();
-
- Resources resources = context.getResources();
- mVerticalDividerMargin =
- resources.getDimensionPixelOffset(R.dimen.list_item_vertical_divider_margin);
- mPaddingTop =
- resources.getDimensionPixelOffset(R.dimen.list_item_padding_top);
- mPaddingBottom =
- resources.getDimensionPixelOffset(R.dimen.list_item_padding_bottom);
- mPaddingLeft =
- resources.getDimensionPixelOffset(R.dimen.list_item_padding_left);
- mPaddingRight =
- resources.getDimensionPixelOffset(R.dimen.list_item_padding_right);
- mGapBetweenImageAndText =
- resources.getDimensionPixelOffset(R.dimen.list_item_gap_between_image_and_text);
- mGapBetweenLabelAndData =
- resources.getDimensionPixelOffset(R.dimen.list_item_gap_between_label_and_data);
- mCallButtonPadding =
- resources.getDimensionPixelOffset(R.dimen.list_item_call_button_padding);
- mPresenceIconMargin =
- resources.getDimensionPixelOffset(R.dimen.list_item_presence_icon_margin);
- mHeaderTextWidth =
- resources.getDimensionPixelOffset(R.dimen.list_item_header_text_width);
- }
-
- /**
- * Installs a call button listener.
- */
- public void setOnCallButtonClickListener(OnClickListener callButtonClickListener) {
- mCallButtonClickListener = callButtonClickListener;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // We will match parent's width and wrap content vertically, but make sure
- // height is no less than listPreferredItemHeight.
- int width = resolveSize(0, widthMeasureSpec);
- int height = 0;
-
- mLine1Height = 0;
- mLine2Height = 0;
- mLine3Height = 0;
-
- // Obtain the natural dimensions of the name text (we only care about height)
- mNameTextView.measure(0, 0);
-
- mLine1Height = mNameTextView.getMeasuredHeight();
-
- if (isVisible(mLabelView)) {
- mLabelView.measure(0, 0);
- mLine2Height = mLabelView.getMeasuredHeight();
- }
-
- if (isVisible(mDataView)) {
- mDataView.measure(0, 0);
- mLine2Height = Math.max(mLine2Height, mDataView.getMeasuredHeight());
- }
-
- if (isVisible(mSnippetView)) {
- mSnippetView.measure(0, 0);
- mLine3Height = mSnippetView.getMeasuredHeight();
- }
-
- height += mLine1Height + mLine2Height + mLine3Height;
-
- if (isVisible(mCallButton)) {
- mCallButton.measure(0, 0);
- }
- if (isVisible(mPresenceIcon)) {
- mPresenceIcon.measure(0, 0);
- }
-
- ensurePhotoViewSize();
-
- height = Math.max(height, mPhotoViewHeight);
- height = Math.max(height, mPreferredHeight);
-
- if (mHeaderVisible) {
- ensureHeaderBackground();
- mHeaderTextView.measure(
- MeasureSpec.makeMeasureSpec(mHeaderTextWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mHeaderBackgroundHeight, MeasureSpec.EXACTLY));
- height += mHeaderBackgroundDrawable.getIntrinsicHeight();
- }
-
- setMeasuredDimension(width, height);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- int height = bottom - top;
- int width = right - left;
-
- // Determine the vertical bounds by laying out the header first.
- int topBound = 0;
-
- if (mHeaderVisible) {
- mHeaderBackgroundDrawable.setBounds(
- 0,
- 0,
- width,
- mHeaderBackgroundHeight);
- mHeaderTextView.layout(0, 0, width, mHeaderBackgroundHeight);
- topBound += mHeaderBackgroundHeight;
- }
-
- // Positions of views on the left are fixed and so are those on the right side.
- // The stretchable part of the layout is in the middle. So, we will start off
- // by laying out the left and right sides. Then we will allocate the remainder
- // to the text fields in the middle.
-
- // Left side
- int leftBound = mPaddingLeft;
- View photoView = mQuickContact != null ? mQuickContact : mPhotoView;
- if (photoView != null) {
- // Center the photo vertically
- int photoTop = topBound + (height - topBound - mPhotoViewHeight) / 2;
- photoView.layout(
- leftBound,
- photoTop,
- leftBound + mPhotoViewWidth,
- photoTop + mPhotoViewHeight);
- leftBound += mPhotoViewWidth + mGapBetweenImageAndText;
- }
-
- // Right side
- int rightBound = right;
- if (isVisible(mCallButton)) {
- int buttonWidth = mCallButton.getMeasuredWidth();
- rightBound -= buttonWidth;
- mCallButton.layout(
- rightBound,
- topBound,
- rightBound + buttonWidth,
- height);
- mVerticalDividerVisible = true;
- ensureVerticalDivider();
- rightBound -= mVerticalDividerWidth;
- mVerticalDividerDrawable.setBounds(
- rightBound,
- topBound + mVerticalDividerMargin,
- rightBound + mVerticalDividerWidth,
- height - mVerticalDividerMargin);
- } else {
- mVerticalDividerVisible = false;
- }
-
- if (isVisible(mPresenceIcon)) {
- int iconWidth = mPresenceIcon.getMeasuredWidth();
- rightBound -= mPresenceIconMargin + iconWidth;
- mPresenceIcon.layout(
- rightBound,
- topBound,
- rightBound + iconWidth,
- height);
- }
-
- if (mHorizontalDividerVisible) {
- ensureHorizontalDivider();
- mHorizontalDividerDrawable.setBounds(
- 0,
- height - mHorizontalDividerHeight,
- width,
- height);
- }
-
- topBound += mPaddingTop;
- int bottomBound = height - mPaddingBottom;
-
- // Text lines, centered vertically
- rightBound -= mPaddingRight;
-
- // Center text vertically
- int totalTextHeight = mLine1Height + mLine2Height + mLine3Height;
- int textTopBound = (bottomBound + topBound - totalTextHeight) / 2;
-
- mNameTextView.layout(leftBound,
- textTopBound,
- rightBound,
- textTopBound + mLine1Height);
-
- int dataLeftBound = leftBound;
- if (isVisible(mLabelView)) {
- dataLeftBound = leftBound + mLabelView.getMeasuredWidth();
- mLabelView.layout(leftBound,
- textTopBound + mLine1Height,
- dataLeftBound,
- textTopBound + mLine1Height + mLine2Height);
- dataLeftBound += mGapBetweenLabelAndData;
- }
-
- if (isVisible(mDataView)) {
- mDataView.layout(dataLeftBound,
- textTopBound + mLine1Height,
- rightBound,
- textTopBound + mLine1Height + mLine2Height);
- }
-
- if (isVisible(mSnippetView)) {
- mSnippetView.layout(leftBound,
- textTopBound + mLine1Height + mLine2Height,
- rightBound,
- textTopBound + mLine1Height + mLine2Height + mLine3Height);
- }
- }
-
- private boolean isVisible(View view) {
- return view != null && view.getVisibility() == View.VISIBLE;
- }
-
- /**
- * Loads the drawable for the vertical divider if it has not yet been loaded.
- */
- private void ensureVerticalDivider() {
- if (mVerticalDividerDrawable == null) {
- mVerticalDividerDrawable = mContext.getResources().getDrawable(
- R.drawable.divider_vertical_dark);
- mVerticalDividerWidth = mVerticalDividerDrawable.getIntrinsicWidth();
- }
- }
-
- /**
- * Loads the drawable for the horizontal divider if it has not yet been loaded.
- */
- private void ensureHorizontalDivider() {
- if (mHorizontalDividerDrawable == null) {
- mHorizontalDividerDrawable = mContext.getResources().getDrawable(
- com.android.internal.R.drawable.divider_horizontal_dark_opaque);
- mHorizontalDividerHeight = mHorizontalDividerDrawable.getIntrinsicHeight();
- }
- }
-
- /**
- * Loads the drawable for the header background if it has not yet been loaded.
- */
- private void ensureHeaderBackground() {
- if (mHeaderBackgroundDrawable == null) {
- mHeaderBackgroundDrawable = mContext.getResources().getDrawable(
- android.R.drawable.dark_header);
- mHeaderBackgroundHeight = mHeaderBackgroundDrawable.getIntrinsicHeight();
- }
- }
-
- /**
- * Extracts width and height from the style
- */
- private void ensurePhotoViewSize() {
- if (mPhotoViewWidth == 0 && mPhotoViewHeight == 0) {
- TypedArray a = mContext.obtainStyledAttributes(null,
- com.android.internal.R.styleable.ViewGroup_Layout,
- QUICK_CONTACT_BADGE_STYLE, 0);
- mPhotoViewWidth = a.getLayoutDimension(
- android.R.styleable.ViewGroup_Layout_layout_width,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- mPhotoViewHeight = a.getLayoutDimension(
- android.R.styleable.ViewGroup_Layout_layout_height,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- a.recycle();
- }
- }
-
- @Override
- public void dispatchDraw(Canvas canvas) {
- if (mHeaderVisible) {
- mHeaderBackgroundDrawable.draw(canvas);
- }
- if (mHorizontalDividerVisible) {
- mHorizontalDividerDrawable.draw(canvas);
- }
- if (mVerticalDividerVisible) {
- mVerticalDividerDrawable.draw(canvas);
- }
- super.dispatchDraw(canvas);
- }
-
- /**
- * Sets the flag that determines whether a divider should drawn at the bottom
- * of the view.
- */
- public void setDividerVisible(boolean visible) {
- mHorizontalDividerVisible = visible;
- }
-
- /**
- * Sets section header or makes it invisible if the title is null.
- */
- public void setSectionHeader(String title) {
- if (!TextUtils.isEmpty(title)) {
- if (mHeaderTextView == null) {
- mHeaderTextView = new TextView(mContext);
- mHeaderTextView.setTypeface(mHeaderTextView.getTypeface(), Typeface.BOLD);
- mHeaderTextView.setTextColor(mContext.getResources()
- .getColor(com.android.internal.R.color.dim_foreground_dark));
- mHeaderTextView.setTextSize(14);
- mHeaderTextView.setGravity(Gravity.CENTER);
- addView(mHeaderTextView);
- }
- mHeaderTextView.setText(title);
- mHeaderTextView.setVisibility(View.VISIBLE);
- mHeaderVisible = true;
- } else {
- if (mHeaderTextView != null) {
- mHeaderTextView.setVisibility(View.GONE);
- }
- mHeaderVisible = false;
- }
- }
-
- /**
- * Returns the quick contact badge, creating it if necessary.
- */
- public QuickContactBadge getQuickContact() {
- if (mQuickContact == null) {
- mQuickContact = new QuickContactBadge(mContext, null, QUICK_CONTACT_BADGE_STYLE);
- mQuickContact.setExcludeMimes(new String[] { Contacts.CONTENT_ITEM_TYPE });
- addView(mQuickContact);
- }
- return mQuickContact;
- }
-
- /**
- * Returns the photo view, creating it if necessary.
- */
- public ImageView getPhotoView() {
- if (mPhotoView == null) {
- mPhotoView = new ImageView(mContext, null, QUICK_CONTACT_BADGE_STYLE);
- // Quick contact style used above will set a background - remove it
- mPhotoView.setBackgroundDrawable(null);
- addView(mPhotoView);
- }
- return mPhotoView;
- }
-
- /**
- * Returns the text view for the contact name, creating it if necessary.
- */
- public TextView getNameTextView() {
- if (mNameTextView == null) {
- mNameTextView = new TextView(mContext);
- mNameTextView.setSingleLine(true);
- mNameTextView.setEllipsize(TruncateAt.MARQUEE);
- mNameTextView.setTextAppearance(mContext, android.R.style.TextAppearance_Large);
- mNameTextView.setGravity(Gravity.CENTER_VERTICAL);
- addView(mNameTextView);
- }
- return mNameTextView;
- }
-
- /**
- * Adds a call button using the supplied arguments as an id and tag.
- */
- public void showCallButton(int id, int tag) {
- if (mCallButton == null) {
- mCallButton = new DontPressWithParentImageView(mContext, null);
- mCallButton.setId(id);
- mCallButton.setOnClickListener(mCallButtonClickListener);
- mCallButton.setBackgroundResource(R.drawable.call_background);
- mCallButton.setImageResource(android.R.drawable.sym_action_call);
- mCallButton.setPadding(mCallButtonPadding, 0, mCallButtonPadding, 0);
- mCallButton.setScaleType(ScaleType.CENTER);
- addView(mCallButton);
- }
-
- mCallButton.setTag(tag);
- mCallButton.setVisibility(View.VISIBLE);
- }
-
- public void hideCallButton() {
- if (mCallButton != null) {
- mCallButton.setVisibility(View.GONE);
- }
- }
-
- /**
- * Adds or updates a text view for the data label.
- */
- public void setLabel(CharSequence text) {
- if (TextUtils.isEmpty(text)) {
- if (mLabelView != null) {
- mLabelView.setVisibility(View.GONE);
- }
- } else {
- getLabelView();
- mLabelView.setText(text);
- mLabelView.setVisibility(VISIBLE);
- }
- }
-
- /**
- * Adds or updates a text view for the data label.
- */
- public void setLabel(char[] text, int size) {
- if (text == null || size == 0) {
- if (mLabelView != null) {
- mLabelView.setVisibility(View.GONE);
- }
- } else {
- getLabelView();
- mLabelView.setText(text, 0, size);
- mLabelView.setVisibility(VISIBLE);
- }
- }
-
- /**
- * Returns the text view for the data label, creating it if necessary.
- */
- public TextView getLabelView() {
- if (mLabelView == null) {
- mLabelView = new TextView(mContext);
- mLabelView.setSingleLine(true);
- mLabelView.setEllipsize(TruncateAt.MARQUEE);
- mLabelView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
- mLabelView.setTypeface(mLabelView.getTypeface(), Typeface.BOLD);
- addView(mLabelView);
- }
- return mLabelView;
- }
-
- /**
- * Adds or updates a text view for the data element.
- */
- public void setData(char[] text, int size) {
- if (text == null || size == 0) {
- if (mDataView != null) {
- mDataView.setVisibility(View.GONE);
- }
- return;
- } else {
- getDataView();
- mDataView.setText(text, 0, size);
- mDataView.setVisibility(VISIBLE);
- }
- }
-
- /**
- * Returns the text view for the data text, creating it if necessary.
- */
- public TextView getDataView() {
- if (mDataView == null) {
- mDataView = new TextView(mContext);
- mDataView.setSingleLine(true);
- mDataView.setEllipsize(TruncateAt.MARQUEE);
- mDataView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
- addView(mDataView);
- }
- return mDataView;
- }
-
- /**
- * Adds or updates a text view for the search snippet.
- */
- public void setSnippet(CharSequence text) {
- if (TextUtils.isEmpty(text)) {
- if (mSnippetView != null) {
- mSnippetView.setVisibility(View.GONE);
- }
- } else {
- getSnippetView();
- mSnippetView.setText(text);
- mSnippetView.setVisibility(VISIBLE);
- }
- }
-
- /**
- * Returns the text view for the search snippet, creating it if necessary.
- */
- public TextView getSnippetView() {
- if (mSnippetView == null) {
- mSnippetView = new TextView(mContext);
- mSnippetView.setSingleLine(true);
- mSnippetView.setEllipsize(TruncateAt.MARQUEE);
- mSnippetView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
- mSnippetView.setTypeface(mSnippetView.getTypeface(), Typeface.BOLD);
- addView(mSnippetView);
- }
- return mSnippetView;
- }
-
- /**
- * Adds or updates the presence icon view.
- */
- public void setPresence(Drawable icon) {
- if (icon != null) {
- if (mPresenceIcon == null) {
- mPresenceIcon = new ImageView(mContext);
- addView(mPresenceIcon);
- }
- mPresenceIcon.setImageDrawable(icon);
- mPresenceIcon.setScaleType(ScaleType.CENTER);
- mPresenceIcon.setVisibility(View.VISIBLE);
- } else {
- if (mPresenceIcon != null) {
- mPresenceIcon.setVisibility(View.GONE);
- }
- }
- }
-}
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
new file mode 100644
index 0000000..f4baf3b
--- /dev/null
+++ b/src/com/android/contacts/ContactLoader.java
@@ -0,0 +1,1020 @@
+/*
+ * Copyright (C) 2010 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.contacts.util.DataStatus;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Entity;
+import android.content.Entity.NamedContentValues;
+import android.content.Loader;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.DisplayNameSources;
+import android.provider.ContactsContract.Groups;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Loads a single Contact and all it constituent RawContacts.
+ */
+public class ContactLoader extends Loader<ContactLoader.Result> {
+ private static final String TAG = "ContactLoader";
+
+ private Uri mLookupUri;
+ private boolean mLoadGroupMetaData;
+ private Result mContact;
+ private ForceLoadContentObserver mObserver;
+ private boolean mDestroyed;
+
+
+ public interface Listener {
+ public void onContactLoaded(Result contact);
+ }
+
+ /**
+ * The result of a load operation. Contains all data necessary to display the contact.
+ */
+ public static final class Result {
+ /**
+ * Singleton instance that represents "No Contact Found"
+ */
+ public static final Result NOT_FOUND = new Result();
+
+ /**
+ * Singleton instance that represents an error, e.g. because of an invalid Uri
+ * TODO: We should come up with something nicer here. Maybe use an Either type so
+ * that we can capture the Exception?
+ */
+ public static final Result ERROR = new Result();
+
+ private final Uri mLookupUri;
+ private final Uri mUri;
+ private final long mDirectoryId;
+ private final String mLookupKey;
+ private final long mId;
+ private final long mNameRawContactId;
+ private final int mDisplayNameSource;
+ private final long mPhotoId;
+ private final String mPhotoUri;
+ private final String mDisplayName;
+ private final String mPhoneticName;
+ private final boolean mStarred;
+ private final Integer mPresence;
+ private final ArrayList<Entity> mEntities;
+ private final HashMap<Long, DataStatus> mStatuses;
+ private final String mStatus;
+ private final Long mStatusTimestamp;
+ private final Integer mStatusLabel;
+ private final String mStatusResPackage;
+
+ private String mDirectoryDisplayName;
+ private String mDirectoryType;
+ private String mDirectoryAccountType;
+ private String mDirectoryAccountName;
+ private int mDirectoryExportSupport;
+
+ private ArrayList<GroupMetaData> mGroups;
+
+ private boolean mLoadingPhoto;
+ private byte[] mPhotoBinaryData;
+
+ /**
+ * Constructor for case "no contact found". This must only be used for the
+ * final {@link Result#NOT_FOUND} singleton
+ */
+ private Result() {
+ mLookupUri = null;
+ mUri = null;
+ mDirectoryId = -1;
+ mLookupKey = null;
+ mId = -1;
+ mEntities = null;
+ mStatuses = null;
+ mNameRawContactId = -1;
+ mDisplayNameSource = DisplayNameSources.UNDEFINED;
+ mPhotoId = -1;
+ mPhotoUri = null;
+ mDisplayName = null;
+ mPhoneticName = null;
+ mStarred = false;
+ mPresence = null;
+ mStatus = null;
+ mStatusTimestamp = null;
+ mStatusLabel = null;
+ mStatusResPackage = null;
+ }
+
+ /**
+ * Constructor to call when contact was found
+ */
+ private Result(Uri uri, Uri lookupUri, long directoryId, String lookupKey, long id,
+ long nameRawContactId, int displayNameSource, long photoId, String photoUri,
+ String displayName, String phoneticName, boolean starred, Integer presence,
+ String status, Long statusTimestamp, Integer statusLabel, String statusResPackage) {
+ mLookupUri = lookupUri;
+ mUri = uri;
+ mDirectoryId = directoryId;
+ mLookupKey = lookupKey;
+ mId = id;
+ mEntities = new ArrayList<Entity>();
+ mStatuses = new HashMap<Long, DataStatus>();
+ mNameRawContactId = nameRawContactId;
+ mDisplayNameSource = displayNameSource;
+ mPhotoId = photoId;
+ mPhotoUri = photoUri;
+ mDisplayName = displayName;
+ mPhoneticName = phoneticName;
+ mStarred = starred;
+ mPresence = presence;
+ mStatus = status;
+ mStatusTimestamp = statusTimestamp;
+ mStatusLabel = statusLabel;
+ mStatusResPackage = statusResPackage;
+ }
+
+ private Result(Result from) {
+ mLookupUri = from.mLookupUri;
+ mUri = from.mUri;
+ mDirectoryId = from.mDirectoryId;
+ mLookupKey = from.mLookupKey;
+ mId = from.mId;
+ mNameRawContactId = from.mNameRawContactId;
+ mDisplayNameSource = from.mDisplayNameSource;
+ mPhotoId = from.mPhotoId;
+ mPhotoUri = from.mPhotoUri;
+ mDisplayName = from.mDisplayName;
+ mPhoneticName = from.mPhoneticName;
+ mStarred = from.mStarred;
+ mPresence = from.mPresence;
+ mEntities = from.mEntities;
+ mStatuses = from.mStatuses;
+ mStatus = from.mStatus;
+ mStatusTimestamp = from.mStatusTimestamp;
+ mStatusLabel = from.mStatusLabel;
+ mStatusResPackage = from.mStatusResPackage;
+
+ mDirectoryDisplayName = from.mDirectoryDisplayName;
+ mDirectoryType = from.mDirectoryType;
+ mDirectoryAccountType = from.mDirectoryAccountType;
+ mDirectoryAccountName = from.mDirectoryAccountName;
+ mDirectoryExportSupport = from.mDirectoryExportSupport;
+
+ mGroups = from.mGroups;
+
+ mLoadingPhoto = from.mLoadingPhoto;
+ mPhotoBinaryData = from.mPhotoBinaryData;
+ }
+
+ /**
+ * @param exportSupport See {@link Directory#EXPORT_SUPPORT}.
+ */
+ private void setDirectoryMetaData(String displayName, String directoryType,
+ String accountType, String accountName, int exportSupport) {
+ mDirectoryDisplayName = displayName;
+ mDirectoryType = directoryType;
+ mDirectoryAccountType = accountType;
+ mDirectoryAccountName = accountName;
+ mDirectoryExportSupport = exportSupport;
+ }
+
+ private void setLoadingPhoto(boolean flag) {
+ mLoadingPhoto = flag;
+ }
+
+ private void setPhotoBinaryData(byte[] photoBinaryData) {
+ mPhotoBinaryData = photoBinaryData;
+ }
+
+ public Uri getLookupUri() {
+ return mLookupUri;
+ }
+
+ public String getLookupKey() {
+ return mLookupKey;
+ }
+
+ public Uri getUri() {
+ return mUri;
+ }
+
+ public long getId() {
+ return mId;
+ }
+
+ public long getNameRawContactId() {
+ return mNameRawContactId;
+ }
+
+ public int getDisplayNameSource() {
+ return mDisplayNameSource;
+ }
+
+ public long getPhotoId() {
+ return mPhotoId;
+ }
+
+ public String getPhotoUri() {
+ return mPhotoUri;
+ }
+
+ public String getDisplayName() {
+ return mDisplayName;
+ }
+
+ public String getPhoneticName() {
+ return mPhoneticName;
+ }
+
+ public boolean getStarred() {
+ return mStarred;
+ }
+
+ public Integer getPresence() {
+ return mPresence;
+ }
+
+ public String getSocialSnippet() {
+ return mStatus;
+ }
+
+ public Long getStatusTimestamp() {
+ return mStatusTimestamp;
+ }
+
+ public Integer getStatusLabel() {
+ return mStatusLabel;
+ }
+
+ public String getStatusResPackage() {
+ return mStatusResPackage;
+ }
+
+ public ArrayList<Entity> getEntities() {
+ return mEntities;
+ }
+
+ public HashMap<Long, DataStatus> getStatuses() {
+ return mStatuses;
+ }
+
+ public long getDirectoryId() {
+ return mDirectoryId;
+ }
+
+ public boolean isDirectoryEntry() {
+ return mDirectoryId != -1 && mDirectoryId != Directory.DEFAULT
+ && mDirectoryId != Directory.LOCAL_INVISIBLE;
+ }
+
+ public int getDirectoryExportSupport() {
+ return mDirectoryExportSupport;
+ }
+
+ public String getDirectoryDisplayName() {
+ return mDirectoryDisplayName;
+ }
+
+ public String getDirectoryType() {
+ return mDirectoryType;
+ }
+
+ public String getDirectoryAccountType() {
+ return mDirectoryAccountType;
+ }
+
+ public String getDirectoryAccountName() {
+ return mDirectoryAccountName;
+ }
+
+ public boolean isLoadingPhoto() {
+ return mLoadingPhoto;
+ }
+
+ public byte[] getPhotoBinaryData() {
+ return mPhotoBinaryData;
+ }
+
+ public ArrayList<ContentValues> getContentValues() {
+ if (mEntities.size() != 1) {
+ throw new IllegalStateException(
+ "Cannot extract content values from an aggregated contact");
+ }
+
+ Entity entity = mEntities.get(0);
+ ArrayList<ContentValues> result = new ArrayList<ContentValues>();
+ ArrayList<NamedContentValues> subValues = entity.getSubValues();
+ if (subValues != null) {
+ int size = subValues.size();
+ for (int i = 0; i < size; i++) {
+ NamedContentValues pair = subValues.get(i);
+ if (Data.CONTENT_URI.equals(pair.uri)) {
+ result.add(pair.values);
+ }
+ }
+ }
+
+ // If the photo was loaded using the URI, create an entry for the photo
+ // binary data.
+ if (mPhotoId == 0 && mPhotoBinaryData != null) {
+ ContentValues photo = new ContentValues();
+ photo.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
+ photo.put(Photo.PHOTO, mPhotoBinaryData);
+ result.add(photo);
+ }
+
+ return result;
+ }
+
+ private void addGroupMetaData(GroupMetaData group) {
+ if (mGroups == null) {
+ mGroups = new ArrayList<GroupMetaData>();
+ }
+ mGroups.add(group);
+ }
+
+ public List<GroupMetaData> getGroupMetaData() {
+ return mGroups;
+ }
+ }
+
+ private static class ContactQuery {
+ // Projection used for the query that loads all data for the entire contact.
+ final static String[] COLUMNS = new String[] {
+ Contacts.NAME_RAW_CONTACT_ID,
+ Contacts.DISPLAY_NAME_SOURCE,
+ Contacts.LOOKUP_KEY,
+ Contacts.DISPLAY_NAME,
+ Contacts.PHONETIC_NAME,
+ Contacts.PHOTO_ID,
+ Contacts.STARRED,
+ Contacts.CONTACT_PRESENCE,
+ Contacts.CONTACT_STATUS,
+ Contacts.CONTACT_STATUS_TIMESTAMP,
+ Contacts.CONTACT_STATUS_RES_PACKAGE,
+ Contacts.CONTACT_STATUS_LABEL,
+ Contacts.Entity.CONTACT_ID,
+ Contacts.Entity.RAW_CONTACT_ID,
+
+ RawContacts.ACCOUNT_NAME,
+ RawContacts.ACCOUNT_TYPE,
+ RawContacts.DIRTY,
+ RawContacts.VERSION,
+ RawContacts.SOURCE_ID,
+ RawContacts.SYNC1,
+ RawContacts.SYNC2,
+ RawContacts.SYNC3,
+ RawContacts.SYNC4,
+ RawContacts.DELETED,
+ RawContacts.IS_RESTRICTED,
+ RawContacts.NAME_VERIFIED,
+
+ Contacts.Entity.DATA_ID,
+ Data.DATA1,
+ Data.DATA2,
+ Data.DATA3,
+ Data.DATA4,
+ Data.DATA5,
+ Data.DATA6,
+ Data.DATA7,
+ Data.DATA8,
+ Data.DATA9,
+ Data.DATA10,
+ Data.DATA11,
+ Data.DATA12,
+ Data.DATA13,
+ Data.DATA14,
+ Data.DATA15,
+ Data.SYNC1,
+ Data.SYNC2,
+ Data.SYNC3,
+ Data.SYNC4,
+ Data.DATA_VERSION,
+ Data.IS_PRIMARY,
+ Data.IS_SUPER_PRIMARY,
+ Data.MIMETYPE,
+ Data.RES_PACKAGE,
+
+ GroupMembership.GROUP_SOURCE_ID,
+
+ Data.PRESENCE,
+ Data.CHAT_CAPABILITY,
+ Data.STATUS,
+ Data.STATUS_RES_PACKAGE,
+ Data.STATUS_ICON,
+ Data.STATUS_LABEL,
+ Data.STATUS_TIMESTAMP,
+
+ Contacts.PHOTO_URI,
+ };
+
+ public final static int NAME_RAW_CONTACT_ID = 0;
+ public final static int DISPLAY_NAME_SOURCE = 1;
+ public final static int LOOKUP_KEY = 2;
+ public final static int DISPLAY_NAME = 3;
+ public final static int PHONETIC_NAME = 4;
+ public final static int PHOTO_ID = 5;
+ public final static int STARRED = 6;
+ public final static int CONTACT_PRESENCE = 7;
+ public final static int CONTACT_STATUS = 8;
+ public final static int CONTACT_STATUS_TIMESTAMP = 9;
+ public final static int CONTACT_STATUS_RES_PACKAGE = 10;
+ public final static int CONTACT_STATUS_LABEL = 11;
+ public final static int CONTACT_ID = 12;
+ public final static int RAW_CONTACT_ID = 13;
+
+ public final static int ACCOUNT_NAME = 14;
+ public final static int ACCOUNT_TYPE = 15;
+ public final static int DIRTY = 16;
+ public final static int VERSION = 17;
+ public final static int SOURCE_ID = 18;
+ public final static int SYNC1 = 19;
+ public final static int SYNC2 = 20;
+ public final static int SYNC3 = 21;
+ public final static int SYNC4 = 22;
+ public final static int DELETED = 23;
+ public final static int IS_RESTRICTED = 24;
+ public final static int NAME_VERIFIED = 25;
+
+ public final static int DATA_ID = 26;
+ public final static int DATA1 = 27;
+ public final static int DATA2 = 28;
+ public final static int DATA3 = 29;
+ public final static int DATA4 = 30;
+ public final static int DATA5 = 31;
+ public final static int DATA6 = 32;
+ public final static int DATA7 = 33;
+ public final static int DATA8 = 34;
+ public final static int DATA9 = 35;
+ public final static int DATA10 = 36;
+ public final static int DATA11 = 37;
+ public final static int DATA12 = 38;
+ public final static int DATA13 = 39;
+ public final static int DATA14 = 40;
+ public final static int DATA15 = 41;
+ public final static int DATA_SYNC1 = 42;
+ public final static int DATA_SYNC2 = 43;
+ public final static int DATA_SYNC3 = 44;
+ public final static int DATA_SYNC4 = 45;
+ public final static int DATA_VERSION = 46;
+ public final static int IS_PRIMARY = 47;
+ public final static int IS_SUPERPRIMARY = 48;
+ public final static int MIMETYPE = 49;
+ public final static int RES_PACKAGE = 50;
+
+ public final static int GROUP_SOURCE_ID = 51;
+
+ public final static int PRESENCE = 52;
+ public final static int CHAT_CAPABILITY = 53;
+ public final static int STATUS = 54;
+ public final static int STATUS_RES_PACKAGE = 55;
+ public final static int STATUS_ICON = 56;
+ public final static int STATUS_LABEL = 57;
+ public final static int STATUS_TIMESTAMP = 58;
+
+ public final static int PHOTO_URI = 59;
+ }
+
+ private static class DirectoryQuery {
+ // Projection used for the query that loads all data for the entire contact.
+ final static String[] COLUMNS = new String[] {
+ Directory.DISPLAY_NAME,
+ Directory.PACKAGE_NAME,
+ Directory.TYPE_RESOURCE_ID,
+ Directory.ACCOUNT_TYPE,
+ Directory.ACCOUNT_NAME,
+ Directory.EXPORT_SUPPORT,
+ };
+
+ public final static int DISPLAY_NAME = 0;
+ public final static int PACKAGE_NAME = 1;
+ public final static int TYPE_RESOURCE_ID = 2;
+ public final static int ACCOUNT_TYPE = 3;
+ public final static int ACCOUNT_NAME = 4;
+ public final static int EXPORT_SUPPORT = 5;
+ }
+
+ private static class GroupQuery {
+ final static String[] COLUMNS = new String[] {
+ Groups.ACCOUNT_NAME,
+ Groups.ACCOUNT_TYPE,
+ Groups._ID,
+ Groups.TITLE,
+ Groups.AUTO_ADD,
+ Groups.FAVORITES,
+ };
+
+ public final static int ACCOUNT_NAME = 0;
+ public final static int ACCOUNT_TYPE = 1;
+ public final static int ID = 2;
+ public final static int TITLE = 3;
+ public final static int AUTO_ADD = 4;
+ public final static int FAVORITES = 5;
+ }
+
+ private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
+
+ @Override
+ protected Result doInBackground(Void... args) {
+ try {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final Uri uriCurrentFormat = ensureIsContactUri(resolver, mLookupUri);
+ Result result = loadContactEntity(resolver, uriCurrentFormat);
+ if (result != Result.NOT_FOUND) {
+ if (result.isDirectoryEntry()) {
+ loadDirectoryMetaData(result);
+ } else if (mLoadGroupMetaData) {
+ loadGroupMetaData(result);
+ }
+ loadPhotoBinaryData(result);
+ }
+ return result;
+ } catch (Exception e) {
+ Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
+ return Result.ERROR;
+ }
+ }
+
+ /**
+ * Transforms the given Uri and returns a Lookup-Uri that represents the contact.
+ * For legacy contacts, a raw-contact lookup is performed.
+ * @param resolver
+ */
+ private Uri ensureIsContactUri(final ContentResolver resolver, final Uri uri) {
+ if (uri == null) throw new IllegalArgumentException("uri must not be null");
+
+ final String authority = uri.getAuthority();
+
+ // Current Style Uri?
+ if (ContactsContract.AUTHORITY.equals(authority)) {
+ final String type = resolver.getType(uri);
+ // Contact-Uri? Good, return it
+ if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
+ return uri;
+ }
+
+ // RawContact-Uri? Transform it to ContactUri
+ if (RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
+ final long rawContactId = ContentUris.parseId(uri);
+ return RawContacts.getContactLookupUri(getContext().getContentResolver(),
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
+ }
+
+ // Anything else? We don't know what this is
+ throw new IllegalArgumentException("uri format is unknown");
+ }
+
+ // Legacy Style? Convert to RawContact
+ final String OBSOLETE_AUTHORITY = "contacts";
+ if (OBSOLETE_AUTHORITY.equals(authority)) {
+ // Legacy Format. Convert to RawContact-Uri and then lookup the contact
+ final long rawContactId = ContentUris.parseId(uri);
+ return RawContacts.getContactLookupUri(resolver,
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
+ }
+
+ throw new IllegalArgumentException("uri authority is unknown");
+ }
+
+ private Result loadContactEntity(ContentResolver resolver, Uri contactUri) {
+ Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY);
+ Cursor cursor = resolver.query(entityUri, ContactQuery.COLUMNS, null, null,
+ Contacts.Entity.RAW_CONTACT_ID);
+ if (cursor == null) {
+ Log.e(TAG, "No cursor returned in loadContactEntity");
+ return Result.NOT_FOUND;
+ }
+
+ try {
+ if (!cursor.moveToFirst()) {
+ cursor.close();
+ return Result.NOT_FOUND;
+ }
+
+ long currentRawContactId = -1;
+ Entity entity = null;
+ Result result = loadContactHeaderData(cursor, contactUri);
+ ArrayList<Entity> entities = result.getEntities();
+ HashMap<Long, DataStatus> statuses = result.getStatuses();
+ for (; !cursor.isAfterLast(); cursor.moveToNext()) {
+ long rawContactId = cursor.getLong(ContactQuery.RAW_CONTACT_ID);
+ if (rawContactId != currentRawContactId) {
+ currentRawContactId = rawContactId;
+ entity = new android.content.Entity(loadRawContact(cursor));
+ entities.add(entity);
+ }
+ if (!cursor.isNull(ContactQuery.DATA_ID)) {
+ ContentValues data = loadData(cursor);
+ entity.addSubValue(ContactsContract.Data.CONTENT_URI, data);
+
+ if (!cursor.isNull(ContactQuery.PRESENCE)
+ || !cursor.isNull(ContactQuery.STATUS)) {
+ final DataStatus status = new DataStatus(cursor);
+ final long dataId = cursor.getLong(ContactQuery.DATA_ID);
+ statuses.put(dataId, status);
+ }
+ }
+ }
+
+ return result;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Looks for the photo data item in entities. If found, creates a new Bitmap instance. If
+ * not found, returns null
+ */
+ private void loadPhotoBinaryData(Result contactData) {
+ final long photoId = contactData.getPhotoId();
+ if (photoId <= 0) {
+ // No photo ID
+ return;
+ }
+
+ for (Entity entity : contactData.getEntities()) {
+ for (NamedContentValues subValue : entity.getSubValues()) {
+ final ContentValues entryValues = subValue.values;
+ final long dataId = entryValues.getAsLong(Data._ID);
+ if (dataId == photoId) {
+ final String mimeType = entryValues.getAsString(Data.MIMETYPE);
+ // Correct Data Id but incorrect MimeType? Don't load
+ if (!Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ return;
+ }
+ contactData.setPhotoBinaryData(entryValues.getAsByteArray(Photo.PHOTO));
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Extracts Contact level columns from the cursor.
+ */
+ private Result loadContactHeaderData(final Cursor cursor, Uri contactUri) {
+ final String directoryParameter =
+ contactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
+ final long directoryId = directoryParameter == null
+ ? Directory.DEFAULT
+ : Long.parseLong(directoryParameter);
+ final long contactId = cursor.getLong(ContactQuery.CONTACT_ID);
+ final String lookupKey = cursor.getString(ContactQuery.LOOKUP_KEY);
+ final long nameRawContactId = cursor.getLong(ContactQuery.NAME_RAW_CONTACT_ID);
+ final int displayNameSource = cursor.getInt(ContactQuery.DISPLAY_NAME_SOURCE);
+ final String displayName = cursor.getString(ContactQuery.DISPLAY_NAME);
+ final String phoneticName = cursor.getString(ContactQuery.PHONETIC_NAME);
+ final long photoId = cursor.getLong(ContactQuery.PHOTO_ID);
+ final String photoUri = cursor.getString(ContactQuery.PHOTO_URI);
+ final boolean starred = cursor.getInt(ContactQuery.STARRED) != 0;
+ final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
+ ? null
+ : cursor.getInt(ContactQuery.CONTACT_PRESENCE);
+ final String status = cursor.getString(ContactQuery.CONTACT_STATUS);
+ final Long statusTimestamp = cursor.isNull(ContactQuery.CONTACT_STATUS_TIMESTAMP)
+ ? null
+ : cursor.getLong(ContactQuery.CONTACT_STATUS_TIMESTAMP);
+ final Integer statusLabel = cursor.isNull(ContactQuery.CONTACT_STATUS_LABEL)
+ ? null
+ : cursor.getInt(ContactQuery.CONTACT_STATUS_LABEL);
+ final String statusResPackage = cursor.getString(
+ ContactQuery.CONTACT_STATUS_RES_PACKAGE);
+
+ Uri lookupUri;
+ if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
+ lookupUri = ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), contactId);
+ } else {
+ lookupUri = contactUri;
+ }
+
+ return new Result(contactUri, lookupUri, directoryId, lookupKey, contactId,
+ nameRawContactId, displayNameSource, photoId, photoUri, displayName,
+ phoneticName, starred, presence, status, statusTimestamp, statusLabel,
+ statusResPackage);
+ }
+
+ /**
+ * Extracts RawContact level columns from the cursor.
+ */
+ private ContentValues loadRawContact(Cursor cursor) {
+ ContentValues cv = new ContentValues();
+
+ cv.put(RawContacts._ID, cursor.getLong(ContactQuery.RAW_CONTACT_ID));
+
+ cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC1);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC2);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC3);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.SYNC4);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.IS_RESTRICTED);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
+
+ return cv;
+ }
+
+ /**
+ * Extracts Data level columns from the cursor.
+ */
+ private ContentValues loadData(Cursor cursor) {
+ ContentValues cv = new ContentValues();
+
+ cv.put(Data._ID, cursor.getLong(ContactQuery.DATA_ID));
+
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA1);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA2);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA3);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA4);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA5);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA6);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA7);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA8);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA9);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA10);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA11);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA12);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA13);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA14);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA15);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC1);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC2);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC3);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SYNC4);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_VERSION);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.IS_PRIMARY);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.IS_SUPERPRIMARY);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.MIMETYPE);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.RES_PACKAGE);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.GROUP_SOURCE_ID);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.CHAT_CAPABILITY);
+
+ return cv;
+ }
+
+ private void cursorColumnToContentValues(
+ Cursor cursor, ContentValues values, int index) {
+ switch (cursor.getType(index)) {
+ case Cursor.FIELD_TYPE_NULL:
+ // don't put anything in the content values
+ break;
+ case Cursor.FIELD_TYPE_INTEGER:
+ values.put(ContactQuery.COLUMNS[index], cursor.getLong(index));
+ break;
+ case Cursor.FIELD_TYPE_STRING:
+ values.put(ContactQuery.COLUMNS[index], cursor.getString(index));
+ break;
+ case Cursor.FIELD_TYPE_BLOB:
+ values.put(ContactQuery.COLUMNS[index], cursor.getBlob(index));
+ break;
+ default:
+ throw new IllegalStateException("Invalid or unhandled data type");
+ }
+ }
+
+ private void loadDirectoryMetaData(Result result) {
+ long directoryId = result.getDirectoryId();
+
+ Cursor cursor = getContext().getContentResolver().query(
+ ContentUris.withAppendedId(Directory.CONTENT_URI, directoryId),
+ DirectoryQuery.COLUMNS, null, null, null);
+ if (cursor == null) {
+ return;
+ }
+ try {
+ if (cursor.moveToFirst()) {
+ final String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
+ final String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
+ final int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
+ final String accountType = cursor.getString(DirectoryQuery.ACCOUNT_TYPE);
+ final String accountName = cursor.getString(DirectoryQuery.ACCOUNT_NAME);
+ final int exportSupport = cursor.getInt(DirectoryQuery.EXPORT_SUPPORT);
+ String directoryType = null;
+ if (!TextUtils.isEmpty(packageName)) {
+ PackageManager pm = getContext().getPackageManager();
+ try {
+ Resources resources = pm.getResourcesForApplication(packageName);
+ directoryType = resources.getString(typeResourceId);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Contact directory resource not found: "
+ + packageName + "." + typeResourceId);
+ }
+ }
+
+ result.setDirectoryMetaData(
+ displayName, directoryType, accountType, accountName, exportSupport);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ /**
+ * Loads groups meta-data for all groups associated with all constituent raw contacts'
+ * accounts.
+ */
+ private void loadGroupMetaData(Result result) {
+ StringBuilder selection = new StringBuilder();
+ ArrayList<String> selectionArgs = new ArrayList<String>();
+ for (Entity entity : result.mEntities) {
+ ContentValues values = entity.getEntityValues();
+ String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
+ String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
+ if (accountName != null && accountType != null) {
+ if (selection.length() != 0) {
+ selection.append(" OR ");
+ }
+ selection.append(
+ "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?)");
+ selectionArgs.add(accountName);
+ selectionArgs.add(accountType);
+ }
+ }
+ Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
+ GroupQuery.COLUMNS, selection.toString(), selectionArgs.toArray(new String[0]),
+ null);
+ try {
+ while (cursor.moveToNext()) {
+ final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
+ final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
+ final long groupId = cursor.getLong(GroupQuery.ID);
+ final String title = cursor.getString(GroupQuery.TITLE);
+ final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
+ ? false
+ : cursor.getInt(GroupQuery.AUTO_ADD) != 0;
+ final boolean favorites = cursor.isNull(GroupQuery.FAVORITES)
+ ? false
+ : cursor.getInt(GroupQuery.FAVORITES) != 0;
+
+ result.addGroupMetaData(new GroupMetaData(
+ accountName, accountType, groupId, title, defaultGroup, favorites));
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Result result) {
+ unregisterObserver();
+
+ // The creator isn't interested in any further updates
+ if (mDestroyed || result == null) {
+ return;
+ }
+
+ mContact = result;
+
+ if (result != Result.ERROR && result != Result.NOT_FOUND) {
+ mLookupUri = result.getLookupUri();
+
+ if (!result.isDirectoryEntry()) {
+ Log.i(TAG, "Registering content observer for " + mLookupUri);
+ if (mObserver == null) {
+ mObserver = new ForceLoadContentObserver();
+ }
+ getContext().getContentResolver().registerContentObserver(
+ mLookupUri, true, mObserver);
+ }
+
+ if (mContact.getPhotoBinaryData() == null && mContact.getPhotoUri() != null) {
+ mContact.setLoadingPhoto(true);
+ new AsyncPhotoLoader().execute(mContact.getPhotoUri());
+ }
+ }
+
+ deliverResult(mContact);
+ }
+ }
+
+ private class AsyncPhotoLoader extends AsyncTask<String, Void, byte[]> {
+
+ private static final int BUFFER_SIZE = 1024*16;
+
+ @Override
+ protected byte[] doInBackground(String... params) {
+ Uri uri = Uri.parse(params[0]);
+ byte[] data = null;
+ try {
+ InputStream is = getContext().getContentResolver().openInputStream(uri);
+ if (is != null) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ byte[] mBuffer = new byte[BUFFER_SIZE];
+
+ int size;
+ while ((size = is.read(mBuffer)) != -1) {
+ baos.write(mBuffer, 0, size);
+ }
+ data = baos.toByteArray();
+ } finally {
+ is.close();
+ }
+ } else {
+ Log.v(TAG, "Cannot load photo " + uri);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Cannot load photo " + uri, e);
+ }
+
+ return data;
+ }
+
+ @Override
+ protected void onPostExecute(byte[] data) {
+ if (mContact != null) {
+ mContact = new Result(mContact);
+ mContact.setPhotoBinaryData(data);
+ mContact.setLoadingPhoto(false);
+ deliverResult(mContact);
+ }
+ }
+ }
+
+ private void unregisterObserver() {
+ if (mObserver != null) {
+ getContext().getContentResolver().unregisterContentObserver(mObserver);
+ mObserver = null;
+ }
+ }
+
+ public ContactLoader(Context context, Uri lookupUri) {
+ this(context, lookupUri, false);
+ }
+
+ public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData) {
+ super(context);
+ mLookupUri = lookupUri;
+ mLoadGroupMetaData = loadGroupMetaData;
+ }
+
+ public Uri getLookupUri() {
+ return mLookupUri;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mContact != null) {
+ deliverResult(mContact);
+ }
+
+ if (takeContentChanged() || mContact == null) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onForceLoad() {
+ final LoadContactTask task = new LoadContactTask();
+ task.execute((Void[])null);
+ }
+
+ @Override
+ protected void onReset() {
+ unregisterObserver();
+ mContact = null;
+ mDestroyed = true;
+ }
+}
diff --git a/src/com/android/contacts/ContactPhotoLoader.java b/src/com/android/contacts/ContactPhotoLoader.java
deleted file mode 100644
index 96f55a6..0000000
--- a/src/com/android/contacts/ContactPhotoLoader.java
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts;
-
-import com.google.android.collect.Lists;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.os.Handler.Callback;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Contacts.Photo;
-import android.widget.ImageView;
-
-import java.lang.ref.SoftReference;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Asynchronously loads contact photos and maintains cache of photos. The class is
- * mostly single-threaded. The only two methods accessed by the loader thread are
- * {@link #cacheBitmap} and {@link #obtainPhotoIdsToLoad}. Those methods access concurrent
- * hash maps shared with the main thread.
- */
-public class ContactPhotoLoader implements Callback {
-
- private static final String LOADER_THREAD_NAME = "ContactPhotoLoader";
-
- /**
- * Type of message sent by the UI thread to itself to indicate that some photos
- * need to be loaded.
- */
- private static final int MESSAGE_REQUEST_LOADING = 1;
-
- /**
- * Type of message sent by the loader thread to indicate that some photos have
- * been loaded.
- */
- private static final int MESSAGE_PHOTOS_LOADED = 2;
-
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
-
- private final String[] COLUMNS = new String[] { Photo._ID, Photo.PHOTO };
-
- /**
- * The resource ID of the image to be used when the photo is unavailable or being
- * loaded.
- */
- private final int mDefaultResourceId;
-
- /**
- * Maintains the state of a particular photo.
- */
- private static class BitmapHolder {
- private static final int NEEDED = 0;
- private static final int LOADING = 1;
- private static final int LOADED = 2;
-
- int state;
- SoftReference<Bitmap> bitmapRef;
- }
-
- /**
- * A soft cache for photos.
- */
- private final ConcurrentHashMap<Long, BitmapHolder> mBitmapCache =
- new ConcurrentHashMap<Long, BitmapHolder>();
-
- /**
- * A map from ImageView to the corresponding photo ID. Please note that this
- * photo ID may change before the photo loading request is started.
- */
- private final ConcurrentHashMap<ImageView, Long> mPendingRequests =
- new ConcurrentHashMap<ImageView, Long>();
-
- /**
- * Handler for messages sent to the UI thread.
- */
- private final Handler mMainThreadHandler = new Handler(this);
-
- /**
- * Thread responsible for loading photos from the database. Created upon
- * the first request.
- */
- private LoaderThread mLoaderThread;
-
- /**
- * A gate to make sure we only send one instance of MESSAGE_PHOTOS_NEEDED at a time.
- */
- private boolean mLoadingRequested;
-
- /**
- * Flag indicating if the image loading is paused.
- */
- private boolean mPaused;
-
- private final Context mContext;
-
- /**
- * Constructor.
- *
- * @param context content context
- * @param defaultResourceId the image resource ID to be used when there is
- * no photo for a contact
- */
- public ContactPhotoLoader(Context context, int defaultResourceId) {
- mDefaultResourceId = defaultResourceId;
- mContext = context;
- }
-
- /**
- * Load photo into the supplied image view. If the photo is already cached,
- * it is displayed immediately. Otherwise a request is sent to load the photo
- * from the database.
- */
- public void loadPhoto(ImageView view, long photoId) {
- if (photoId == 0) {
- // No photo is needed
- view.setImageResource(mDefaultResourceId);
- mPendingRequests.remove(view);
- } else {
- boolean loaded = loadCachedPhoto(view, photoId);
- if (loaded) {
- mPendingRequests.remove(view);
- } else {
- mPendingRequests.put(view, photoId);
- if (!mPaused) {
- // Send a request to start loading photos
- requestLoading();
- }
- }
- }
- }
-
- /**
- * Checks if the photo is present in cache. If so, sets the photo on the view,
- * otherwise sets the state of the photo to {@link BitmapHolder#NEEDED} and
- * temporarily set the image to the default resource ID.
- */
- private boolean loadCachedPhoto(ImageView view, long photoId) {
- BitmapHolder holder = mBitmapCache.get(photoId);
- if (holder == null) {
- holder = new BitmapHolder();
- mBitmapCache.put(photoId, holder);
- } else if (holder.state == BitmapHolder.LOADED) {
- // Null bitmap reference means that database contains no bytes for the photo
- if (holder.bitmapRef == null) {
- view.setImageResource(mDefaultResourceId);
- return true;
- }
-
- Bitmap bitmap = holder.bitmapRef.get();
- if (bitmap != null) {
- view.setImageBitmap(bitmap);
- return true;
- }
-
- // Null bitmap means that the soft reference was released by the GC
- // and we need to reload the photo.
- holder.bitmapRef = null;
- }
-
- // The bitmap has not been loaded - should display the placeholder image.
- view.setImageResource(mDefaultResourceId);
- holder.state = BitmapHolder.NEEDED;
- return false;
- }
-
- /**
- * Stops loading images, kills the image loader thread and clears all caches.
- */
- public void stop() {
- pause();
-
- if (mLoaderThread != null) {
- mLoaderThread.quit();
- mLoaderThread = null;
- }
-
- mPendingRequests.clear();
- mBitmapCache.clear();
- }
-
- public void clear() {
- mPendingRequests.clear();
- mBitmapCache.clear();
- }
-
- /**
- * Temporarily stops loading photos from the database.
- */
- public void pause() {
- mPaused = true;
- }
-
- /**
- * Resumes loading photos from the database.
- */
- public void resume() {
- mPaused = false;
- if (!mPendingRequests.isEmpty()) {
- requestLoading();
- }
- }
-
- /**
- * Sends a message to this thread itself to start loading images. If the current
- * view contains multiple image views, all of those image views will get a chance
- * to request their respective photos before any of those requests are executed.
- * This allows us to load images in bulk.
- */
- private void requestLoading() {
- if (!mLoadingRequested) {
- mLoadingRequested = true;
- mMainThreadHandler.sendEmptyMessage(MESSAGE_REQUEST_LOADING);
- }
- }
-
- /**
- * Processes requests on the main thread.
- */
- public boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_REQUEST_LOADING: {
- mLoadingRequested = false;
- if (!mPaused) {
- if (mLoaderThread == null) {
- mLoaderThread = new LoaderThread(mContext.getContentResolver());
- mLoaderThread.start();
- }
-
- mLoaderThread.requestLoading();
- }
- return true;
- }
-
- case MESSAGE_PHOTOS_LOADED: {
- if (!mPaused) {
- processLoadedImages();
- }
- return true;
- }
- }
- return false;
- }
-
- /**
- * Goes over pending loading requests and displays loaded photos. If some of the
- * photos still haven't been loaded, sends another request for image loading.
- */
- private void processLoadedImages() {
- Iterator<ImageView> iterator = mPendingRequests.keySet().iterator();
- while (iterator.hasNext()) {
- ImageView view = iterator.next();
- long photoId = mPendingRequests.get(view);
- boolean loaded = loadCachedPhoto(view, photoId);
- if (loaded) {
- iterator.remove();
- }
- }
-
- if (!mPendingRequests.isEmpty()) {
- requestLoading();
- }
- }
-
- /**
- * Stores the supplied bitmap in cache.
- */
- private void cacheBitmap(long id, byte[] bytes) {
- if (mPaused) {
- return;
- }
-
- BitmapHolder holder = new BitmapHolder();
- holder.state = BitmapHolder.LOADED;
- if (bytes != null) {
- try {
- Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null);
- holder.bitmapRef = new SoftReference<Bitmap>(bitmap);
- } catch (OutOfMemoryError e) {
- // Do nothing - the photo will appear to be missing
- }
- }
- mBitmapCache.put(id, holder);
- }
-
- /**
- * Populates an array of photo IDs that need to be loaded.
- */
- private void obtainPhotoIdsToLoad(ArrayList<Long> photoIds,
- ArrayList<String> photoIdsAsStrings) {
- photoIds.clear();
- photoIdsAsStrings.clear();
-
- /*
- * Since the call is made from the loader thread, the map could be
- * changing during the iteration. That's not really a problem:
- * ConcurrentHashMap will allow those changes to happen without throwing
- * exceptions. Since we may miss some requests in the situation of
- * concurrent change, we will need to check the map again once loading
- * is complete.
- */
- Iterator<Long> iterator = mPendingRequests.values().iterator();
- while (iterator.hasNext()) {
- Long id = iterator.next();
- BitmapHolder holder = mBitmapCache.get(id);
- if (holder != null && holder.state == BitmapHolder.NEEDED) {
- // Assuming atomic behavior
- holder.state = BitmapHolder.LOADING;
- photoIds.add(id);
- photoIdsAsStrings.add(id.toString());
- }
- }
- }
-
- /**
- * The thread that performs loading of photos from the database.
- */
- private class LoaderThread extends HandlerThread implements Callback {
- private final ContentResolver mResolver;
- private final StringBuilder mStringBuilder = new StringBuilder();
- private final ArrayList<Long> mPhotoIds = Lists.newArrayList();
- private final ArrayList<String> mPhotoIdsAsStrings = Lists.newArrayList();
- private Handler mLoaderThreadHandler;
-
- public LoaderThread(ContentResolver resolver) {
- super(LOADER_THREAD_NAME);
- mResolver = resolver;
- }
-
- /**
- * Sends a message to this thread to load requested photos.
- */
- public void requestLoading() {
- if (mLoaderThreadHandler == null) {
- mLoaderThreadHandler = new Handler(getLooper(), this);
- }
- mLoaderThreadHandler.sendEmptyMessage(0);
- }
-
- /**
- * Receives the above message, loads photos and then sends a message
- * to the main thread to process them.
- */
- public boolean handleMessage(Message msg) {
- loadPhotosFromDatabase();
- mMainThreadHandler.sendEmptyMessage(MESSAGE_PHOTOS_LOADED);
- return true;
- }
-
- private void loadPhotosFromDatabase() {
- obtainPhotoIdsToLoad(mPhotoIds, mPhotoIdsAsStrings);
-
- int count = mPhotoIds.size();
- if (count == 0) {
- return;
- }
-
- mStringBuilder.setLength(0);
- mStringBuilder.append(Photo._ID + " IN(");
- for (int i = 0; i < count; i++) {
- if (i != 0) {
- mStringBuilder.append(',');
- }
- mStringBuilder.append('?');
- }
- mStringBuilder.append(')');
-
- Cursor cursor = null;
- try {
- cursor = mResolver.query(Data.CONTENT_URI,
- COLUMNS,
- mStringBuilder.toString(),
- mPhotoIdsAsStrings.toArray(EMPTY_STRING_ARRAY),
- null);
-
- if (cursor != null) {
- while (cursor.moveToNext()) {
- Long id = cursor.getLong(0);
- byte[] bytes = cursor.getBlob(1);
- cacheBitmap(id, bytes);
- mPhotoIds.remove(id);
- }
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- // Remaining photos were not found in the database - mark the cache accordingly.
- count = mPhotoIds.size();
- for (int i = 0; i < count; i++) {
- cacheBitmap(mPhotoIds.get(i), null);
- }
- }
- }
-}
diff --git a/src/com/android/contacts/ContactPresenceIconUtil.java b/src/com/android/contacts/ContactPresenceIconUtil.java
index 1a2d58e..8e1af72 100644
--- a/src/com/android/contacts/ContactPresenceIconUtil.java
+++ b/src/com/android/contacts/ContactPresenceIconUtil.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.StatusUpdates;
/**
@@ -26,7 +27,7 @@
public class ContactPresenceIconUtil {
/**
* Get the presence icon resource according the status.
- *
+ *
* @return null means don't show the status icon.
*/
public static Drawable getPresenceIcon (Context context, int status) {
@@ -45,4 +46,43 @@
return null;
}
}
+
+ public static Drawable getChatCapabilityIcon(Context context, int status, int chatCapability) {
+ int resourceId = 0;
+ if ((chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) {
+ switch(status) {
+ case StatusUpdates.AVAILABLE:
+ resourceId = android.R.drawable.presence_video_online;
+ break;
+ case StatusUpdates.IDLE:
+ case StatusUpdates.AWAY:
+ resourceId = android.R.drawable.presence_video_away;
+ break;
+ case StatusUpdates.DO_NOT_DISTURB:
+ resourceId = android.R.drawable.presence_video_busy;
+ break;
+ }
+ } else if ((chatCapability & Im.CAPABILITY_HAS_VOICE) != 0) {
+ switch(status) {
+ case StatusUpdates.AVAILABLE:
+ resourceId = android.R.drawable.presence_audio_online;
+ break;
+ case StatusUpdates.IDLE:
+ case StatusUpdates.AWAY:
+ resourceId = android.R.drawable.presence_audio_away;
+ break;
+ case StatusUpdates.DO_NOT_DISTURB:
+ resourceId = android.R.drawable.presence_audio_busy;
+ break;
+ }
+ } else if (status != StatusUpdates.OFFLINE) {
+ resourceId = StatusUpdates.getPresenceIconResourceId(status);
+ }
+
+ if (resourceId != 0) {
+ return context.getResources().getDrawable(resourceId);
+ }
+
+ return null;
+ }
}
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
new file mode 100644
index 0000000..c07dd68
--- /dev/null
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -0,0 +1,554 @@
+/*
+ * Copyright (C) 2010 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.contacts.R;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Sets;
+
+import android.accounts.Account;
+import android.app.IntentService;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderOperation.Builder;
+import android.content.ContentProviderResult;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.AggregationExceptions;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Groups;
+import android.provider.ContactsContract.RawContacts;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * A service responsible for saving changes to the content provider.
+ */
+public class ContactSaveService extends IntentService {
+ private static final String TAG = "ContactSaveService";
+
+ public static final String ACTION_NEW_RAW_CONTACT = "newRawContact";
+
+ public static final String EXTRA_ACCOUNT_NAME = "accountName";
+ public static final String EXTRA_ACCOUNT_TYPE = "accountType";
+ public static final String EXTRA_CONTENT_VALUES = "contentValues";
+ public static final String EXTRA_CALLBACK_INTENT = "callbackIntent";
+
+ public static final String EXTRA_OPERATIONS = "Operations";
+
+ public static final String ACTION_CREATE_GROUP = "createGroup";
+ public static final String ACTION_RENAME_GROUP = "renameGroup";
+ public static final String ACTION_DELETE_GROUP = "deleteGroup";
+ public static final String EXTRA_GROUP_ID = "groupId";
+ public static final String EXTRA_GROUP_LABEL = "groupLabel";
+
+ public static final String ACTION_SET_STARRED = "setStarred";
+ public static final String ACTION_DELETE_CONTACT = "delete";
+ public static final String EXTRA_CONTACT_URI = "contactUri";
+ public static final String EXTRA_STARRED_FLAG = "starred";
+
+ public static final String ACTION_SET_SUPER_PRIMARY = "setSuperPrimary";
+ public static final String ACTION_CLEAR_PRIMARY = "clearPrimary";
+ public static final String EXTRA_DATA_ID = "dataId";
+
+ public static final String ACTION_JOIN_CONTACTS = "joinContacts";
+ public static final String EXTRA_CONTACT_ID1 = "contactId1";
+ public static final String EXTRA_CONTACT_ID2 = "contactId2";
+ public static final String EXTRA_CONTACT_WRITABLE = "contactWritable";
+
+ private static final HashSet<String> ALLOWED_DATA_COLUMNS = Sets.newHashSet(
+ Data.MIMETYPE,
+ Data.IS_PRIMARY,
+ Data.DATA1,
+ Data.DATA2,
+ Data.DATA3,
+ Data.DATA4,
+ Data.DATA5,
+ Data.DATA6,
+ Data.DATA7,
+ Data.DATA8,
+ Data.DATA9,
+ Data.DATA10,
+ Data.DATA11,
+ Data.DATA12,
+ Data.DATA13,
+ Data.DATA14,
+ Data.DATA15
+ );
+
+ public ContactSaveService() {
+ super(TAG);
+ setIntentRedelivery(true);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ String action = intent.getAction();
+ if (ACTION_NEW_RAW_CONTACT.equals(action)) {
+ createRawContact(intent);
+ } else if (ACTION_CREATE_GROUP.equals(action)) {
+ createGroup(intent);
+ } else if (ACTION_RENAME_GROUP.equals(action)) {
+ renameGroup(intent);
+ } else if (ACTION_DELETE_GROUP.equals(action)) {
+ deleteGroup(intent);
+ } else if (ACTION_SET_STARRED.equals(action)) {
+ setStarred(intent);
+ } else if (ACTION_SET_SUPER_PRIMARY.equals(action)) {
+ setSuperPrimary(intent);
+ } else if (ACTION_CLEAR_PRIMARY.equals(action)) {
+ clearPrimary(intent);
+ } else if (ACTION_DELETE_CONTACT.equals(action)) {
+ deleteContact(intent);
+ } else if (ACTION_JOIN_CONTACTS.equals(action)) {
+ joinContacts(intent);
+ }
+ }
+
+ /**
+ * Creates an intent that can be sent to this service to create a new raw contact
+ * using data presented as a set of ContentValues.
+ */
+ public static Intent createNewRawContactIntent(Context context,
+ ArrayList<ContentValues> values, Account account, Class<?> callbackActivity,
+ String callbackAction) {
+ Intent serviceIntent = new Intent(
+ context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_NEW_RAW_CONTACT);
+ if (account != null) {
+ serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_NAME, account.name);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_TYPE, account.type);
+ }
+ serviceIntent.putParcelableArrayListExtra(
+ ContactSaveService.EXTRA_CONTENT_VALUES, values);
+
+ // Callback intent will be invoked by the service once the new contact is
+ // created. The service will put the URI of the new contact as "data" on
+ // the callback intent.
+ Intent callbackIntent = new Intent(context, callbackActivity);
+ callbackIntent.setAction(callbackAction);
+ callbackIntent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
+ return serviceIntent;
+ }
+
+ private void createRawContact(Intent intent) {
+ String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
+ String accountType = intent.getStringExtra(EXTRA_ACCOUNT_TYPE);
+ List<ContentValues> valueList = intent.getParcelableArrayListExtra(EXTRA_CONTENT_VALUES);
+ Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
+
+ ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
+ operations.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
+ .withValue(RawContacts.ACCOUNT_NAME, accountName)
+ .withValue(RawContacts.ACCOUNT_TYPE, accountType)
+ .build());
+
+ int size = valueList.size();
+ for (int i = 0; i < size; i++) {
+ ContentValues values = valueList.get(i);
+ values.keySet().retainAll(ALLOWED_DATA_COLUMNS);
+ operations.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
+ .withValueBackReference(Data.RAW_CONTACT_ID, 0)
+ .withValues(values)
+ .build());
+ }
+
+ ContentResolver resolver = getContentResolver();
+ ContentProviderResult[] results;
+ try {
+ results = resolver.applyBatch(ContactsContract.AUTHORITY, operations);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to store new contact", e);
+ }
+
+ Uri rawContactUri = results[0].uri;
+ callbackIntent.setData(RawContacts.getContactLookupUri(resolver, rawContactUri));
+
+ startActivity(callbackIntent);
+ }
+
+ /**
+ * Creates an intent that can be sent to this service to create a new group.
+ */
+ public static Intent createNewGroupIntent(Context context, Account account, String label,
+ Class<?> callbackActivity, String callbackAction) {
+ Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_CREATE_GROUP);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_TYPE, account.type);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_NAME, account.name);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_LABEL, label);
+
+ // Callback intent will be invoked by the service once the new group is
+ // created. The service will put a group membership row in the extras
+ // of the callback intent.
+ Intent callbackIntent = new Intent(context, callbackActivity);
+ callbackIntent.setAction(callbackAction);
+ callbackIntent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
+
+ return serviceIntent;
+ }
+
+ private void createGroup(Intent intent) {
+ String accountType = intent.getStringExtra(EXTRA_ACCOUNT_TYPE);
+ String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
+ String label = intent.getStringExtra(EXTRA_GROUP_LABEL);
+
+ ContentValues values = new ContentValues();
+ values.put(Groups.ACCOUNT_TYPE, accountType);
+ values.put(Groups.ACCOUNT_NAME, accountName);
+ values.put(Groups.TITLE, label);
+
+ Uri groupUri = getContentResolver().insert(Groups.CONTENT_URI, values);
+ if (groupUri == null) {
+ return;
+ }
+
+ values.clear();
+ values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
+ values.put(GroupMembership.GROUP_ROW_ID, ContentUris.parseId(groupUri));
+
+ Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
+ callbackIntent.putExtra(ContactsContract.Intents.Insert.DATA, Lists.newArrayList(values));
+
+ startActivity(callbackIntent);
+ }
+
+ /**
+ * Creates an intent that can be sent to this service to rename a group.
+ */
+ public static Intent createGroupRenameIntent(Context context, long groupId, String newLabel) {
+ Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_RENAME_GROUP);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_LABEL, newLabel);
+ return serviceIntent;
+ }
+
+ private void renameGroup(Intent intent) {
+ long groupId = intent.getLongExtra(EXTRA_GROUP_ID, -1);
+ String label = intent.getStringExtra(EXTRA_GROUP_LABEL);
+
+ if (groupId == -1) {
+ Log.e(TAG, "Invalid arguments for renameGroup request");
+ return;
+ }
+
+ ContentValues values = new ContentValues();
+ values.put(Groups.TITLE, label);
+ getContentResolver().update(
+ ContentUris.withAppendedId(Groups.CONTENT_URI, groupId), values, null, null);
+ }
+
+ /**
+ * Creates an intent that can be sent to this service to delete a group.
+ */
+ public static Intent createGroupDeletionIntent(Context context, long groupId) {
+ Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_DELETE_GROUP);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId);
+ return serviceIntent;
+ }
+
+ private void deleteGroup(Intent intent) {
+ long groupId = intent.getLongExtra(EXTRA_GROUP_ID, -1);
+ if (groupId == -1) {
+ Log.e(TAG, "Invalid arguments for deleteGroup request");
+ return;
+ }
+
+ getContentResolver().delete(
+ ContentUris.withAppendedId(Groups.CONTENT_URI, groupId), null, null);
+ }
+
+ /**
+ * Creates an intent that can be sent to this service to star or un-star a contact.
+ */
+ public static Intent createSetStarredIntent(Context context, Uri contactUri, boolean value) {
+ Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_SET_STARRED);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_URI, contactUri);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_STARRED_FLAG, value);
+
+ return serviceIntent;
+ }
+
+ private void setStarred(Intent intent) {
+ Uri contactUri = intent.getParcelableExtra(EXTRA_CONTACT_URI);
+ boolean value = intent.getBooleanExtra(EXTRA_STARRED_FLAG, false);
+ if (contactUri == null) {
+ Log.e(TAG, "Invalid arguments for setStarred request");
+ return;
+ }
+
+ final ContentValues values = new ContentValues(1);
+ values.put(Contacts.STARRED, value);
+ getContentResolver().update(contactUri, values, null, null);
+ }
+
+ /**
+ * Creates an intent that sets the selected data item as super primary (default)
+ */
+ public static Intent createSetSuperPrimaryIntent(Context context, long dataId) {
+ Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_SET_SUPER_PRIMARY);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_DATA_ID, dataId);
+ return serviceIntent;
+ }
+
+ private void setSuperPrimary(Intent intent) {
+ long dataId = intent.getLongExtra(EXTRA_DATA_ID, -1);
+ if (dataId == -1) {
+ Log.e(TAG, "Invalid arguments for setSuperPrimary request");
+ return;
+ }
+
+ // Update the primary values in the data record.
+ ContentValues values = new ContentValues(1);
+ values.put(Data.IS_SUPER_PRIMARY, 1);
+ values.put(Data.IS_PRIMARY, 1);
+
+ getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
+ values, null, null);
+ }
+
+ /**
+ * Creates an intent that clears the primary flag of all data items that belong to the same
+ * raw_contact as the given data item. Will only clear, if the data item was primary before
+ * this call
+ */
+ public static Intent createClearPrimaryIntent(Context context, long dataId) {
+ Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_CLEAR_PRIMARY);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_DATA_ID, dataId);
+ return serviceIntent;
+ }
+
+ private void clearPrimary(Intent intent) {
+ long dataId = intent.getLongExtra(EXTRA_DATA_ID, -1);
+ if (dataId == -1) {
+ Log.e(TAG, "Invalid arguments for clearPrimary request");
+ return;
+ }
+
+ // Update the primary values in the data record.
+ ContentValues values = new ContentValues(1);
+ values.put(Data.IS_SUPER_PRIMARY, 0);
+ values.put(Data.IS_PRIMARY, 0);
+
+ getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
+ values, null, null);
+ }
+
+ /**
+ * Creates an intent that can be sent to this service to delete a contact.
+ */
+ public static Intent createDeleteContactIntent(Context context, Uri contactUri) {
+ Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_DELETE_CONTACT);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_URI, contactUri);
+ return serviceIntent;
+ }
+
+ private void deleteContact(Intent intent) {
+ Uri contactUri = intent.getParcelableExtra(EXTRA_CONTACT_URI);
+ if (contactUri == null) {
+ Log.e(TAG, "Invalid arguments for deleteContact request");
+ return;
+ }
+
+ getContentResolver().delete(contactUri, null, null);
+ }
+
+ /**
+ * Creates an intent that can be sent to this service to join two contacts.
+ */
+ public static Intent createJoinContactsIntent(Context context, long contactId1,
+ long contactId2, boolean contactWritable,
+ Class<?> callbackActivity, String callbackAction) {
+ Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_JOIN_CONTACTS);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_ID1, contactId1);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_ID2, contactId2);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_WRITABLE, contactWritable);
+
+ // Callback intent will be invoked by the service once the contacts are joined.
+ Intent callbackIntent = new Intent(context, callbackActivity);
+ callbackIntent.setAction(callbackAction);
+ callbackIntent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
+
+ return serviceIntent;
+ }
+
+
+ private interface JoinContactQuery {
+ String[] PROJECTION = {
+ RawContacts._ID,
+ RawContacts.CONTACT_ID,
+ RawContacts.NAME_VERIFIED,
+ RawContacts.DISPLAY_NAME_SOURCE,
+ };
+
+ String SELECTION = RawContacts.CONTACT_ID + "=? OR " + RawContacts.CONTACT_ID + "=?";
+
+ int _ID = 0;
+ int CONTACT_ID = 1;
+ int NAME_VERIFIED = 2;
+ int DISPLAY_NAME_SOURCE = 3;
+ }
+
+ private void joinContacts(Intent intent) {
+ long contactId1 = intent.getLongExtra(EXTRA_CONTACT_ID1, -1);
+ long contactId2 = intent.getLongExtra(EXTRA_CONTACT_ID2, -1);
+ boolean writable = intent.getBooleanExtra(EXTRA_CONTACT_WRITABLE, false);
+ if (contactId1 == -1 || contactId2 == -1) {
+ Log.e(TAG, "Invalid arguments for joinContacts request");
+ return;
+ }
+
+ final ContentResolver resolver = getContentResolver();
+
+ // Load raw contact IDs for all raw contacts involved - currently edited and selected
+ // in the join UIs
+ Cursor c = resolver.query(RawContacts.CONTENT_URI,
+ JoinContactQuery.PROJECTION,
+ JoinContactQuery.SELECTION,
+ new String[]{String.valueOf(contactId1), String.valueOf(contactId2)}, null);
+
+ long rawContactIds[];
+ long verifiedNameRawContactId = -1;
+ try {
+ int maxDisplayNameSource = -1;
+ rawContactIds = new long[c.getCount()];
+ for (int i = 0; i < rawContactIds.length; i++) {
+ c.moveToPosition(i);
+ long rawContactId = c.getLong(JoinContactQuery._ID);
+ rawContactIds[i] = rawContactId;
+ int nameSource = c.getInt(JoinContactQuery.DISPLAY_NAME_SOURCE);
+ if (nameSource > maxDisplayNameSource) {
+ maxDisplayNameSource = nameSource;
+ }
+ }
+
+ // Find an appropriate display name for the joined contact:
+ // if should have a higher DisplayNameSource or be the name
+ // of the original contact that we are joining with another.
+ if (writable) {
+ for (int i = 0; i < rawContactIds.length; i++) {
+ c.moveToPosition(i);
+ if (c.getLong(JoinContactQuery.CONTACT_ID) == contactId1) {
+ int nameSource = c.getInt(JoinContactQuery.DISPLAY_NAME_SOURCE);
+ if (nameSource == maxDisplayNameSource
+ && (verifiedNameRawContactId == -1
+ || c.getInt(JoinContactQuery.NAME_VERIFIED) != 0)) {
+ verifiedNameRawContactId = c.getLong(JoinContactQuery._ID);
+ }
+ }
+ }
+ }
+ } finally {
+ c.close();
+ }
+
+ // For each pair of raw contacts, insert an aggregation exception
+ ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
+ for (int i = 0; i < rawContactIds.length; i++) {
+ for (int j = 0; j < rawContactIds.length; j++) {
+ if (i != j) {
+ buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]);
+ }
+ }
+ }
+
+ // Mark the original contact as "name verified" to make sure that the contact
+ // display name does not change as a result of the join
+ if (verifiedNameRawContactId != -1) {
+ Builder builder = ContentProviderOperation.newUpdate(
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, verifiedNameRawContactId));
+ builder.withValue(RawContacts.NAME_VERIFIED, 1);
+ operations.add(builder.build());
+ }
+
+ boolean success = false;
+ // Apply all aggregation exceptions as one batch
+ try {
+ resolver.applyBatch(ContactsContract.AUTHORITY, operations);
+ showToast(R.string.contactsJoinedMessage);
+ success = true;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to apply aggregation exception batch", e);
+ showToast(R.string.contactSavedErrorToast);
+ } catch (OperationApplicationException e) {
+ Log.e(TAG, "Failed to apply aggregation exception batch", e);
+ showToast(R.string.contactSavedErrorToast);
+ }
+
+ Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
+ if (success) {
+ Uri uri = RawContacts.getContactLookupUri(resolver,
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactIds[0]));
+ callbackIntent.setData(uri);
+ }
+ startActivity(callbackIntent);
+ }
+
+ /**
+ * Construct a {@link AggregationExceptions#TYPE_KEEP_TOGETHER} ContentProviderOperation.
+ */
+ private void buildJoinContactDiff(ArrayList<ContentProviderOperation> operations,
+ long rawContactId1, long rawContactId2) {
+ Builder builder =
+ ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI);
+ builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
+ builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
+ builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
+ operations.add(builder.build());
+ }
+
+ /**
+ * Shows a toast on the UI thread.
+ */
+ private void showToast(final int message) {
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+
+ @Override
+ public void run() {
+ Toast.makeText(ContactSaveService.this, message, Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+}
diff --git a/src/com/android/contacts/ContactsActivity.java b/src/com/android/contacts/ContactsActivity.java
new file mode 100644
index 0000000..79ebecb
--- /dev/null
+++ b/src/com/android/contacts/ContactsActivity.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 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.contacts.test.InjectedServices;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.SharedPreferences;
+
+/**
+ * A common superclass for Contacts activities that handles application-wide services.
+ */
+public abstract class ContactsActivity extends Activity {
+
+ private ContentResolver mContentResolver;
+
+ @Override
+ public ContentResolver getContentResolver() {
+ if (mContentResolver == null) {
+ InjectedServices services = ContactsApplication.getInjectedServices();
+ if (services != null) {
+ mContentResolver = services.getContentResolver();
+ }
+ if (mContentResolver == null) {
+ mContentResolver = super.getContentResolver();
+ }
+ }
+ return mContentResolver;
+ }
+
+ @Override
+ public SharedPreferences getSharedPreferences(String name, int mode) {
+ InjectedServices services = ContactsApplication.getInjectedServices();
+ if (services != null) {
+ SharedPreferences prefs = services.getSharedPreferences();
+ if (prefs != null) {
+ return prefs;
+ }
+ }
+
+ return super.getSharedPreferences(name, mode);
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ Object service = super.getSystemService(name);
+ if (service != null) {
+ return service;
+ }
+
+ return getApplicationContext().getSystemService(name);
+ }
+}
diff --git a/src/com/android/contacts/ContactsApplication.java b/src/com/android/contacts/ContactsApplication.java
new file mode 100644
index 0000000..8346c04
--- /dev/null
+++ b/src/com/android/contacts/ContactsApplication.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 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.contacts.model.AccountTypeManager;
+import com.android.contacts.test.InjectedServices;
+
+import android.app.Application;
+import android.app.LoaderManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.StrictMode;
+import android.preference.PreferenceManager;
+
+public final class ContactsApplication extends Application {
+
+ private static InjectedServices sInjectedServices;
+ private AccountTypeManager mAccountTypeManager;
+
+ /**
+ * Overrides the system services with mocks for testing.
+ */
+ public static void injectServices(InjectedServices services) {
+ sInjectedServices = services;
+ }
+
+ public static InjectedServices getInjectedServices() {
+ return sInjectedServices;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ if (sInjectedServices != null) {
+ ContentResolver resolver = sInjectedServices.getContentResolver();
+ if (resolver != null) {
+ return resolver;
+ }
+ }
+ return super.getContentResolver();
+ }
+
+ @Override
+ public SharedPreferences getSharedPreferences(String name, int mode) {
+ if (sInjectedServices != null) {
+ SharedPreferences prefs = sInjectedServices.getSharedPreferences();
+ if (prefs != null) {
+ return prefs;
+ }
+ }
+
+ return super.getSharedPreferences(name, mode);
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ if (sInjectedServices != null) {
+ Object service = sInjectedServices.getSystemService(name);
+ if (service != null) {
+ return service;
+ }
+ }
+
+ if (AccountTypeManager.ACCOUNT_TYPE_SERVICE.equals(name)) {
+ if (mAccountTypeManager == null) {
+ mAccountTypeManager = AccountTypeManager.createAccountTypeManager(this);
+ }
+ return mAccountTypeManager;
+ }
+
+ return super.getSystemService(name);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ // Priming caches to placate the StrictMode police
+ Context context = getApplicationContext();
+ PreferenceManager.getDefaultSharedPreferences(context);
+ AccountTypeManager.getInstance(context);
+ LoaderManager.enableDebugLogging(true);
+
+ StrictMode.setThreadPolicy(
+ new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
+ }
+}
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
deleted file mode 100644
index ac6a3a8..0000000
--- a/src/com/android/contacts/ContactsListActivity.java
+++ /dev/null
@@ -1,3614 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts;
-
-import com.android.contacts.TextHighlightingAnimation.TextWithHighlighting;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.Sources;
-import com.android.contacts.ui.ContactsPreferences;
-import com.android.contacts.ui.ContactsPreferencesActivity;
-import com.android.contacts.ui.ContactsPreferencesActivity.Prefs;
-import com.android.contacts.util.AccountSelectionUtil;
-import com.android.contacts.util.Constants;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-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.IContentService;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.UriMatcher;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.database.CharArrayBuffer;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.Typeface;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.net.Uri.Builder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.preference.PreferenceManager;
-import android.provider.ContactsContract;
-import android.provider.Settings;
-import android.provider.Contacts.ContactMethods;
-import android.provider.Contacts.People;
-import android.provider.Contacts.PeopleColumns;
-import android.provider.Contacts.Phones;
-import android.provider.ContactsContract.ContactCounts;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Intents;
-import android.provider.ContactsContract.ProviderStatus;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.SearchSnippetColumns;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Nickname;
-import android.provider.ContactsContract.CommonDataKinds.Organization;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.Contacts.AggregationSuggestions;
-import android.provider.ContactsContract.Intents.Insert;
-import android.provider.ContactsContract.Intents.UI;
-import android.telephony.TelephonyManager;
-import android.text.Editable;
-import android.text.Html;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.ContextThemeWrapper;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
-import android.view.View.OnTouchListener;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.CursorAdapter;
-import android.widget.Filter;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.QuickContactBadge;
-import android.widget.SectionIndexer;
-import android.widget.TextView;
-import android.widget.Toast;
-import android.widget.AbsListView.OnScrollListener;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
-/**
- * Displays a list of contacts. Usually is embedded into the ContactsActivity.
- */
-@SuppressWarnings("deprecation")
-public class ContactsListActivity extends ListActivity implements View.OnCreateContextMenuListener,
- View.OnClickListener, View.OnKeyListener, TextWatcher, TextView.OnEditorActionListener,
- OnFocusChangeListener, OnTouchListener {
-
- public static class JoinContactActivity extends ContactsListActivity {
-
- }
-
- public static class ContactsSearchActivity extends ContactsListActivity {
-
- }
-
- private static final String TAG = "ContactsListActivity";
-
- private static final boolean ENABLE_ACTION_ICON_OVERLAYS = true;
-
- private static final String LIST_STATE_KEY = "liststate";
- private static final String SHORTCUT_ACTION_KEY = "shortcutAction";
-
- 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;
-
- private static final int SUBACTIVITY_NEW_CONTACT = 1;
- private static final int SUBACTIVITY_VIEW_CONTACT = 2;
- private static final int SUBACTIVITY_DISPLAY_GROUP = 3;
- private static final int SUBACTIVITY_SEARCH = 4;
- private static final int SUBACTIVITY_FILTER = 5;
-
- private static final int TEXT_HIGHLIGHTING_ANIMATION_DURATION = 350;
-
- /**
- * The action for the join contact activity.
- * <p>
- * Input: extra field {@link #EXTRA_AGGREGATE_ID} is the aggregate ID.
- *
- * TODO: move to {@link ContactsContract}.
- */
- public static final String JOIN_AGGREGATE =
- "com.android.contacts.action.JOIN_AGGREGATE";
-
- /**
- * Used with {@link #JOIN_AGGREGATE} to give it the target for aggregation.
- * <p>
- * Type: LONG
- */
- public static final String EXTRA_AGGREGATE_ID =
- "com.android.contacts.action.AGGREGATE_ID";
-
- /**
- * Used with {@link #JOIN_AGGREGATE} to give it the name of the aggregation target.
- * <p>
- * Type: STRING
- */
- @Deprecated
- public static final String EXTRA_AGGREGATE_NAME =
- "com.android.contacts.action.AGGREGATE_NAME";
-
- public static final String AUTHORITIES_FILTER_KEY = "authorities";
-
- private static final Uri CONTACTS_CONTENT_URI_WITH_LETTER_COUNTS =
- buildSectionIndexerUri(Contacts.CONTENT_URI);
-
- /** 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;
- /** Mask for showing photos in the list */
- static final int MODE_MASK_SHOW_PHOTOS = 0x08000000;
- /** Mask for hiding additional information e.g. primary phone number in the list */
- static final int MODE_MASK_NO_DATA = 0x04000000;
- /** Mask for showing a call button in the list */
- static final int MODE_MASK_SHOW_CALL_BUTTON = 0x02000000;
- /** Mask to disable quickcontact (images will show as normal images) */
- static final int MODE_MASK_DISABLE_QUIKCCONTACT = 0x01000000;
- /** Mask to show the total number of contacts at the top */
- static final int MODE_MASK_SHOW_NUMBER_OF_CONTACTS = 0x00800000;
-
- /** Unknown mode */
- static final int MODE_UNKNOWN = 0;
- /** Default mode */
- static final int MODE_DEFAULT = 4 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
- /** Custom mode */
- static final int MODE_CUSTOM = 8;
- /** Show all starred contacts */
- static final int MODE_STARRED = 20 | MODE_MASK_SHOW_PHOTOS;
- /** Show frequently contacted contacts */
- static final int MODE_FREQUENT = 30 | MODE_MASK_SHOW_PHOTOS;
- /** Show starred and the frequent */
- static final int MODE_STREQUENT = 35 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_SHOW_CALL_BUTTON;
- /** Show all contacts and pick them when clicking */
- static final int MODE_PICK_CONTACT = 40 | MODE_MASK_PICKER | MODE_MASK_SHOW_PHOTOS
- | MODE_MASK_DISABLE_QUIKCCONTACT;
- /** 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
- | MODE_MASK_SHOW_PHOTOS | MODE_MASK_DISABLE_QUIKCCONTACT;
- /** Show all people through the legacy provider and pick them when clicking */
- static final int MODE_LEGACY_PICK_PERSON = 43 | MODE_MASK_PICKER
- | MODE_MASK_DISABLE_QUIKCCONTACT;
- /** Show all people through the legacy provider as well as the option to create a new one */
- static final int MODE_LEGACY_PICK_OR_CREATE_PERSON = 44 | MODE_MASK_PICKER
- | MODE_MASK_CREATE_NEW | MODE_MASK_DISABLE_QUIKCCONTACT;
- /** 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
- | MODE_MASK_SHOW_PHOTOS | MODE_MASK_DISABLE_QUIKCCONTACT;
- /** 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 phone numbers through the legacy provider and pick them when clicking */
- static final int MODE_LEGACY_PICK_PHONE =
- 51 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE | MODE_MASK_NO_FILTER;
- /** 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;
- /** Show all postal addresses and pick them when clicking */
- static final int MODE_LEGACY_PICK_POSTAL =
- 56 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE | MODE_MASK_NO_FILTER;
- static final int MODE_GROUP = 57 | MODE_MASK_SHOW_PHOTOS;
- /** Run a search query */
- static final int MODE_QUERY = 60 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_NO_FILTER
- | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
- /** Run a search query in PICK mode, but that still launches to VIEW */
- static final int MODE_QUERY_PICK_TO_VIEW = 65 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_PICKER
- | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
-
- /** Show join suggestions followed by an A-Z list */
- static final int MODE_JOIN_CONTACT = 70 | MODE_MASK_PICKER | MODE_MASK_NO_PRESENCE
- | MODE_MASK_NO_DATA | MODE_MASK_SHOW_PHOTOS | MODE_MASK_DISABLE_QUIKCCONTACT;
-
- /** Run a search query in a PICK mode */
- static final int MODE_QUERY_PICK = 75 | MODE_MASK_SHOW_PHOTOS | MODE_MASK_NO_FILTER
- | MODE_MASK_PICKER | MODE_MASK_DISABLE_QUIKCCONTACT | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
-
- /** Run a search query in a PICK_PHONE mode */
- static final int MODE_QUERY_PICK_PHONE = 80 | MODE_MASK_NO_FILTER | MODE_MASK_PICKER
- | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
-
- /** Run a search query in PICK mode, but that still launches to EDIT */
- static final int MODE_QUERY_PICK_TO_EDIT = 85 | MODE_MASK_NO_FILTER | MODE_MASK_SHOW_PHOTOS
- | MODE_MASK_PICKER | MODE_MASK_SHOW_NUMBER_OF_CONTACTS;
-
- /**
- * An action used to do perform search while in a contact picker. It is initiated
- * by the ContactListActivity itself.
- */
- private static final String ACTION_SEARCH_INTERNAL = "com.android.contacts.INTERNAL_SEARCH";
-
- /** Maximum number of suggestions shown for joining aggregates */
- static final int MAX_SUGGESTIONS = 4;
-
- static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
- Contacts._ID, // 0
- Contacts.DISPLAY_NAME_PRIMARY, // 1
- Contacts.DISPLAY_NAME_ALTERNATIVE, // 2
- Contacts.SORT_KEY_PRIMARY, // 3
- Contacts.STARRED, // 4
- Contacts.TIMES_CONTACTED, // 5
- Contacts.CONTACT_PRESENCE, // 6
- Contacts.PHOTO_ID, // 7
- Contacts.LOOKUP_KEY, // 8
- Contacts.PHONETIC_NAME, // 9
- Contacts.HAS_PHONE_NUMBER, // 10
- };
- static final String[] CONTACTS_SUMMARY_PROJECTION_FROM_EMAIL = new String[] {
- Contacts._ID, // 0
- Contacts.DISPLAY_NAME_PRIMARY, // 1
- Contacts.DISPLAY_NAME_ALTERNATIVE, // 2
- Contacts.SORT_KEY_PRIMARY, // 3
- Contacts.STARRED, // 4
- Contacts.TIMES_CONTACTED, // 5
- Contacts.CONTACT_PRESENCE, // 6
- Contacts.PHOTO_ID, // 7
- Contacts.LOOKUP_KEY, // 8
- Contacts.PHONETIC_NAME, // 9
- // email lookup doesn't included HAS_PHONE_NUMBER in projection
- };
-
- static final String[] CONTACTS_SUMMARY_FILTER_PROJECTION = new String[] {
- Contacts._ID, // 0
- Contacts.DISPLAY_NAME_PRIMARY, // 1
- Contacts.DISPLAY_NAME_ALTERNATIVE, // 2
- Contacts.SORT_KEY_PRIMARY, // 3
- Contacts.STARRED, // 4
- Contacts.TIMES_CONTACTED, // 5
- Contacts.CONTACT_PRESENCE, // 6
- Contacts.PHOTO_ID, // 7
- Contacts.LOOKUP_KEY, // 8
- Contacts.PHONETIC_NAME, // 9
- Contacts.HAS_PHONE_NUMBER, // 10
- SearchSnippetColumns.SNIPPET_MIMETYPE, // 11
- SearchSnippetColumns.SNIPPET_DATA1, // 12
- SearchSnippetColumns.SNIPPET_DATA4, // 13
- };
-
- static final String[] LEGACY_PEOPLE_PROJECTION = new String[] {
- People._ID, // 0
- People.DISPLAY_NAME, // 1
- People.DISPLAY_NAME, // 2
- People.DISPLAY_NAME, // 3
- People.STARRED, // 4
- PeopleColumns.TIMES_CONTACTED, // 5
- People.PRESENCE_STATUS, // 6
- };
- static final int SUMMARY_ID_COLUMN_INDEX = 0;
- static final int SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX = 1;
- static final int SUMMARY_DISPLAY_NAME_ALTERNATIVE_COLUMN_INDEX = 2;
- static final int SUMMARY_SORT_KEY_PRIMARY_COLUMN_INDEX = 3;
- static final int SUMMARY_STARRED_COLUMN_INDEX = 4;
- static final int SUMMARY_TIMES_CONTACTED_COLUMN_INDEX = 5;
- static final int SUMMARY_PRESENCE_STATUS_COLUMN_INDEX = 6;
- static final int SUMMARY_PHOTO_ID_COLUMN_INDEX = 7;
- static final int SUMMARY_LOOKUP_KEY_COLUMN_INDEX = 8;
- static final int SUMMARY_PHONETIC_NAME_COLUMN_INDEX = 9;
- static final int SUMMARY_HAS_PHONE_COLUMN_INDEX = 10;
- static final int SUMMARY_SNIPPET_MIMETYPE_COLUMN_INDEX = 11;
- static final int SUMMARY_SNIPPET_DATA1_COLUMN_INDEX = 12;
- static final int SUMMARY_SNIPPET_DATA4_COLUMN_INDEX = 13;
-
- static final String[] PHONES_PROJECTION = new String[] {
- Phone._ID, //0
- Phone.TYPE, //1
- Phone.LABEL, //2
- Phone.NUMBER, //3
- Phone.DISPLAY_NAME, // 4
- Phone.CONTACT_ID, // 5
- };
- static final String[] LEGACY_PHONES_PROJECTION = new String[] {
- Phones._ID, //0
- Phones.TYPE, //1
- Phones.LABEL, //2
- Phones.NUMBER, //3
- People.DISPLAY_NAME, // 4
- };
- static final int PHONE_ID_COLUMN_INDEX = 0;
- static final int PHONE_TYPE_COLUMN_INDEX = 1;
- static final int PHONE_LABEL_COLUMN_INDEX = 2;
- static final int PHONE_NUMBER_COLUMN_INDEX = 3;
- static final int PHONE_DISPLAY_NAME_COLUMN_INDEX = 4;
- static final int PHONE_CONTACT_ID_COLUMN_INDEX = 5;
-
- static final String[] POSTALS_PROJECTION = new String[] {
- StructuredPostal._ID, //0
- StructuredPostal.TYPE, //1
- StructuredPostal.LABEL, //2
- StructuredPostal.DATA, //3
- StructuredPostal.DISPLAY_NAME, // 4
- };
- static final String[] LEGACY_POSTALS_PROJECTION = new String[] {
- ContactMethods._ID, //0
- ContactMethods.TYPE, //1
- ContactMethods.LABEL, //2
- ContactMethods.DATA, //3
- People.DISPLAY_NAME, // 4
- };
- static final String[] RAW_CONTACTS_PROJECTION = new String[] {
- RawContacts._ID, //0
- RawContacts.CONTACT_ID, //1
- RawContacts.ACCOUNT_TYPE, //2
- };
-
- static final int POSTAL_ID_COLUMN_INDEX = 0;
- static final int POSTAL_TYPE_COLUMN_INDEX = 1;
- static final int POSTAL_LABEL_COLUMN_INDEX = 2;
- static final int POSTAL_ADDRESS_COLUMN_INDEX = 3;
- static final int POSTAL_DISPLAY_NAME_COLUMN_INDEX = 4;
-
- private static final int QUERY_TOKEN = 42;
-
- static final String KEY_PICKER_MODE = "picker_mode";
-
- private ContactItemListAdapter mAdapter;
-
- int mMode = MODE_DEFAULT;
-
- private QueryHandler mQueryHandler;
- private boolean mJustCreated;
- private boolean mSyncEnabled;
- Uri mSelectedContactUri;
-
-// private boolean mDisplayAll;
- private boolean mDisplayOnlyPhones;
-
- private Uri mGroupUri;
-
- private long mQueryAggregateId;
-
- private ArrayList<Long> mWritableRawContactIds = new ArrayList<Long>();
- private int mWritableSourcesCnt;
- private int mReadOnlySourcesCnt;
-
- /**
- * Used to keep track of the scroll state of the list.
- */
- private Parcelable mListState = null;
-
- private String mShortcutAction;
-
- /**
- * Internal query type when in mode {@link #MODE_QUERY_PICK_TO_VIEW}.
- */
- private int mQueryMode = QUERY_MODE_NONE;
-
- private static final int QUERY_MODE_NONE = -1;
- private static final int QUERY_MODE_MAILTO = 1;
- private static final int QUERY_MODE_TEL = 2;
-
- private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
-
- private boolean mSearchMode;
- private boolean mSearchResultsMode;
- private boolean mShowNumberOfContacts;
-
- private boolean mShowSearchSnippets;
- private boolean mSearchInitiated;
-
- private String mInitialFilter;
-
- private static final String CLAUSE_ONLY_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1";
- private static final String CLAUSE_ONLY_PHONES = Contacts.HAS_PHONE_NUMBER + "=1";
-
- /**
- * In the {@link #MODE_JOIN_CONTACT} determines whether we display a list item with the label
- * "Show all contacts" or actually show all contacts
- */
- private boolean mJoinModeShowAllContacts;
-
- /**
- * The ID of the special item described above.
- */
- private static final long JOIN_MODE_SHOW_ALL_CONTACTS_ID = -2;
-
- // Uri matcher for contact id
- private static final int CONTACTS_ID = 1001;
- private static final UriMatcher sContactsIdMatcher;
-
- private ContactPhotoLoader mPhotoLoader;
-
- final String[] sLookupProjection = new String[] {
- Contacts.LOOKUP_KEY
- };
-
- static {
- sContactsIdMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- sContactsIdMatcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
- }
-
- private class DeleteClickListener implements DialogInterface.OnClickListener {
- public void onClick(DialogInterface dialog, int which) {
- if (mSelectedContactUri != null) {
- getContentResolver().delete(mSelectedContactUri, null, null);
- }
- }
- }
-
- /**
- * A {@link TextHighlightingAnimation} that redraws just the contact display name in a
- * list item.
- */
- private static class NameHighlightingAnimation extends TextHighlightingAnimation {
- private final ListView mListView;
-
- private NameHighlightingAnimation(ListView listView, int duration) {
- super(duration);
- this.mListView = listView;
- }
-
- /**
- * Redraws all visible items of the list corresponding to contacts
- */
- @Override
- protected void invalidate() {
- int childCount = mListView.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View itemView = mListView.getChildAt(i);
- if (itemView instanceof ContactListItemView) {
- final ContactListItemView view = (ContactListItemView)itemView;
- view.getNameTextView().invalidate();
- }
- }
- }
-
- @Override
- protected void onAnimationStarted() {
- mListView.setScrollingCacheEnabled(false);
- }
-
- @Override
- protected void onAnimationEnded() {
- mListView.setScrollingCacheEnabled(true);
- }
- }
-
- // The size of a home screen shortcut icon.
- private int mIconSize;
- private ContactsPreferences mContactsPrefs;
- private int mDisplayOrder;
- private int mSortOrder;
- private boolean mHighlightWhenScrolling;
- private TextHighlightingAnimation mHighlightingAnimation;
- private SearchEditText mSearchEditText;
-
- /**
- * An approximation of the background color of the pinned header. This color
- * is used when the pinned header is being pushed up. At that point the header
- * "fades away". Rather than computing a faded bitmap based on the 9-patch
- * normally used for the background, we will use a solid color, which will
- * provide better performance and reduced complexity.
- */
- private int mPinnedHeaderBackgroundColor;
-
- private ContentObserver mProviderStatusObserver = new ContentObserver(new Handler()) {
-
- @Override
- public void onChange(boolean selfChange) {
- checkProviderState(true);
- }
- };
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mIconSize = getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
- mContactsPrefs = new ContactsPreferences(this);
- mPhotoLoader = new ContactPhotoLoader(this, R.drawable.ic_contact_list_picture);
-
- // 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(UI.TITLE_EXTRA_KEY);
- if (title != null) {
- setTitle(title);
- }
-
- String action = intent.getAction();
- String component = intent.getComponent().getClassName();
-
- // When we get a FILTER_CONTACTS_ACTION, it represents search in the context
- // of some other action. Let's retrieve the original action to provide proper
- // context for the search queries.
- if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
- mSearchMode = true;
- mShowSearchSnippets = true;
- Bundle extras = intent.getExtras();
- if (extras != null) {
- mInitialFilter = extras.getString(UI.FILTER_TEXT_EXTRA_KEY);
- String originalAction =
- extras.getString(ContactsSearchManager.ORIGINAL_ACTION_EXTRA_KEY);
- if (originalAction != null) {
- action = originalAction;
- }
- String originalComponent =
- extras.getString(ContactsSearchManager.ORIGINAL_COMPONENT_EXTRA_KEY);
- if (originalComponent != null) {
- component = originalComponent;
- }
- } else {
- mInitialFilter = null;
- }
- }
-
- Log.i(TAG, "Called with action: " + action);
- mMode = MODE_UNKNOWN;
- if (UI.LIST_DEFAULT.equals(action) || UI.FILTER_CONTACTS_ACTION.equals(action)) {
- mMode = MODE_DEFAULT;
- // 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;
- }
- buildUserGroupUri(groupName);
- } else if (UI.LIST_ALL_CONTACTS_ACTION.equals(action)) {
- mMode = MODE_CUSTOM;
- mDisplayOnlyPhones = false;
- } else if (UI.LIST_STARRED_ACTION.equals(action)) {
- mMode = mSearchMode ? MODE_DEFAULT : MODE_STARRED;
- } else if (UI.LIST_FREQUENT_ACTION.equals(action)) {
- mMode = mSearchMode ? MODE_DEFAULT : MODE_FREQUENT;
- } else if (UI.LIST_STREQUENT_ACTION.equals(action)) {
- mMode = mSearchMode ? MODE_DEFAULT : MODE_STREQUENT;
- } else if (UI.LIST_CONTACTS_WITH_PHONES_ACTION.equals(action)) {
- mMode = MODE_CUSTOM;
- mDisplayOnlyPhones = true;
- } 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 (Contacts.CONTENT_TYPE.equals(type)) {
- mMode = MODE_PICK_CONTACT;
- } else if (People.CONTENT_TYPE.equals(type)) {
- mMode = MODE_LEGACY_PICK_PERSON;
- } else if (Phone.CONTENT_TYPE.equals(type)) {
- mMode = MODE_PICK_PHONE;
- } else if (Phones.CONTENT_TYPE.equals(type)) {
- mMode = MODE_LEGACY_PICK_PHONE;
- } else if (StructuredPostal.CONTENT_TYPE.equals(type)) {
- mMode = MODE_PICK_POSTAL;
- } else if (ContactMethods.CONTENT_POSTAL_TYPE.equals(type)) {
- mMode = MODE_LEGACY_PICK_POSTAL;
- }
- } else if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
- if (component.equals("alias.DialShortcut")) {
- mMode = MODE_PICK_PHONE;
- mShortcutAction = Intent.ACTION_CALL;
- mShowSearchSnippets = false;
- setTitle(R.string.callShortcutActivityTitle);
- } else if (component.equals("alias.MessageShortcut")) {
- mMode = MODE_PICK_PHONE;
- mShortcutAction = Intent.ACTION_SENDTO;
- mShowSearchSnippets = false;
- setTitle(R.string.messageShortcutActivityTitle);
- } else if (mSearchMode) {
- mMode = MODE_PICK_CONTACT;
- mShortcutAction = Intent.ACTION_VIEW;
- setTitle(R.string.shortcutActivityTitle);
- } else {
- mMode = MODE_PICK_OR_CREATE_CONTACT;
- mShortcutAction = Intent.ACTION_VIEW;
- setTitle(R.string.shortcutActivityTitle);
- }
- } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
- final String type = intent.resolveType(this);
- if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
- if (mSearchMode) {
- mMode = MODE_PICK_CONTACT;
- } else {
- mMode = MODE_PICK_OR_CREATE_CONTACT;
- }
- } else if (Phone.CONTENT_ITEM_TYPE.equals(type)) {
- mMode = MODE_PICK_PHONE;
- } else if (Phones.CONTENT_ITEM_TYPE.equals(type)) {
- mMode = MODE_LEGACY_PICK_PHONE;
- } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(type)) {
- mMode = MODE_PICK_POSTAL;
- } else if (ContactMethods.CONTENT_POSTAL_ITEM_TYPE.equals(type)) {
- mMode = MODE_LEGACY_PICK_POSTAL;
- } else if (People.CONTENT_ITEM_TYPE.equals(type)) {
- if (mSearchMode) {
- mMode = MODE_LEGACY_PICK_PERSON;
- } else {
- mMode = MODE_LEGACY_PICK_OR_CREATE_PERSON;
- }
- }
-
- } 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;
- }
-
- // See if search request has extras to specify query
- if (intent.hasExtra(Insert.EMAIL)) {
- mMode = MODE_QUERY_PICK_TO_VIEW;
- mQueryMode = QUERY_MODE_MAILTO;
- mInitialFilter = intent.getStringExtra(Insert.EMAIL);
- } else if (intent.hasExtra(Insert.PHONE)) {
- mMode = MODE_QUERY_PICK_TO_VIEW;
- mQueryMode = QUERY_MODE_TEL;
- mInitialFilter = intent.getStringExtra(Insert.PHONE);
- } else {
- // Otherwise handle the more normal search case
- mMode = MODE_QUERY;
- mShowSearchSnippets = true;
- mInitialFilter = getIntent().getStringExtra(SearchManager.QUERY);
- }
- mSearchResultsMode = true;
- } else if (ACTION_SEARCH_INTERNAL.equals(action)) {
- String originalAction = null;
- Bundle extras = intent.getExtras();
- if (extras != null) {
- originalAction = extras.getString(ContactsSearchManager.ORIGINAL_ACTION_EXTRA_KEY);
- }
- mShortcutAction = intent.getStringExtra(SHORTCUT_ACTION_KEY);
-
- if (Intent.ACTION_INSERT_OR_EDIT.equals(originalAction)) {
- mMode = MODE_QUERY_PICK_TO_EDIT;
- mShowSearchSnippets = true;
- mInitialFilter = getIntent().getStringExtra(SearchManager.QUERY);
- } else if (mShortcutAction != null && intent.hasExtra(Insert.PHONE)) {
- mMode = MODE_QUERY_PICK_PHONE;
- mQueryMode = QUERY_MODE_TEL;
- mInitialFilter = intent.getStringExtra(Insert.PHONE);
- } else {
- mMode = MODE_QUERY_PICK;
- mQueryMode = QUERY_MODE_NONE;
- mShowSearchSnippets = true;
- mInitialFilter = getIntent().getStringExtra(SearchManager.QUERY);
- }
- mSearchResultsMode = true;
- // 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)) {
- Uri data = intent.getData();
- Uri telUri = null;
- if (sContactsIdMatcher.match(data) == CONTACTS_ID) {
- long contactId = Long.valueOf(data.getLastPathSegment());
- final Cursor cursor = queryPhoneNumbers(contactId);
- if (cursor != null) {
- if (cursor.getCount() == 1 && cursor.moveToFirst()) {
- int phoneNumberIndex = cursor.getColumnIndex(Phone.NUMBER);
- String phoneNumber = cursor.getString(phoneNumberIndex);
- telUri = Uri.parse("tel:" + phoneNumber);
- }
- cursor.close();
- }
- }
- // See if the suggestion was clicked with a search action key (call button)
- Intent newIntent;
- if ("call".equals(intent.getStringExtra(SearchManager.ACTION_MSG)) && telUri != null) {
- newIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, telUri);
- } else {
- newIntent = new Intent(Intent.ACTION_VIEW, data);
- }
- 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)) {
- // TODO actually support this in EditContactActivity.
- String number = intent.getData().getSchemeSpecificPart();
- Intent newIntent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
- newIntent.putExtra(Intents.Insert.PHONE, number);
- startActivity(newIntent);
- finish();
- return;
- }
-
- if (JOIN_AGGREGATE.equals(action)) {
- if (mSearchMode) {
- mMode = MODE_PICK_CONTACT;
- } else {
- mMode = MODE_JOIN_CONTACT;
- mQueryAggregateId = intent.getLongExtra(EXTRA_AGGREGATE_ID, -1);
- if (mQueryAggregateId == -1) {
- Log.e(TAG, "Intent " + action + " is missing required extra: "
- + EXTRA_AGGREGATE_ID);
- setResult(RESULT_CANCELED);
- finish();
- }
- }
- }
-
- if (mMode == MODE_UNKNOWN) {
- mMode = MODE_DEFAULT;
- }
-
- if (((mMode & MODE_MASK_SHOW_NUMBER_OF_CONTACTS) != 0 || mSearchMode)
- && !mSearchResultsMode) {
- mShowNumberOfContacts = true;
- }
-
- if (mMode == MODE_JOIN_CONTACT) {
- setContentView(R.layout.contacts_list_content_join);
- TextView blurbView = (TextView)findViewById(R.id.join_contact_blurb);
-
- String blurb = getString(R.string.blurbJoinContactDataWith,
- getContactDisplayName(mQueryAggregateId));
- blurbView.setText(blurb);
- mJoinModeShowAllContacts = true;
- } else if (mSearchMode) {
- setContentView(R.layout.contacts_search_content);
- } else if (mSearchResultsMode) {
- setContentView(R.layout.contacts_list_search_results);
- TextView titleText = (TextView)findViewById(R.id.search_results_for);
- titleText.setText(Html.fromHtml(getString(R.string.search_results_for,
- "<b>" + mInitialFilter + "</b>")));
- } else {
- setContentView(R.layout.contacts_list_content);
- }
-
- setupListView();
- if (mSearchMode) {
- setupSearchView();
- }
-
- mQueryHandler = new QueryHandler(this);
- mJustCreated = true;
-
- mSyncEnabled = true;
- }
-
- /**
- * Register an observer for provider status changes - we will need to
- * reflect them in the UI.
- */
- private void registerProviderStatusObserver() {
- getContentResolver().registerContentObserver(ProviderStatus.CONTENT_URI,
- false, mProviderStatusObserver);
- }
-
- /**
- * Register an observer for provider status changes - we will need to
- * reflect them in the UI.
- */
- private void unregisterProviderStatusObserver() {
- getContentResolver().unregisterContentObserver(mProviderStatusObserver);
- }
-
- private void setupListView() {
- final ListView list = getListView();
- final LayoutInflater inflater = getLayoutInflater();
-
- mHighlightingAnimation =
- new NameHighlightingAnimation(list, TEXT_HIGHLIGHTING_ANIMATION_DURATION);
-
- // Tell list view to not show dividers. We'll do it ourself so that we can *not* show
- // them when an A-Z headers is visible.
- list.setDividerHeight(0);
- list.setOnCreateContextMenuListener(this);
-
- mAdapter = new ContactItemListAdapter(this);
- setListAdapter(mAdapter);
-
- if (list instanceof PinnedHeaderListView && mAdapter.getDisplaySectionHeadersEnabled()) {
- mPinnedHeaderBackgroundColor =
- getResources().getColor(R.color.pinned_header_background);
- PinnedHeaderListView pinnedHeaderList = (PinnedHeaderListView)list;
- View pinnedHeader = inflater.inflate(R.layout.list_section, list, false);
- pinnedHeaderList.setPinnedHeaderView(pinnedHeader);
- }
-
- list.setOnScrollListener(mAdapter);
- list.setOnKeyListener(this);
- list.setOnFocusChangeListener(this);
- list.setOnTouchListener(this);
-
- // We manually save/restore the listview state
- list.setSaveEnabled(false);
- }
-
- /**
- * Configures search UI.
- */
- private void setupSearchView() {
- mSearchEditText = (SearchEditText)findViewById(R.id.search_src_text);
- mSearchEditText.addTextChangedListener(this);
- mSearchEditText.setOnEditorActionListener(this);
- mSearchEditText.setText(mInitialFilter);
- }
-
- private String getContactDisplayName(long contactId) {
- String contactName = null;
- Cursor c = getContentResolver().query(
- ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
- new String[] {Contacts.DISPLAY_NAME}, null, null, null);
- try {
- if (c != null && c.moveToFirst()) {
- contactName = c.getString(0);
- }
- } finally {
- if (c != null) {
- c.close();
- }
- }
-
- if (contactName == null) {
- contactName = "";
- }
-
- return contactName;
- }
-
- private int getSummaryDisplayNameColumnIndex() {
- if (mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
- return SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX;
- } else {
- return SUMMARY_DISPLAY_NAME_ALTERNATIVE_COLUMN_INDEX;
- }
- }
-
- /** {@inheritDoc} */
- public void onClick(View v) {
- int id = v.getId();
- switch (id) {
- // TODO a better way of identifying the button
- case android.R.id.button1: {
- final int position = (Integer)v.getTag();
- Cursor c = mAdapter.getCursor();
- if (c != null) {
- c.moveToPosition(position);
- callContact(c);
- }
- break;
- }
- }
- }
-
- private void setEmptyText() {
- if (mMode == MODE_JOIN_CONTACT || mSearchMode) {
- return;
- }
-
- TextView empty = (TextView) findViewById(R.id.emptyText);
- if (mDisplayOnlyPhones) {
- empty.setText(getText(R.string.noContactsWithPhoneNumbers));
- } else if (mMode == MODE_STREQUENT || mMode == MODE_STARRED) {
- empty.setText(getText(R.string.noFavoritesHelpText));
- } else if (mMode == MODE_QUERY || mMode == MODE_QUERY_PICK
- || mMode == MODE_QUERY_PICK_PHONE || mMode == MODE_QUERY_PICK_TO_VIEW
- || mMode == MODE_QUERY_PICK_TO_EDIT) {
- empty.setText(getText(R.string.noMatchingContacts));
- } else {
- boolean hasSim = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE))
- .hasIccCard();
- boolean createShortcut = Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction());
- if (isSyncActive()) {
- if (createShortcut) {
- // Help text is the same no matter whether there is SIM or not.
- empty.setText(getText(R.string.noContactsHelpTextWithSyncForCreateShortcut));
- } else if (hasSim) {
- empty.setText(getText(R.string.noContactsHelpTextWithSync));
- } else {
- empty.setText(getText(R.string.noContactsNoSimHelpTextWithSync));
- }
- } else {
- if (createShortcut) {
- // Help text is the same no matter whether there is SIM or not.
- empty.setText(getText(R.string.noContactsHelpTextForCreateShortcut));
- } else if (hasSim) {
- empty.setText(getText(R.string.noContactsHelpText));
- } else {
- empty.setText(getText(R.string.noContactsNoSimHelpText));
- }
- }
- }
- }
-
- private boolean isSyncActive() {
- Account[] accounts = AccountManager.get(this).getAccounts();
- if (accounts != null && accounts.length > 0) {
- IContentService contentService = ContentResolver.getContentService();
- for (Account account : accounts) {
- try {
- if (contentService.isSyncActive(account, ContactsContract.AUTHORITY)) {
- return true;
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Could not get the sync status");
- }
- }
- }
- return false;
- }
-
- private void buildUserGroupUri(String group) {
- mGroupUri = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, group);
- }
-
- /**
- * Sets the mode when the request is for "default"
- */
- private void setDefaultMode() {
- // Load the preferences
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
-
- mDisplayOnlyPhones = prefs.getBoolean(Prefs.DISPLAY_ONLY_PHONES,
- Prefs.DISPLAY_ONLY_PHONES_DEFAULT);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mPhotoLoader.stop();
- }
-
- @Override
- protected void onStart() {
- super.onStart();
-
- mContactsPrefs.registerChangeListener(mPreferencesChangeListener);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- unregisterProviderStatusObserver();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- registerProviderStatusObserver();
- mPhotoLoader.resume();
-
- Activity parent = getParent();
-
- // Do this before setting the filter. The filter thread relies
- // on some state that is initialized in setDefaultMode
- if (mMode == MODE_DEFAULT) {
- // 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 (mSearchMode) {
- mSearchEditText.requestFocus();
- }
-
- if (!mSearchMode && !checkProviderState(mJustCreated)) {
- return;
- }
-
- if (mJustCreated) {
- // 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;
- mSearchInitiated = false;
- }
-
- /**
- * Obtains the contacts provider status and configures the UI accordingly.
- *
- * @param loadData true if the method needs to start a query when the
- * provider is in the normal state
- * @return true if the provider status is normal
- */
- private boolean checkProviderState(boolean loadData) {
- View importFailureView = findViewById(R.id.import_failure);
- if (importFailureView == null) {
- return true;
- }
-
- TextView messageView = (TextView) findViewById(R.id.emptyText);
-
- // This query can be performed on the UI thread because
- // the API explicitly allows such use.
- Cursor cursor = getContentResolver().query(ProviderStatus.CONTENT_URI, new String[] {
- ProviderStatus.STATUS, ProviderStatus.DATA1
- }, null, null, null);
- try {
- if (cursor.moveToFirst()) {
- int status = cursor.getInt(0);
- if (status != mProviderStatus) {
- mProviderStatus = status;
- switch (status) {
- case ProviderStatus.STATUS_NORMAL:
- mAdapter.notifyDataSetInvalidated();
- if (loadData) {
- startQuery();
- }
- break;
-
- case ProviderStatus.STATUS_CHANGING_LOCALE:
- messageView.setText(R.string.locale_change_in_progress);
- mAdapter.changeCursor(null);
- mAdapter.notifyDataSetInvalidated();
- break;
-
- case ProviderStatus.STATUS_UPGRADING:
- messageView.setText(R.string.upgrade_in_progress);
- mAdapter.changeCursor(null);
- mAdapter.notifyDataSetInvalidated();
- break;
-
- case ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY:
- long size = cursor.getLong(1);
- String message = getResources().getString(
- R.string.upgrade_out_of_memory, new Object[] {size});
- messageView.setText(message);
- configureImportFailureView(importFailureView);
- mAdapter.changeCursor(null);
- mAdapter.notifyDataSetInvalidated();
- break;
- }
- }
- }
- } finally {
- cursor.close();
- }
-
- importFailureView.setVisibility(
- mProviderStatus == ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY
- ? View.VISIBLE
- : View.GONE);
- return mProviderStatus == ProviderStatus.STATUS_NORMAL;
- }
-
- private void configureImportFailureView(View importFailureView) {
-
- OnClickListener listener = new OnClickListener(){
-
- public void onClick(View v) {
- switch(v.getId()) {
- case R.id.import_failure_uninstall_apps: {
- startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS));
- break;
- }
- case R.id.import_failure_retry_upgrade: {
- // Send a provider status update, which will trigger a retry
- ContentValues values = new ContentValues();
- values.put(ProviderStatus.STATUS, ProviderStatus.STATUS_UPGRADING);
- getContentResolver().update(ProviderStatus.CONTENT_URI, values, null, null);
- break;
- }
- }
- }};
-
- Button uninstallApps = (Button) findViewById(R.id.import_failure_uninstall_apps);
- uninstallApps.setOnClickListener(listener);
-
- Button retryUpgrade = (Button) findViewById(R.id.import_failure_retry_upgrade);
- retryUpgrade.setOnClickListener(listener);
- }
-
- private String getTextFilter() {
- if (mSearchEditText != null) {
- return mSearchEditText.getText().toString();
- }
- return null;
- }
-
- @Override
- protected void onRestart() {
- super.onRestart();
-
- if (!checkProviderState(false)) {
- return;
- }
-
- // The cursor was killed off in onStop(), so we need to get a new one here
- // We do not perform the query if a filter is set on the list because the
- // filter will cause the query to happen anyway
- if (TextUtils.isEmpty(getTextFilter())) {
- startQuery();
- } else {
- // Run the filtered query on the adapter
- mAdapter.onContentChanged();
- }
- }
-
- @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
- if (mList != null) {
- icicle.putParcelable(LIST_STATE_KEY, mList.onSaveInstanceState());
- }
- }
-
- @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);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
-
- mContactsPrefs.unregisterChangeListener();
- mAdapter.setSuggestionsCursor(null);
- 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) {
- super.onCreateOptionsMenu(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;
- }
-
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.list, menu);
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- final boolean defaultMode = (mMode == MODE_DEFAULT);
- menu.findItem(R.id.menu_display_groups).setVisible(defaultMode);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_display_groups: {
- final Intent intent = new Intent(this, ContactsPreferencesActivity.class);
- startActivityForResult(intent, SUBACTIVITY_DISPLAY_GROUP);
- return true;
- }
- case R.id.menu_search: {
- onSearchRequested();
- return true;
- }
- case R.id.menu_add: {
- final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
- startActivity(intent);
- return true;
- }
- case R.id.menu_import_export: {
- displayImportExportDialog();
- return true;
- }
- case R.id.menu_accounts: {
- final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
- intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
- ContactsContract.AUTHORITY
- });
- startActivity(intent);
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
- boolean globalSearch) {
- if (mProviderStatus != ProviderStatus.STATUS_NORMAL) {
- return;
- }
-
- if (globalSearch) {
- super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
- } else {
- if (!mSearchMode && (mMode & MODE_MASK_NO_FILTER) == 0) {
- if ((mMode & MODE_MASK_PICKER) != 0) {
- ContactsSearchManager.startSearchForResult(this, initialQuery,
- SUBACTIVITY_FILTER);
- } else {
- ContactsSearchManager.startSearch(this, initialQuery);
- }
- }
- }
- }
-
- /**
- * Performs filtering of the list based on the search query entered in the
- * search text edit.
- */
- protected void onSearchTextChanged() {
- // Set the proper empty string
- setEmptyText();
-
- Filter filter = mAdapter.getFilter();
- filter.filter(getTextFilter());
- }
-
- /**
- * Starts a new activity that will run a search query and display search results.
- */
- private void doSearch() {
- String query = getTextFilter();
- if (TextUtils.isEmpty(query)) {
- return;
- }
-
- Intent intent = new Intent(this, SearchResultsActivity.class);
- Intent originalIntent = getIntent();
- Bundle originalExtras = originalIntent.getExtras();
- if (originalExtras != null) {
- intent.putExtras(originalExtras);
- }
-
- intent.putExtra(SearchManager.QUERY, query);
- if ((mMode & MODE_MASK_PICKER) != 0) {
- intent.setAction(ACTION_SEARCH_INTERNAL);
- intent.putExtra(SHORTCUT_ACTION_KEY, mShortcutAction);
- if (mShortcutAction != null) {
- if (Intent.ACTION_CALL.equals(mShortcutAction)
- || Intent.ACTION_SENDTO.equals(mShortcutAction)) {
- intent.putExtra(Insert.PHONE, query);
- }
- } else {
- switch (mQueryMode) {
- case QUERY_MODE_MAILTO:
- intent.putExtra(Insert.EMAIL, query);
- break;
- case QUERY_MODE_TEL:
- intent.putExtra(Insert.PHONE, query);
- break;
- }
- }
- startActivityForResult(intent, SUBACTIVITY_SEARCH);
- } else {
- intent.setAction(Intent.ACTION_SEARCH);
- startActivity(intent);
- }
- }
-
- @Override
- protected Dialog onCreateDialog(int id, Bundle bundle) {
- switch (id) {
- case R.string.import_from_sim:
- case R.string.import_from_sdcard: {
- return AccountSelectionUtil.getSelectAccountDialog(this, id);
- }
- case R.id.dialog_sdcard_not_found: {
- return new AlertDialog.Builder(this)
- .setTitle(R.string.no_sdcard_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.no_sdcard_message)
- .setPositiveButton(android.R.string.ok, null).create();
- }
- case R.id.dialog_delete_contact_confirmation: {
- 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,
- new DeleteClickListener()).create();
- }
- case R.id.dialog_readonly_contact_hide_confirmation: {
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.readOnlyContactWarning)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok,
- new DeleteClickListener()).create();
- }
- case R.id.dialog_readonly_contact_delete_confirmation: {
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.readOnlyContactDeleteConfirmation)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok,
- new DeleteClickListener()).create();
- }
- case R.id.dialog_multiple_contact_delete_confirmation: {
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.multipleContactDeleteConfirmation)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok,
- new DeleteClickListener()).create();
- }
- }
- return super.onCreateDialog(id, bundle);
- }
-
- /**
- * Create a {@link Dialog} that allows the user to pick from a bulk import
- * or bulk export task across all contacts.
- */
- private void displayImportExportDialog() {
- // Wrap our context to inflate list items using correct theme
- final Context dialogContext = new ContextThemeWrapper(this, android.R.style.Theme_Light);
- final Resources res = dialogContext.getResources();
- final LayoutInflater dialogInflater = (LayoutInflater)dialogContext
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- // Adapter that shows a list of string resources
- final ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this,
- android.R.layout.simple_list_item_1) {
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = dialogInflater.inflate(android.R.layout.simple_list_item_1,
- parent, false);
- }
-
- final int resId = this.getItem(position);
- ((TextView)convertView).setText(resId);
- return convertView;
- }
- };
-
- if (TelephonyManager.getDefault().hasIccCard()) {
- adapter.add(R.string.import_from_sim);
- }
- if (res.getBoolean(R.bool.config_allow_import_from_sdcard)) {
- adapter.add(R.string.import_from_sdcard);
- }
- if (res.getBoolean(R.bool.config_allow_export_to_sdcard)) {
- adapter.add(R.string.export_to_sdcard);
- }
- if (res.getBoolean(R.bool.config_allow_share_visible_contacts)) {
- adapter.add(R.string.share_visible_contacts);
- }
-
- final DialogInterface.OnClickListener clickListener =
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
-
- final int resId = adapter.getItem(which);
- switch (resId) {
- case R.string.import_from_sim:
- case R.string.import_from_sdcard: {
- handleImportRequest(resId);
- break;
- }
- case R.string.export_to_sdcard: {
- Context context = ContactsListActivity.this;
- Intent exportIntent = new Intent(context, ExportVCardActivity.class);
- context.startActivity(exportIntent);
- break;
- }
- case R.string.share_visible_contacts: {
- doShareVisibleContacts();
- break;
- }
- default: {
- Log.e(TAG, "Unexpected resource: " +
- getResources().getResourceEntryName(resId));
- }
- }
- }
- };
-
- new AlertDialog.Builder(this)
- .setTitle(R.string.dialog_import_export)
- .setNegativeButton(android.R.string.cancel, null)
- .setSingleChoiceItems(adapter, -1, clickListener)
- .show();
- }
-
- private void doShareVisibleContacts() {
- final Cursor cursor = getContentResolver().query(Contacts.CONTENT_URI,
- sLookupProjection, getContactSelection(), null, null);
- try {
- if (!cursor.moveToFirst()) {
- Toast.makeText(this, R.string.share_error, Toast.LENGTH_SHORT).show();
- return;
- }
-
- StringBuilder uriListBuilder = new StringBuilder();
- int index = 0;
- for (;!cursor.isAfterLast(); cursor.moveToNext()) {
- if (index != 0)
- uriListBuilder.append(':');
- uriListBuilder.append(cursor.getString(0));
- index++;
- }
- Uri uri = Uri.withAppendedPath(
- Contacts.CONTENT_MULTI_VCARD_URI,
- Uri.encode(uriListBuilder.toString()));
-
- final Intent intent = new Intent(Intent.ACTION_SEND);
- intent.setType(Contacts.CONTENT_VCARD_TYPE);
- intent.putExtra(Intent.EXTRA_STREAM, uri);
- startActivity(intent);
- } finally {
- cursor.close();
- }
- }
-
- private void handleImportRequest(int resId) {
- // There's three possibilities:
- // - more than one accounts -> ask the user
- // - just one account -> use the account without asking the user
- // - no account -> use phone-local storage without asking the user
- final Sources sources = Sources.getInstance(this);
- final List<Account> accountList = sources.getAccounts(true);
- final int size = accountList.size();
- if (size > 1) {
- showDialog(resId);
- return;
- }
-
- AccountSelectionUtil.doImport(this, resId, (size == 1 ? accountList.get(0) : null));
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case SUBACTIVITY_NEW_CONTACT:
- if (resultCode == RESULT_OK) {
- returnPickerResult(null, data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME),
- data.getData(), (mMode & MODE_MASK_PICKER) != 0
- ? Intent.FLAG_GRANT_READ_URI_PERMISSION : 0);
- }
- break;
-
- case SUBACTIVITY_VIEW_CONTACT:
- if (resultCode == RESULT_OK) {
- mAdapter.notifyDataSetChanged();
- }
- break;
-
- case SUBACTIVITY_DISPLAY_GROUP:
- // Mark as just created so we re-run the view query
- mJustCreated = true;
- break;
-
- case SUBACTIVITY_FILTER:
- case SUBACTIVITY_SEARCH:
- // Pass through results of filter or search UI
- if (resultCode == RESULT_OK) {
- setResult(RESULT_OK, data);
- finish();
- }
- break;
- }
- }
-
- @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 contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, id);
- long rawContactId = ContactsUtils.queryForRawContactId(getContentResolver(), id);
- Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
-
- // Setup the menu header
- menu.setHeaderTitle(cursor.getString(getSummaryDisplayNameColumnIndex()));
-
- // View contact details
- final Intent viewContactIntent = new Intent(Intent.ACTION_VIEW, contactUri);
- StickyTabs.setTab(viewContactIntent, getIntent());
- menu.add(0, MENU_ITEM_VIEW_CONTACT, 0, R.string.menu_viewContact)
- .setIntent(viewContactIntent);
-
- if (cursor.getInt(SUMMARY_HAS_PHONE_COLUMN_INDEX) != 0) {
- // Calling contact
- menu.add(0, MENU_ITEM_CALL, 0, getString(R.string.menu_call));
- // Send SMS item
- menu.add(0, MENU_ITEM_SEND_SMS, 0, getString(R.string.menu_sendSMS));
- }
-
- // Star toggling
- int starState = cursor.getInt(SUMMARY_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, rawContactUri));
- 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(Contacts.STARRED, cursor.getInt(SUMMARY_STARRED_COLUMN_INDEX) == 0 ? 1 : 0);
- final Uri selectedUri = this.getContactUri(info.position);
- getContentResolver().update(selectedUri, values, null, null);
- return true;
- }
-
- case MENU_ITEM_CALL: {
- callContact(cursor);
- return true;
- }
-
- case MENU_ITEM_SEND_SMS: {
- smsContact(cursor);
- return true;
- }
-
- case MENU_ITEM_DELETE: {
- doContactDelete(getContactUri(info.position));
- return true;
- }
- }
-
- return super.onContextItemSelected(item);
- }
-
- /**
- * Event handler for the use case where the user starts typing without
- * bringing up the search UI first.
- */
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (!mSearchMode && (mMode & MODE_MASK_NO_FILTER) == 0 && !mSearchInitiated) {
- int unicodeChar = event.getUnicodeChar();
- if (unicodeChar != 0) {
- mSearchInitiated = true;
- startSearch(new String(new int[]{unicodeChar}, 0, 1), false, null, false);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Event handler for search UI.
- */
- public void afterTextChanged(Editable s) {
- onSearchTextChanged();
- }
-
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- /**
- * Event handler for search UI.
- */
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- if (actionId == EditorInfo.IME_ACTION_DONE) {
- hideSoftKeyboard();
- if (TextUtils.isEmpty(getTextFilter())) {
- finish();
- }
- return true;
- }
- return false;
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_CALL: {
- if (callSelection()) {
- return true;
- }
- break;
- }
-
- case KeyEvent.KEYCODE_DEL: {
- if (deleteSelection()) {
- return true;
- }
- break;
- }
- }
-
- return super.onKeyDown(keyCode, event);
- }
-
- private boolean deleteSelection() {
- if ((mMode & MODE_MASK_PICKER) != 0) {
- return false;
- }
-
- final int position = getListView().getSelectedItemPosition();
- if (position != ListView.INVALID_POSITION) {
- Uri contactUri = getContactUri(position);
- if (contactUri != null) {
- doContactDelete(contactUri);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Prompt the user before deleting the given {@link Contacts} entry.
- */
- protected void doContactDelete(Uri contactUri) {
- mReadOnlySourcesCnt = 0;
- mWritableSourcesCnt = 0;
- mWritableRawContactIds.clear();
-
- Sources sources = Sources.getInstance(ContactsListActivity.this);
- Cursor c = getContentResolver().query(RawContacts.CONTENT_URI, RAW_CONTACTS_PROJECTION,
- RawContacts.CONTACT_ID + "=" + ContentUris.parseId(contactUri), null,
- null);
- if (c != null) {
- try {
- while (c.moveToNext()) {
- final String accountType = c.getString(2);
- final long rawContactId = c.getLong(0);
- ContactsSource contactsSource = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_SUMMARY);
- if (contactsSource != null && contactsSource.readOnly) {
- mReadOnlySourcesCnt += 1;
- } else {
- mWritableSourcesCnt += 1;
- mWritableRawContactIds.add(rawContactId);
- }
- }
- } finally {
- c.close();
- }
- }
-
- mSelectedContactUri = contactUri;
- if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt > 0) {
- showDialog(R.id.dialog_readonly_contact_delete_confirmation);
- } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
- showDialog(R.id.dialog_readonly_contact_hide_confirmation);
- } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
- showDialog(R.id.dialog_multiple_contact_delete_confirmation);
- } else {
- showDialog(R.id.dialog_delete_contact_confirmation);
- }
- }
-
- /**
- * Dismisses the soft keyboard when the list takes focus.
- */
- public void onFocusChange(View view, boolean hasFocus) {
- if (view == getListView() && hasFocus) {
- hideSoftKeyboard();
- }
- }
-
- /**
- * Dismisses the soft keyboard when the list takes focus.
- */
- public boolean onTouch(View view, MotionEvent event) {
- if (view == getListView()) {
- hideSoftKeyboard();
- }
- return false;
- }
-
- /**
- * Dismisses the search UI along with the keyboard if the filter text is empty.
- */
- public boolean onKeyPreIme(int keyCode, KeyEvent event) {
- if (mSearchMode && keyCode == KeyEvent.KEYCODE_BACK && TextUtils.isEmpty(getTextFilter())) {
- hideSoftKeyboard();
- onBackPressed();
- return true;
- }
- return false;
- }
-
- @Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
- hideSoftKeyboard();
-
- if (mSearchMode && mAdapter.isSearchAllContactsItemPosition(position)) {
- doSearch();
- } else if (mMode == MODE_INSERT_OR_EDIT_CONTACT || mMode == MODE_QUERY_PICK_TO_EDIT) {
- Intent intent;
- if (position == 0 && !mSearchMode && mMode != MODE_QUERY_PICK_TO_EDIT) {
- intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
- } else {
- intent = new Intent(Intent.ACTION_EDIT, getSelectedUri(position));
- }
- intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
- Bundle extras = getIntent().getExtras();
- if (extras != null) {
- intent.putExtras(extras);
- }
- intent.putExtra(KEY_PICKER_MODE, (mMode & MODE_MASK_PICKER) == MODE_MASK_PICKER);
-
- startActivity(intent);
- finish();
- } else if ((mMode & MODE_MASK_CREATE_NEW) == MODE_MASK_CREATE_NEW
- && position == 0) {
- Intent newContact = new Intent(Intents.Insert.ACTION, Contacts.CONTENT_URI);
- startActivityForResult(newContact, SUBACTIVITY_NEW_CONTACT);
- } else if (mMode == MODE_JOIN_CONTACT && id == JOIN_MODE_SHOW_ALL_CONTACTS_ID) {
- mJoinModeShowAllContacts = false;
- startQuery();
- } else if (id > 0) {
- final Uri uri = getSelectedUri(position);
- if ((mMode & MODE_MASK_PICKER) == 0) {
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- StickyTabs.setTab(intent, getIntent());
- startActivityForResult(intent, SUBACTIVITY_VIEW_CONTACT);
- } else if (mMode == MODE_JOIN_CONTACT) {
- returnPickerResult(null, null, uri, 0);
- } else if (mMode == MODE_QUERY_PICK_TO_VIEW) {
- // Started with query that should launch to view contact
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- startActivity(intent);
- finish();
- } else if (mMode == MODE_PICK_PHONE || mMode == MODE_QUERY_PICK_PHONE) {
- Cursor c = (Cursor) mAdapter.getItem(position);
- returnPickerResult(c, c.getString(PHONE_DISPLAY_NAME_COLUMN_INDEX), uri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- } else if ((mMode & MODE_MASK_PICKER) != 0) {
- Cursor c = (Cursor) mAdapter.getItem(position);
- returnPickerResult(c, c.getString(getSummaryDisplayNameColumnIndex()), uri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- } else if (mMode == MODE_PICK_POSTAL
- || mMode == MODE_LEGACY_PICK_POSTAL
- || mMode == MODE_LEGACY_PICK_PHONE) {
- returnPickerResult(null, null, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- } else {
- signalError();
- }
- }
-
- private void hideSoftKeyboard() {
- // Hide soft keyboard, if visible
- InputMethodManager inputMethodManager = (InputMethodManager)
- getSystemService(Context.INPUT_METHOD_SERVICE);
- inputMethodManager.hideSoftInputFromWindow(mList.getWindowToken(), 0);
- }
-
- /**
- * @param selectedUri In most cases, this should be a lookup {@link Uri}, possibly
- * generated through {@link Contacts#getLookupUri(long, String)}.
- */
- private void returnPickerResult(Cursor c, String name, Uri selectedUri, int uriPerms) {
- final Intent intent = new Intent();
-
- if (mShortcutAction != null) {
- Intent shortcutIntent;
- if (Intent.ACTION_VIEW.equals(mShortcutAction)) {
- // This is a simple shortcut to view a contact.
- shortcutIntent = new Intent(ContactsContract.QuickContact.ACTION_QUICK_CONTACT);
- shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-
- shortcutIntent.setData(selectedUri);
- shortcutIntent.putExtra(ContactsContract.QuickContact.EXTRA_MODE,
- ContactsContract.QuickContact.MODE_LARGE);
- shortcutIntent.putExtra(ContactsContract.QuickContact.EXTRA_EXCLUDE_MIMES,
- (String[]) null);
-
- final Bitmap icon = framePhoto(loadContactPhoto(selectedUri, null));
- if (icon != null) {
- intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, scaleToAppIconSize(icon));
- } else {
- intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
- Intent.ShortcutIconResource.fromContext(this,
- R.drawable.ic_launcher_shortcut_contact));
- }
- } else {
- // This is a direct dial or sms shortcut.
- String number = c.getString(PHONE_NUMBER_COLUMN_INDEX);
- int type = c.getInt(PHONE_TYPE_COLUMN_INDEX);
- String scheme;
- int resid;
- if (Intent.ACTION_CALL.equals(mShortcutAction)) {
- scheme = Constants.SCHEME_TEL;
- resid = R.drawable.badge_action_call;
- } else {
- scheme = Constants.SCHEME_SMSTO;
- resid = R.drawable.badge_action_sms;
- }
-
- // Make the URI a direct tel: URI so that it will always continue to work
- Uri phoneUri = Uri.fromParts(scheme, number, null);
- shortcutIntent = new Intent(mShortcutAction, phoneUri);
-
- intent.putExtra(Intent.EXTRA_SHORTCUT_ICON,
- generatePhoneNumberIcon(selectedUri, type, resid));
- }
- shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
- intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
- setResult(RESULT_OK, intent);
- } else {
- intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
- intent.addFlags(uriPerms);
- setResult(RESULT_OK, intent.setData(selectedUri));
- }
- finish();
- }
-
- private Bitmap framePhoto(Bitmap photo) {
- final Resources r = getResources();
- final Drawable frame = r.getDrawable(com.android.internal.R.drawable.quickcontact_badge);
-
- final int width = r.getDimensionPixelSize(R.dimen.contact_shortcut_frame_width);
- final int height = r.getDimensionPixelSize(R.dimen.contact_shortcut_frame_height);
-
- frame.setBounds(0, 0, width, height);
-
- final Rect padding = new Rect();
- frame.getPadding(padding);
-
- final Rect source = new Rect(0, 0, photo.getWidth(), photo.getHeight());
- final Rect destination = new Rect(padding.left, padding.top,
- width - padding.right, height - padding.bottom);
-
- final int d = Math.max(width, height);
- final Bitmap b = Bitmap.createBitmap(d, d, Bitmap.Config.ARGB_8888);
- final Canvas c = new Canvas(b);
-
- c.translate((d - width) / 2.0f, (d - height) / 2.0f);
- frame.draw(c);
- c.drawBitmap(photo, source, destination, new Paint(Paint.FILTER_BITMAP_FLAG));
-
- return b;
- }
-
- /**
- * Generates a phone number shortcut icon. Adds an overlay describing the type of the phone
- * number, and if there is a photo also adds the call action icon.
- *
- * @param lookupUri The person the phone number belongs to
- * @param type The type of the phone number
- * @param actionResId The ID for the action resource
- * @return The bitmap for the icon
- */
- private Bitmap generatePhoneNumberIcon(Uri lookupUri, int type, int actionResId) {
- final Resources r = getResources();
- boolean drawPhoneOverlay = true;
- final float scaleDensity = getResources().getDisplayMetrics().scaledDensity;
-
- Bitmap photo = loadContactPhoto(lookupUri, null);
- if (photo == null) {
- // If there isn't a photo use the generic phone action icon instead
- Bitmap phoneIcon = getPhoneActionIcon(r, actionResId);
- if (phoneIcon != null) {
- photo = phoneIcon;
- drawPhoneOverlay = false;
- } else {
- return null;
- }
- }
-
- // Setup the drawing classes
- Bitmap icon = createShortcutBitmap();
- Canvas canvas = new Canvas(icon);
-
- // Copy in the photo
- Paint photoPaint = new Paint();
- photoPaint.setDither(true);
- photoPaint.setFilterBitmap(true);
- Rect src = new Rect(0,0, photo.getWidth(),photo.getHeight());
- Rect dst = new Rect(0,0, mIconSize, mIconSize);
- canvas.drawBitmap(photo, src, dst, photoPaint);
-
- // Create an overlay for the phone number type
- String overlay = null;
- switch (type) {
- case Phone.TYPE_HOME:
- overlay = getString(R.string.type_short_home);
- break;
-
- case Phone.TYPE_MOBILE:
- overlay = getString(R.string.type_short_mobile);
- break;
-
- case Phone.TYPE_WORK:
- overlay = getString(R.string.type_short_work);
- break;
-
- case Phone.TYPE_PAGER:
- overlay = getString(R.string.type_short_pager);
- break;
-
- case Phone.TYPE_OTHER:
- overlay = getString(R.string.type_short_other);
- break;
- }
- if (overlay != null) {
- Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
- textPaint.setTextSize(20.0f * scaleDensity);
- textPaint.setTypeface(Typeface.DEFAULT_BOLD);
- textPaint.setColor(r.getColor(R.color.textColorIconOverlay));
- textPaint.setShadowLayer(3f, 1, 1, r.getColor(R.color.textColorIconOverlayShadow));
- canvas.drawText(overlay, 2 * scaleDensity, 16 * scaleDensity, textPaint);
- }
-
- // Draw the phone action icon as an overlay
- if (ENABLE_ACTION_ICON_OVERLAYS && drawPhoneOverlay) {
- Bitmap phoneIcon = getPhoneActionIcon(r, actionResId);
- if (phoneIcon != null) {
- src.set(0, 0, phoneIcon.getWidth(), phoneIcon.getHeight());
- int iconWidth = icon.getWidth();
- dst.set(iconWidth - ((int) (20 * scaleDensity)), -1,
- iconWidth, ((int) (19 * scaleDensity)));
- canvas.drawBitmap(phoneIcon, src, dst, photoPaint);
- }
- }
-
- return icon;
- }
-
- private Bitmap scaleToAppIconSize(Bitmap photo) {
- // Setup the drawing classes
- Bitmap icon = createShortcutBitmap();
- Canvas canvas = new Canvas(icon);
-
- // Copy in the photo
- Paint photoPaint = new Paint();
- photoPaint.setDither(true);
- photoPaint.setFilterBitmap(true);
- Rect src = new Rect(0,0, photo.getWidth(),photo.getHeight());
- Rect dst = new Rect(0,0, mIconSize, mIconSize);
- canvas.drawBitmap(photo, src, dst, photoPaint);
-
- return icon;
- }
-
- private Bitmap createShortcutBitmap() {
- return Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
- }
-
- /**
- * Returns the icon for the phone call action.
- *
- * @param r The resources to load the icon from
- * @param resId The resource ID to load
- * @return the icon for the phone call action
- */
- private Bitmap getPhoneActionIcon(Resources r, int resId) {
- Drawable phoneIcon = r.getDrawable(resId);
- if (phoneIcon instanceof BitmapDrawable) {
- BitmapDrawable bd = (BitmapDrawable) phoneIcon;
- return bd.getBitmap();
- } else {
- return null;
- }
- }
-
- private Uri getUriToQuery() {
- switch(mMode) {
- case MODE_JOIN_CONTACT:
- return getJoinSuggestionsUri(null);
- case MODE_FREQUENT:
- case MODE_STARRED:
- return Contacts.CONTENT_URI;
-
- case MODE_DEFAULT:
- case MODE_CUSTOM:
- case MODE_INSERT_OR_EDIT_CONTACT:
- case MODE_PICK_CONTACT:
- case MODE_PICK_OR_CREATE_CONTACT:{
- return CONTACTS_CONTENT_URI_WITH_LETTER_COUNTS;
- }
- case MODE_STREQUENT: {
- return Contacts.CONTENT_STREQUENT_URI;
- }
- case MODE_LEGACY_PICK_PERSON:
- case MODE_LEGACY_PICK_OR_CREATE_PERSON: {
- return People.CONTENT_URI;
- }
- case MODE_PICK_PHONE: {
- return buildSectionIndexerUri(Phone.CONTENT_URI);
- }
- case MODE_LEGACY_PICK_PHONE: {
- return Phones.CONTENT_URI;
- }
- case MODE_PICK_POSTAL: {
- return buildSectionIndexerUri(StructuredPostal.CONTENT_URI);
- }
- case MODE_LEGACY_PICK_POSTAL: {
- return ContactMethods.CONTENT_URI;
- }
- case MODE_QUERY_PICK_TO_VIEW: {
- if (mQueryMode == QUERY_MODE_MAILTO) {
- return Uri.withAppendedPath(Email.CONTENT_FILTER_URI,
- Uri.encode(mInitialFilter));
- } else if (mQueryMode == QUERY_MODE_TEL) {
- return Uri.withAppendedPath(Phone.CONTENT_FILTER_URI,
- Uri.encode(mInitialFilter));
- }
- return CONTACTS_CONTENT_URI_WITH_LETTER_COUNTS;
- }
- case MODE_QUERY:
- case MODE_QUERY_PICK:
- case MODE_QUERY_PICK_TO_EDIT: {
- return getContactFilterUri(mInitialFilter);
- }
- case MODE_QUERY_PICK_PHONE: {
- return Uri.withAppendedPath(Phone.CONTENT_FILTER_URI,
- Uri.encode(mInitialFilter));
- }
- case MODE_GROUP: {
- return mGroupUri;
- }
- default: {
- throw new IllegalStateException("Can't generate URI: Unsupported Mode.");
- }
- }
- }
-
- /**
- * Build the {@link Contacts#CONTENT_LOOKUP_URI} for the given
- * {@link ListView} position, using {@link #mAdapter}.
- */
- private Uri getContactUri(int position) {
- if (position == ListView.INVALID_POSITION) {
- throw new IllegalArgumentException("Position not in list bounds");
- }
-
- final Cursor cursor = (Cursor)mAdapter.getItem(position);
- if (cursor == null) {
- return null;
- }
-
- switch(mMode) {
- case MODE_LEGACY_PICK_PERSON:
- case MODE_LEGACY_PICK_OR_CREATE_PERSON: {
- final long personId = cursor.getLong(SUMMARY_ID_COLUMN_INDEX);
- return ContentUris.withAppendedId(People.CONTENT_URI, personId);
- }
-
- default: {
- // Build and return soft, lookup reference
- final long contactId = cursor.getLong(SUMMARY_ID_COLUMN_INDEX);
- final String lookupKey = cursor.getString(SUMMARY_LOOKUP_KEY_COLUMN_INDEX);
- return Contacts.getLookupUri(contactId, lookupKey);
- }
- }
- }
-
- /**
- * Build the {@link Uri} for the given {@link ListView} position, which can
- * be used as result when in {@link #MODE_MASK_PICKER} mode.
- */
- private Uri getSelectedUri(int position) {
- if (position == ListView.INVALID_POSITION) {
- throw new IllegalArgumentException("Position not in list bounds");
- }
-
- final long id = mAdapter.getItemId(position);
- switch(mMode) {
- case MODE_LEGACY_PICK_PERSON:
- case MODE_LEGACY_PICK_OR_CREATE_PERSON: {
- return ContentUris.withAppendedId(People.CONTENT_URI, id);
- }
- case MODE_PICK_PHONE:
- case MODE_QUERY_PICK_PHONE: {
- return ContentUris.withAppendedId(Data.CONTENT_URI, id);
- }
- case MODE_LEGACY_PICK_PHONE: {
- return ContentUris.withAppendedId(Phones.CONTENT_URI, id);
- }
- case MODE_PICK_POSTAL: {
- return ContentUris.withAppendedId(Data.CONTENT_URI, id);
- }
- case MODE_LEGACY_PICK_POSTAL: {
- return ContentUris.withAppendedId(ContactMethods.CONTENT_URI, id);
- }
- default: {
- return getContactUri(position);
- }
- }
- }
-
- String[] getProjectionForQuery() {
- switch(mMode) {
- case MODE_JOIN_CONTACT:
- case MODE_STREQUENT:
- case MODE_FREQUENT:
- case MODE_STARRED:
- case MODE_DEFAULT:
- case MODE_CUSTOM:
- case MODE_INSERT_OR_EDIT_CONTACT:
- case MODE_GROUP:
- case MODE_PICK_CONTACT:
- case MODE_PICK_OR_CREATE_CONTACT: {
- return mSearchMode
- ? CONTACTS_SUMMARY_FILTER_PROJECTION
- : CONTACTS_SUMMARY_PROJECTION;
- }
- case MODE_QUERY:
- case MODE_QUERY_PICK:
- case MODE_QUERY_PICK_TO_EDIT: {
- return CONTACTS_SUMMARY_FILTER_PROJECTION;
- }
- case MODE_LEGACY_PICK_PERSON:
- case MODE_LEGACY_PICK_OR_CREATE_PERSON: {
- return LEGACY_PEOPLE_PROJECTION ;
- }
- case MODE_QUERY_PICK_PHONE:
- case MODE_PICK_PHONE: {
- return PHONES_PROJECTION;
- }
- case MODE_LEGACY_PICK_PHONE: {
- return LEGACY_PHONES_PROJECTION;
- }
- case MODE_PICK_POSTAL: {
- return POSTALS_PROJECTION;
- }
- case MODE_LEGACY_PICK_POSTAL: {
- return LEGACY_POSTALS_PROJECTION;
- }
- case MODE_QUERY_PICK_TO_VIEW: {
- if (mQueryMode == QUERY_MODE_MAILTO) {
- return CONTACTS_SUMMARY_PROJECTION_FROM_EMAIL;
- } else if (mQueryMode == QUERY_MODE_TEL) {
- return PHONES_PROJECTION;
- }
- break;
- }
- }
-
- // Default to normal aggregate projection
- return CONTACTS_SUMMARY_PROJECTION;
- }
-
- private Bitmap loadContactPhoto(Uri selectedUri, BitmapFactory.Options options) {
- Uri contactUri = null;
- if (Contacts.CONTENT_ITEM_TYPE.equals(getContentResolver().getType(selectedUri))) {
- // TODO we should have a "photo" directory under the lookup URI itself
- contactUri = Contacts.lookupContact(getContentResolver(), selectedUri);
- } else {
-
- Cursor cursor = getContentResolver().query(selectedUri,
- new String[] { Data.CONTACT_ID }, null, null, null);
- try {
- if (cursor != null && cursor.moveToFirst()) {
- final long contactId = cursor.getLong(0);
- contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
- }
- } finally {
- if (cursor != null) cursor.close();
- }
- }
-
- Cursor cursor = null;
- Bitmap bm = null;
-
- try {
- Uri photoUri = Uri.withAppendedPath(contactUri, Contacts.Photo.CONTENT_DIRECTORY);
- cursor = getContentResolver().query(photoUri, new String[] {Photo.PHOTO},
- null, null, null);
- if (cursor != null && cursor.moveToFirst()) {
- bm = ContactsUtils.loadContactPhoto(cursor, 0, options);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- if (bm == null) {
- final int[] fallbacks = {
- R.drawable.ic_contact_picture,
- R.drawable.ic_contact_picture_2,
- R.drawable.ic_contact_picture_3
- };
- bm = BitmapFactory.decodeResource(getResources(),
- fallbacks[new Random().nextInt(fallbacks.length)]);
- }
-
- return bm;
- }
-
- /**
- * Return the selection arguments for a default query based on the
- * {@link #mDisplayOnlyPhones} flag.
- */
- private String getContactSelection() {
- if (mDisplayOnlyPhones) {
- return CLAUSE_ONLY_VISIBLE + " AND " + CLAUSE_ONLY_PHONES;
- } else {
- return CLAUSE_ONLY_VISIBLE;
- }
- }
-
- private Uri getContactFilterUri(String filter) {
- Uri baseUri;
- if (!TextUtils.isEmpty(filter)) {
- baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(filter));
- } else {
- baseUri = Contacts.CONTENT_URI;
- }
-
- if (mAdapter.getDisplaySectionHeadersEnabled()) {
- return buildSectionIndexerUri(baseUri);
- } else {
- return baseUri;
- }
- }
-
- private Uri getPeopleFilterUri(String filter) {
- if (!TextUtils.isEmpty(filter)) {
- return Uri.withAppendedPath(People.CONTENT_FILTER_URI, Uri.encode(filter));
- } else {
- return People.CONTENT_URI;
- }
- }
-
- private static Uri buildSectionIndexerUri(Uri uri) {
- return uri.buildUpon()
- .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
- }
-
- private Uri getJoinSuggestionsUri(String filter) {
- Builder builder = Contacts.CONTENT_URI.buildUpon();
- builder.appendEncodedPath(String.valueOf(mQueryAggregateId));
- builder.appendEncodedPath(AggregationSuggestions.CONTENT_DIRECTORY);
- if (!TextUtils.isEmpty(filter)) {
- builder.appendEncodedPath(Uri.encode(filter));
- }
- builder.appendQueryParameter("limit", String.valueOf(MAX_SUGGESTIONS));
- return builder.build();
- }
-
- private String getSortOrder(String[] projectionType) {
- if (mSortOrder == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
- return Contacts.SORT_KEY_PRIMARY;
- } else {
- return Contacts.SORT_KEY_ALTERNATIVE;
- }
- }
-
- void startQuery() {
- // Set the proper empty string
- setEmptyText();
-
- if (mSearchResultsMode) {
- TextView foundContactsText = (TextView)findViewById(R.id.search_results_found);
- foundContactsText.setText(R.string.search_results_searching);
- }
-
- mAdapter.setLoading(true);
-
- // Cancel any pending queries
- mQueryHandler.cancelOperation(QUERY_TOKEN);
- mQueryHandler.setLoadingJoinSuggestions(false);
-
- mSortOrder = mContactsPrefs.getSortOrder();
- mDisplayOrder = mContactsPrefs.getDisplayOrder();
-
- // When sort order and display order contradict each other, we want to
- // highlight the part of the name used for sorting.
- mHighlightWhenScrolling = false;
- if (mSortOrder == ContactsContract.Preferences.SORT_ORDER_PRIMARY &&
- mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_ALTERNATIVE) {
- mHighlightWhenScrolling = true;
- } else if (mSortOrder == ContactsContract.Preferences.SORT_ORDER_ALTERNATIVE &&
- mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
- mHighlightWhenScrolling = true;
- }
-
- String[] projection = getProjectionForQuery();
- if (mSearchMode && TextUtils.isEmpty(getTextFilter())) {
- mAdapter.changeCursor(new MatrixCursor(projection));
- return;
- }
-
- String callingPackage = getCallingPackage();
- Uri uri = getUriToQuery();
- if (!TextUtils.isEmpty(callingPackage)) {
- uri = uri.buildUpon()
- .appendQueryParameter(ContactsContract.REQUESTING_PACKAGE_PARAM_KEY,
- callingPackage)
- .build();
- }
-
- // Kick off the new query
- switch (mMode) {
- case MODE_GROUP:
- case MODE_DEFAULT:
- case MODE_CUSTOM:
- case MODE_PICK_CONTACT:
- case MODE_PICK_OR_CREATE_CONTACT:
- case MODE_INSERT_OR_EDIT_CONTACT:
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, getContactSelection(),
- null, getSortOrder(projection));
- break;
-
- case MODE_LEGACY_PICK_PERSON:
- case MODE_LEGACY_PICK_OR_CREATE_PERSON: {
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, null, null,
- People.DISPLAY_NAME);
- break;
- }
- case MODE_PICK_POSTAL:
- case MODE_QUERY:
- case MODE_QUERY_PICK:
- case MODE_QUERY_PICK_PHONE:
- case MODE_QUERY_PICK_TO_VIEW:
- case MODE_QUERY_PICK_TO_EDIT: {
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, null, null,
- getSortOrder(projection));
- break;
- }
-
- case MODE_STARRED:
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
- projection, Contacts.STARRED + "=1", null,
- getSortOrder(projection));
- break;
-
- case MODE_FREQUENT:
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
- projection,
- Contacts.TIMES_CONTACTED + " > 0", null,
- Contacts.TIMES_CONTACTED + " DESC, "
- + getSortOrder(projection));
- break;
-
- case MODE_STREQUENT:
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection, null, null, null);
- break;
-
- case MODE_PICK_PHONE:
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
- projection, CLAUSE_ONLY_VISIBLE, null, getSortOrder(projection));
- break;
-
- case MODE_LEGACY_PICK_PHONE:
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
- projection, null, null, Phones.DISPLAY_NAME);
- break;
-
- case MODE_LEGACY_PICK_POSTAL:
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri,
- projection,
- ContactMethods.KIND + "=" + android.provider.Contacts.KIND_POSTAL, null,
- ContactMethods.DISPLAY_NAME);
- break;
-
- case MODE_JOIN_CONTACT:
- mQueryHandler.setLoadingJoinSuggestions(true);
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri, projection,
- null, null, null);
- 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) {
- String[] projection = getProjectionForQuery();
- if (mSearchMode && TextUtils.isEmpty(getTextFilter())) {
- return new MatrixCursor(projection);
- }
-
- final ContentResolver resolver = getContentResolver();
- switch (mMode) {
- case MODE_DEFAULT:
- case MODE_CUSTOM:
- case MODE_PICK_CONTACT:
- case MODE_PICK_OR_CREATE_CONTACT:
- case MODE_INSERT_OR_EDIT_CONTACT: {
- return resolver.query(getContactFilterUri(filter), projection,
- getContactSelection(), null, getSortOrder(projection));
- }
-
- case MODE_LEGACY_PICK_PERSON:
- case MODE_LEGACY_PICK_OR_CREATE_PERSON: {
- return resolver.query(getPeopleFilterUri(filter), projection, null, null,
- People.DISPLAY_NAME);
- }
-
- case MODE_STARRED: {
- return resolver.query(getContactFilterUri(filter), projection,
- Contacts.STARRED + "=1", null,
- getSortOrder(projection));
- }
-
- case MODE_FREQUENT: {
- return resolver.query(getContactFilterUri(filter), projection,
- Contacts.TIMES_CONTACTED + " > 0", null,
- Contacts.TIMES_CONTACTED + " DESC, "
- + getSortOrder(projection));
- }
-
- case MODE_STREQUENT: {
- Uri uri;
- if (!TextUtils.isEmpty(filter)) {
- uri = Uri.withAppendedPath(Contacts.CONTENT_STREQUENT_FILTER_URI,
- Uri.encode(filter));
- } else {
- uri = Contacts.CONTENT_STREQUENT_URI;
- }
- return resolver.query(uri, projection, null, null, null);
- }
-
- case MODE_PICK_PHONE: {
- Uri uri = getUriToQuery();
- if (!TextUtils.isEmpty(filter)) {
- uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(filter));
- }
- return resolver.query(uri, projection, CLAUSE_ONLY_VISIBLE, null,
- getSortOrder(projection));
- }
-
- case MODE_LEGACY_PICK_PHONE: {
- //TODO: Support filtering here (bug 2092503)
- break;
- }
-
- case MODE_JOIN_CONTACT: {
-
- // We are on a background thread. Run queries one after the other synchronously
- Cursor cursor = resolver.query(getJoinSuggestionsUri(filter), projection, null,
- null, null);
- mAdapter.setSuggestionsCursor(cursor);
- mJoinModeShowAllContacts = false;
- return resolver.query(getContactFilterUri(filter), projection,
- Contacts._ID + " != " + mQueryAggregateId + " AND " + CLAUSE_ONLY_VISIBLE,
- null, getSortOrder(projection));
- }
- }
- throw new UnsupportedOperationException("filtering not allowed in mode " + mMode);
- }
-
- private Cursor getShowAllContactsLabelCursor(String[] projection) {
- MatrixCursor matrixCursor = new MatrixCursor(projection);
- Object[] row = new Object[projection.length];
- // The only columns we care about is the id
- row[SUMMARY_ID_COLUMN_INDEX] = JOIN_MODE_SHOW_ALL_CONTACTS_ID;
- matrixCursor.addRow(row);
- return matrixCursor;
- }
-
- /**
- * 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();
- return callContact(cursor);
- }
- return false;
- }
-
- boolean callContact(Cursor cursor) {
- return callOrSmsContact(cursor, false /*call*/);
- }
-
- boolean smsContact(Cursor cursor) {
- return callOrSmsContact(cursor, true /*sms*/);
- }
-
- /**
- * Calls the contact which the cursor is point to.
- * @return true if the call was initiated, false otherwise
- */
- boolean callOrSmsContact(Cursor cursor, boolean sendSms) {
- if (cursor == null) {
- return false;
- }
-
- switch (mMode) {
- case MODE_PICK_PHONE:
- case MODE_LEGACY_PICK_PHONE:
- case MODE_QUERY_PICK_PHONE: {
- String phone = cursor.getString(PHONE_NUMBER_COLUMN_INDEX);
- if (sendSms) {
- ContactsUtils.initiateSms(this, phone);
- } else {
- ContactsUtils.initiateCall(this, phone);
- }
- return true;
- }
-
- case MODE_PICK_POSTAL:
- case MODE_LEGACY_PICK_POSTAL: {
- return false;
- }
-
- default: {
-
- boolean hasPhone = cursor.getInt(SUMMARY_HAS_PHONE_COLUMN_INDEX) != 0;
- if (!hasPhone) {
- // There is no phone number.
- signalError();
- return false;
- }
-
- String phone = null;
- Cursor phonesCursor = null;
- phonesCursor = queryPhoneNumbers(cursor.getLong(SUMMARY_ID_COLUMN_INDEX));
- if (phonesCursor == null || phonesCursor.getCount() == 0) {
- // No valid number
- signalError();
- return false;
- } else if (phonesCursor.getCount() == 1) {
- // only one number, call it.
- phone = phonesCursor.getString(phonesCursor.getColumnIndex(Phone.NUMBER));
- } else {
- phonesCursor.moveToPosition(-1);
- while (phonesCursor.moveToNext()) {
- if (phonesCursor.getInt(phonesCursor.
- getColumnIndex(Phone.IS_SUPER_PRIMARY)) != 0) {
- // Found super primary, call it.
- phone = phonesCursor.
- getString(phonesCursor.getColumnIndex(Phone.NUMBER));
- break;
- }
- }
- }
-
- if (phone == null) {
- // Display dialog to choose a number to call.
- PhoneDisambigDialog phoneDialog = new PhoneDisambigDialog(
- this, phonesCursor, sendSms, StickyTabs.getTab(getIntent()));
- phoneDialog.show();
- } else {
- if (sendSms) {
- ContactsUtils.initiateSms(this, phone);
- } else {
- StickyTabs.saveTab(this, getIntent());
- ContactsUtils.initiateCall(this, phone);
- }
- }
- }
- }
- return true;
- }
-
- private Cursor queryPhoneNumbers(long contactId) {
- Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
- Uri dataUri = Uri.withAppendedPath(baseUri, Contacts.Data.CONTENT_DIRECTORY);
-
- Cursor c = getContentResolver().query(dataUri,
- new String[] {Phone._ID, Phone.NUMBER, Phone.IS_SUPER_PRIMARY,
- RawContacts.ACCOUNT_TYPE, Phone.TYPE, Phone.LABEL},
- Data.MIMETYPE + "=?", new String[] {Phone.CONTENT_ITEM_TYPE}, null);
- if (c != null) {
- if (c.moveToFirst()) {
- return c;
- }
- c.close();
- }
- return null;
- }
-
- // TODO: fix PluralRules to handle zero correctly and use Resources.getQuantityText directly
- protected String getQuantityText(int count, int zeroResourceId, int pluralResourceId) {
- if (count == 0) {
- return getString(zeroResourceId);
- } else {
- String format = getResources().getQuantityText(pluralResourceId, count).toString();
- return String.format(format, count);
- }
- }
-
- /**
- * 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 static class QueryHandler extends AsyncQueryHandler {
- protected final WeakReference<ContactsListActivity> mActivity;
- protected boolean mLoadingJoinSuggestions = false;
-
- public QueryHandler(Context context) {
- super(context.getContentResolver());
- mActivity = new WeakReference<ContactsListActivity>((ContactsListActivity) context);
- }
-
- public void setLoadingJoinSuggestions(boolean flag) {
- mLoadingJoinSuggestions = flag;
- }
-
- @Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- final ContactsListActivity activity = mActivity.get();
- if (activity != null && !activity.isFinishing()) {
-
- // Whenever we get a suggestions cursor, we need to immediately kick off
- // another query for the complete list of contacts
- if (cursor != null && mLoadingJoinSuggestions) {
- mLoadingJoinSuggestions = false;
- if (cursor.getCount() > 0) {
- activity.mAdapter.setSuggestionsCursor(cursor);
- } else {
- cursor.close();
- activity.mAdapter.setSuggestionsCursor(null);
- }
-
- if (activity.mAdapter.mSuggestionsCursorCount == 0
- || !activity.mJoinModeShowAllContacts) {
- startQuery(QUERY_TOKEN, null, activity.getContactFilterUri(
- activity.getTextFilter()),
- CONTACTS_SUMMARY_PROJECTION,
- Contacts._ID + " != " + activity.mQueryAggregateId
- + " AND " + CLAUSE_ONLY_VISIBLE, null,
- activity.getSortOrder(CONTACTS_SUMMARY_PROJECTION));
- return;
- }
-
- cursor = activity.getShowAllContactsLabelCursor(CONTACTS_SUMMARY_PROJECTION);
- }
-
- 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);
- activity.mListState = null;
- }
- } else {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
- }
-
- final static class ContactListItemCache {
- public CharArrayBuffer nameBuffer = new CharArrayBuffer(128);
- public CharArrayBuffer dataBuffer = new CharArrayBuffer(128);
- public CharArrayBuffer highlightedTextBuffer = new CharArrayBuffer(128);
- public TextWithHighlighting textWithHighlighting;
- public CharArrayBuffer phoneticNameBuffer = new CharArrayBuffer(128);
- }
-
- final static class PinnedHeaderCache {
- public TextView titleView;
- public ColorStateList textColor;
- public Drawable background;
- }
-
- private final class ContactItemListAdapter extends CursorAdapter
- implements SectionIndexer, OnScrollListener, PinnedHeaderListView.PinnedHeaderAdapter {
- private SectionIndexer mIndexer;
- private boolean mLoading = true;
- private CharSequence mUnknownNameText;
- private boolean mDisplayPhotos = false;
- private boolean mDisplayCallButton = false;
- private boolean mDisplayAdditionalData = true;
- private int mFrequentSeparatorPos = ListView.INVALID_POSITION;
- private boolean mDisplaySectionHeaders = true;
- private Cursor mSuggestionsCursor;
- private int mSuggestionsCursorCount;
-
- public ContactItemListAdapter(Context context) {
- super(context, null, false);
-
- mUnknownNameText = context.getText(android.R.string.unknownName);
- switch (mMode) {
- case MODE_LEGACY_PICK_POSTAL:
- case MODE_PICK_POSTAL:
- case MODE_LEGACY_PICK_PHONE:
- case MODE_PICK_PHONE:
- case MODE_STREQUENT:
- case MODE_FREQUENT:
- mDisplaySectionHeaders = false;
- break;
- }
-
- if (mSearchMode) {
- mDisplaySectionHeaders = false;
- }
-
- // Do not display the second line of text if in a specific SEARCH query mode, usually for
- // matching a specific E-mail or phone number. Any contact details
- // shown would be identical, and columns might not even be present
- // in the returned cursor.
- if (mMode != MODE_QUERY_PICK_PHONE && mQueryMode != QUERY_MODE_NONE) {
- mDisplayAdditionalData = false;
- }
-
- if ((mMode & MODE_MASK_NO_DATA) == MODE_MASK_NO_DATA) {
- mDisplayAdditionalData = false;
- }
-
- if ((mMode & MODE_MASK_SHOW_CALL_BUTTON) == MODE_MASK_SHOW_CALL_BUTTON) {
- mDisplayCallButton = true;
- }
-
- if ((mMode & MODE_MASK_SHOW_PHOTOS) == MODE_MASK_SHOW_PHOTOS) {
- mDisplayPhotos = true;
- }
- }
-
- public boolean getDisplaySectionHeadersEnabled() {
- return mDisplaySectionHeaders;
- }
-
- public void setSuggestionsCursor(Cursor cursor) {
- if (mSuggestionsCursor != null) {
- mSuggestionsCursor.close();
- }
- mSuggestionsCursor = cursor;
- mSuggestionsCursorCount = cursor == null ? 0 : cursor.getCount();
- }
-
- /**
- * 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 = 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 (mProviderStatus != ProviderStatus.STATUS_NORMAL) {
- return true;
- }
-
- if (mSearchMode) {
- return TextUtils.isEmpty(getTextFilter());
- } else 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 (mCursor == null || mLoading) {
- // We don't want the empty state to show when loading.
- return false;
- } else {
- return super.isEmpty();
- }
- }
- }
-
- @Override
- public int getItemViewType(int position) {
- if (position == 0 && (mShowNumberOfContacts || (mMode & MODE_MASK_CREATE_NEW) != 0)) {
- return IGNORE_ITEM_VIEW_TYPE;
- }
-
- if (isShowAllContactsItemPosition(position)) {
- return IGNORE_ITEM_VIEW_TYPE;
- }
-
- if (isSearchAllContactsItemPosition(position)) {
- return IGNORE_ITEM_VIEW_TYPE;
- }
-
- if (getSeparatorId(position) != 0) {
- // We don't want the separator view to be recycled.
- return IGNORE_ITEM_VIEW_TYPE;
- }
-
- return super.getItemViewType(position);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (!mDataValid) {
- throw new IllegalStateException(
- "this should only be called when the cursor is valid");
- }
-
- // handle the total contacts item
- if (position == 0 && mShowNumberOfContacts) {
- return getTotalContactCountView(parent);
- }
-
- if (position == 0 && (mMode & MODE_MASK_CREATE_NEW) != 0) {
- // Add the header for creating a new contact
- return getLayoutInflater().inflate(R.layout.create_new_contact, parent, false);
- }
-
- if (isShowAllContactsItemPosition(position)) {
- return getLayoutInflater().
- inflate(R.layout.contacts_list_show_all_item, parent, false);
- }
-
- if (isSearchAllContactsItemPosition(position)) {
- return getLayoutInflater().
- inflate(R.layout.contacts_list_search_all_item, parent, false);
- }
-
- // Handle the separator specially
- int separatorId = getSeparatorId(position);
- if (separatorId != 0) {
- TextView view = (TextView) getLayoutInflater().
- inflate(R.layout.list_separator, parent, false);
- view.setText(separatorId);
- return view;
- }
-
- boolean showingSuggestion;
- Cursor cursor;
- if (mSuggestionsCursorCount != 0 && position < mSuggestionsCursorCount + 2) {
- showingSuggestion = true;
- cursor = mSuggestionsCursor;
- } else {
- showingSuggestion = false;
- cursor = mCursor;
- }
-
- int realPosition = getRealPosition(position);
- if (!cursor.moveToPosition(realPosition)) {
- throw new IllegalStateException("couldn't move cursor to position " + position);
- }
-
- boolean newView;
- View v;
- if (convertView == null || convertView.getTag() == null) {
- newView = true;
- v = newView(mContext, cursor, parent);
- } else {
- newView = false;
- v = convertView;
- }
- bindView(v, mContext, cursor);
- bindSectionHeader(v, realPosition, mDisplaySectionHeaders && !showingSuggestion);
- return v;
- }
-
- private View getTotalContactCountView(ViewGroup parent) {
- final LayoutInflater inflater = getLayoutInflater();
- View view = inflater.inflate(R.layout.total_contacts, parent, false);
-
- TextView totalContacts = (TextView) view.findViewById(R.id.totalContactsText);
-
- String text;
- int count = getRealCount();
-
- if (mSearchMode && !TextUtils.isEmpty(getTextFilter())) {
- text = getQuantityText(count, R.string.listFoundAllContactsZero,
- R.plurals.searchFoundContacts);
- } else {
- if (mDisplayOnlyPhones) {
- text = getQuantityText(count, R.string.listTotalPhoneContactsZero,
- R.plurals.listTotalPhoneContacts);
- } else {
- text = getQuantityText(count, R.string.listTotalAllContactsZero,
- R.plurals.listTotalAllContacts);
- }
- }
- totalContacts.setText(text);
- return view;
- }
-
- private boolean isShowAllContactsItemPosition(int position) {
- return mMode == MODE_JOIN_CONTACT && mJoinModeShowAllContacts
- && mSuggestionsCursorCount != 0 && position == mSuggestionsCursorCount + 2;
- }
-
- private boolean isSearchAllContactsItemPosition(int position) {
- return mSearchMode && position == getCount() - 1;
- }
-
- private int getSeparatorId(int position) {
- int separatorId = 0;
- if (position == mFrequentSeparatorPos) {
- separatorId = R.string.favoritesFrquentSeparator;
- }
- if (mSuggestionsCursorCount != 0) {
- if (position == 0) {
- separatorId = R.string.separatorJoinAggregateSuggestions;
- } else if (position == mSuggestionsCursorCount + 1) {
- separatorId = R.string.separatorJoinAggregateAll;
- }
- }
- return separatorId;
- }
-
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent) {
- final ContactListItemView view = new ContactListItemView(context, null);
- view.setOnCallButtonClickListener(ContactsListActivity.this);
- view.setTag(new ContactListItemCache());
- return view;
- }
-
- @Override
- public void bindView(View itemView, Context context, Cursor cursor) {
- final ContactListItemView view = (ContactListItemView)itemView;
- final ContactListItemCache cache = (ContactListItemCache) view.getTag();
-
- int typeColumnIndex;
- int dataColumnIndex;
- int labelColumnIndex;
- int defaultType;
- int nameColumnIndex;
- int phoneticNameColumnIndex;
- boolean displayAdditionalData = mDisplayAdditionalData;
- boolean highlightingEnabled = false;
- switch(mMode) {
- case MODE_PICK_PHONE:
- case MODE_LEGACY_PICK_PHONE:
- case MODE_QUERY_PICK_PHONE: {
- nameColumnIndex = PHONE_DISPLAY_NAME_COLUMN_INDEX;
- phoneticNameColumnIndex = -1;
- dataColumnIndex = PHONE_NUMBER_COLUMN_INDEX;
- typeColumnIndex = PHONE_TYPE_COLUMN_INDEX;
- labelColumnIndex = PHONE_LABEL_COLUMN_INDEX;
- defaultType = Phone.TYPE_HOME;
- break;
- }
- case MODE_PICK_POSTAL:
- case MODE_LEGACY_PICK_POSTAL: {
- nameColumnIndex = POSTAL_DISPLAY_NAME_COLUMN_INDEX;
- phoneticNameColumnIndex = -1;
- dataColumnIndex = POSTAL_ADDRESS_COLUMN_INDEX;
- typeColumnIndex = POSTAL_TYPE_COLUMN_INDEX;
- labelColumnIndex = POSTAL_LABEL_COLUMN_INDEX;
- defaultType = StructuredPostal.TYPE_HOME;
- break;
- }
- default: {
- nameColumnIndex = getSummaryDisplayNameColumnIndex();
- if (mMode == MODE_LEGACY_PICK_PERSON
- || mMode == MODE_LEGACY_PICK_OR_CREATE_PERSON) {
- phoneticNameColumnIndex = -1;
- } else {
- phoneticNameColumnIndex = SUMMARY_PHONETIC_NAME_COLUMN_INDEX;
- }
- dataColumnIndex = -1;
- typeColumnIndex = -1;
- labelColumnIndex = -1;
- defaultType = Phone.TYPE_HOME;
- displayAdditionalData = false;
- highlightingEnabled = mHighlightWhenScrolling && mMode != MODE_STREQUENT;
- }
- }
-
- // Set the name
- cursor.copyStringToBuffer(nameColumnIndex, cache.nameBuffer);
- TextView nameView = view.getNameTextView();
- int size = cache.nameBuffer.sizeCopied;
- if (size != 0) {
- if (highlightingEnabled) {
- if (cache.textWithHighlighting == null) {
- cache.textWithHighlighting =
- mHighlightingAnimation.createTextWithHighlighting();
- }
- buildDisplayNameWithHighlighting(nameView, cursor, cache.nameBuffer,
- cache.highlightedTextBuffer, cache.textWithHighlighting);
- } else {
- nameView.setText(cache.nameBuffer.data, 0, size);
- }
- } else {
- nameView.setText(mUnknownNameText);
- }
-
- boolean hasPhone = cursor.getColumnCount() >= SUMMARY_HAS_PHONE_COLUMN_INDEX
- && cursor.getInt(SUMMARY_HAS_PHONE_COLUMN_INDEX) != 0;
-
- // Make the call button visible if requested.
- if (mDisplayCallButton && hasPhone) {
- int pos = cursor.getPosition();
- view.showCallButton(android.R.id.button1, pos);
- } else {
- view.hideCallButton();
- }
-
- // Set the photo, if requested
- if (mDisplayPhotos) {
- boolean useQuickContact = (mMode & MODE_MASK_DISABLE_QUIKCCONTACT) == 0;
-
- long photoId = 0;
- if (!cursor.isNull(SUMMARY_PHOTO_ID_COLUMN_INDEX)) {
- photoId = cursor.getLong(SUMMARY_PHOTO_ID_COLUMN_INDEX);
- }
-
- ImageView viewToUse;
- if (useQuickContact) {
- // Build soft lookup reference
- final long contactId = cursor.getLong(SUMMARY_ID_COLUMN_INDEX);
- final String lookupKey = cursor.getString(SUMMARY_LOOKUP_KEY_COLUMN_INDEX);
- QuickContactBadge quickContact = view.getQuickContact();
- quickContact.assignContactUri(Contacts.getLookupUri(contactId, lookupKey));
- quickContact.setSelectedContactsAppTabIndex(StickyTabs.getTab(getIntent()));
- viewToUse = quickContact;
- } else {
- viewToUse = view.getPhotoView();
- }
-
- final int position = cursor.getPosition();
- mPhotoLoader.loadPhoto(viewToUse, photoId);
- }
-
- if ((mMode & MODE_MASK_NO_PRESENCE) == 0) {
- // Set the proper icon (star or presence or nothing)
- int serverStatus;
- if (!cursor.isNull(SUMMARY_PRESENCE_STATUS_COLUMN_INDEX)) {
- serverStatus = cursor.getInt(SUMMARY_PRESENCE_STATUS_COLUMN_INDEX);
- Drawable icon = ContactPresenceIconUtil.getPresenceIcon(mContext, serverStatus);
- if (icon != null) {
- view.setPresence(icon);
- } else {
- view.setPresence(null);
- }
- } else {
- view.setPresence(null);
- }
- } else {
- view.setPresence(null);
- }
-
- if (mShowSearchSnippets) {
- boolean showSnippet = false;
- String snippetMimeType = cursor.getString(SUMMARY_SNIPPET_MIMETYPE_COLUMN_INDEX);
- if (Email.CONTENT_ITEM_TYPE.equals(snippetMimeType)) {
- String email = cursor.getString(SUMMARY_SNIPPET_DATA1_COLUMN_INDEX);
- if (!TextUtils.isEmpty(email)) {
- view.setSnippet(email);
- showSnippet = true;
- }
- } else if (Organization.CONTENT_ITEM_TYPE.equals(snippetMimeType)) {
- String company = cursor.getString(SUMMARY_SNIPPET_DATA1_COLUMN_INDEX);
- String title = cursor.getString(SUMMARY_SNIPPET_DATA4_COLUMN_INDEX);
- if (!TextUtils.isEmpty(company)) {
- if (!TextUtils.isEmpty(title)) {
- view.setSnippet(company + " / " + title);
- } else {
- view.setSnippet(company);
- }
- showSnippet = true;
- } else if (!TextUtils.isEmpty(title)) {
- view.setSnippet(title);
- showSnippet = true;
- }
- } else if (Nickname.CONTENT_ITEM_TYPE.equals(snippetMimeType)) {
- String nickname = cursor.getString(SUMMARY_SNIPPET_DATA1_COLUMN_INDEX);
- if (!TextUtils.isEmpty(nickname)) {
- view.setSnippet(nickname);
- showSnippet = true;
- }
- }
-
- if (!showSnippet) {
- view.setSnippet(null);
- }
- }
-
- if (!displayAdditionalData) {
- if (phoneticNameColumnIndex != -1) {
-
- // Set the name
- cursor.copyStringToBuffer(phoneticNameColumnIndex, cache.phoneticNameBuffer);
- int phoneticNameSize = cache.phoneticNameBuffer.sizeCopied;
- if (phoneticNameSize != 0) {
- view.setLabel(cache.phoneticNameBuffer.data, phoneticNameSize);
- } else {
- view.setLabel(null);
- }
- } else {
- view.setLabel(null);
- }
- return;
- }
-
- // Set the data.
- cursor.copyStringToBuffer(dataColumnIndex, cache.dataBuffer);
-
- size = cache.dataBuffer.sizeCopied;
- view.setData(cache.dataBuffer.data, size);
-
- // Set the label.
- if (!cursor.isNull(typeColumnIndex)) {
- final int type = cursor.getInt(typeColumnIndex);
- final String label = cursor.getString(labelColumnIndex);
-
- if (mMode == MODE_LEGACY_PICK_POSTAL || mMode == MODE_PICK_POSTAL) {
- // TODO cache
- view.setLabel(StructuredPostal.getTypeLabel(context.getResources(), type,
- label));
- } else {
- // TODO cache
- view.setLabel(Phone.getTypeLabel(context.getResources(), type, label));
- }
- } else {
- view.setLabel(null);
- }
- }
-
- /**
- * Computes the span of the display name that has highlighted parts and configures
- * the display name text view accordingly.
- */
- private void buildDisplayNameWithHighlighting(TextView textView, Cursor cursor,
- CharArrayBuffer buffer1, CharArrayBuffer buffer2,
- TextWithHighlighting textWithHighlighting) {
- int oppositeDisplayOrderColumnIndex;
- if (mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
- oppositeDisplayOrderColumnIndex = SUMMARY_DISPLAY_NAME_ALTERNATIVE_COLUMN_INDEX;
- } else {
- oppositeDisplayOrderColumnIndex = SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX;
- }
- cursor.copyStringToBuffer(oppositeDisplayOrderColumnIndex, buffer2);
-
- textWithHighlighting.setText(buffer1, buffer2);
- textView.setText(textWithHighlighting);
- }
-
- private void bindSectionHeader(View itemView, int position, boolean displaySectionHeaders) {
- final ContactListItemView view = (ContactListItemView)itemView;
- final ContactListItemCache cache = (ContactListItemCache) view.getTag();
- if (!displaySectionHeaders) {
- view.setSectionHeader(null);
- view.setDividerVisible(true);
- } else {
- final int section = getSectionForPosition(position);
- if (getPositionForSection(section) == position) {
- String title = (String)mIndexer.getSections()[section];
- view.setSectionHeader(title);
- } else {
- view.setDividerVisible(false);
- view.setSectionHeader(null);
- }
-
- // move the divider for the last item in a section
- if (getPositionForSection(section + 1) - 1 == position) {
- view.setDividerVisible(false);
- } else {
- view.setDividerVisible(true);
- }
- }
- }
-
- @Override
- public void changeCursor(Cursor cursor) {
- if (cursor != null) {
- setLoading(false);
- }
-
- // Get the split between starred and frequent items, if the mode is strequent
- mFrequentSeparatorPos = ListView.INVALID_POSITION;
- int cursorCount = 0;
- if (cursor != null && (cursorCount = cursor.getCount()) > 0
- && mMode == MODE_STREQUENT) {
- cursor.move(-1);
- for (int i = 0; cursor.moveToNext(); i++) {
- int starred = cursor.getInt(SUMMARY_STARRED_COLUMN_INDEX);
- if (starred == 0) {
- if (i > 0) {
- // Only add the separator when there are starred items present
- mFrequentSeparatorPos = i;
- }
- break;
- }
- }
- }
-
- if (cursor != null && mSearchResultsMode) {
- TextView foundContactsText = (TextView)findViewById(R.id.search_results_found);
- String text = getQuantityText(cursor.getCount(), R.string.listFoundAllContactsZero,
- R.plurals.listFoundAllContacts);
- foundContactsText.setText(text);
- }
-
- super.changeCursor(cursor);
- // Update the indexer for the fast scroll widget
- updateIndexer(cursor);
- }
-
- private void updateIndexer(Cursor cursor) {
- if (cursor == null) {
- mIndexer = null;
- return;
- }
-
- Bundle bundle = cursor.getExtras();
- if (bundle.containsKey(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES)) {
- String sections[] =
- bundle.getStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
- int counts[] = bundle.getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
- mIndexer = new ContactsSectionIndexer(sections, counts);
- } else {
- mIndexer = null;
- }
- }
-
- /**
- * 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 (mIndexer == null) {
- return new String[] { " " };
- } else {
- return mIndexer.getSections();
- }
- }
-
- public int getPositionForSection(int sectionIndex) {
- if (mIndexer == null) {
- return -1;
- }
-
- return mIndexer.getPositionForSection(sectionIndex);
- }
-
- public int getSectionForPosition(int position) {
- if (mIndexer == null) {
- return -1;
- }
-
- return mIndexer.getSectionForPosition(position);
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return mMode != MODE_STARRED
- && !mShowNumberOfContacts
- && mSuggestionsCursorCount == 0;
- }
-
- @Override
- public boolean isEnabled(int position) {
- if (mShowNumberOfContacts) {
- if (position == 0) {
- return false;
- }
- position--;
- }
-
- if (mSuggestionsCursorCount > 0) {
- return position != 0 && position != mSuggestionsCursorCount + 1;
- }
- return position != mFrequentSeparatorPos;
- }
-
- @Override
- public int getCount() {
- if (!mDataValid) {
- return 0;
- }
- int superCount = super.getCount();
-
- if (mShowNumberOfContacts && (mSearchMode || superCount > 0)) {
- // We don't want to count this header if it's the only thing visible, so that
- // the empty text will display.
- superCount++;
- }
-
- if (mSearchMode) {
- // Last element in the list is the "Find
- superCount++;
- }
-
- // We do not show the "Create New" button in Search mode
- if ((mMode & MODE_MASK_CREATE_NEW) != 0 && !mSearchMode) {
- // Count the "Create new contact" line
- superCount++;
- }
-
- if (mSuggestionsCursorCount != 0) {
- // When showing suggestions, we have 2 additional list items: the "Suggestions"
- // and "All contacts" headers.
- return mSuggestionsCursorCount + superCount + 2;
- }
- else if (mFrequentSeparatorPos != ListView.INVALID_POSITION) {
- // When showing strequent list, we have an additional list item - the separator.
- return superCount + 1;
- } else {
- return superCount;
- }
- }
-
- /**
- * Gets the actual count of contacts and excludes all the headers.
- */
- public int getRealCount() {
- return super.getCount();
- }
-
- private int getRealPosition(int pos) {
- if (mShowNumberOfContacts) {
- pos--;
- }
-
- if ((mMode & MODE_MASK_CREATE_NEW) != 0 && !mSearchMode) {
- return pos - 1;
- } else if (mSuggestionsCursorCount != 0) {
- // When showing suggestions, we have 2 additional list items: the "Suggestions"
- // and "All contacts" separators.
- if (pos < mSuggestionsCursorCount + 2) {
- // We are in the upper partition (Suggestions). Adjusting for the "Suggestions"
- // separator.
- return pos - 1;
- } else {
- // We are in the lower partition (All contacts). Adjusting for the size
- // of the upper partition plus the two separators.
- return pos - mSuggestionsCursorCount - 2;
- }
- } else if (mFrequentSeparatorPos == ListView.INVALID_POSITION) {
- // No separator, identity map
- return pos;
- } else if (pos <= mFrequentSeparatorPos) {
- // Before or at the separator, identity map
- return pos;
- } else {
- // After the separator, remove 1 from the pos to get the real underlying pos
- return pos - 1;
- }
- }
-
- @Override
- public Object getItem(int pos) {
- if (mSuggestionsCursorCount != 0 && pos <= mSuggestionsCursorCount) {
- mSuggestionsCursor.moveToPosition(getRealPosition(pos));
- return mSuggestionsCursor;
- } else if (isSearchAllContactsItemPosition(pos)){
- return null;
- } else {
- int realPosition = getRealPosition(pos);
- if (realPosition < 0) {
- return null;
- }
- return super.getItem(realPosition);
- }
- }
-
- @Override
- public long getItemId(int pos) {
- if (mSuggestionsCursorCount != 0 && pos < mSuggestionsCursorCount + 2) {
- if (mSuggestionsCursor.moveToPosition(pos - 1)) {
- return mSuggestionsCursor.getLong(mRowIDColumn);
- } else {
- return 0;
- }
- } else if (isSearchAllContactsItemPosition(pos)) {
- return 0;
- }
- int realPosition = getRealPosition(pos);
- if (realPosition < 0) {
- return 0;
- }
- return super.getItemId(realPosition);
- }
-
- public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- if (view instanceof PinnedHeaderListView) {
- ((PinnedHeaderListView)view).configureHeaderView(firstVisibleItem);
- }
- }
-
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- if (mHighlightWhenScrolling) {
- if (scrollState != OnScrollListener.SCROLL_STATE_IDLE) {
- mHighlightingAnimation.startHighlighting();
- } else {
- mHighlightingAnimation.stopHighlighting();
- }
- }
-
- if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {
- mPhotoLoader.pause();
- } else if (mDisplayPhotos) {
- mPhotoLoader.resume();
- }
- }
-
- /**
- * Computes the state of the pinned header. It can be invisible, fully
- * visible or partially pushed up out of the view.
- */
- public int getPinnedHeaderState(int position) {
- if (mIndexer == null || mCursor == null || mCursor.getCount() == 0) {
- return PINNED_HEADER_GONE;
- }
-
- int realPosition = getRealPosition(position);
- if (realPosition < 0) {
- return PINNED_HEADER_GONE;
- }
-
- // The header should get pushed up if the top item shown
- // is the last item in a section for a particular letter.
- int section = getSectionForPosition(realPosition);
- int nextSectionPosition = getPositionForSection(section + 1);
- if (nextSectionPosition != -1 && realPosition == nextSectionPosition - 1) {
- return PINNED_HEADER_PUSHED_UP;
- }
-
- return PINNED_HEADER_VISIBLE;
- }
-
- /**
- * Configures the pinned header by setting the appropriate text label
- * and also adjusting color if necessary. The color needs to be
- * adjusted when the pinned header is being pushed up from the view.
- */
- public void configurePinnedHeader(View header, int position, int alpha) {
- PinnedHeaderCache cache = (PinnedHeaderCache)header.getTag();
- if (cache == null) {
- cache = new PinnedHeaderCache();
- cache.titleView = (TextView)header.findViewById(R.id.header_text);
- cache.textColor = cache.titleView.getTextColors();
- cache.background = header.getBackground();
- header.setTag(cache);
- }
-
- int realPosition = getRealPosition(position);
- int section = getSectionForPosition(realPosition);
-
- String title = (String)mIndexer.getSections()[section];
- cache.titleView.setText(title);
-
- if (alpha == 255) {
- // Opaque: use the default background, and the original text color
- header.setBackgroundDrawable(cache.background);
- cache.titleView.setTextColor(cache.textColor);
- } else {
- // Faded: use a solid color approximation of the background, and
- // a translucent text color
- header.setBackgroundColor(Color.rgb(
- Color.red(mPinnedHeaderBackgroundColor) * alpha / 255,
- Color.green(mPinnedHeaderBackgroundColor) * alpha / 255,
- Color.blue(mPinnedHeaderBackgroundColor) * alpha / 255));
-
- int textColor = cache.textColor.getDefaultColor();
- cache.titleView.setTextColor(Color.argb(alpha,
- Color.red(textColor), Color.green(textColor), Color.blue(textColor)));
- }
- }
- }
-
- private ContactsPreferences.ChangeListener mPreferencesChangeListener =
- new ContactsPreferences.ChangeListener() {
- @Override
- public void onChange() {
- // When returning from DisplayOptions, onActivityResult ensures that we reload the list,
- // so we do not have to do anything here. However, ContactsPreferences requires a change
- // listener, otherwise it would not reload its settings.
- }
- };
-}
diff --git a/src/com/android/contacts/ContactsLiveFolders.java b/src/com/android/contacts/ContactsLiveFolders.java
index 64be0e2..9cb7e72 100644
--- a/src/com/android/contacts/ContactsLiveFolders.java
+++ b/src/com/android/contacts/ContactsLiveFolders.java
@@ -39,7 +39,7 @@
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_folder_live_contacts_starred));
+ R.mipmap.ic_launcher_folder_live_contacts_starred));
} else {
setResult(RESULT_CANCELED);
}
@@ -62,7 +62,7 @@
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_folder_live_contacts_phone));
+ R.mipmap.ic_launcher_folder_live_contacts_phone));
} else {
setResult(RESULT_CANCELED);
}
@@ -85,7 +85,7 @@
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_folder_live_contacts));
+ R.mipmap.ic_launcher_folder_live_contacts));
} else {
setResult(RESULT_CANCELED);
}
diff --git a/src/com/android/contacts/ContactsSearchManager.java b/src/com/android/contacts/ContactsSearchManager.java
index d65e079..edb2016 100644
--- a/src/com/android/contacts/ContactsSearchManager.java
+++ b/src/com/android/contacts/ContactsSearchManager.java
@@ -16,6 +16,8 @@
package com.android.contacts;
+import com.android.contacts.list.ContactsRequest;
+
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
@@ -31,27 +33,28 @@
* An extra that provides context for search UI and defines the scope for
* the search queries.
*/
- public static final String ORIGINAL_ACTION_EXTRA_KEY = "originalAction";
-
- /**
- * An extra that provides context for search UI and defines the scope for
- * the search queries.
- */
- public static final String ORIGINAL_COMPONENT_EXTRA_KEY = "originalComponent";
+ public static final String ORIGINAL_REQUEST_KEY = "originalRequest";
/**
* Starts the contact list activity in the search mode.
*/
public static void startSearch(Activity context, String initialQuery) {
- context.startActivity(buildIntent(context, initialQuery));
+ context.startActivity(buildIntent(context, initialQuery, null));
}
public static void startSearchForResult(Activity context, String initialQuery,
- int requestCode) {
- context.startActivityForResult(buildIntent(context, initialQuery), requestCode);
+ int requestCode, ContactsRequest originalRequest) {
+ context.startActivityForResult(
+ buildIntent(context, initialQuery, originalRequest), requestCode);
}
- private static Intent buildIntent(Activity context, String initialQuery) {
+ public static void startSearch(Activity context, String initialQuery,
+ ContactsRequest originalRequest) {
+ context.startActivity(buildIntent(context, initialQuery, originalRequest));
+ }
+
+ private static Intent buildIntent(
+ Activity context, String initialQuery, ContactsRequest originalRequest) {
Intent intent = new Intent();
intent.setData(ContactsContract.Contacts.CONTENT_URI);
intent.setAction(UI.FILTER_CONTACTS_ACTION);
@@ -62,8 +65,9 @@
intent.putExtras(originalExtras);
}
intent.putExtra(UI.FILTER_TEXT_EXTRA_KEY, initialQuery);
- intent.putExtra(ORIGINAL_ACTION_EXTRA_KEY, originalIntent.getAction());
- intent.putExtra(ORIGINAL_COMPONENT_EXTRA_KEY, originalIntent.getComponent().getClassName());
+ if (originalRequest != null) {
+ intent.putExtra(ORIGINAL_REQUEST_KEY, originalRequest);
+ }
return intent;
}
}
diff --git a/src/com/android/contacts/ContactsSectionIndexer.java b/src/com/android/contacts/ContactsSectionIndexer.java
deleted file mode 100644
index 01d461f..0000000
--- a/src/com/android/contacts/ContactsSectionIndexer.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2010 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.widget.SectionIndexer;
-
-import java.util.Arrays;
-
-/**
- * A section indexer that is configured with precomputed section titles and
- * their respective counts.
- */
-public class ContactsSectionIndexer implements SectionIndexer {
-
- private final String[] mSections;
- private final int[] mPositions;
- private final int mCount;
-
- /**
- * Constructor.
- *
- * @param sections a non-null array
- * @param counts a non-null array of the same size as <code>sections</code>
- */
- public ContactsSectionIndexer(String[] sections, int[] counts) {
- if (sections == null || counts == null) {
- throw new NullPointerException();
- }
-
- if (sections.length != counts.length) {
- throw new IllegalArgumentException(
- "The sections and counts arrays must have the same length");
- }
-
- // TODO process sections/counts based on current locale and/or specific section titles
-
- this.mSections = sections;
- mPositions = new int[counts.length];
- int position = 0;
- for (int i = 0; i < counts.length; i++) {
- if (mSections[i] == null) {
- mSections[i] = " ";
- } else {
- mSections[i] = mSections[i].trim();
- }
-
- mPositions[i] = position;
- position += counts[i];
- }
- mCount = position;
- }
-
- public Object[] getSections() {
- return mSections;
- }
-
- public int getPositionForSection(int section) {
- if (section < 0 || section >= mSections.length) {
- return -1;
- }
-
- return mPositions[section];
- }
-
- public int getSectionForPosition(int position) {
- if (position < 0 || position >= mCount) {
- return -1;
- }
-
- int index = Arrays.binarySearch(mPositions, position);
-
- /*
- * Consider this example: section positions are 0, 3, 5; the supplied
- * position is 4. The section corresponding to position 4 starts at
- * position 3, so the expected return value is 1. Binary search will not
- * find 4 in the array and thus will return -insertPosition-1, i.e. -3.
- * To get from that number to the expected value of 1 we need to negate
- * and subtract 2.
- */
- return index >= 0 ? index : -index - 2;
- }
-}
diff --git a/src/com/android/contacts/ContactsUtils.java b/src/com/android/contacts/ContactsUtils.java
index 94dabba..0e75a7f 100644
--- a/src/com/android/contacts/ContactsUtils.java
+++ b/src/com/android/contacts/ContactsUtils.java
@@ -16,180 +16,18 @@
package com.android.contacts;
-
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.util.Constants;
-
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.location.CountryDetector;
import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.util.ArrayList;
public class ContactsUtils {
private static final String TAG = "ContactsUtils";
private static final String WAIT_SYMBOL_AS_STRING = String.valueOf(PhoneNumberUtils.WAIT);
- /**
- * Build the display title for the {@link Data#CONTENT_URI} entry in the
- * provided cursor, assuming the given mimeType.
- */
- public static final CharSequence getDisplayLabel(Context context,
- String mimeType, Cursor cursor) {
- // Try finding the type and label for this mimetype
- int colType;
- int colLabel;
- if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)
- || Constants.MIME_SMS_ADDRESS.equals(mimeType)) {
- // Reset to phone mimetype so we generate a label for SMS case
- mimeType = Phone.CONTENT_ITEM_TYPE;
- colType = cursor.getColumnIndex(Phone.TYPE);
- colLabel = cursor.getColumnIndex(Phone.LABEL);
- } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
- colType = cursor.getColumnIndex(Email.TYPE);
- colLabel = cursor.getColumnIndex(Email.LABEL);
- } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
- colType = cursor.getColumnIndex(StructuredPostal.TYPE);
- colLabel = cursor.getColumnIndex(StructuredPostal.LABEL);
- } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
- colType = cursor.getColumnIndex(Organization.TYPE);
- colLabel = cursor.getColumnIndex(Organization.LABEL);
- } else {
- return null;
- }
-
- final int type = cursor.getInt(colType);
- final CharSequence label = cursor.getString(colLabel);
-
- return getDisplayLabel(context, mimeType, type, label);
- }
-
- public static final CharSequence getDisplayLabel(Context context, String mimetype, int type,
- CharSequence label) {
- CharSequence display = "";
- final int customType;
- final int defaultType;
- final int arrayResId;
-
- if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
- defaultType = Phone.TYPE_HOME;
- customType = Phone.TYPE_CUSTOM;
- arrayResId = com.android.internal.R.array.phoneTypes;
- } else if (Email.CONTENT_ITEM_TYPE.equals(mimetype)) {
- defaultType = Email.TYPE_HOME;
- customType = Email.TYPE_CUSTOM;
- arrayResId = com.android.internal.R.array.emailAddressTypes;
- } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimetype)) {
- defaultType = StructuredPostal.TYPE_HOME;
- customType = StructuredPostal.TYPE_CUSTOM;
- arrayResId = com.android.internal.R.array.postalAddressTypes;
- } else if (Organization.CONTENT_ITEM_TYPE.equals(mimetype)) {
- defaultType = Organization.TYPE_WORK;
- customType = Organization.TYPE_CUSTOM;
- arrayResId = com.android.internal.R.array.organizationTypes;
- } else {
- // Can't return display label for given mimetype.
- return display;
- }
-
- if (type != customType) {
- CharSequence[] labels = context.getResources().getTextArray(arrayResId);
- try {
- display = labels[type - 1];
- } catch (ArrayIndexOutOfBoundsException e) {
- display = labels[defaultType - 1];
- }
- } else {
- if (!TextUtils.isEmpty(label)) {
- display = label;
- }
- }
- return display;
- }
-
- /**
- * Opens an InputStream for the person's photo and returns the photo as a Bitmap.
- * If the person's photo isn't present returns null.
- *
- * @param aggCursor the Cursor pointing to the data record containing the photo.
- * @param bitmapColumnIndex the column index where the photo Uri is stored.
- * @param options the decoding options, can be set to null
- * @return the photo Bitmap
- */
- public static Bitmap loadContactPhoto(Cursor cursor, int bitmapColumnIndex,
- BitmapFactory.Options options) {
- if (cursor == null) {
- return null;
- }
-
- byte[] data = cursor.getBlob(bitmapColumnIndex);
- return BitmapFactory.decodeByteArray(data, 0, data.length, options);
- }
-
- /**
- * Loads a placeholder photo.
- *
- * @param placeholderImageResource the resource to use for the placeholder image
- * @param context the Context
- * @param options the decoding options, can be set to null
- * @return the placeholder Bitmap.
- */
- public static Bitmap loadPlaceholderPhoto(int placeholderImageResource, Context context,
- BitmapFactory.Options options) {
- if (placeholderImageResource == 0) {
- return null;
- }
- return BitmapFactory.decodeResource(context.getResources(),
- placeholderImageResource, options);
- }
-
- public static Bitmap loadContactPhoto(Context context, long photoId,
- BitmapFactory.Options options) {
- Cursor photoCursor = null;
- Bitmap photoBm = null;
-
- try {
- photoCursor = context.getContentResolver().query(
- ContentUris.withAppendedId(Data.CONTENT_URI, photoId),
- new String[] { Photo.PHOTO },
- null, null, null);
-
- if (photoCursor.moveToFirst() && !photoCursor.isNull(0)) {
- byte[] photoData = photoCursor.getBlob(0);
- photoBm = BitmapFactory.decodeByteArray(photoData, 0,
- photoData.length, options);
- }
- } finally {
- if (photoCursor != null) {
- photoCursor.close();
- }
- }
-
- return photoBm;
- }
// TODO find a proper place for the canonical version of these
public interface ProviderNames {
@@ -237,193 +75,6 @@
}
/**
- * Build {@link Intent} to launch an action for the given {@link Im} or
- * {@link Email} row. Returns null when missing protocol or data.
- */
- public static Intent buildImIntent(ContentValues values) {
- final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(values.getAsString(Data.MIMETYPE));
-
- if (!isEmail && !isProtocolValid(values)) {
- return null;
- }
-
- final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : values.getAsInteger(Im.PROTOCOL);
-
- String host = values.getAsString(Im.CUSTOM_PROTOCOL);
- String data = values.getAsString(isEmail ? Email.DATA : Im.DATA);
- if (protocol != Im.PROTOCOL_CUSTOM) {
- // Try bringing in a well-known host for specific protocols
- host = ContactsUtils.lookupProviderNameFromId(protocol);
- }
-
- if (!TextUtils.isEmpty(host) && !TextUtils.isEmpty(data)) {
- final String authority = host.toLowerCase();
- final Uri imUri = new Uri.Builder().scheme(Constants.SCHEME_IMTO).authority(
- authority).appendPath(data).build();
- return new Intent(Intent.ACTION_SENDTO, imUri);
- } else {
- return null;
- }
- }
-
- private static boolean isProtocolValid(ContentValues values) {
- String protocolString = values.getAsString(Im.PROTOCOL);
- if (protocolString == null) {
- return false;
- }
- try {
- Integer.valueOf(protocolString);
- } catch (NumberFormatException e) {
- return false;
- }
- return true;
- }
-
- public static long queryForContactId(ContentResolver cr, long rawContactId) {
- Cursor contactIdCursor = null;
- long contactId = -1;
- try {
- contactIdCursor = cr.query(RawContacts.CONTENT_URI,
- new String[] {RawContacts.CONTACT_ID},
- RawContacts._ID + "=" + rawContactId, null, null);
- if (contactIdCursor != null && contactIdCursor.moveToFirst()) {
- contactId = contactIdCursor.getLong(0);
- }
- } finally {
- if (contactIdCursor != null) {
- contactIdCursor.close();
- }
- }
- return contactId;
- }
-
- public static String querySuperPrimaryPhone(ContentResolver cr, long contactId) {
- Cursor c = null;
- String phone = null;
- try {
- Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
- Uri dataUri = Uri.withAppendedPath(baseUri, Contacts.Data.CONTENT_DIRECTORY);
-
- c = cr.query(dataUri,
- new String[] {Phone.NUMBER},
- Data.MIMETYPE + "=" + Phone.MIMETYPE +
- " AND " + Data.IS_SUPER_PRIMARY + "=1",
- null, null);
- if (c != null && c.moveToFirst()) {
- // Just return the first one.
- phone = c.getString(0);
- }
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return phone;
- }
-
- public static long queryForRawContactId(ContentResolver cr, long contactId) {
- Cursor rawContactIdCursor = null;
- long rawContactId = -1;
- try {
- rawContactIdCursor = cr.query(RawContacts.CONTENT_URI,
- new String[] {RawContacts._ID},
- RawContacts.CONTACT_ID + "=" + contactId, null, null);
- if (rawContactIdCursor != null && rawContactIdCursor.moveToFirst()) {
- // Just return the first one.
- rawContactId = rawContactIdCursor.getLong(0);
- }
- } finally {
- if (rawContactIdCursor != null) {
- rawContactIdCursor.close();
- }
- }
- return rawContactId;
- }
-
- public static ArrayList<Long> queryForAllRawContactIds(ContentResolver cr, long contactId) {
- Cursor rawContactIdCursor = null;
- ArrayList<Long> rawContactIds = new ArrayList<Long>();
- try {
- rawContactIdCursor = cr.query(RawContacts.CONTENT_URI,
- new String[] {RawContacts._ID},
- RawContacts.CONTACT_ID + "=" + contactId, null, null);
- if (rawContactIdCursor != null) {
- while (rawContactIdCursor.moveToNext()) {
- rawContactIds.add(rawContactIdCursor.getLong(0));
- }
- }
- } finally {
- if (rawContactIdCursor != null) {
- rawContactIdCursor.close();
- }
- }
- return rawContactIds;
- }
-
-
- /**
- * Utility for creating a standard tab indicator view.
- *
- * @param parent The parent ViewGroup to attach the new view to.
- * @param label The label to display in the tab indicator. If null, not label will be displayed.
- * @param icon The icon to display. If null, no icon will be displayed.
- * @return The tab indicator View.
- */
- public static View createTabIndicatorView(ViewGroup parent, CharSequence label, Drawable icon) {
- final LayoutInflater inflater = (LayoutInflater)parent.getContext().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- final View tabIndicator = inflater.inflate(R.layout.tab_indicator, parent, false);
- tabIndicator.getBackground().setDither(true);
-
- final TextView tv = (TextView) tabIndicator.findViewById(R.id.tab_title);
- tv.setText(label);
-
- final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.tab_icon);
- iconView.setImageDrawable(icon);
-
- return tabIndicator;
- }
-
- /**
- * Utility for creating a standard tab indicator view.
- *
- * @param parent The parent ViewGroup to attach the new view to.
- * @param source The {@link ContactsSource} to build the tab view from.
- * @return The tab indicator View.
- */
- public static View createTabIndicatorView(ViewGroup parent, ContactsSource source) {
- Drawable icon = null;
- if (source != null) {
- icon = source.getDisplayIcon(parent.getContext());
- }
- return createTabIndicatorView(parent, null, icon);
- }
-
- /**
- * Kick off an intent to initiate a call.
- *
- * @param phoneNumber must not be null.
- * @throws NullPointerException when the given argument is null.
- */
- public static void initiateCall(Context context, CharSequence phoneNumber) {
- Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
- Uri.fromParts("tel", phoneNumber.toString(), null));
- context.startActivity(intent);
- }
-
- /**
- * Kick off an intent to initiate an Sms/Mms message.
- *
- * @param phoneNumber must not be null.
- * @throws NullPointerException when the given argument is null.
- */
- public static void initiateSms(Context context, CharSequence phoneNumber) {
- Intent intent = new Intent(Intent.ACTION_SENDTO,
- Uri.fromParts("sms", phoneNumber.toString(), null));
- context.startActivity(intent);
- }
-
- /**
* Test if the given {@link CharSequence} contains any graphic characters,
* first checking {@link TextUtils#isEmpty(CharSequence)} to handle null.
*/
@@ -490,4 +141,14 @@
}
return TextUtils.equals(a.getAction(), b.getAction());
}
+
+ /**
+ * @return The ISO 3166-1 two letters country code of the country the user
+ * is in.
+ */
+ public static final String getCurrentCountryIso(Context context) {
+ CountryDetector detector =
+ (CountryDetector) context.getSystemService(Context.COUNTRY_DETECTOR);
+ return detector.detectCountry().getCountryIso();
+ }
}
diff --git a/src/com/android/contacts/DialtactsActivity.java b/src/com/android/contacts/DialtactsActivity.java
index ef183c5..dc69194 100644
--- a/src/com/android/contacts/DialtactsActivity.java
+++ b/src/com/android/contacts/DialtactsActivity.java
@@ -16,6 +16,8 @@
package com.android.contacts;
+import com.android.contacts.activities.ContactsFrontDoor;
+import com.android.contacts.activities.ContactBrowserActivity;
import com.android.internal.telephony.ITelephony;
import android.app.Activity;
@@ -41,8 +43,6 @@
*/
public class DialtactsActivity extends TabActivity implements TabHost.OnTabChangeListener {
private static final String TAG = "Dailtacts";
- private static final String FAVORITES_ENTRY_COMPONENT =
- "com.android.contacts.DialtactsFavoritesEntryActivity";
private static final int TAB_INDEX_DIALER = 0;
private static final int TAB_INDEX_CALL_LOG = 1;
@@ -51,19 +51,32 @@
static final String EXTRA_IGNORE_STATE = "ignore-state";
+ /** Name of the dialtacts shared preferences */
+ static final String PREFS_DIALTACTS = "dialtacts";
/** If true, when handling the contacts intent the favorites tab will be shown instead */
- private static final String PREF_FAVORITES_AS_CONTACTS = "favorites_as_contacts";
- private static final boolean PREF_FAVORITES_AS_CONTACTS_DEFAULT = false;
+ static final String PREF_FAVORITES_AS_CONTACTS = "favorites_as_contacts";
+ static final boolean PREF_FAVORITES_AS_CONTACTS_DEFAULT = false;
+
+ /** Last manually selected tab index */
+ private static final String PREF_LAST_MANUALLY_SELECTED_TAB = "last_manually_selected_tab";
+ private static final int PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT = TAB_INDEX_DIALER;
private TabHost mTabHost;
private String mFilterText;
private Uri mDialUri;
+ /**
+ * The index of the tab that has last been manually selected (the user clicked on a tab).
+ * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call)
+ */
+ private int mLastManuallySelectedTab;
+
@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);
@@ -77,9 +90,14 @@
setupContactsTab();
setupFavoritesTab();
+ // Load the last manually loaded tab
+ final SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE);
+ mLastManuallySelectedTab = prefs.getInt(PREF_LAST_MANUALLY_SELECTED_TAB,
+ PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT);
+
setCurrentTab(intent);
- if (intent.getAction().equals(UI.FILTER_CONTACTS_ACTION)
+ if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
&& icicle == null) {
setupFilterText(intent);
}
@@ -91,19 +109,30 @@
final int currentTabIndex = mTabHost.getCurrentTab();
final SharedPreferences.Editor editor =
- getSharedPreferences(StickyTabs.PREFERENCES_NAME, MODE_PRIVATE).edit();
+ getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE).edit();
if (currentTabIndex == TAB_INDEX_CONTACTS || currentTabIndex == TAB_INDEX_FAVORITES) {
editor.putBoolean(PREF_FAVORITES_AS_CONTACTS, currentTabIndex == TAB_INDEX_FAVORITES);
}
+ editor.putInt(PREF_LAST_MANUALLY_SELECTED_TAB, mLastManuallySelectedTab);
editor.apply();
}
+ 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() {
// Force the class since overriding tab entries doesn't work
Intent intent = new Intent("com.android.phone.action.RECENT_CALLS");
intent.setClass(this, RecentCallsListActivity.class);
- StickyTabs.setTab(intent, TAB_INDEX_CALL_LOG);
mTabHost.addTab(mTabHost.newTabSpec("call_log")
.setIndicator(getString(R.string.recentCallsIconLabel),
@@ -114,7 +143,6 @@
private void setupDialerTab() {
Intent intent = new Intent("com.android.phone.action.TOUCH_DIALER");
intent.setClass(this, TwelveKeyDialer.class);
- StickyTabs.setTab(intent, TAB_INDEX_DIALER);
mTabHost.addTab(mTabHost.newTabSpec("dialer")
.setIndicator(getString(R.string.dialerIconLabel),
@@ -124,8 +152,7 @@
private void setupContactsTab() {
Intent intent = new Intent(UI.LIST_DEFAULT);
- intent.setClass(this, ContactsListActivity.class);
- StickyTabs.setTab(intent, TAB_INDEX_CONTACTS);
+ intent.setClass(this, ContactBrowserActivity.class);
mTabHost.addTab(mTabHost.newTabSpec("contacts")
.setIndicator(getText(R.string.contactsIconLabel),
@@ -135,8 +162,7 @@
private void setupFavoritesTab() {
Intent intent = new Intent(UI.LIST_STREQUENT_ACTION);
- intent.setClass(this, ContactsListActivity.class);
- StickyTabs.setTab(intent, TAB_INDEX_FAVORITES);
+ intent.setClass(this, ContactBrowserActivity.class);
mTabHost.addTab(mTabHost.newTabSpec("favorites")
.setIndicator(getString(R.string.contactsFavoritesLabel),
@@ -145,11 +171,43 @@
}
/**
+ * 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 intent Intent that contains information about which tab should be selected
*/
private void setCurrentTab(Intent intent) {
+ // If we got here by hitting send and we're in call forward along to the in-call activity
+ final boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.getType());
+ if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
+ finish();
+ return;
+ }
+
// Dismiss menu provided by any children activities
Activity activity = getLocalActivityManager().
getActivity(mTabHost.getCurrentTabTag());
@@ -161,41 +219,39 @@
// state and instead reload their state from the parent's intent
intent.putExtra(EXTRA_IGNORE_STATE, true);
+ // Remember the old manually selected tab index so that it can be restored if it is
+ // overwritten by one of the programmatic tab selections
+ final int savedTabIndex = mLastManuallySelectedTab;
+
// Choose the tab based on the inbound intent
- String componentName = intent.getComponent().getClassName();
- if (getClass().getName().equals(componentName)) {
- if (phoneIsInUse()) {
- // We are in a call, show the dialer tab (which allows going back to the call)
- mTabHost.setCurrentTab(TAB_INDEX_DIALER);
- } else if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
- // launched from history (long-press home) --> nothing to change
- } else if (isDialIntent(intent)) {
- // The dialer was explicitly requested
- mTabHost.setCurrentTab(TAB_INDEX_DIALER);
- } else if (Calls.CONTENT_TYPE.equals(intent.getType())) {
- // After a call, show the call log
- mTabHost.setCurrentTab(TAB_INDEX_CALL_LOG);
- } else {
- // Load the last tab used to make a phone call. default to the dialer in
- // first launch
- mTabHost.setCurrentTab(StickyTabs.loadTab(this, TAB_INDEX_DIALER));
- }
- } else if (FAVORITES_ENTRY_COMPONENT.equals(componentName)) {
- mTabHost.setCurrentTab(TAB_INDEX_FAVORITES);
- } else {
- // Launched as "Contacts" --> Go either to favorites or contacts, whichever is more
- // recent
- final SharedPreferences prefs = getSharedPreferences(StickyTabs.PREFERENCES_NAME,
- MODE_PRIVATE);
- final boolean favoritesAsContacts = prefs.getBoolean(PREF_FAVORITES_AS_CONTACTS,
+ if (intent.getBooleanExtra(ContactsFrontDoor.EXTRA_FRONT_DOOR, false)) {
+ // Launched through the contacts front door, set the proper contacts tab (sticky
+ // between favorites and contacts)
+ SharedPreferences prefs = getSharedPreferences(PREFS_DIALTACTS, MODE_PRIVATE);
+ boolean favoritesAsContacts = prefs.getBoolean(PREF_FAVORITES_AS_CONTACTS,
PREF_FAVORITES_AS_CONTACTS_DEFAULT);
if (favoritesAsContacts) {
mTabHost.setCurrentTab(TAB_INDEX_FAVORITES);
} else {
mTabHost.setCurrentTab(TAB_INDEX_CONTACTS);
}
+ } else {
+ // Not launched through the front door, look at the component to determine the tab
+ String componentName = intent.getComponent().getClassName();
+ if (getClass().getName().equals(componentName)) {
+ if (recentCallsRequest) {
+ mTabHost.setCurrentTab(TAB_INDEX_CALL_LOG);
+ } else {
+ mTabHost.setCurrentTab(TAB_INDEX_DIALER);
+ }
+ } else {
+ mTabHost.setCurrentTab(mLastManuallySelectedTab);
+ }
}
+ // Restore to the previous manual selection
+ mLastManuallySelectedTab = savedTabIndex;
+
// Tell the children activities that they should honor their saved states
// instead of the state from the parent's intent
intent.putExtra(EXTRA_IGNORE_STATE, false);
@@ -204,9 +260,10 @@
@Override
public void onNewIntent(Intent newIntent) {
setIntent(newIntent);
+ fixIntent(newIntent);
setCurrentTab(newIntent);
final String action = newIntent.getAction();
- if (action.equals(UI.FILTER_CONTACTS_ACTION)) {
+ if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
setupFilterText(newIntent);
} else if (isDialIntent(newIntent)) {
setupDialUri(newIntent);
@@ -300,7 +357,6 @@
}
/** {@inheritDoc} */
- @Override
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
@@ -310,20 +366,9 @@
if (activity != null) {
activity.onWindowFocusChanged(true);
}
- }
- /**
- * @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;
+ // Remember this tab index. This function is also called, if the tab is set automatically
+ // in which case the setter (setCurrentTab) has to set this to its old value afterwards
+ mLastManuallySelectedTab = mTabHost.getCurrentTab();
}
}
diff --git a/src/com/android/contacts/ExportVCardActivity.java b/src/com/android/contacts/ExportVCardActivity.java
deleted file mode 100644
index ceeecfc..0000000
--- a/src/com/android/contacts/ExportVCardActivity.java
+++ /dev/null
@@ -1,458 +0,0 @@
-/*
- * 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.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.pim.vcard.VCardComposer;
-import android.pim.vcard.VCardConfig;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Class for exporting vCard.
- *
- * Note that this Activity assumes that the instance is a "one-shot Activity", which will be
- * finished (with the method {@link Activity#finish()}) after the export and never reuse
- * any Dialog in the instance. So this code is careless about the management around managed
- * dialogs stuffs (like how onCreateDialog() is used).
- */
-public class ExportVCardActivity extends Activity {
- private static final String LOG_TAG = "ExportVCardActivity";
-
- // If true, VCardExporter is able to emits files longer than 8.3 format.
- private static final boolean ALLOW_LONG_FILE_NAME = false;
- private String mTargetDirectory;
- private String mFileNamePrefix;
- private String mFileNameSuffix;
- private int mFileIndexMinimum;
- private int mFileIndexMaximum;
- private String mFileNameExtension;
- private String mVCardTypeStr;
- private Set<String> mExtensionsToConsider;
-
- private ProgressDialog mProgressDialog;
- private String mExportingFileName;
-
- private Handler mHandler = new Handler();
-
- // Used temporaly when asking users to confirm the file name
- private String mTargetFileName;
-
- // String for storing error reason temporaly.
- private String mErrorReason;
-
- private ActualExportThread mActualExportThread;
-
- private class CancelListener
- implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- public void onCancel(DialogInterface dialog) {
- finish();
- }
- }
-
- private CancelListener mCancelListener = new CancelListener();
-
- private class ErrorReasonDisplayer implements Runnable {
- private final int mResId;
- public ErrorReasonDisplayer(int resId) {
- mResId = resId;
- }
- public ErrorReasonDisplayer(String errorReason) {
- mResId = R.id.dialog_fail_to_export_with_reason;
- mErrorReason = errorReason;
- }
- public void run() {
- // Show the Dialog only when the parent Activity is still alive.
- if (!ExportVCardActivity.this.isFinishing()) {
- showDialog(mResId);
- }
- }
- }
-
- private class ExportConfirmationListener implements DialogInterface.OnClickListener {
- private final String mFileName;
-
- public ExportConfirmationListener(String fileName) {
- mFileName = fileName;
- }
-
- public void onClick(DialogInterface dialog, int which) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- mActualExportThread = new ActualExportThread(mFileName);
- showDialog(R.id.dialog_exporting_vcard);
- }
- }
- }
-
- private class ActualExportThread extends Thread
- implements DialogInterface.OnCancelListener {
- private PowerManager.WakeLock mWakeLock;
- private boolean mCanceled = false;
-
- public ActualExportThread(String fileName) {
- mExportingFileName = fileName;
- PowerManager powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(
- PowerManager.SCREEN_DIM_WAKE_LOCK |
- PowerManager.ON_AFTER_RELEASE, LOG_TAG);
- }
-
- @Override
- public void run() {
- boolean shouldCallFinish = true;
- mWakeLock.acquire();
- VCardComposer composer = null;
- try {
- OutputStream outputStream = null;
- try {
- outputStream = new FileOutputStream(mExportingFileName);
- } catch (FileNotFoundException e) {
- final String errorReason =
- getString(R.string.fail_reason_could_not_open_file,
- mExportingFileName, e.getMessage());
- shouldCallFinish = false;
- mHandler.post(new ErrorReasonDisplayer(errorReason));
- return;
- }
-
- // composer = new VCardComposer(ExportVCardActivity.this, mVCardTypeStr, true);
- int vcardType = VCardConfig.VCARD_TYPE_V30_GENERIC;
- composer = new VCardComposer(ExportVCardActivity.this, vcardType, true);
-
- composer.addHandler(composer.new HandlerForOutputStream(outputStream));
-
- if (!composer.init()) {
- final String errorReason = composer.getErrorReason();
- Log.e(LOG_TAG, "initialization of vCard composer failed: " + errorReason);
- final String translatedErrorReason =
- translateComposerError(errorReason);
- mHandler.post(new ErrorReasonDisplayer(
- getString(R.string.fail_reason_could_not_initialize_exporter,
- translatedErrorReason)));
- shouldCallFinish = false;
- return;
- }
-
- int size = composer.getCount();
-
- if (size == 0) {
- mHandler.post(new ErrorReasonDisplayer(
- getString(R.string.fail_reason_no_exportable_contact)));
- shouldCallFinish = false;
- return;
- }
-
- mProgressDialog.setProgressNumberFormat(
- getString(R.string.exporting_contact_list_progress));
- mProgressDialog.setMax(size);
- mProgressDialog.setProgress(0);
-
- while (!composer.isAfterLast()) {
- if (mCanceled) {
- return;
- }
- if (!composer.createOneEntry()) {
- final String errorReason = composer.getErrorReason();
- Log.e(LOG_TAG, "Failed to read a contact: " + errorReason);
- final String translatedErrorReason =
- translateComposerError(errorReason);
- mHandler.post(new ErrorReasonDisplayer(
- getString(R.string.fail_reason_error_occurred_during_export,
- translatedErrorReason)));
- shouldCallFinish = false;
- return;
- }
- mProgressDialog.incrementProgressBy(1);
- }
- } finally {
- if (composer != null) {
- composer.terminate();
- }
- mWakeLock.release();
- mProgressDialog.dismiss();
- if (shouldCallFinish && !isFinishing()) {
- finish();
- }
- }
- }
-
- @Override
- public void finalize() {
- if (mWakeLock != null && mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- }
-
- public void cancel() {
- mCanceled = true;
- }
-
- public void onCancel(DialogInterface dialog) {
- cancel();
- }
- }
-
- private String translateComposerError(String errorMessage) {
- Resources resources = getResources();
- if (VCardComposer.FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO.equals(errorMessage)) {
- return resources.getString(R.string.composer_failed_to_get_database_infomation);
- } else if (VCardComposer.FAILURE_REASON_NO_ENTRY.equals(errorMessage)) {
- return resources.getString(R.string.composer_has_no_exportable_contact);
- } else if (VCardComposer.FAILURE_REASON_NOT_INITIALIZED.equals(errorMessage)) {
- return resources.getString(R.string.composer_not_initialized);
- } else {
- return errorMessage;
- }
- }
-
- @Override
- protected void onCreate(Bundle bundle) {
- super.onCreate(bundle);
-
- mTargetDirectory = getString(R.string.config_export_dir);
- mFileNamePrefix = getString(R.string.config_export_file_prefix);
- mFileNameSuffix = getString(R.string.config_export_file_suffix);
- mFileNameExtension = getString(R.string.config_export_file_extension);
- mVCardTypeStr = getString(R.string.config_export_vcard_type);
-
- mExtensionsToConsider = new HashSet<String>();
- mExtensionsToConsider.add(mFileNameExtension);
-
- final String additionalExtensions =
- getString(R.string.config_export_extensions_to_consider);
- if (!TextUtils.isEmpty(additionalExtensions)) {
- for (String extension : additionalExtensions.split(",")) {
- String trimed = extension.trim();
- if (trimed.length() > 0) {
- mExtensionsToConsider.add(trimed);
- }
- }
- }
-
- final Resources resources = getResources();
- mFileIndexMinimum = resources.getInteger(R.integer.config_export_file_min_index);
- mFileIndexMaximum = resources.getInteger(R.integer.config_export_file_max_index);
-
- startExportVCardToSdCard();
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case R.id.dialog_export_confirmation: {
- return getExportConfirmationDialog();
- }
- case R.string.fail_reason_too_many_vcard: {
- return new AlertDialog.Builder(this)
- .setTitle(R.string.exporting_contact_failed_title)
- .setMessage(getString(R.string.exporting_contact_failed_message,
- getString(R.string.fail_reason_too_many_vcard)))
- .setPositiveButton(android.R.string.ok, mCancelListener)
- .create();
- }
- case R.id.dialog_fail_to_export_with_reason: {
- return getErrorDialogWithReason();
- }
- case R.id.dialog_sdcard_not_found: {
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setTitle(R.string.no_sdcard_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.no_sdcard_message)
- .setPositiveButton(android.R.string.ok, mCancelListener);
- return builder.create();
- }
- case R.id.dialog_exporting_vcard: {
- if (mProgressDialog == null) {
- String title = getString(R.string.exporting_contact_list_title);
- String message = getString(R.string.exporting_contact_list_message,
- mExportingFileName);
- mProgressDialog = new ProgressDialog(ExportVCardActivity.this);
- mProgressDialog.setTitle(title);
- mProgressDialog.setMessage(message);
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mProgressDialog.setOnCancelListener(mActualExportThread);
- mActualExportThread.start();
- }
- return mProgressDialog;
- }
- }
- return super.onCreateDialog(id);
- }
-
- @Override
- protected void onPrepareDialog(int id, Dialog dialog) {
- if (id == R.id.dialog_fail_to_export_with_reason) {
- ((AlertDialog)dialog).setMessage(getErrorReason());
- } else if (id == R.id.dialog_export_confirmation) {
- ((AlertDialog)dialog).setMessage(
- getString(R.string.confirm_export_message, mTargetFileName));
- } else {
- super.onPrepareDialog(id, dialog);
- }
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- if (mActualExportThread != null) {
- // The Activity is no longer visible. Stop the thread.
- mActualExportThread.cancel();
- mActualExportThread = null;
- }
-
- if (!isFinishing()) {
- finish();
- }
- }
-
- /**
- * Tries to start exporting VCard. If there's no SDCard available,
- * an error dialog is shown.
- */
- public void startExportVCardToSdCard() {
- File targetDirectory = new File(mTargetDirectory);
-
- if (!(targetDirectory.exists() &&
- targetDirectory.isDirectory() &&
- targetDirectory.canRead()) &&
- !targetDirectory.mkdirs()) {
- showDialog(R.id.dialog_sdcard_not_found);
- } else {
- mTargetFileName = getAppropriateFileName(mTargetDirectory);
- if (TextUtils.isEmpty(mTargetFileName)) {
- mTargetFileName = null;
- // finish() is called via the error dialog. Do not call the method here.
- return;
- }
-
- showDialog(R.id.dialog_export_confirmation);
- }
- }
-
- /**
- * Tries to get an appropriate filename. Returns null if it fails.
- */
- private String getAppropriateFileName(final String destDirectory) {
- int fileNumberStringLength = 0;
- {
- // Calling Math.Log10() is costly.
- int tmp;
- for (fileNumberStringLength = 0, tmp = mFileIndexMaximum; tmp > 0;
- fileNumberStringLength++, tmp /= 10) {
- }
- }
- String bodyFormat = "%s%0" + fileNumberStringLength + "d%s";
-
- if (!ALLOW_LONG_FILE_NAME) {
- String possibleBody = String.format(bodyFormat,mFileNamePrefix, 1, mFileNameSuffix);
- if (possibleBody.length() > 8 || mFileNameExtension.length() > 3) {
- Log.e(LOG_TAG, "This code does not allow any long file name.");
- mErrorReason = getString(R.string.fail_reason_too_long_filename,
- String.format("%s.%s", possibleBody, mFileNameExtension));
- showDialog(R.id.dialog_fail_to_export_with_reason);
- // finish() is called via the error dialog. Do not call the method here.
- return null;
- }
- }
-
- // Note that this logic assumes that the target directory is case insensitive.
- // As of 2009-07-16, it is true since the external storage is only sdcard, and
- // it is formated as FAT/VFAT.
- // TODO: fix this.
- for (int i = mFileIndexMinimum; i <= mFileIndexMaximum; i++) {
- boolean numberIsAvailable = true;
- // SD Association's specification seems to require this feature, though we cannot
- // have the specification since it is proprietary...
- String body = null;
- for (String possibleExtension : mExtensionsToConsider) {
- body = String.format(bodyFormat, mFileNamePrefix, i, mFileNameSuffix);
- File file = new File(String.format("%s/%s.%s",
- destDirectory, body, possibleExtension));
- if (file.exists()) {
- numberIsAvailable = false;
- break;
- }
- }
- if (numberIsAvailable) {
- return String.format("%s/%s.%s", destDirectory, body, mFileNameExtension);
- }
- }
- showDialog(R.string.fail_reason_too_many_vcard);
- return null;
- }
-
- public Dialog getExportConfirmationDialog() {
- if (TextUtils.isEmpty(mTargetFileName)) {
- Log.e(LOG_TAG, "Target file name is empty, which must not be!");
- // This situation is not acceptable (probably a bug!), but we don't have no reason to
- // show...
- mErrorReason = null;
- return getErrorDialogWithReason();
- }
-
- return new AlertDialog.Builder(this)
- .setTitle(R.string.confirm_export_title)
- .setMessage(getString(R.string.confirm_export_message, mTargetFileName))
- .setPositiveButton(android.R.string.ok,
- new ExportConfirmationListener(mTargetFileName))
- .setNegativeButton(android.R.string.cancel, mCancelListener)
- .setOnCancelListener(mCancelListener)
- .create();
- }
-
- public Dialog getErrorDialogWithReason() {
- if (mErrorReason == null) {
- Log.e(LOG_TAG, "Error reason must have been set.");
- mErrorReason = getString(R.string.fail_reason_unknown);
- }
- return new AlertDialog.Builder(this)
- .setTitle(R.string.exporting_contact_failed_title)
- .setMessage(getString(R.string.exporting_contact_failed_message, mErrorReason))
- .setPositiveButton(android.R.string.ok, mCancelListener)
- .setOnCancelListener(mCancelListener)
- .create();
- }
-
- public void cancelExport() {
- if (mActualExportThread != null) {
- mActualExportThread.cancel();
- mActualExportThread = null;
- }
- }
-
- public String getErrorReason() {
- return mErrorReason;
- }
-}
\ No newline at end of file
diff --git a/src/com/android/contacts/GroupMetaData.java b/src/com/android/contacts/GroupMetaData.java
new file mode 100644
index 0000000..39e955d
--- /dev/null
+++ b/src/com/android/contacts/GroupMetaData.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 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;
+
+/**
+ * Meta-data for a contact group. We load all groups associated with the contact's
+ * constituent accounts.
+ */
+public final class GroupMetaData {
+ private String mAccountName;
+ private String mAccountType;
+ private long mGroupId;
+ private String mTitle;
+ private boolean mDefaultGroup;
+ private boolean mFavorites;
+
+ public GroupMetaData(String accountName, String accountType, long groupId, String title,
+ boolean defaultGroup, boolean favorites) {
+ this.mAccountName = accountName;
+ this.mAccountType = accountType;
+ this.mGroupId = groupId;
+ this.mTitle = title;
+ this.mDefaultGroup = defaultGroup;
+ this.mFavorites = favorites;
+ }
+
+ public String getAccountName() {
+ return mAccountName;
+ }
+
+ public String getAccountType() {
+ return mAccountType;
+ }
+
+ public long getGroupId() {
+ return mGroupId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public boolean isDefaultGroup() {
+ return mDefaultGroup;
+ }
+
+ public boolean isFavorites() {
+ return mFavorites;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/GroupMetaDataLoader.java b/src/com/android/contacts/GroupMetaDataLoader.java
new file mode 100644
index 0000000..d08d007
--- /dev/null
+++ b/src/com/android/contacts/GroupMetaDataLoader.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.contacts;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.provider.ContactsContract.Groups;
+
+/**
+ * Group meta-data loader. Loads all groups from the database.
+ */
+public final class GroupMetaDataLoader extends CursorLoader {
+
+ private final static String[] COLUMNS = new String[] {
+ Groups.ACCOUNT_NAME,
+ Groups.ACCOUNT_TYPE,
+ Groups._ID,
+ Groups.TITLE,
+ Groups.AUTO_ADD,
+ Groups.FAVORITES,
+ };
+
+ public final static int ACCOUNT_NAME = 0;
+ public final static int ACCOUNT_TYPE = 1;
+ public final static int GROUP_ID = 2;
+ public final static int TITLE = 3;
+ public final static int AUTO_ADD = 4;
+ public final static int FAVORITES = 5;
+
+ public GroupMetaDataLoader(Context context) {
+ super(context, Groups.CONTENT_URI, COLUMNS, Groups.ACCOUNT_TYPE + " NOT NULL AND "
+ + Groups.ACCOUNT_NAME + " NOT NULL", null, null);
+ }
+}
diff --git a/src/com/android/contacts/GroupingListAdapter.java b/src/com/android/contacts/GroupingListAdapter.java
deleted file mode 100644
index 5937a6d..0000000
--- a/src/com/android/contacts/GroupingListAdapter.java
+++ /dev/null
@@ -1,475 +0,0 @@
-/*
- * Copyright (C) 2010 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.util.ArrayUtils;
-
-import android.content.Context;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.DataSetObserver;
-import android.os.Handler;
-import android.util.SparseIntArray;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-
-/**
- * Maintains a list that groups adjacent items sharing the same value of
- * a "group-by" field. The list has three types of elements: stand-alone, group header and group
- * child. Groups are collapsible and collapsed by default.
- */
-public abstract class GroupingListAdapter extends BaseAdapter {
-
- private static final int GROUP_METADATA_ARRAY_INITIAL_SIZE = 16;
- private static final int GROUP_METADATA_ARRAY_INCREMENT = 128;
- private static final long GROUP_OFFSET_MASK = 0x00000000FFFFFFFFL;
- private static final long GROUP_SIZE_MASK = 0x7FFFFFFF00000000L;
- private static final long EXPANDED_GROUP_MASK = 0x8000000000000000L;
-
- public static final int ITEM_TYPE_STANDALONE = 0;
- public static final int ITEM_TYPE_GROUP_HEADER = 1;
- public static final int ITEM_TYPE_IN_GROUP = 2;
-
- /**
- * Information about a specific list item: is it a group, if so is it expanded.
- * Otherwise, is it a stand-alone item or a group member.
- */
- protected static class PositionMetadata {
- int itemType;
- boolean isExpanded;
- int cursorPosition;
- int childCount;
- private int groupPosition;
- private int listPosition = -1;
- }
-
- private Context mContext;
- private Cursor mCursor;
-
- /**
- * Count of list items.
- */
- private int mCount;
-
- private int mRowIdColumnIndex;
-
- /**
- * Count of groups in the list.
- */
- private int mGroupCount;
-
- /**
- * Information about where these groups are located in the list, how large they are
- * and whether they are expanded.
- */
- private long[] mGroupMetadata;
-
- private SparseIntArray mPositionCache = new SparseIntArray();
- private int mLastCachedListPosition;
- private int mLastCachedCursorPosition;
- private int mLastCachedGroup;
-
- /**
- * A reusable temporary instance of PositionMetadata
- */
- private PositionMetadata mPositionMetadata = new PositionMetadata();
-
- protected ContentObserver mChangeObserver = new ContentObserver(new Handler()) {
-
- @Override
- public boolean deliverSelfNotifications() {
- return true;
- }
-
- @Override
- public void onChange(boolean selfChange) {
- onContentChanged();
- }
- };
-
- protected DataSetObserver mDataSetObserver = new DataSetObserver() {
-
- @Override
- public void onChanged() {
- notifyDataSetChanged();
- }
-
- @Override
- public void onInvalidated() {
- notifyDataSetInvalidated();
- }
- };
-
- public GroupingListAdapter(Context context) {
- mContext = context;
- resetCache();
- }
-
- /**
- * Finds all groups of adjacent items in the cursor and calls {@link #addGroup} for
- * each of them.
- */
- protected abstract void addGroups(Cursor cursor);
-
- protected abstract View newStandAloneView(Context context, ViewGroup parent);
- protected abstract void bindStandAloneView(View view, Context context, Cursor cursor);
-
- protected abstract View newGroupView(Context context, ViewGroup parent);
- protected abstract void bindGroupView(View view, Context context, Cursor cursor, int groupSize,
- boolean expanded);
-
- protected abstract View newChildView(Context context, ViewGroup parent);
- protected abstract void bindChildView(View view, Context context, Cursor cursor);
-
- /**
- * Cache should be reset whenever the cursor changes or groups are expanded or collapsed.
- */
- private void resetCache() {
- mCount = -1;
- mLastCachedListPosition = -1;
- mLastCachedCursorPosition = -1;
- mLastCachedGroup = -1;
- mPositionMetadata.listPosition = -1;
- mPositionCache.clear();
- }
-
- protected void onContentChanged() {
- }
-
- public void changeCursor(Cursor cursor) {
- if (cursor == mCursor) {
- return;
- }
-
- if (mCursor != null) {
- mCursor.unregisterContentObserver(mChangeObserver);
- mCursor.unregisterDataSetObserver(mDataSetObserver);
- mCursor.close();
- }
- mCursor = cursor;
- resetCache();
- findGroups();
-
- if (cursor != null) {
- cursor.registerContentObserver(mChangeObserver);
- cursor.registerDataSetObserver(mDataSetObserver);
- mRowIdColumnIndex = cursor.getColumnIndexOrThrow("_id");
- notifyDataSetChanged();
- } else {
- // notify the observers about the lack of a data set
- notifyDataSetInvalidated();
- }
-
- }
-
- public Cursor getCursor() {
- return mCursor;
- }
-
- /**
- * Scans over the entire cursor looking for duplicate phone numbers that need
- * to be collapsed.
- */
- private void findGroups() {
- mGroupCount = 0;
- mGroupMetadata = new long[GROUP_METADATA_ARRAY_INITIAL_SIZE];
-
- if (mCursor == null) {
- return;
- }
-
- addGroups(mCursor);
- }
-
- /**
- * Records information about grouping in the list. Should be called by the overridden
- * {@link #addGroups} method.
- */
- protected void addGroup(int cursorPosition, int size, boolean expanded) {
- if (mGroupCount >= mGroupMetadata.length) {
- int newSize = ArrayUtils.idealLongArraySize(
- mGroupMetadata.length + GROUP_METADATA_ARRAY_INCREMENT);
- long[] array = new long[newSize];
- System.arraycopy(mGroupMetadata, 0, array, 0, mGroupCount);
- mGroupMetadata = array;
- }
-
- long metadata = ((long)size << 32) | cursorPosition;
- if (expanded) {
- metadata |= EXPANDED_GROUP_MASK;
- }
- mGroupMetadata[mGroupCount++] = metadata;
- }
-
- public int getCount() {
- if (mCursor == null) {
- return 0;
- }
-
- if (mCount != -1) {
- return mCount;
- }
-
- int cursorPosition = 0;
- int count = 0;
- for (int i = 0; i < mGroupCount; i++) {
- long metadata = mGroupMetadata[i];
- int offset = (int)(metadata & GROUP_OFFSET_MASK);
- boolean expanded = (metadata & EXPANDED_GROUP_MASK) != 0;
- int size = (int)((metadata & GROUP_SIZE_MASK) >> 32);
-
- count += (offset - cursorPosition);
-
- if (expanded) {
- count += size + 1;
- } else {
- count++;
- }
-
- cursorPosition = offset + size;
- }
-
- mCount = count + mCursor.getCount() - cursorPosition;
- return mCount;
- }
-
- /**
- * Figures out whether the item at the specified position represents a
- * stand-alone element, a group or a group child. Also computes the
- * corresponding cursor position.
- */
- public void obtainPositionMetadata(PositionMetadata metadata, int position) {
-
- // If the description object already contains requested information, just return
- if (metadata.listPosition == position) {
- return;
- }
-
- int listPosition = 0;
- int cursorPosition = 0;
- int firstGroupToCheck = 0;
-
- // Check cache for the supplied position. What we are looking for is
- // the group descriptor immediately preceding the supplied position.
- // Once we have that, we will be able to tell whether the position
- // is the header of the group, a member of the group or a standalone item.
- if (mLastCachedListPosition != -1) {
- if (position <= mLastCachedListPosition) {
-
- // Have SparceIntArray do a binary search for us.
- int index = mPositionCache.indexOfKey(position);
-
- // If we get back a positive number, the position corresponds to
- // a group header.
- if (index < 0) {
-
- // We had a cache miss, but we did obtain valuable information anyway.
- // The negative number will allow us to compute the location of
- // the group header immediately preceding the supplied position.
- index = ~index - 1;
-
- if (index >= mPositionCache.size()) {
- index--;
- }
- }
-
- // A non-negative index gives us the position of the group header
- // corresponding or preceding the position, so we can
- // search for the group information at the supplied position
- // starting with the cached group we just found
- if (index >= 0) {
- listPosition = mPositionCache.keyAt(index);
- firstGroupToCheck = mPositionCache.valueAt(index);
- long descriptor = mGroupMetadata[firstGroupToCheck];
- cursorPosition = (int)(descriptor & GROUP_OFFSET_MASK);
- }
- } else {
-
- // If we haven't examined groups beyond the supplied position,
- // we will start where we left off previously
- firstGroupToCheck = mLastCachedGroup;
- listPosition = mLastCachedListPosition;
- cursorPosition = mLastCachedCursorPosition;
- }
- }
-
- for (int i = firstGroupToCheck; i < mGroupCount; i++) {
- long group = mGroupMetadata[i];
- int offset = (int)(group & GROUP_OFFSET_MASK);
-
- // Move pointers to the beginning of the group
- listPosition += (offset - cursorPosition);
- cursorPosition = offset;
-
- if (i > mLastCachedGroup) {
- mPositionCache.append(listPosition, i);
- mLastCachedListPosition = listPosition;
- mLastCachedCursorPosition = cursorPosition;
- mLastCachedGroup = i;
- }
-
- // Now we have several possibilities:
- // A) The requested position precedes the group
- if (position < listPosition) {
- metadata.itemType = ITEM_TYPE_STANDALONE;
- metadata.cursorPosition = cursorPosition - (listPosition - position);
- return;
- }
-
- boolean expanded = (group & EXPANDED_GROUP_MASK) != 0;
- int size = (int) ((group & GROUP_SIZE_MASK) >> 32);
-
- // B) The requested position is a group header
- if (position == listPosition) {
- metadata.itemType = ITEM_TYPE_GROUP_HEADER;
- metadata.groupPosition = i;
- metadata.isExpanded = expanded;
- metadata.childCount = size;
- metadata.cursorPosition = offset;
- return;
- }
-
- if (expanded) {
- // C) The requested position is an element in the expanded group
- if (position < listPosition + size + 1) {
- metadata.itemType = ITEM_TYPE_IN_GROUP;
- metadata.cursorPosition = cursorPosition + (position - listPosition) - 1;
- return;
- }
-
- // D) The element is past the expanded group
- listPosition += size + 1;
- } else {
-
- // E) The element is past the collapsed group
- listPosition++;
- }
-
- // Move cursor past the group
- cursorPosition += size;
- }
-
- // The required item is past the last group
- metadata.itemType = ITEM_TYPE_STANDALONE;
- metadata.cursorPosition = cursorPosition + (position - listPosition);
- }
-
- /**
- * Returns true if the specified position in the list corresponds to a
- * group header.
- */
- public boolean isGroupHeader(int position) {
- obtainPositionMetadata(mPositionMetadata, position);
- return mPositionMetadata.itemType == ITEM_TYPE_GROUP_HEADER;
- }
-
- /**
- * Given a position of a groups header in the list, returns the size of
- * the corresponding group.
- */
- public int getGroupSize(int position) {
- obtainPositionMetadata(mPositionMetadata, position);
- return mPositionMetadata.childCount;
- }
-
- /**
- * Mark group as expanded if it is collapsed and vice versa.
- */
- public void toggleGroup(int position) {
- obtainPositionMetadata(mPositionMetadata, position);
- if (mPositionMetadata.itemType != ITEM_TYPE_GROUP_HEADER) {
- throw new IllegalArgumentException("Not a group at position " + position);
- }
-
-
- if (mPositionMetadata.isExpanded) {
- mGroupMetadata[mPositionMetadata.groupPosition] &= ~EXPANDED_GROUP_MASK;
- } else {
- mGroupMetadata[mPositionMetadata.groupPosition] |= EXPANDED_GROUP_MASK;
- }
- resetCache();
- notifyDataSetChanged();
- }
-
- @Override
- public int getViewTypeCount() {
- return 3;
- }
-
- @Override
- public int getItemViewType(int position) {
- obtainPositionMetadata(mPositionMetadata, position);
- return mPositionMetadata.itemType;
- }
-
- public Object getItem(int position) {
- if (mCursor == null) {
- return null;
- }
-
- obtainPositionMetadata(mPositionMetadata, position);
- if (mCursor.moveToPosition(mPositionMetadata.cursorPosition)) {
- return mCursor;
- } else {
- return null;
- }
- }
-
- public long getItemId(int position) {
- Object item = getItem(position);
- if (item != null) {
- return mCursor.getLong(mRowIdColumnIndex);
- } else {
- return -1;
- }
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- obtainPositionMetadata(mPositionMetadata, position);
- View view = convertView;
- if (view == null) {
- switch (mPositionMetadata.itemType) {
- case ITEM_TYPE_STANDALONE:
- view = newStandAloneView(mContext, parent);
- break;
- case ITEM_TYPE_GROUP_HEADER:
- view = newGroupView(mContext, parent);
- break;
- case ITEM_TYPE_IN_GROUP:
- view = newChildView(mContext, parent);
- break;
- }
- }
-
- mCursor.moveToPosition(mPositionMetadata.cursorPosition);
- switch (mPositionMetadata.itemType) {
- case ITEM_TYPE_STANDALONE:
- bindStandAloneView(view, mContext, mCursor);
- break;
- case ITEM_TYPE_GROUP_HEADER:
- bindGroupView(view, mContext, mCursor, mPositionMetadata.childCount,
- mPositionMetadata.isExpanded);
- break;
- case ITEM_TYPE_IN_GROUP:
- bindChildView(view, mContext, mCursor);
- break;
-
- }
- return view;
- }
-}
diff --git a/src/com/android/contacts/ImportVCardActivity.java b/src/com/android/contacts/ImportVCardActivity.java
deleted file mode 100644
index 46cdf32..0000000
--- a/src/com/android/contacts/ImportVCardActivity.java
+++ /dev/null
@@ -1,1002 +0,0 @@
-/*
- * 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 com.android.contacts.model.Sources;
-import com.android.contacts.util.AccountSelectionUtil;
-
-import android.accounts.Account;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.DialogInterface.OnClickListener;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.pim.vcard.VCardConfig;
-import android.pim.vcard.VCardEntryCommitter;
-import android.pim.vcard.VCardEntryConstructor;
-import android.pim.vcard.VCardEntryCounter;
-import android.pim.vcard.VCardInterpreter;
-import android.pim.vcard.VCardInterpreterCollection;
-import android.pim.vcard.VCardParser;
-import android.pim.vcard.VCardParser_V21;
-import android.pim.vcard.VCardParser_V30;
-import android.pim.vcard.VCardSourceDetector;
-import android.pim.vcard.exception.VCardException;
-import android.pim.vcard.exception.VCardNestedException;
-import android.pim.vcard.exception.VCardNotSupportedException;
-import android.pim.vcard.exception.VCardVersionException;
-import android.provider.ContactsContract.RawContacts;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.TextUtils;
-import android.text.style.RelativeSizeSpan;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.Vector;
-
-class VCardFile {
- private String mName;
- private String mCanonicalPath;
- private long mLastModified;
-
- public VCardFile(String name, String canonicalPath, long lastModified) {
- mName = name;
- mCanonicalPath = canonicalPath;
- mLastModified = lastModified;
- }
-
- public String getName() {
- return mName;
- }
-
- public String getCanonicalPath() {
- return mCanonicalPath;
- }
-
- public long getLastModified() {
- return mLastModified;
- }
-}
-
-/**
- * Class for importing vCard. Several user interaction will be required while reading
- * (selecting a file, waiting a moment, etc.)
- *
- * Note that this Activity assumes that the instance is a "one-shot Activity", which will be
- * finished (with the method {@link Activity#finish()}) after the import and never reuse
- * any Dialog in the instance. So this code is careless about the management around managed
- * dialogs stuffs (like how onCreateDialog() is used).
- */
-public class ImportVCardActivity extends Activity {
- private static final String LOG_TAG = "ImportVCardActivity";
- private static final boolean DO_PERFORMANCE_PROFILE = false;
-
- private final static int VCARD_VERSION_V21 = 1;
- private final static int VCARD_VERSION_V30 = 2;
- private final static int VCARD_VERSION_V40 = 3;
-
- // Run on the UI thread. Must not be null except after onDestroy().
- private Handler mHandler = new Handler();
-
- private AccountSelectionUtil.AccountSelectedListener mAccountSelectionListener;
- private Account mAccount;
-
- private ProgressDialog mProgressDialogForScanVCard;
-
- private List<VCardFile> mAllVCardFileList;
- private VCardScanThread mVCardScanThread;
- private VCardReadThread mVCardReadThread;
- private ProgressDialog mProgressDialogForReadVCard;
-
- private String mErrorMessage;
-
- private boolean mNeedReview = false;
-
- // Runs on the UI thread.
- private class DialogDisplayer implements Runnable {
- private final int mResId;
- public DialogDisplayer(int resId) {
- mResId = resId;
- }
- public DialogDisplayer(String errorMessage) {
- mResId = R.id.dialog_error_with_message;
- mErrorMessage = errorMessage;
- }
- public void run() {
- showDialog(mResId);
- }
- }
-
- private class CancelListener
- implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
-
- public void onCancel(DialogInterface dialog) {
- finish();
- }
- }
-
- private CancelListener mCancelListener = new CancelListener();
-
- private class VCardReadThread extends Thread
- implements DialogInterface.OnCancelListener {
- private ContentResolver mResolver;
- private VCardParser mVCardParser;
- private boolean mCanceled;
- private PowerManager.WakeLock mWakeLock;
- private Uri mUri;
- private File mTempFile;
-
- private List<VCardFile> mSelectedVCardFileList;
- private List<String> mErrorFileNameList;
-
- public VCardReadThread(Uri uri) {
- mUri = uri;
- init();
- }
-
- public VCardReadThread(final List<VCardFile> selectedVCardFileList) {
- mSelectedVCardFileList = selectedVCardFileList;
- mErrorFileNameList = new ArrayList<String>();
- init();
- }
-
- private void init() {
- Context context = ImportVCardActivity.this;
- mResolver = context.getContentResolver();
- PowerManager powerManager = (PowerManager)context.getSystemService(
- Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(
- PowerManager.SCREEN_DIM_WAKE_LOCK |
- PowerManager.ON_AFTER_RELEASE, LOG_TAG);
- }
-
- @Override
- public void finalize() {
- if (mWakeLock != null && mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- }
-
- @Override
- public void run() {
- boolean shouldCallFinish = true;
- mWakeLock.acquire();
- Uri createdUri = null;
- mTempFile = null;
- // Some malicious vCard data may make this thread broken
- // (e.g. OutOfMemoryError).
- // Even in such cases, some should be done.
- try {
- if (mUri != null) { // Read one vCard expressed by mUri
- final Uri targetUri = mUri;
- mProgressDialogForReadVCard.setProgressNumberFormat("");
- mProgressDialogForReadVCard.setProgress(0);
-
- // Count the number of VCard entries
- mProgressDialogForReadVCard.setIndeterminate(true);
- long start;
- if (DO_PERFORMANCE_PROFILE) {
- start = System.currentTimeMillis();
- }
- final VCardEntryCounter counter = new VCardEntryCounter();
- final VCardSourceDetector detector = new VCardSourceDetector();
- final VCardInterpreterCollection builderCollection =
- new VCardInterpreterCollection(Arrays.asList(counter, detector));
- boolean result;
- try {
- // We don't know which type should be useld to parse the Uri.
- // It is possble to misinterpret the vCard, but we expect the parser
- // lets VCardSourceDetector detect the type before the misinterpretation.
- result = readOneVCardFile(targetUri, VCardConfig.VCARD_TYPE_UNKNOWN,
- builderCollection, true, null);
- } catch (VCardNestedException e) {
- try {
- final int estimatedVCardType = detector.getEstimatedType();
- final String estimatedCharset = detector.getEstimatedCharset();
- // Assume that VCardSourceDetector was able to detect the source.
- // Try again with the detector.
- result = readOneVCardFile(targetUri, estimatedVCardType,
- counter, false, null);
- } catch (VCardNestedException e2) {
- result = false;
- Log.e(LOG_TAG, "Must not reach here. " + e2);
- }
- }
- if (DO_PERFORMANCE_PROFILE) {
- long time = System.currentTimeMillis() - start;
- Log.d(LOG_TAG, "time for counting the number of vCard entries: " +
- time + " ms");
- }
- if (!result) {
- shouldCallFinish = false;
- return;
- }
-
- mProgressDialogForReadVCard.setProgressNumberFormat(
- getString(R.string.reading_vcard_contacts));
- mProgressDialogForReadVCard.setIndeterminate(false);
- mProgressDialogForReadVCard.setMax(counter.getCount());
- String charset = detector.getEstimatedCharset();
- createdUri = doActuallyReadOneVCard(targetUri, mAccount, true, detector,
- mErrorFileNameList);
- } else { // Read multiple files.
- mProgressDialogForReadVCard.setProgressNumberFormat(
- getString(R.string.reading_vcard_files));
- mProgressDialogForReadVCard.setMax(mSelectedVCardFileList.size());
- mProgressDialogForReadVCard.setProgress(0);
-
- for (VCardFile vcardFile : mSelectedVCardFileList) {
- if (mCanceled) {
- return;
- }
- // TODO: detect scheme!
- final Uri targetUri = Uri.parse("file://" + vcardFile.getCanonicalPath());
- VCardSourceDetector detector = new VCardSourceDetector();
- try {
- if (!readOneVCardFile(targetUri, VCardConfig.VCARD_TYPE_UNKNOWN,
- detector, true, mErrorFileNameList)) {
- continue;
- }
- } catch (VCardNestedException e) {
- // Assume that VCardSourceDetector was able to detect the source.
- }
- String charset = detector.getEstimatedCharset();
- doActuallyReadOneVCard(targetUri, mAccount,
- false, detector, mErrorFileNameList);
- mProgressDialogForReadVCard.incrementProgressBy(1);
- }
- }
- } finally {
- mWakeLock.release();
- mProgressDialogForReadVCard.dismiss();
- if (mTempFile != null) {
- if (!mTempFile.delete()) {
- Log.w(LOG_TAG, "Failed to delete a cache file.");
- }
- mTempFile = null;
- }
- // finish() is called via mCancelListener, which is used in DialogDisplayer.
- if (shouldCallFinish && !isFinishing()) {
- if (mErrorFileNameList == null || mErrorFileNameList.isEmpty()) {
- finish();
- if (mNeedReview) {
- mNeedReview = false;
- Log.v("importVCardActivity", "Prepare to review the imported contact");
-
- if (createdUri != null) {
- // get contact_id of this raw_contact
- final long rawContactId = ContentUris.parseId(createdUri);
- Uri contactUri = RawContacts.getContactLookupUri(
- getContentResolver(), ContentUris.withAppendedId(
- RawContacts.CONTENT_URI, rawContactId));
-
- Intent viewIntent = new Intent(Intent.ACTION_VIEW, contactUri);
- startActivity(viewIntent);
- }
- }
- } else {
- StringBuilder builder = new StringBuilder();
- boolean first = true;
- for (String fileName : mErrorFileNameList) {
- if (first) {
- first = false;
- } else {
- builder.append(", ");
- }
- builder.append(fileName);
- }
-
- runOnUIThread(new DialogDisplayer(
- getString(R.string.fail_reason_failed_to_read_files,
- builder.toString())));
- }
- }
- }
- }
-
- private Uri doActuallyReadOneVCard(Uri uri, Account account,
- boolean showEntryParseProgress,
- VCardSourceDetector detector, List<String> errorFileNameList) {
- final Context context = ImportVCardActivity.this;
- int vcardType = detector.getEstimatedType();
- if (vcardType == VCardConfig.VCARD_TYPE_UNKNOWN) {
- vcardType = VCardConfig.getVCardTypeFromString(
- context.getString(R.string.config_import_vcard_type));
- }
- final String estimatedCharset = detector.getEstimatedCharset();
- final String currentLanguage = Locale.getDefault().getLanguage();
- VCardEntryConstructor builder;
- builder = new VCardEntryConstructor(vcardType, mAccount, estimatedCharset);
- final VCardEntryCommitter committer = new VCardEntryCommitter(mResolver);
- builder.addEntryHandler(committer);
- if (showEntryParseProgress) {
- builder.addEntryHandler(new ProgressShower(mProgressDialogForReadVCard,
- context.getString(R.string.reading_vcard_message),
- ImportVCardActivity.this,
- mHandler));
- }
-
- try {
- if (!readOneVCardFile(uri, vcardType, builder, false, null)) {
- return null;
- }
- } catch (VCardNestedException e) {
- Log.e(LOG_TAG, "Never reach here.");
- }
- final ArrayList<Uri> createdUris = committer.getCreatedUris();
- return (createdUris == null || createdUris.size() != 1) ? null : createdUris.get(0);
- }
-
- /**
- * Charset should be handled by {@link VCardEntryConstructor}.
- */
- private boolean readOneVCardFile(Uri uri, int vcardType,
- VCardInterpreter interpreter,
- boolean throwNestedException, List<String> errorFileNameList)
- throws VCardNestedException {
- InputStream is;
- try {
- is = mResolver.openInputStream(uri);
- mVCardParser = new VCardParser_V21(vcardType);
-
- try {
- mVCardParser.parse(is, interpreter);
- } catch (VCardVersionException e1) {
- try {
- is.close();
- } catch (IOException e) {
- }
- if (interpreter instanceof VCardEntryConstructor) {
- // Let the object clean up internal temporal objects,
- ((VCardEntryConstructor)interpreter).clear();
- } else if (interpreter instanceof VCardInterpreterCollection) {
- for (VCardInterpreter elem :
- ((VCardInterpreterCollection) interpreter).getCollection()) {
- if (elem instanceof VCardEntryConstructor) {
- ((VCardEntryConstructor)elem).clear();
- }
- }
- }
-
- is = mResolver.openInputStream(uri);
-
- try {
- mVCardParser = new VCardParser_V30(vcardType);
- mVCardParser.parse(is, interpreter);
- } catch (VCardVersionException e2) {
- throw new VCardException("vCard with unspported version.");
- }
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- }
- }
- }
- } catch (IOException e) {
- Log.e(LOG_TAG, "IOException was emitted: " + e.getMessage());
-
- mProgressDialogForReadVCard.dismiss();
-
- if (errorFileNameList != null) {
- errorFileNameList.add(uri.toString());
- } else {
- runOnUIThread(new DialogDisplayer(
- getString(R.string.fail_reason_io_error) +
- ": " + e.getLocalizedMessage()));
- }
- return false;
- } catch (VCardNotSupportedException e) {
- if ((e instanceof VCardNestedException) && throwNestedException) {
- throw (VCardNestedException)e;
- }
- if (errorFileNameList != null) {
- errorFileNameList.add(uri.toString());
- } else {
- runOnUIThread(new DialogDisplayer(
- getString(R.string.fail_reason_vcard_not_supported_error) +
- " (" + e.getMessage() + ")"));
- }
- return false;
- } catch (VCardException e) {
- if (errorFileNameList != null) {
- errorFileNameList.add(uri.toString());
- } else {
- runOnUIThread(new DialogDisplayer(
- getString(R.string.fail_reason_vcard_parse_error) +
- " (" + e.getMessage() + ")"));
- }
- return false;
- }
- return true;
- }
-
- public void cancel() {
- mCanceled = true;
- if (mVCardParser != null) {
- mVCardParser.cancel();
- }
- }
-
- public void onCancel(DialogInterface dialog) {
- cancel();
- }
- }
-
- private class ImportTypeSelectedListener implements
- DialogInterface.OnClickListener {
- public static final int IMPORT_ONE = 0;
- public static final int IMPORT_MULTIPLE = 1;
- public static final int IMPORT_ALL = 2;
- public static final int IMPORT_TYPE_SIZE = 3;
-
- private int mCurrentIndex;
-
- public void onClick(DialogInterface dialog, int which) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- switch (mCurrentIndex) {
- case IMPORT_ALL:
- importMultipleVCardFromSDCard(mAllVCardFileList);
- break;
- case IMPORT_MULTIPLE:
- showDialog(R.id.dialog_select_multiple_vcard);
- break;
- default:
- showDialog(R.id.dialog_select_one_vcard);
- break;
- }
- } else if (which == DialogInterface.BUTTON_NEGATIVE) {
- finish();
- } else {
- mCurrentIndex = which;
- }
- }
- }
-
- private class VCardSelectedListener implements
- DialogInterface.OnClickListener, DialogInterface.OnMultiChoiceClickListener {
- private int mCurrentIndex;
- private Set<Integer> mSelectedIndexSet;
-
- public VCardSelectedListener(boolean multipleSelect) {
- mCurrentIndex = 0;
- if (multipleSelect) {
- mSelectedIndexSet = new HashSet<Integer>();
- }
- }
-
- public void onClick(DialogInterface dialog, int which) {
- if (which == DialogInterface.BUTTON_POSITIVE) {
- if (mSelectedIndexSet != null) {
- List<VCardFile> selectedVCardFileList = new ArrayList<VCardFile>();
- int size = mAllVCardFileList.size();
- // We'd like to sort the files by its index, so we do not use Set iterator.
- for (int i = 0; i < size; i++) {
- if (mSelectedIndexSet.contains(i)) {
- selectedVCardFileList.add(mAllVCardFileList.get(i));
- }
- }
- importMultipleVCardFromSDCard(selectedVCardFileList);
- } else {
- String canonicalPath = mAllVCardFileList.get(mCurrentIndex).getCanonicalPath();
- final Uri uri = Uri.parse("file://" + canonicalPath);
- importOneVCardFromSDCard(uri);
- }
- } else if (which == DialogInterface.BUTTON_NEGATIVE) {
- finish();
- } else {
- // Some file is selected.
- mCurrentIndex = which;
- if (mSelectedIndexSet != null) {
- if (mSelectedIndexSet.contains(which)) {
- mSelectedIndexSet.remove(which);
- } else {
- mSelectedIndexSet.add(which);
- }
- }
- }
- }
-
- public void onClick(DialogInterface dialog, int which, boolean isChecked) {
- if (mSelectedIndexSet == null || (mSelectedIndexSet.contains(which) == isChecked)) {
- Log.e(LOG_TAG, String.format("Inconsist state in index %d (%s)", which,
- mAllVCardFileList.get(which).getCanonicalPath()));
- } else {
- onClick(dialog, which);
- }
- }
- }
-
- /**
- * Thread scanning VCard from SDCard. After scanning, the dialog which lets a user select
- * a vCard file is shown. After the choice, VCardReadThread starts running.
- */
- private class VCardScanThread extends Thread implements OnCancelListener, OnClickListener {
- private boolean mCanceled;
- private boolean mGotIOException;
- private File mRootDirectory;
-
- // To avoid recursive link.
- private Set<String> mCheckedPaths;
- private PowerManager.WakeLock mWakeLock;
-
- private class CanceledException extends Exception {
- }
-
- public VCardScanThread(File sdcardDirectory) {
- mCanceled = false;
- mGotIOException = false;
- mRootDirectory = sdcardDirectory;
- mCheckedPaths = new HashSet<String>();
- PowerManager powerManager = (PowerManager)ImportVCardActivity.this.getSystemService(
- Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(
- PowerManager.SCREEN_DIM_WAKE_LOCK |
- PowerManager.ON_AFTER_RELEASE, LOG_TAG);
- }
-
- @Override
- public void run() {
- mAllVCardFileList = new Vector<VCardFile>();
- try {
- mWakeLock.acquire();
- getVCardFileRecursively(mRootDirectory);
- } catch (CanceledException e) {
- mCanceled = true;
- } catch (IOException e) {
- mGotIOException = true;
- } finally {
- mWakeLock.release();
- }
-
- if (mCanceled) {
- mAllVCardFileList = null;
- }
-
- mProgressDialogForScanVCard.dismiss();
- mProgressDialogForScanVCard = null;
-
- if (mGotIOException) {
- runOnUIThread(new DialogDisplayer(R.id.dialog_io_exception));
- } else if (mCanceled) {
- finish();
- } else {
- int size = mAllVCardFileList.size();
- final Context context = ImportVCardActivity.this;
- if (size == 0) {
- runOnUIThread(new DialogDisplayer(R.id.dialog_vcard_not_found));
- } else {
- startVCardSelectAndImport();
- }
- }
- }
-
- private void getVCardFileRecursively(File directory)
- throws CanceledException, IOException {
- if (mCanceled) {
- throw new CanceledException();
- }
-
- // e.g. secured directory may return null toward listFiles().
- final File[] files = directory.listFiles();
- if (files == null) {
- Log.w(LOG_TAG, "listFiles() returned null (directory: " + directory + ")");
- return;
- }
- for (File file : directory.listFiles()) {
- if (mCanceled) {
- throw new CanceledException();
- }
- String canonicalPath = file.getCanonicalPath();
- if (mCheckedPaths.contains(canonicalPath)) {
- continue;
- }
-
- mCheckedPaths.add(canonicalPath);
-
- if (file.isDirectory()) {
- getVCardFileRecursively(file);
- } else if (canonicalPath.toLowerCase().endsWith(".vcf") &&
- file.canRead()){
- String fileName = file.getName();
- VCardFile vcardFile = new VCardFile(
- fileName, canonicalPath, file.lastModified());
- mAllVCardFileList.add(vcardFile);
- }
- }
- }
-
- public void onCancel(DialogInterface dialog) {
- mCanceled = true;
- }
-
- public void onClick(DialogInterface dialog, int which) {
- if (which == DialogInterface.BUTTON_NEGATIVE) {
- mCanceled = true;
- }
- }
- }
-
- private void startVCardSelectAndImport() {
- int size = mAllVCardFileList.size();
- if (getResources().getBoolean(R.bool.config_import_all_vcard_from_sdcard_automatically)) {
- importMultipleVCardFromSDCard(mAllVCardFileList);
- } else if (size == 1) {
- String canonicalPath = mAllVCardFileList.get(0).getCanonicalPath();
- Uri uri = Uri.parse("file://" + canonicalPath);
- importOneVCardFromSDCard(uri);
- } else if (getResources().getBoolean(R.bool.config_allow_users_select_all_vcard_import)) {
- runOnUIThread(new DialogDisplayer(R.id.dialog_select_import_type));
- } else {
- runOnUIThread(new DialogDisplayer(R.id.dialog_select_one_vcard));
- }
- }
-
- private void importMultipleVCardFromSDCard(final List<VCardFile> selectedVCardFileList) {
- runOnUIThread(new Runnable() {
- public void run() {
- mVCardReadThread = new VCardReadThread(selectedVCardFileList);
- showDialog(R.id.dialog_reading_vcard);
- }
- });
- }
-
- private void importOneVCardFromSDCard(final Uri uri) {
- runOnUIThread(new Runnable() {
- public void run() {
- mVCardReadThread = new VCardReadThread(uri);
- showDialog(R.id.dialog_reading_vcard);
- }
- });
- }
-
- private Dialog getSelectImportTypeDialog() {
- DialogInterface.OnClickListener listener =
- new ImportTypeSelectedListener();
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setTitle(R.string.select_vcard_title)
- .setPositiveButton(android.R.string.ok, listener)
- .setOnCancelListener(mCancelListener)
- .setNegativeButton(android.R.string.cancel, mCancelListener);
-
- String[] items = new String[ImportTypeSelectedListener.IMPORT_TYPE_SIZE];
- items[ImportTypeSelectedListener.IMPORT_ONE] =
- getString(R.string.import_one_vcard_string);
- items[ImportTypeSelectedListener.IMPORT_MULTIPLE] =
- getString(R.string.import_multiple_vcard_string);
- items[ImportTypeSelectedListener.IMPORT_ALL] =
- getString(R.string.import_all_vcard_string);
- builder.setSingleChoiceItems(items, ImportTypeSelectedListener.IMPORT_ONE, listener);
- return builder.create();
- }
-
- private Dialog getVCardFileSelectDialog(boolean multipleSelect) {
- int size = mAllVCardFileList.size();
- VCardSelectedListener listener = new VCardSelectedListener(multipleSelect);
- AlertDialog.Builder builder =
- new AlertDialog.Builder(this)
- .setTitle(R.string.select_vcard_title)
- .setPositiveButton(android.R.string.ok, listener)
- .setOnCancelListener(mCancelListener)
- .setNegativeButton(android.R.string.cancel, mCancelListener);
-
- CharSequence[] items = new CharSequence[size];
- DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- for (int i = 0; i < size; i++) {
- VCardFile vcardFile = mAllVCardFileList.get(i);
- SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
- stringBuilder.append(vcardFile.getName());
- stringBuilder.append('\n');
- int indexToBeSpanned = stringBuilder.length();
- // Smaller date text looks better, since each file name becomes easier to read.
- // The value set to RelativeSizeSpan is arbitrary. You can change it to any other
- // value (but the value bigger than 1.0f would not make nice appearance :)
- stringBuilder.append(
- "(" + dateFormat.format(new Date(vcardFile.getLastModified())) + ")");
- stringBuilder.setSpan(
- new RelativeSizeSpan(0.7f), indexToBeSpanned, stringBuilder.length(),
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- items[i] = stringBuilder;
- }
- if (multipleSelect) {
- builder.setMultiChoiceItems(items, (boolean[])null, listener);
- } else {
- builder.setSingleChoiceItems(items, 0, listener);
- }
- return builder.create();
- }
-
- @Override
- protected void onCreate(Bundle bundle) {
- super.onCreate(bundle);
-
- final Intent intent = getIntent();
- if (intent != null) {
- final String accountName = intent.getStringExtra("account_name");
- final String accountType = intent.getStringExtra("account_type");
- if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
- mAccount = new Account(accountName, accountType);
- }
- } else {
- Log.e(LOG_TAG, "intent does not exist");
- }
-
- // The caller often does not know account information at all, so we show the UI instead.
- if (mAccount == null) {
- // There's three possibilities:
- // - more than one accounts -> ask the user
- // - just one account -> use the account without asking the user
- // - no account -> use phone-local storage without asking the user
- final Sources sources = Sources.getInstance(this);
- final List<Account> accountList = sources.getAccounts(true);
- final int size = accountList.size();
- if (size > 1) {
- final int resId = R.string.import_from_sdcard;
- mAccountSelectionListener =
- new AccountSelectionUtil.AccountSelectedListener(
- this, accountList, resId) {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- mAccount = mAccountList.get(which);
- // Instead of using Intent mechanism, call the relevant private method,
- // to avoid throwing an Intent to itself again.
- startImport();
- }
- };
- showDialog(resId);
- return;
- } else {
- mAccount = size > 0 ? accountList.get(0) : null;
- }
- }
-
- startImport();
- }
-
- private void startImport() {
- Intent intent = getIntent();
- final String action = intent.getAction();
- final Uri uri = intent.getData();
- Log.v(LOG_TAG, "action = " + action + " ; path = " + uri);
- if (Intent.ACTION_VIEW.equals(action)) {
- // Import the file directly and then go to EDIT screen
- mNeedReview = true;
- }
-
- if (uri != null) {
- importOneVCardFromSDCard(uri);
- } else {
- doScanExternalStorageAndImportVCard();
- }
- }
-
- @Override
- protected Dialog onCreateDialog(int resId) {
- switch (resId) {
- case R.string.import_from_sdcard: {
- if (mAccountSelectionListener == null) {
- throw new NullPointerException(
- "mAccountSelectionListener must not be null.");
- }
- return AccountSelectionUtil.getSelectAccountDialog(this, resId,
- mAccountSelectionListener,
- new CancelListener());
- }
- case R.id.dialog_searching_vcard: {
- if (mProgressDialogForScanVCard == null) {
- String title = getString(R.string.searching_vcard_title);
- String message = getString(R.string.searching_vcard_message);
- mProgressDialogForScanVCard =
- ProgressDialog.show(this, title, message, true, false);
- mProgressDialogForScanVCard.setOnCancelListener(mVCardScanThread);
- mVCardScanThread.start();
- }
- return mProgressDialogForScanVCard;
- }
- case R.id.dialog_sdcard_not_found: {
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setTitle(R.string.no_sdcard_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.no_sdcard_message)
- .setOnCancelListener(mCancelListener)
- .setPositiveButton(android.R.string.ok, mCancelListener);
- return builder.create();
- }
- case R.id.dialog_vcard_not_found: {
- String message = (getString(R.string.scanning_sdcard_failed_message,
- getString(R.string.fail_reason_no_vcard_file)));
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setTitle(R.string.scanning_sdcard_failed_title)
- .setMessage(message)
- .setOnCancelListener(mCancelListener)
- .setPositiveButton(android.R.string.ok, mCancelListener);
- return builder.create();
- }
- case R.id.dialog_select_import_type: {
- return getSelectImportTypeDialog();
- }
- case R.id.dialog_select_multiple_vcard: {
- return getVCardFileSelectDialog(true);
- }
- case R.id.dialog_select_one_vcard: {
- return getVCardFileSelectDialog(false);
- }
- case R.id.dialog_reading_vcard: {
- if (mProgressDialogForReadVCard == null) {
- String title = getString(R.string.reading_vcard_title);
- String message = getString(R.string.reading_vcard_message);
- mProgressDialogForReadVCard = new ProgressDialog(this);
- mProgressDialogForReadVCard.setTitle(title);
- mProgressDialogForReadVCard.setMessage(message);
- mProgressDialogForReadVCard.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mProgressDialogForReadVCard.setOnCancelListener(mVCardReadThread);
- mVCardReadThread.start();
- }
- return mProgressDialogForReadVCard;
- }
- case R.id.dialog_io_exception: {
- String message = (getString(R.string.scanning_sdcard_failed_message,
- getString(R.string.fail_reason_io_error)));
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setTitle(R.string.scanning_sdcard_failed_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(message)
- .setOnCancelListener(mCancelListener)
- .setPositiveButton(android.R.string.ok, mCancelListener);
- return builder.create();
- }
- case R.id.dialog_error_with_message: {
- String message = mErrorMessage;
- if (TextUtils.isEmpty(message)) {
- Log.e(LOG_TAG, "Error message is null while it must not.");
- message = getString(R.string.fail_reason_unknown);
- }
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setTitle(getString(R.string.reading_vcard_failed_title))
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(message)
- .setOnCancelListener(mCancelListener)
- .setPositiveButton(android.R.string.ok, mCancelListener);
- return builder.create();
- }
- }
-
- return super.onCreateDialog(resId);
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- if (mVCardReadThread != null) {
- // The Activity is no longer visible. Stop the thread.
- mVCardReadThread.cancel();
- mVCardReadThread = null;
- }
-
- // ImportVCardActivity should not be persistent. In other words, if there's some
- // event calling onPause(), this Activity should finish its work and give the main
- // screen back to the caller Activity.
- if (!isFinishing()) {
- finish();
- }
- }
-
- @Override
- protected void onDestroy() {
- // The code assumes the handler runs on the UI thread. If not,
- // clearing the message queue is not enough, one would have to
- // make sure that the handler does not run any callback when
- // this activity isFinishing().
-
- // Need to make sure any worker thread is done before we flush and
- // nullify the message handler.
- if (mVCardReadThread != null) {
- Log.w(LOG_TAG, "VCardReadThread exists while this Activity is now being killed!");
- mVCardReadThread.cancel();
- int attempts = 0;
- while (mVCardReadThread.isAlive() && attempts < 10) {
- try {
- Thread.currentThread().sleep(20);
- } catch (InterruptedException ie) {
- // Keep on going until max attempts is reached.
- }
- attempts++;
- }
- if (mVCardReadThread.isAlive()) {
- // Find out why the thread did not exit in a timely
- // fashion. Last resort: increase the sleep duration
- // and/or the number of attempts.
- Log.e(LOG_TAG, "VCardReadThread is still alive after max attempts.");
- }
- mVCardReadThread = null;
- }
-
- // Callbacks messages have what == 0.
- if (mHandler.hasMessages(0)) {
- mHandler.removeMessages(0);
- }
-
- mHandler = null; // Prevents memory leaks by breaking any circular dependency.
- super.onDestroy();
- }
-
- /**
- * Tries to run a given Runnable object when the UI thread can. Ignore it otherwise
- */
- private void runOnUIThread(Runnable runnable) {
- if (mHandler == null) {
- Log.w(LOG_TAG, "Handler object is null. No dialog is shown.");
- } else {
- mHandler.post(runnable);
- }
- }
-
- @Override
- public void finalize() {
- // TODO: This should not be needed. Throw exception instead.
- if (mVCardReadThread != null) {
- // Not sure this procedure is really needed, but just in case...
- Log.e(LOG_TAG, "VCardReadThread exists while this Activity is now being killed!");
- mVCardReadThread.cancel();
- mVCardReadThread = null;
- }
- }
-
- /**
- * Scans vCard in external storage (typically SDCard) and tries to import it.
- * - When there's no SDCard available, an error dialog is shown.
- * - When multiple vCard files are available, asks a user to select one.
- */
- private void doScanExternalStorageAndImportVCard() {
- // TODO: should use getExternalStorageState().
- final File file = Environment.getExternalStorageDirectory();
- if (!file.exists() || !file.isDirectory() || !file.canRead()) {
- showDialog(R.id.dialog_sdcard_not_found);
- } else {
- mVCardScanThread = new VCardScanThread(file);
- showDialog(R.id.dialog_searching_vcard);
- }
- }
-}
diff --git a/src/com/android/contacts/PhoneDisambigDialog.java b/src/com/android/contacts/PhoneDisambigDialog.java
deleted file mode 100644
index 3e3d534..0000000
--- a/src/com/android/contacts/PhoneDisambigDialog.java
+++ /dev/null
@@ -1,227 +0,0 @@
-/*
- * 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 com.android.contacts.Collapser.Collapsible;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.Sources;
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.model.ContactsSource.StringInflater;
-
-import android.app.AlertDialog;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.database.Cursor;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.telephony.PhoneNumberUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class used for displaying a dialog with a list of phone numbers of which
- * one will be chosen to make a call or initiate an sms message.
- */
-public class PhoneDisambigDialog implements DialogInterface.OnClickListener,
- DialogInterface.OnDismissListener, CompoundButton.OnCheckedChangeListener{
-
- private boolean mMakePrimary = false;
- private Context mContext;
- private AlertDialog mDialog;
- private boolean mSendSms;
- private int mStickyTab;
- private Cursor mPhonesCursor;
- private ListAdapter mPhonesAdapter;
- private ArrayList<PhoneItem> mPhoneItemList;
-
- public PhoneDisambigDialog(Context context, Cursor phonesCursor, boolean sendSms,
- int stickyTab) {
- mContext = context;
- mSendSms = sendSms;
- mPhonesCursor = phonesCursor;
- mStickyTab = stickyTab;
-
- mPhoneItemList = makePhoneItemsList(phonesCursor);
- Collapser.collapseList(mPhoneItemList);
-
- mPhonesAdapter = new PhonesAdapter(mContext, mPhoneItemList, mSendSms);
-
- LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- View setPrimaryView = inflater.
- inflate(R.layout.set_primary_checkbox, null);
- ((CheckBox) setPrimaryView.findViewById(R.id.setPrimary)).
- setOnCheckedChangeListener(this);
-
- // Need to show disambig dialogue.
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext).
- setAdapter(mPhonesAdapter, this).
- setTitle(sendSms ?
- R.string.sms_disambig_title : R.string.call_disambig_title).
- setView(setPrimaryView);
-
- mDialog = dialogBuilder.create();
- }
-
- /**
- * Show the dialog.
- */
- public void show() {
- if (mPhoneItemList.size() == 1) {
- // If there is only one after collapse, just select it, and close;
- onClick(mDialog, 0);
- return;
- }
- mDialog.show();
- }
-
- public void onClick(DialogInterface dialog, int which) {
- if (mPhoneItemList.size() > which && which >= 0) {
- PhoneItem phoneItem = mPhoneItemList.get(which);
- long id = phoneItem.id;
- String phone = phoneItem.phoneNumber;
-
- if (mMakePrimary) {
- ContentValues values = new ContentValues(1);
- values.put(Data.IS_SUPER_PRIMARY, 1);
- mContext.getContentResolver().update(ContentUris.
- withAppendedId(Data.CONTENT_URI, id), values, null, null);
- }
-
- if (mSendSms) {
- ContactsUtils.initiateSms(mContext, phone);
- } else {
- StickyTabs.saveTab(mContext, mStickyTab);
- ContactsUtils.initiateCall(mContext, phone);
- }
- } else {
- dialog.dismiss();
- }
- }
-
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- mMakePrimary = isChecked;
- }
-
- public void onDismiss(DialogInterface dialog) {
- mPhonesCursor.close();
- }
-
- private static class PhonesAdapter extends ArrayAdapter<PhoneItem> {
- private final boolean sendSms;
- private final Sources mSources;
-
- public PhonesAdapter(Context context, List<PhoneItem> objects, boolean sendSms) {
- super(context, R.layout.phone_disambig_item,
- android.R.id.text2, objects);
- this.sendSms = sendSms;
- mSources = Sources.getInstance(context);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View view = super.getView(position, convertView, parent);
-
- PhoneItem item = getItem(position);
- ContactsSource source = mSources.getInflatedSource(item.accountType,
- ContactsSource.LEVEL_SUMMARY);
-
- // Obtain a string representation of the phone type specific to the
- // ContactSource associated with that phone number
- TextView typeView = (TextView)view.findViewById(android.R.id.text1);
- DataKind kind = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
- if (kind != null) {
- ContentValues values = new ContentValues();
- values.put(Phone.TYPE, item.type);
- values.put(Phone.LABEL, item.label);
- StringInflater header = sendSms ? kind.actionAltHeader : kind.actionHeader;
- typeView.setText(header.inflateUsing(getContext(), values));
- } else {
- typeView.setText(R.string.call_other);
- }
- return view;
- }
- }
-
- private class PhoneItem implements Collapsible<PhoneItem> {
-
- final long id;
- final String phoneNumber;
- final String accountType;
- final long type;
- final String label;
-
- public PhoneItem(long id, String phoneNumber, String accountType, int type, String label) {
- this.id = id;
- this.phoneNumber = (phoneNumber != null ? phoneNumber : "");
- this.accountType = accountType;
- this.type = type;
- this.label = label;
- }
-
- public boolean collapseWith(PhoneItem phoneItem) {
- if (!shouldCollapseWith(phoneItem)) {
- return false;
- }
- // Just keep the number and id we already have.
- return true;
- }
-
- public boolean shouldCollapseWith(PhoneItem phoneItem) {
- if (PhoneNumberUtils.compare(PhoneDisambigDialog.this.mContext,
- phoneNumber, phoneItem.phoneNumber)) {
- return true;
- }
- return false;
- }
-
- @Override
- public String toString() {
- return phoneNumber;
- }
- }
-
- private ArrayList<PhoneItem> makePhoneItemsList(Cursor phonesCursor) {
- ArrayList<PhoneItem> phoneList = new ArrayList<PhoneItem>();
-
- phonesCursor.moveToPosition(-1);
- while (phonesCursor.moveToNext()) {
- long id = phonesCursor.getLong(phonesCursor.getColumnIndex(Data._ID));
- String phone = phonesCursor.getString(phonesCursor.getColumnIndex(Phone.NUMBER));
- String accountType =
- phonesCursor.getString(phonesCursor.getColumnIndex(RawContacts.ACCOUNT_TYPE));
- int type = phonesCursor.getInt(phonesCursor.getColumnIndex(Phone.TYPE));
- String label = phonesCursor.getString(phonesCursor.getColumnIndex(Phone.LABEL));
-
- phoneList.add(new PhoneItem(id, phone, accountType, type, label));
- }
-
- return phoneList;
- }
-}
diff --git a/src/com/android/contacts/PinnedHeaderListView.java b/src/com/android/contacts/PinnedHeaderListView.java
deleted file mode 100644
index 9d1391b..0000000
--- a/src/com/android/contacts/PinnedHeaderListView.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2010 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.graphics.Canvas;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-
-/**
- * A ListView that maintains a header pinned at the top of the list. The
- * pinned header can be pushed up and dissolved as needed.
- */
-public class PinnedHeaderListView extends ListView {
-
- /**
- * Adapter interface. The list adapter must implement this interface.
- */
- public interface PinnedHeaderAdapter {
-
- /**
- * Pinned header state: don't show the header.
- */
- public static final int PINNED_HEADER_GONE = 0;
-
- /**
- * Pinned header state: show the header at the top of the list.
- */
- public static final int PINNED_HEADER_VISIBLE = 1;
-
- /**
- * Pinned header state: show the header. If the header extends beyond
- * the bottom of the first shown element, push it up and clip.
- */
- public static final int PINNED_HEADER_PUSHED_UP = 2;
-
- /**
- * Computes the desired state of the pinned header for the given
- * position of the first visible list item. Allowed return values are
- * {@link #PINNED_HEADER_GONE}, {@link #PINNED_HEADER_VISIBLE} or
- * {@link #PINNED_HEADER_PUSHED_UP}.
- */
- int getPinnedHeaderState(int position);
-
- /**
- * Configures the pinned header view to match the first visible list item.
- *
- * @param header pinned header view.
- * @param position position of the first visible list item.
- * @param alpha fading of the header view, between 0 and 255.
- */
- void configurePinnedHeader(View header, int position, int alpha);
- }
-
- private static final int MAX_ALPHA = 255;
-
- private PinnedHeaderAdapter mAdapter;
- private View mHeaderView;
- private boolean mHeaderViewVisible;
-
- private int mHeaderViewWidth;
-
- private int mHeaderViewHeight;
-
- public PinnedHeaderListView(Context context) {
- super(context);
- }
-
- public PinnedHeaderListView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public PinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public void setPinnedHeaderView(View view) {
- mHeaderView = view;
-
- // Disable vertical fading when the pinned header is present
- // TODO change ListView to allow separate measures for top and bottom fading edge;
- // in this particular case we would like to disable the top, but not the bottom edge.
- if (mHeaderView != null) {
- setFadingEdgeLength(0);
- }
- requestLayout();
- }
-
- @Override
- public void setAdapter(ListAdapter adapter) {
- super.setAdapter(adapter);
- mAdapter = (PinnedHeaderAdapter)adapter;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mHeaderView != null) {
- measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec);
- mHeaderViewWidth = mHeaderView.getMeasuredWidth();
- mHeaderViewHeight = mHeaderView.getMeasuredHeight();
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (mHeaderView != null) {
- mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
- configureHeaderView(getFirstVisiblePosition());
- }
- }
-
- public void configureHeaderView(int position) {
- if (mHeaderView == null) {
- return;
- }
-
- int state = mAdapter.getPinnedHeaderState(position);
- switch (state) {
- case PinnedHeaderAdapter.PINNED_HEADER_GONE: {
- mHeaderViewVisible = false;
- break;
- }
-
- case PinnedHeaderAdapter.PINNED_HEADER_VISIBLE: {
- mAdapter.configurePinnedHeader(mHeaderView, position, MAX_ALPHA);
- if (mHeaderView.getTop() != 0) {
- mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
- }
- mHeaderViewVisible = true;
- break;
- }
-
- case PinnedHeaderAdapter.PINNED_HEADER_PUSHED_UP: {
- View firstView = getChildAt(0);
- int bottom = firstView.getBottom();
- int itemHeight = firstView.getHeight();
- int headerHeight = mHeaderView.getHeight();
- int y;
- int alpha;
- if (bottom < headerHeight) {
- y = (bottom - headerHeight);
- alpha = MAX_ALPHA * (headerHeight + y) / headerHeight;
- } else {
- y = 0;
- alpha = MAX_ALPHA;
- }
- mAdapter.configurePinnedHeader(mHeaderView, position, alpha);
- if (mHeaderView.getTop() != y) {
- mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight + y);
- }
- mHeaderViewVisible = true;
- break;
- }
- }
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- if (mHeaderViewVisible) {
- drawChild(canvas, mHeaderView, getDrawingTime());
- }
- }
-}
diff --git a/src/com/android/contacts/ProgressShower.java b/src/com/android/contacts/ProgressShower.java
deleted file mode 100644
index a5ad2a2..0000000
--- a/src/com/android/contacts/ProgressShower.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.ProgressDialog;
-import android.content.Context;
-import android.os.Handler;
-import android.pim.vcard.VCardEntry;
-import android.pim.vcard.VCardEntryHandler;
-import android.pim.vcard.VCardConfig;
-import android.util.Log;
-
-public class ProgressShower implements VCardEntryHandler {
- public static final String LOG_TAG = "vcard.ProgressShower";
-
- private final Context mContext;
- private final Handler mHandler;
- private final ProgressDialog mProgressDialog;
- private final String mProgressMessage;
-
- private long mTime;
-
- private class ShowProgressRunnable implements Runnable {
- private VCardEntry mContact;
-
- public ShowProgressRunnable(VCardEntry contact) {
- mContact = contact;
- }
-
- public void run() {
- mProgressDialog.setMessage( mProgressMessage + "\n" +
- mContact.getDisplayName());
- mProgressDialog.incrementProgressBy(1);
- }
- }
-
- public ProgressShower(ProgressDialog progressDialog,
- String progressMessage,
- Context context,
- Handler handler) {
- mContext = context;
- mHandler = handler;
- mProgressDialog = progressDialog;
- mProgressMessage = progressMessage;
- }
-
- public void onStart() {
- }
-
- public void onEntryCreated(VCardEntry contactStruct) {
- long start = System.currentTimeMillis();
-
- if (!contactStruct.isIgnorable()) {
- if (mProgressDialog != null && mProgressMessage != null) {
- if (mHandler != null) {
- mHandler.post(new ShowProgressRunnable(contactStruct));
- } else {
- mProgressDialog.setMessage(mContext.getString(R.string.progress_shower_message,
- mProgressMessage,
- contactStruct.getDisplayName()));
- }
- }
- }
-
- mTime += System.currentTimeMillis() - start;
- }
-
- public void onEnd() {
- if (VCardConfig.showPerformanceLog()) {
- Log.d(LOG_TAG,
- String.format("Time to progress a dialog: %d ms", mTime));
- }
- }
-}
diff --git a/src/com/android/contacts/RecentCallsListActivity.java b/src/com/android/contacts/RecentCallsListActivity.java
index abda325..0a3c178 100644
--- a/src/com/android/contacts/RecentCallsListActivity.java
+++ b/src/com/android/contacts/RecentCallsListActivity.java
@@ -16,6 +16,7 @@
package com.android.contacts;
+import com.android.common.widget.GroupingListAdapter;
import com.android.internal.telephony.CallerInfo;
import com.android.internal.telephony.ITelephony;
@@ -55,7 +56,6 @@
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
-import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
@@ -77,7 +77,6 @@
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.LinkedList;
-import java.util.Locale;
/**
* Displays a list of call log entries.
@@ -95,8 +94,8 @@
Calls.TYPE,
Calls.CACHED_NAME,
Calls.CACHED_NUMBER_TYPE,
- Calls.CACHED_NUMBER_LABEL
- };
+ Calls.CACHED_NUMBER_LABEL,
+ Calls.COUNTRY_ISO};
static final int ID_COLUMN_INDEX = 0;
static final int NUMBER_COLUMN_INDEX = 1;
@@ -106,6 +105,7 @@
static final int CALLER_NAME_COLUMN_INDEX = 5;
static final int CALLER_NUMBERTYPE_COLUMN_INDEX = 6;
static final int CALLER_NUMBERLABEL_COLUMN_INDEX = 7;
+ static final int COUNTRY_ISO_COLUMN_INDEX = 8;
/** The projection to use when querying the phones table */
static final String[] PHONES_PROJECTION = new String[] {
@@ -113,18 +113,19 @@
PhoneLookup.DISPLAY_NAME,
PhoneLookup.TYPE,
PhoneLookup.LABEL,
- PhoneLookup.NUMBER
- };
+ PhoneLookup.NUMBER,
+ PhoneLookup.NORMALIZED_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;
+ static final int NORMALIZED_NUMBER_COLUMN_INDEX = 5;
- private static final int MENU_ITEM_DELETE_ALL = 1;
- private static final int CONTEXT_MENU_ITEM_DELETE = 1;
- private static final int CONTEXT_MENU_CALL_CONTACT = 2;
+ 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;
@@ -134,6 +135,7 @@
RecentCallsAdapter mAdapter;
private QueryHandler mQueryHandler;
String mVoiceMailNumber;
+ private String mCurrentCountryIso;
private boolean mScrollToTop;
@@ -144,6 +146,7 @@
public String label;
public String number;
public String formattedNumber;
+ public String normalizedNumber;
public static ContactInfo EMPTY = new ContactInfo();
}
@@ -167,23 +170,6 @@
String numberLabel;
}
- /**
- * Shared builder used by {@link #formatPhoneNumber(String)} to minimize
- * allocations when formatting phone numbers.
- */
- private static final SpannableStringBuilder sEditable = new SpannableStringBuilder();
-
- /**
- * Invalid formatting type constant for {@link #sFormattingType}.
- */
- private static final int FORMATTING_TYPE_INVALID = -1;
-
- /**
- * Cached formatting type for current {@link Locale}, as provided by
- * {@link PhoneNumberUtils#getFormatTypeForLocale(Locale)}.
- */
- private static int sFormattingType = FORMATTING_TYPE_INVALID;
-
/** Adapter class to fill in data for the Call Log */
final class RecentCallsAdapter extends GroupingListAdapter
implements Runnable, ViewTreeObserver.OnPreDrawListener, View.OnClickListener {
@@ -221,7 +207,6 @@
} else {
callUri = Uri.fromParts("tel", number, null);
}
- StickyTabs.saveTab(RecentCallsListActivity.this, getIntent());
startActivity(new Intent(Intent.ACTION_CALL_PRIVILEGED, callUri));
}
}
@@ -415,6 +400,7 @@
// Note Data.DATA1 and SipAddress.SIP_ADDRESS are equivalent.
info.number = dataTableCursor.getString(
dataTableCursor.getColumnIndex(Data.DATA1));
+ info.normalizedNumber = null; // meaningless for SIP addresses
infoUpdated = true;
}
@@ -426,7 +412,7 @@
Cursor phonesCursor =
RecentCallsListActivity.this.getContentResolver().query(
Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
- Uri.encode(ciq.number)),
+ Uri.encode(ciq.number)),
PHONES_PROJECTION, null, null, null);
if (phonesCursor != null) {
if (phonesCursor.moveToFirst()) {
@@ -436,6 +422,8 @@
info.type = phonesCursor.getInt(PHONE_TYPE_COLUMN_INDEX);
info.label = phonesCursor.getString(LABEL_COLUMN_INDEX);
info.number = phonesCursor.getString(MATCHED_NUMBER_COLUMN_INDEX);
+ info.normalizedNumber =
+ phonesCursor.getString(NORMALIZED_NUMBER_COLUMN_INDEX);
infoUpdated = true;
}
@@ -623,7 +611,7 @@
String callerName = c.getString(CALLER_NAME_COLUMN_INDEX);
int callerNumberType = c.getInt(CALLER_NUMBERTYPE_COLUMN_INDEX);
String callerNumberLabel = c.getString(CALLER_NUMBERLABEL_COLUMN_INDEX);
-
+ String countryIso = c.getString(COUNTRY_ISO_COLUMN_INDEX);
// Store away the number so we can call it directly if you click on the call icon
views.callView.setTag(number);
@@ -650,7 +638,8 @@
// Format and cache phone number for found contact
if (info.formattedNumber == null) {
- info.formattedNumber = formatPhoneNumber(info.number);
+ info.formattedNumber =
+ formatPhoneNumber(info.number, info.normalizedNumber, countryIso);
}
formattedNumber = info.formattedNumber;
}
@@ -667,7 +656,7 @@
label = callerNumberLabel;
// Format the cached call_log phone number
- formattedNumber = formatPhoneNumber(number);
+ formattedNumber = formatPhoneNumber(number, null, countryIso);
}
// Set the text lines and call icon.
// Assumes the call back feature is on most of the
@@ -731,7 +720,7 @@
number = getString(R.string.voicemail);
} else {
// Just a raw number, and no cache, so format it nicely
- number = formatPhoneNumber(number);
+ number = formatPhoneNumber(number, null, countryIso);
}
views.line1View.setText(number);
@@ -850,8 +839,7 @@
.getVoiceMailNumber();
mQueryHandler = new QueryHandler(this);
- // Reset locale-based formatting cache
- sFormattingType = FORMATTING_TYPE_INVALID;
+ mCurrentCountryIso = ContactsUtils.getCurrentCountryIso(this);
}
@Override
@@ -914,36 +902,28 @@
}
/**
- * Format the given phone number using
- * {@link PhoneNumberUtils#formatNumber(android.text.Editable, int)}. This
- * helper method uses {@link #sEditable} and {@link #sFormattingType} to
- * prevent allocations between multiple calls.
- * <p>
- * Because of the shared {@link #sEditable} builder, <b>this method is not
- * thread safe</b>, and should only be called from the GUI thread.
- * <p>
- * If the given String object is null or empty, return an empty String.
+ * Format the given phone number
+ *
+ * @param number the number to be formatted.
+ * @param normalizedNumber the normalized number of the given number.
+ * @param countryIso the ISO 3166-1 two letters country code, the country's
+ * convention will be used to format the number if the normalized
+ * phone is null.
+ *
+ * @return the formatted number, or the given number if it was formatted.
*/
- private String formatPhoneNumber(String number) {
+ private String formatPhoneNumber(String number, String normalizedNumber, String countryIso) {
if (TextUtils.isEmpty(number)) {
return "";
}
-
// If "number" is really a SIP address, don't try to do any formatting at all.
if (PhoneNumberUtils.isUriNumber(number)) {
return number;
}
-
- // Cache formatting type if not already present
- if (sFormattingType == FORMATTING_TYPE_INVALID) {
- sFormattingType = PhoneNumberUtils.getFormatTypeForLocale(Locale.getDefault());
+ if (TextUtils.isEmpty(countryIso)) {
+ countryIso = mCurrentCountryIso;
}
-
- sEditable.clear();
- sEditable.append(number);
-
- PhoneNumberUtils.formatNumber(sEditable, sFormattingType);
- return sEditable.toString();
+ return PhoneNumberUtils.formatNumber(number, normalizedNumber, countryIso);
}
private void resetNewCallsFlag() {
@@ -1017,16 +997,14 @@
if (numberUri != null) {
Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, numberUri);
- menu.add(0, CONTEXT_MENU_CALL_CONTACT, 0,
- getResources().getString(R.string.recentCalls_callNumber, number))
+ menu.add(0, 0, 0, getResources().getString(R.string.recentCalls_callNumber, number))
.setIntent(intent);
}
if (contactInfoPresent) {
- Intent intent = new Intent(Intent.ACTION_VIEW,
- ContentUris.withAppendedId(Contacts.CONTENT_URI, info.personId));
- StickyTabs.setTab(intent, getIntent());
- menu.add(0, 0, 0, R.string.menu_viewContact).setIntent(intent);
+ menu.add(0, 0, 0, R.string.menu_viewContact)
+ .setIntent(new Intent(Intent.ACTION_VIEW,
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, info.personId)));
}
if (numberUri != null && !isVoicemail && !isSipNumber) {
@@ -1055,7 +1033,7 @@
menu.add(0, 0, 0, R.string.recentCalls_addToContact)
.setIntent(intent);
}
- menu.add(0, CONTEXT_MENU_ITEM_DELETE, 0, R.string.recentCalls_removeFromRecentList);
+ menu.add(0, MENU_ITEM_DELETE, 0, R.string.recentCalls_removeFromRecentList);
}
@Override
@@ -1064,7 +1042,7 @@
case DIALOG_CONFIRM_DELETE_ALL:
return new AlertDialog.Builder(this)
.setTitle(R.string.clearCallLogConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
.setMessage(R.string.clearCallLogConfirmation)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(android.R.string.ok, new OnClickListener() {
@@ -1089,23 +1067,30 @@
showDialog(DIALOG_CONFIRM_DELETE_ALL);
return true;
}
+
+ case MENU_ITEM_VIEW_CONTACTS: {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Contacts.CONTENT_URI);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ return true;
+ }
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case CONTEXT_MENU_ITEM_DELETE: {
- // 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;
- }
+ // 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 = (Cursor)mAdapter.getItem(menuInfo.position);
int groupSize = 1;
if (mAdapter.isGroupHeader(menuInfo.position)) {
@@ -1124,17 +1109,9 @@
getContentResolver().delete(Calls.CONTENT_URI, Calls._ID + " IN (" + sb + ")",
null);
- return true;
- }
- case CONTEXT_MENU_CALL_CONTACT: {
- StickyTabs.saveTab(this, getIntent());
- startActivity(item.getIntent());
- return true;
- }
- default: {
- return super.onContextItemSelected(item);
}
}
+ return super.onContextItemSelected(item);
}
@Override
@@ -1253,7 +1230,6 @@
intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Uri.fromParts("tel", number, null));
}
- StickyTabs.saveTab(this, getIntent());
intent.setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
@@ -1267,7 +1243,6 @@
} else {
Intent intent = new Intent(this, CallDetailActivity.class);
intent.setData(ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI, id));
- StickyTabs.setTab(intent, getIntent());
startActivity(intent);
}
}
diff --git a/src/com/android/contacts/ScrollingTabWidget.java b/src/com/android/contacts/ScrollingTabWidget.java
deleted file mode 100644
index b45abe4..0000000
--- a/src/com/android/contacts/ScrollingTabWidget.java
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * 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.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.View.OnClickListener;
-import android.view.View.OnFocusChangeListener;
-import android.widget.HorizontalScrollView;
-import android.widget.ImageView;
-import android.widget.RelativeLayout;
-
-/*
- * Tab widget that can contain more tabs than can fit on screen at once and scroll over them.
- */
-public class ScrollingTabWidget extends RelativeLayout
- implements OnClickListener, ViewTreeObserver.OnGlobalFocusChangeListener,
- OnFocusChangeListener {
-
- private static final String TAG = "ScrollingTabWidget";
-
- private OnTabSelectionChangedListener mSelectionChangedListener;
- private int mSelectedTab = 0;
- private ImageView mLeftArrowView;
- private ImageView mRightArrowView;
- private HorizontalScrollView mTabsScrollWrapper;
- private TabStripView mTabsView;
- private LayoutInflater mInflater;
-
- // Keeps track of the left most visible tab.
- private int mLeftMostVisibleTabIndex = 0;
-
- public ScrollingTabWidget(Context context) {
- this(context, null);
- }
-
- public ScrollingTabWidget(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public ScrollingTabWidget(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs);
-
- mInflater = (LayoutInflater) mContext.getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
-
- setFocusable(true);
- setOnFocusChangeListener(this);
- if (!hasFocus()) {
- setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
- }
-
- mLeftArrowView = (ImageView) mInflater.inflate(R.layout.tab_left_arrow, this, false);
- mLeftArrowView.setOnClickListener(this);
- mRightArrowView = (ImageView) mInflater.inflate(R.layout.tab_right_arrow, this, false);
- mRightArrowView.setOnClickListener(this);
- mTabsScrollWrapper = (HorizontalScrollView) mInflater.inflate(
- R.layout.tab_layout, this, false);
- mTabsView = (TabStripView) mTabsScrollWrapper.findViewById(android.R.id.tabs);
- View accountNameView = mInflater.inflate(R.layout.tab_account_name, this, false);
-
- mLeftArrowView.setVisibility(View.INVISIBLE);
- mRightArrowView.setVisibility(View.INVISIBLE);
-
- addView(mTabsScrollWrapper);
- addView(mLeftArrowView);
- addView(mRightArrowView);
- addView(accountNameView);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- final ViewTreeObserver treeObserver = getViewTreeObserver();
- if (treeObserver != null) {
- treeObserver.addOnGlobalFocusChangeListener(this);
- }
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- final ViewTreeObserver treeObserver = getViewTreeObserver();
- if (treeObserver != null) {
- treeObserver.removeOnGlobalFocusChangeListener(this);
- }
- }
-
- protected void updateArrowVisibility() {
- int scrollViewLeftEdge = mTabsScrollWrapper.getScrollX();
- int tabsViewLeftEdge = mTabsView.getLeft();
- int scrollViewRightEdge = scrollViewLeftEdge + mTabsScrollWrapper.getWidth();
- int tabsViewRightEdge = mTabsView.getRight();
-
- int rightArrowCurrentVisibility = mRightArrowView.getVisibility();
- if (scrollViewRightEdge == tabsViewRightEdge
- && rightArrowCurrentVisibility == View.VISIBLE) {
- mRightArrowView.setVisibility(View.INVISIBLE);
- } else if (scrollViewRightEdge < tabsViewRightEdge
- && rightArrowCurrentVisibility != View.VISIBLE) {
- mRightArrowView.setVisibility(View.VISIBLE);
- }
-
- int leftArrowCurrentVisibility = mLeftArrowView.getVisibility();
- if (scrollViewLeftEdge == tabsViewLeftEdge
- && leftArrowCurrentVisibility == View.VISIBLE) {
- mLeftArrowView.setVisibility(View.INVISIBLE);
- } else if (scrollViewLeftEdge > tabsViewLeftEdge
- && leftArrowCurrentVisibility != View.VISIBLE) {
- mLeftArrowView.setVisibility(View.VISIBLE);
- }
- }
-
- /**
- * Returns the tab indicator view at the given index.
- *
- * @param index the zero-based index of the tab indicator view to return
- * @return the tab indicator view at the given index
- */
- public View getChildTabViewAt(int index) {
- return mTabsView.getChildAt(index);
- }
-
- /**
- * Returns the number of tab indicator views.
- *
- * @return the number of tab indicator views.
- */
- public int getTabCount() {
- return mTabsView.getChildCount();
- }
-
- /**
- * Returns the {@link ViewGroup} that actually contains the tabs. This is where the tab
- * views should be attached to when being inflated.
- */
- public ViewGroup getTabParent() {
- return mTabsView;
- }
-
- public void removeAllTabs() {
- mTabsView.removeAllViews();
- }
-
- @Override
- public void dispatchDraw(Canvas canvas) {
- updateArrowVisibility();
- super.dispatchDraw(canvas);
- }
-
- /**
- * Sets the current tab.
- * This method is used to bring a tab to the front of the Widget,
- * and is used to post to the rest of the UI that a different tab
- * has been brought to the foreground.
- *
- * Note, this is separate from the traditional "focus" that is
- * employed from the view logic.
- *
- * For instance, if we have a list in a tabbed view, a user may be
- * navigating up and down the list, moving the UI focus (orange
- * highlighting) through the list items. The cursor movement does
- * not effect the "selected" tab though, because what is being
- * scrolled through is all on the same tab. The selected tab only
- * changes when we navigate between tabs (moving from the list view
- * to the next tabbed view, in this example).
- *
- * To move both the focus AND the selected tab at once, please use
- * {@link #focusCurrentTab}. Normally, the view logic takes care of
- * adjusting the focus, so unless you're circumventing the UI,
- * you'll probably just focus your interest here.
- *
- * @param index The tab that you want to indicate as the selected
- * tab (tab brought to the front of the widget)
- *
- * @see #focusCurrentTab
- */
- public void setCurrentTab(int index) {
- if (index < 0 || index >= getTabCount()) {
- return;
- }
-
- if (mSelectedTab < getTabCount()) {
- mTabsView.setSelected(mSelectedTab, false);
- }
- mSelectedTab = index;
- mTabsView.setSelected(mSelectedTab, true);
- }
-
- /**
- * Return index of the currently selected tab.
- */
- public int getCurrentTab() {
- return mSelectedTab;
- }
-
- /**
- * Sets the current tab and focuses the UI on it.
- * This method makes sure that the focused tab matches the selected
- * tab, normally at {@link #setCurrentTab}. Normally this would not
- * be an issue if we go through the UI, since the UI is responsible
- * for calling TabWidget.onFocusChanged(), but in the case where we
- * are selecting the tab programmatically, we'll need to make sure
- * focus keeps up.
- *
- * @param index The tab that you want focused (highlighted in orange)
- * and selected (tab brought to the front of the widget)
- *
- * @see #setCurrentTab
- */
- public void focusCurrentTab(int index) {
- if (index < 0 || index >= getTabCount()) {
- return;
- }
-
- setCurrentTab(index);
- getChildTabViewAt(index).requestFocus();
-
- }
-
- /**
- * Adds a tab to the list of tabs. The tab's indicator view is specified
- * by a layout id. InflateException will be thrown if there is a problem
- * inflating.
- *
- * @param layoutResId The layout id to be inflated to make the tab indicator.
- */
- public void addTab(int layoutResId) {
- addTab(mInflater.inflate(layoutResId, mTabsView, false));
- }
-
- /**
- * Adds a tab to the list of tabs. The tab's indicator view must be provided.
- *
- * @param child
- */
- public void addTab(View child) {
- if (child == null) {
- return;
- }
-
- if (child.getLayoutParams() == null) {
- final LayoutParams lp = new LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- lp.setMargins(0, 0, 0, 0);
- child.setLayoutParams(lp);
- }
-
- // Ensure you can navigate to the tab with the keyboard, and you can touch it
- child.setFocusable(true);
- child.setClickable(true);
- child.setOnClickListener(new TabClickListener());
- child.setOnFocusChangeListener(this);
-
- mTabsView.addView(child);
- }
-
- /**
- * Provides a way for ViewContactActivity and EditContactActivity to be notified that the
- * user clicked on a tab indicator.
- */
- public void setTabSelectionListener(OnTabSelectionChangedListener listener) {
- mSelectionChangedListener = listener;
- }
-
- public void onGlobalFocusChanged(View oldFocus, View newFocus) {
- if (isTab(oldFocus) && !isTab(newFocus)) {
- onLoseFocus();
- }
- }
-
- public void onFocusChange(View v, boolean hasFocus) {
- if (v == this && hasFocus) {
- onObtainFocus();
- return;
- }
-
- if (hasFocus) {
- for (int i = 0; i < getTabCount(); i++) {
- if (getChildTabViewAt(i) == v) {
- setCurrentTab(i);
- mSelectionChangedListener.onTabSelectionChanged(i, false);
- break;
- }
- }
- }
- }
-
- /**
- * Called when the {@link ScrollingTabWidget} gets focus. Here the
- * widget decides which of it's tabs should have focus.
- */
- protected void onObtainFocus() {
- // Setting this flag, allows the children of this View to obtain focus.
- setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
- // Assign focus to the last selected tab.
- focusCurrentTab(mSelectedTab);
- mSelectionChangedListener.onTabSelectionChanged(mSelectedTab, false);
- }
-
- /**
- * Called when the focus has left the {@link ScrollingTabWidget} or its
- * descendants. At this time we want the children of this view to be marked
- * as un-focusable, so that next time focus is moved to the widget, the widget
- * gets control, and can assign focus where it wants.
- */
- protected void onLoseFocus() {
- // Setting this flag will effectively make the tabs unfocusable. This will
- // be toggled when the widget obtains focus again.
- setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
- }
-
- public boolean isTab(View v) {
- for (int i = 0; i < getTabCount(); i++) {
- if (getChildTabViewAt(i) == v) {
- return true;
- }
- }
- return false;
- }
-
- private class TabClickListener implements OnClickListener {
- public void onClick(View v) {
- for (int i = 0; i < getTabCount(); i++) {
- if (getChildTabViewAt(i) == v) {
- setCurrentTab(i);
- mSelectionChangedListener.onTabSelectionChanged(i, true);
- break;
- }
- }
- }
- }
-
- public interface OnTabSelectionChangedListener {
- /**
- * Informs the tab widget host which tab was selected. It also indicates
- * if the tab was clicked/pressed or just focused into.
- *
- * @param tabIndex index of the tab that was selected
- * @param clicked whether the selection changed due to a touch/click
- * or due to focus entering the tab through navigation. Pass true
- * if it was due to a press/click and false otherwise.
- */
- void onTabSelectionChanged(int tabIndex, boolean clicked);
- }
-
- public void onClick(View v) {
- updateLeftMostVisible();
- if (v == mRightArrowView && (mLeftMostVisibleTabIndex + 1 < getTabCount())) {
- tabScroll(true /* right */);
- } else if (v == mLeftArrowView && mLeftMostVisibleTabIndex > 0) {
- tabScroll(false /* left */);
- }
- }
-
- /*
- * Updates our record of the left most visible tab. We keep track of this explicitly
- * on arrow clicks, but need to re-calibrate after focus navigation.
- */
- protected void updateLeftMostVisible() {
- int viewableLeftEdge = mTabsScrollWrapper.getScrollX();
-
- if (mLeftArrowView.getVisibility() == View.VISIBLE) {
- viewableLeftEdge += mLeftArrowView.getWidth();
- }
-
- for (int i = 0; i < getTabCount(); i++) {
- View tab = getChildTabViewAt(i);
- int tabLeftEdge = tab.getLeft();
- if (tabLeftEdge >= viewableLeftEdge) {
- mLeftMostVisibleTabIndex = i;
- break;
- }
- }
- }
-
- /**
- * Scrolls the tabs by exactly one tab width.
- *
- * @param directionRight if true, scroll to the right, if false, scroll to the left.
- */
- protected void tabScroll(boolean directionRight) {
- int scrollWidth = 0;
- View newLeftMostVisibleTab = null;
- if (directionRight) {
- newLeftMostVisibleTab = getChildTabViewAt(++mLeftMostVisibleTabIndex);
- } else {
- newLeftMostVisibleTab = getChildTabViewAt(--mLeftMostVisibleTabIndex);
- }
-
- scrollWidth = newLeftMostVisibleTab.getLeft() - mTabsScrollWrapper.getScrollX();
- if (mLeftMostVisibleTabIndex > 0) {
- scrollWidth -= mLeftArrowView.getWidth();
- }
- mTabsScrollWrapper.smoothScrollBy(scrollWidth, 0);
- }
-
-}
diff --git a/src/com/android/contacts/SearchEditText.java b/src/com/android/contacts/SearchEditText.java
deleted file mode 100644
index 7683f23..0000000
--- a/src/com/android/contacts/SearchEditText.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2010 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.graphics.drawable.Drawable;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.widget.EditText;
-
-/**
- * A custom text editor that helps automatically dismiss the activity along with the soft
- * keyboard.
- */
-public class SearchEditText extends EditText {
-
- private boolean mMagnifyingGlassShown = true;
- private Drawable mMagnifyingGlass;
-
- public SearchEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- mMagnifyingGlass = getCompoundDrawables()[2];
- }
-
- /**
- * Conditionally shows a magnifying glass icon on the right side of the text field
- * when the text it empty.
- */
- @Override
- public boolean onPreDraw() {
- boolean emptyText = TextUtils.isEmpty(getText());
- if (mMagnifyingGlassShown != emptyText) {
- mMagnifyingGlassShown = emptyText;
- if (mMagnifyingGlassShown) {
- setCompoundDrawables(null, null, mMagnifyingGlass, null);
- } else {
- setCompoundDrawables(null, null, null, null);
- }
- return false;
- }
- return super.onPreDraw();
- }
-
- /**
- * Forwards the onKeyPreIme call to the view's activity.
- */
- @Override
- public boolean onKeyPreIme(int keyCode, KeyEvent event) {
- if (((ContactsListActivity)getContext()).onKeyPreIme(keyCode, event)) {
- return true;
- }
- return super.onKeyPreIme(keyCode, event);
- }
-}
diff --git a/src/com/android/contacts/SearchResultsActivity.java b/src/com/android/contacts/SearchResultsActivity.java
deleted file mode 100644
index 09f0014..0000000
--- a/src/com/android/contacts/SearchResultsActivity.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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;
-
-/**
- * The activity that displays the list of contact search results. We need a separate
- * class because it uses a different theme from {@link ContactsListActivity}.
- */
-public class SearchResultsActivity extends ContactsListActivity {
-}
diff --git a/src/com/android/contacts/SpecialCharSequenceMgr.java b/src/com/android/contacts/SpecialCharSequenceMgr.java
index 140e7d4..a047a68 100644
--- a/src/com/android/contacts/SpecialCharSequenceMgr.java
+++ b/src/com/android/contacts/SpecialCharSequenceMgr.java
@@ -184,7 +184,7 @@
static boolean handleIMEIDisplay(Context context, String input, boolean useSystemWindow) {
if (input.equals(MMI_IMEI_DISPLAY)) {
int phoneType = ((TelephonyManager)context.getSystemService(
- Context.TELEPHONY_SERVICE)).getPhoneType();
+ Context.TELEPHONY_SERVICE)).getCurrentPhoneType();
if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
showIMEIPanel(context, useSystemWindow);
diff --git a/src/com/android/contacts/SplitAggregateView.java b/src/com/android/contacts/SplitAggregateView.java
index b85a4ab..d99ba85 100644
--- a/src/com/android/contacts/SplitAggregateView.java
+++ b/src/com/android/contacts/SplitAggregateView.java
@@ -16,8 +16,8 @@
package com.android.contacts;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.Sources;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -73,7 +73,7 @@
private final Uri mAggregateUri;
private OnContactSelectedListener mListener;
- private Sources mSources;
+ private AccountTypeManager mAccountTypes;
/**
* Listener interface that gets the contact ID of the user-selected contact.
@@ -90,7 +90,7 @@
mAggregateUri = aggregateUri;
- mSources = Sources.getInstance(context);
+ mAccountTypes = AccountTypeManager.getInstance(context);
final List<RawContactInfo> list = loadData();
@@ -247,10 +247,9 @@
cache.additionalData.setText(info.getAdditionalData());
Drawable icon = null;
- ContactsSource source = mSources.getInflatedSource(info.accountType,
- ContactsSource.LEVEL_SUMMARY);
- if (source != null) {
- icon = source.getDisplayIcon(getContext());
+ AccountType accountType = mAccountTypes.getAccountType(info.accountType);
+ if (accountType != null) {
+ icon = accountType.getDisplayIcon(getContext());
}
if (icon != null) {
cache.sourceIcon.setImageDrawable(icon);
diff --git a/src/com/android/contacts/StickyTabs.java b/src/com/android/contacts/StickyTabs.java
deleted file mode 100644
index 4ecc970..0000000
--- a/src/com/android/contacts/StickyTabs.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.provider.ContactsContract.QuickContact;
-import android.util.Log;
-
-/**
- * Utility class to annotate Intents with extra data required for the Sticky-Tab behavior, which
- * allows storing the app to go to the last tab that was used to make a call. Also handles saving
- * and restoring the tab index
- */
-public final class StickyTabs {
- private static final String TAG = "StickyTabs";
- private static final boolean LOGV = false;
-
- private static final String EXTRA_TAB_INDEX =
- QuickContact.EXTRA_SELECTED_CONTACTS_APP_TAB_INDEX;
-
- /**
- * Name of the shared setting. We are using the same name as in FroYo to prevent
- * having an orphan here
- */
- public static final String PREFERENCES_NAME = "dialtacts";
-
- /**
- * Name of the shared setting. We are using the same name as in FroYo to prevent
- * having an orphan there
- */
- private static final String PREF_LAST_PHONECALL_TAB = "last_manually_selected_tab";
-
- /**
- * Writes the selected tab to the passed intent
- * @param intent The intent to modify.
- * @param tabIndex The tab index to write to the intent
- * @return Returns the modified intent. Notice that this is not a new instance (the passed-in
- * intent is modified)
- */
- public static Intent setTab(Intent intent, int tabIndex) {
- if (LOGV) Log.v(TAG, "*********** Setting tab index of intent to " + tabIndex);
-
- if (tabIndex == -1) {
- intent.removeExtra(EXTRA_TAB_INDEX);
- } else {
- intent.putExtra(EXTRA_TAB_INDEX, tabIndex);
- }
- return intent;
- }
-
- /**
- * Writes the selected tab to the passed intent by retrieving it from the originalIntent that
- * was passed in
- * @param intent The intent to modify.
- * @param originalIntent The intent where the tab index should be read from
- * @return Returns the modified intent. Notice that this is not a new instance (the passed-in
- * intent is modified)
- */
- public static Intent setTab(Intent intent, Intent originalIntent) {
- return setTab(intent, getTab(originalIntent));
- }
-
- /**
- * Returns the selected tab or -1 if no tab is stored
- */
- public static int getTab(Intent intent) {
- if (intent.getExtras() == null) return -1;
- return intent.getExtras().getInt(EXTRA_TAB_INDEX, -1);
- }
-
- /**
- * Persists the given tabIndex. If the value is -1, the previously persisted value is not
- * overriden
- */
- public static void saveTab(Context context, int tabIndex) {
- if (LOGV) Log.v(TAG, "*********** Persisting tab index " + tabIndex);
- if (tabIndex == -1) return;
-
- final SharedPreferences.Editor editor =
- context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE).edit();
- editor.putInt(PREF_LAST_PHONECALL_TAB, tabIndex);
- editor.apply();
- }
-
- /**
- * Persists the tab as it is stored in the Intent. If the intent does not have a tab index,
- * the persisted value is not overriden
- */
- public static void saveTab(Context context, Intent intent) {
- saveTab(context, getTab(intent));
- }
-
- /**
- * Returns the previously persisted tab or defaultValue if nothing is saved
- */
- public static int loadTab(Context context, int defaultValue) {
- final SharedPreferences prefs = context.getSharedPreferences(PREFERENCES_NAME,
- Context.MODE_PRIVATE);
- final int result = prefs.getInt(PREF_LAST_PHONECALL_TAB, defaultValue);
- if (LOGV) Log.v(TAG, "*********** Loaded tab index: " + result);
- return result;
- }
-}
diff --git a/src/com/android/contacts/TextHighlightingAnimation.java b/src/com/android/contacts/TextHighlightingAnimation.java
deleted file mode 100644
index e35ae1e..0000000
--- a/src/com/android/contacts/TextHighlightingAnimation.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (C) 2010 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.R;
-
-import android.database.CharArrayBuffer;
-import android.graphics.Color;
-import android.os.Handler;
-import android.text.Spanned;
-import android.text.TextPaint;
-import android.text.style.CharacterStyle;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-
-/**
- * An animation that alternately dims and brightens the non-highlighted portion of text.
- */
-public abstract class TextHighlightingAnimation implements Runnable {
-
- private static final int MAX_ALPHA = 255;
- private static final int MIN_ALPHA = 50;
-
- private AccelerateInterpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
- private DecelerateInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
-
- private final static DimmingSpan[] sEmptySpans = new DimmingSpan[0];
-
- /**
- * Frame rate expressed a number of millis between frames.
- */
- private static final long FRAME_RATE = 50;
-
- private DimmingSpan mDimmingSpan;
- private Handler mHandler;
- private boolean mAnimating;
- private boolean mDimming;
- private long mTargetTime;
- private final int mDuration;
-
- /**
- * A Spanned that highlights a part of text by dimming another part of that text.
- */
- public class TextWithHighlighting implements Spanned {
-
- private final DimmingSpan[] mSpans;
- private boolean mDimmingEnabled;
- private CharArrayBuffer mText;
- private int mDimmingSpanStart;
- private int mDimmingSpanEnd;
- private String mString;
-
- public TextWithHighlighting() {
- mSpans = new DimmingSpan[] { mDimmingSpan };
- }
-
- public void setText(CharArrayBuffer baseText, CharArrayBuffer highlightedText) {
- mText = baseText;
-
- // TODO figure out a way to avoid string allocation
- mString = new String(mText.data, 0, mText.sizeCopied);
-
- int index = indexOf(baseText, highlightedText);
-
- if (index == 0 || index == -1) {
- mDimmingEnabled = false;
- } else {
- mDimmingEnabled = true;
- mDimmingSpanStart = 0;
- mDimmingSpanEnd = index;
- }
- }
-
- /**
- * An implementation of indexOf on CharArrayBuffers that finds the first match of
- * the start of buffer2 in buffer1. For example, indexOf("abcd", "cdef") == 2
- */
- private int indexOf(CharArrayBuffer buffer1, CharArrayBuffer buffer2) {
- char[] string1 = buffer1.data;
- char[] string2 = buffer2.data;
- int count1 = buffer1.sizeCopied;
- int count2 = buffer2.sizeCopied;
-
- // Ignore matching tails of the two buffers
- while (count1 > 0 && count2 > 0 && string1[count1 - 1] == string2[count2 - 1]) {
- count1--;
- count2--;
- }
-
- int size = count2;
- for (int i = 0; i < count1; i++) {
- if (i + size > count1) {
- size = count1 - i;
- }
- int j;
- for (j = 0; j < size; j++) {
- if (string1[i+j] != string2[j]) {
- break;
- }
- }
- if (j == size) {
- return i;
- }
- }
-
- return -1;
- }
-
-
- @SuppressWarnings("unchecked")
- public <T> T[] getSpans(int start, int end, Class<T> type) {
- if (mDimmingEnabled) {
- return (T[])mSpans;
- } else {
- return (T[])sEmptySpans;
- }
- }
-
- public int getSpanStart(Object tag) {
- // We only have one span - no need to check the tag parameter
- return mDimmingSpanStart;
- }
-
- public int getSpanEnd(Object tag) {
- // We only have one span - no need to check the tag parameter
- return mDimmingSpanEnd;
- }
-
- public int getSpanFlags(Object tag) {
- // String is immutable - flags not needed
- return 0;
- }
-
- public int nextSpanTransition(int start, int limit, Class type) {
- // Never called since we only have one span
- return 0;
- }
-
- public char charAt(int index) {
- return mText.data[index];
- }
-
- public int length() {
- return mText.sizeCopied;
- }
-
- public CharSequence subSequence(int start, int end) {
- // Never called - implementing for completeness
- return new String(mText.data, start, end);
- }
-
- @Override
- public String toString() {
- return mString;
- }
- }
-
- /**
- * A Span that modifies alpha of the default foreground color.
- */
- private static class DimmingSpan extends CharacterStyle {
- private int mAlpha;
-
- public void setAlpha(int alpha) {
- mAlpha = alpha;
- }
-
- @Override
- public void updateDrawState(TextPaint ds) {
-
- // Only dim the text in the basic state; not selected, focused or pressed
- int[] states = ds.drawableState;
- if (states != null) {
- int count = states.length;
- for (int i = 0; i < count; i++) {
- switch (states[i]) {
- case R.attr.state_pressed:
- case R.attr.state_selected:
- case R.attr.state_focused:
- // We can simply return, because the supplied text
- // paint is already configured with defaults.
- return;
- }
- }
- }
-
- int color = ds.getColor();
- color = Color.argb(mAlpha, Color.red(color), Color.green(color), Color.blue(color));
- ds.setColor(color);
- }
- }
-
- /**
- * Constructor.
- */
- public TextHighlightingAnimation(int duration) {
- mDuration = duration;
- mHandler = new Handler();
- mDimmingSpan = new DimmingSpan();
- mDimmingSpan.setAlpha(MAX_ALPHA);
- }
-
- /**
- * Returns a Spanned that can be used by a text view to show text with highlighting.
- */
- public TextWithHighlighting createTextWithHighlighting() {
- return new TextWithHighlighting();
- }
-
- /**
- * Override and invalidate (redraw) TextViews showing {@link TextWithHighlighting}.
- */
- protected abstract void invalidate();
-
- /**
- * Starts the highlighting animation, which will dim portions of text.
- */
- public void startHighlighting() {
- startAnimation(true);
- }
-
- /**
- * Starts un-highlighting animation, which will brighten the dimmed portions of text
- * to the brightness level of the rest of text.
- */
- public void stopHighlighting() {
- startAnimation(false);
- }
-
- /**
- * Called when the animation starts.
- */
- protected void onAnimationStarted() {
- }
-
- /**
- * Called when the animation has stopped.
- */
- protected void onAnimationEnded() {
- }
-
- private void startAnimation(boolean dim) {
- if (mDimming != dim) {
- mDimming = dim;
- long now = System.currentTimeMillis();
- if (!mAnimating) {
- mAnimating = true;
- mTargetTime = now + mDuration;
- onAnimationStarted();
- mHandler.post(this);
- } else {
-
- // If we have started dimming, reverse the direction and adjust the target
- // time accordingly.
- mTargetTime = (now + mDuration) - (mTargetTime - now);
- }
- }
- }
-
- /**
- * Animation step.
- */
- public void run() {
- long now = System.currentTimeMillis();
- long timeLeft = mTargetTime - now;
- if (timeLeft < 0) {
- mDimmingSpan.setAlpha(mDimming ? MIN_ALPHA : MAX_ALPHA);
- mAnimating = false;
- onAnimationEnded();
- return;
- }
-
- // Start=1, end=0
- float virtualTime = (float)timeLeft / mDuration;
- if (mDimming) {
- float interpolatedTime = DECELERATE_INTERPOLATOR.getInterpolation(virtualTime);
- mDimmingSpan.setAlpha((int)(MIN_ALPHA + (MAX_ALPHA-MIN_ALPHA) * interpolatedTime));
- } else {
- float interpolatedTime = ACCELERATE_INTERPOLATOR.getInterpolation(virtualTime);
- mDimmingSpan.setAlpha((int)(MIN_ALPHA + (MAX_ALPHA-MIN_ALPHA) * (1-interpolatedTime)));
- }
-
- invalidate();
-
- // Repeat
- mHandler.postDelayed(this, FRAME_RATE);
- }
-}
diff --git a/src/com/android/contacts/TwelveKeyDialer.java b/src/com/android/contacts/TwelveKeyDialer.java
index 5219d99..b93375c 100644
--- a/src/com/android/contacts/TwelveKeyDialer.java
+++ b/src/com/android/contacts/TwelveKeyDialer.java
@@ -137,6 +137,8 @@
/** Indicates if we are opening this dialer to add a call from the InCallScreen. */
private boolean mIsAddCallMode;
+ private String mCurrentCountryIso;
+
PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
/**
* Listen for phone state changes so that we can take down the
@@ -190,6 +192,7 @@
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
+ mCurrentCountryIso = ContactsUtils.getCurrentCountryIso(this);
Resources r = getResources();
// Do not show title in the case the device is in carmode.
if ((r.getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK) ==
@@ -268,7 +271,7 @@
}
protected void maybeAddNumberFormatting() {
- mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
+ mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher(mCurrentCountryIso));
}
/**
@@ -308,18 +311,19 @@
if ("tel".equals(uri.getScheme())) {
// Put the requested number into the input area
String data = uri.getSchemeSpecificPart();
- setFormattedDigits(data);
+ setFormattedDigits(data, null);
} 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);
+ new String[] {PhonesColumns.NUMBER, PhonesColumns.NUMBER_KEY},
+ null, null, null);
if (c != null) {
if (c.moveToFirst()) {
// Put the number into the input area
- setFormattedDigits(c.getString(0));
+ setFormattedDigits(c.getString(0), c.getString(1));
}
c.close();
}
@@ -359,10 +363,11 @@
return ignoreState;
}
- protected void setFormattedDigits(String data) {
+ protected void setFormattedDigits(String data, String normalizedNumber) {
// strip the non-dialable numbers out of the data string.
String dialString = PhoneNumberUtils.extractNetworkPortion(data);
- dialString = PhoneNumberUtils.formatNumber(dialString);
+ dialString =
+ PhoneNumberUtils.formatNumber(dialString, normalizedNumber, mCurrentCountryIso);
if (!TextUtils.isEmpty(dialString)) {
Editable digits = mDigits.getText();
digits.replace(0, digits.length(), dialString);
@@ -617,25 +622,6 @@
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_CALL: {
- // TODO: In dialButtonPressed we do some of these
- // tests again. We should try to consolidate them in
- // one place.
- if (!phoneIsCdma() && mIsAddCallMode && isDigitsEmpty()) {
- // For CDMA phones, we always call
- // dialButtonPressed() because we may need to send
- // an empty flash command to the network.
- // Otherwise, 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();
- }
-
- // If we're CDMA, regardless of where we are adding a call from (either
- // InCallScreen or Dialtacts), the user may need to send an empty
- // flash command to the network. So let's call dialButtonPressed() regardless
- // and dialButtonPressed will handle this functionality for us.
- // otherwise, we place the call.
dialButtonPressed();
return true;
}
@@ -774,54 +760,55 @@
}
void callVoicemail() {
- StickyTabs.saveTab(this, getIntent());
- Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
- Uri.fromParts("voicemail", EMPTY_NUMBER, null));
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- mDigits.getText().clear();
+ startActivity(newVoicemailIntent());
+ mDigits.getText().clear(); // TODO: Fix bug 1745781
finish();
}
+ /**
+ * In most cases, when the dial button is pressed, there is a
+ * number in digits area. Pack it in the intent, start the
+ * outgoing call broadcast as a separate task and finish this
+ * activity.
+ *
+ * When there is no digit and the phone is CDMA and off hook,
+ * we're sending a blank flash for CDMA. CDMA networks use Flash
+ * messages when special processing needs to be done, mainly for
+ * 3-way or call waiting scenarios. Presumably, here we're in a
+ * special 3-way scenario where the network needs a blank flash
+ * before being able to add the new participant. (This is not the
+ * case with all 3-way calls, just certain CDMA infrastructures.)
+ *
+ * Otherwise, there is no digit, display the last dialed
+ * number. Don't finish since the user may want to edit it. The
+ * user needs to press the dial button again, to dial it (general
+ * case described above).
+ */
void dialButtonPressed() {
- final String number = mDigits.getText().toString();
- boolean sendEmptyFlash = false;
- Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED);
-
- if (isDigitsEmpty()) { // There is no number entered.
+ if (isDigitsEmpty()) { // No number entered.
if (phoneIsCdma() && phoneIsOffhook()) {
- // On CDMA phones, if we're already on a call, pressing
- // the Dial button without entering any digits means "send
- // an empty flash."
- intent.setData(Uri.fromParts("tel", EMPTY_NUMBER, null));
- intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
- sendEmptyFlash = true;
- } else if (!TextUtils.isEmpty(mLastNumberDialed)) {
- // Otherwise, pressing the Dial button without entering
- // any digits means "recall the last number dialed".
- mDigits.setText(mLastNumberDialed);
- return;
+ // This is really CDMA specific. On GSM is it possible
+ // to be off hook and wanted to add a 3rd party using
+ // the redial feature.
+ startActivity(newFlashIntent());
} else {
- // Rare case: there's no "last number dialed". There's
- // nothing useful for the Dial button to do in this case.
- playTone(ToneGenerator.TONE_PROP_NACK);
- return;
+ if (!TextUtils.isEmpty(mLastNumberDialed)) {
+ mDigits.setText(mLastNumberDialed);
+ } else {
+ // There's no "last number dialed" or the
+ // background query is still running. There's
+ // nothing useful for the Dial button to do in
+ // this case. Note: with a soft dial button, this
+ // can never happens since the dial button is
+ // disabled under these conditons.
+ playTone(ToneGenerator.TONE_PROP_NACK);
+ }
}
- } else { // There is a number.
- intent.setData(Uri.fromParts("tel", number, null));
- }
+ } else {
+ final String number = mDigits.getText().toString();
- StickyTabs.saveTab(this, getIntent());
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- mDigits.getText().clear();
-
- // Don't finish TwelveKeyDialer yet if we're sending a blank flash for CDMA. CDMA
- // networks use Flash messages when special processing needs to be done, mainly for
- // 3-way or call waiting scenarios. Presumably, here we're in a special 3-way scenario
- // where the network needs a blank flash before being able to add the new participant.
- // (This is not the case with all 3-way calls, just certain CDMA infrastructures.)
- if (!sendEmptyFlash) {
+ startActivity(newDialNumberIntent(number));
+ mDigits.getText().clear(); // TODO: Fix bug 1745781
finish();
}
}
@@ -1269,4 +1256,25 @@
ContactsSearchManager.startSearch(this, initialQuery);
}
}
+
+ // Helpers for the call intents.
+ private Intent newVoicemailIntent() {
+ final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+ Uri.fromParts("voicemail", EMPTY_NUMBER, null));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
+
+ private Intent newFlashIntent() {
+ final Intent intent = newDialNumberIntent(EMPTY_NUMBER);
+ intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
+ return intent;
+ }
+
+ private Intent newDialNumberIntent(String number) {
+ final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+ Uri.fromParts("tel", number, null));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
}
diff --git a/src/com/android/contacts/TypePrecedence.java b/src/com/android/contacts/TypePrecedence.java
index 62520a0..2adf3b5 100644
--- a/src/com/android/contacts/TypePrecedence.java
+++ b/src/com/android/contacts/TypePrecedence.java
@@ -99,7 +99,7 @@
private static int[] getTypePrecedenceList(String mimetype) {
if (mimetype.equals(Phone.CONTENT_ITEM_TYPE)) {
return TYPE_PRECEDENCE_PHONES;
- } else if (mimetype.equals(Constants.MIME_SMS_ADDRESS)) {
+ } else if (mimetype.equals(Constants.MIME_TYPE_SMS_ADDRESS)) {
return TYPE_PRECEDENCE_PHONES;
} else if (mimetype.equals(Email.CONTENT_ITEM_TYPE)) {
return TYPE_PRECEDENCE_EMAIL;
@@ -107,6 +107,8 @@
return TYPE_PRECEDENCE_POSTAL;
} else if (mimetype.equals(Im.CONTENT_ITEM_TYPE)) {
return TYPE_PRECEDENCE_IM;
+ } else if (mimetype.equals(Constants.MIME_TYPE_VIDEO_CHAT)) {
+ return TYPE_PRECEDENCE_IM;
} else if (mimetype.equals(Organization.CONTENT_ITEM_TYPE)) {
return TYPE_PRECEDENCE_ORG;
} else {
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
deleted file mode 100644
index eeb0a0d..0000000
--- a/src/com/android/contacts/ViewContactActivity.java
+++ /dev/null
@@ -1,1388 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts;
-import com.android.contacts.Collapser.Collapsible;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.Sources;
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.ui.EditContactActivity;
-import com.android.contacts.util.Constants;
-import com.android.contacts.util.DataStatus;
-import com.android.contacts.util.NotifyingAsyncQueryHandler;
-import com.android.internal.telephony.ITelephony;
-import com.android.internal.widget.ContactHeaderWidget;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
-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.Entity;
-import android.content.EntityIterator;
-import android.content.Intent;
-import android.content.Entity.NamedContentValues;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.graphics.drawable.Drawable;
-import android.net.ParseException;
-import android.net.Uri;
-import android.net.WebAddress;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.AggregationExceptions;
-import android.provider.ContactsContract.CommonDataKinds;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.DisplayNameSources;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.RawContactsEntity;
-import android.provider.ContactsContract.StatusUpdates;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Nickname;
-import android.provider.ContactsContract.CommonDataKinds.Note;
-import android.provider.ContactsContract.CommonDataKinds.Organization;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.SipAddress;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.widget.AdapterView;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Displays the details of a specific contact.
- */
-public class ViewContactActivity extends Activity
- implements View.OnCreateContextMenuListener, DialogInterface.OnClickListener,
- AdapterView.OnItemClickListener, NotifyingAsyncQueryHandler.AsyncQueryListener {
- private static final String TAG = "ViewContact";
-
- private static final boolean SHOW_SEPARATORS = false;
-
- private static final int DIALOG_CONFIRM_DELETE = 1;
- private static final int DIALOG_CONFIRM_READONLY_DELETE = 2;
- private static final int DIALOG_CONFIRM_MULTIPLE_DELETE = 3;
- private static final int DIALOG_CONFIRM_READONLY_HIDE = 4;
-
- private static final int REQUEST_JOIN_CONTACT = 1;
- private static final int REQUEST_EDIT_CONTACT = 2;
-
- public static final int MENU_ITEM_MAKE_DEFAULT = 3;
- public static final int MENU_ITEM_CALL = 4;
-
- protected Uri mLookupUri;
- private ContentResolver mResolver;
- private ViewAdapter mAdapter;
- private int mNumPhoneNumbers = 0;
-
- /**
- * A list of distinct contact IDs included in the current contact.
- */
- private ArrayList<Long> mRawContactIds = new ArrayList<Long>();
-
- /* 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> mNicknameEntries = new ArrayList<ViewEntry>();
- /* package */ ArrayList<ViewEntry> mOrganizationEntries = new ArrayList<ViewEntry>();
- /* package */ ArrayList<ViewEntry> mGroupEntries = new ArrayList<ViewEntry>();
- /* package */ ArrayList<ViewEntry> mOtherEntries = new ArrayList<ViewEntry>();
- /* package */ ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
-
- private Cursor mCursor;
-
- protected ContactHeaderWidget mContactHeaderWidget;
- private NotifyingAsyncQueryHandler mHandler;
-
- protected LayoutInflater mInflater;
-
- protected int mReadOnlySourcesCnt;
- protected int mWritableSourcesCnt;
- protected boolean mAllRestricted;
-
- protected Uri mPrimaryPhoneUri = null;
-
- protected ArrayList<Long> mWritableRawContactIds = new ArrayList<Long>();
-
- private static final int TOKEN_ENTITIES = 0;
- private static final int TOKEN_STATUSES = 1;
-
- private boolean mHasEntities = false;
- private boolean mHasStatuses = false;
-
- private long mNameRawContactId = -1;
- private int mDisplayNameSource = DisplayNameSources.UNDEFINED;
-
- private ArrayList<Entity> mEntities = Lists.newArrayList();
- private HashMap<Long, DataStatus> mStatuses = Maps.newHashMap();
-
- /**
- * The view shown if the detail list is empty.
- * We set this to the list view when first bind the adapter, so that it won't be shown while
- * we're loading data.
- */
- private View mEmptyView;
-
- private ContentObserver mObserver = new ContentObserver(new Handler()) {
- @Override
- public boolean deliverSelfNotifications() {
- return true;
- }
-
- @Override
- public void onChange(boolean selfChange) {
- if (mCursor != null && !mCursor.isClosed()) {
- startEntityQuery();
- }
- }
- };
-
- public void onClick(DialogInterface dialog, int which) {
- closeCursor();
- getContentResolver().delete(mLookupUri, null, null);
- finish();
- }
-
- private ListView mListView;
- private boolean mShowSmsLinksForAllPhones;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- final Intent intent = getIntent();
- Uri data = intent.getData();
- String authority = data.getAuthority();
- if (ContactsContract.AUTHORITY.equals(authority)) {
- mLookupUri = data;
- } else if (android.provider.Contacts.AUTHORITY.equals(authority)) {
- final long rawContactId = ContentUris.parseId(data);
- mLookupUri = RawContacts.getContactLookupUri(getContentResolver(),
- ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
-
- }
- mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- setContentView(R.layout.contact_card_layout);
-
- mContactHeaderWidget = (ContactHeaderWidget) findViewById(R.id.contact_header_widget);
- mContactHeaderWidget.showStar(true);
- mContactHeaderWidget.setExcludeMimes(new String[] {
- Contacts.CONTENT_ITEM_TYPE
- });
- mContactHeaderWidget.setSelectedContactsAppTabIndex(StickyTabs.getTab(getIntent()));
-
- mHandler = new NotifyingAsyncQueryHandler(this, this);
-
- mListView = (ListView) findViewById(R.id.contact_data);
- mListView.setOnCreateContextMenuListener(this);
- mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
- mListView.setOnItemClickListener(this);
- // Don't set it to mListView yet. We do so later when we bind the adapter.
- mEmptyView = findViewById(android.R.id.empty);
-
- 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(mNicknameEntries);
- mSections.add(mOrganizationEntries);
- mSections.add(mGroupEntries);
- mSections.add(mOtherEntries);
-
- //TODO Read this value from a preference
- mShowSmsLinksForAllPhones = true;
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- startEntityQuery();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- closeCursor();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- closeCursor();
- }
-
- @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();
- case DIALOG_CONFIRM_READONLY_DELETE:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.readOnlyContactDeleteConfirmation)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, this)
- .setCancelable(false)
- .create();
- case DIALOG_CONFIRM_MULTIPLE_DELETE:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.multipleContactDeleteConfirmation)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, this)
- .setCancelable(false)
- .create();
- case DIALOG_CONFIRM_READONLY_HIDE: {
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.readOnlyContactWarning)
- .setPositiveButton(android.R.string.ok, this)
- .create();
- }
-
- }
- return null;
- }
-
- /** {@inheritDoc} */
- public void onQueryComplete(int token, Object cookie, final Cursor cursor) {
- if (token == TOKEN_STATUSES) {
- try {
- // Read available social rows and consider binding
- readStatuses(cursor);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- considerBindData();
- return;
- }
-
- // One would think we could just iterate over the Cursor
- // directly here, as the result set should be small, and we've
- // already run the query in an AsyncTask, but a lot of ANRs
- // were being reported in this code nonetheless. See bug
- // 2539603 for details. The real bug which makes this result
- // set huge and CPU-heavy may be elsewhere.
- // TODO: if we keep this async, perhaps the entity iteration
- // should also be original AsyncTask, rather than ping-ponging
- // between threads like this.
- final ArrayList<Entity> oldEntities = mEntities;
- (new AsyncTask<Void, Void, ArrayList<Entity>>() {
- @Override
- protected ArrayList<Entity> doInBackground(Void... params) {
- ArrayList<Entity> newEntities = new ArrayList<Entity>(cursor.getCount());
- EntityIterator iterator = RawContacts.newEntityIterator(cursor);
- try {
- while (iterator.hasNext()) {
- Entity entity = iterator.next();
- newEntities.add(entity);
- }
- } finally {
- iterator.close();
- }
- return newEntities;
- }
-
- @Override
- protected void onPostExecute(ArrayList<Entity> newEntities) {
- if (newEntities == null) {
- // There was an error loading.
- return;
- }
- synchronized (ViewContactActivity.this) {
- if (mEntities != oldEntities) {
- // Multiple async tasks were in flight and we
- // lost the race.
- return;
- }
- mEntities = newEntities;
- mHasEntities = true;
- }
- considerBindData();
- }
- }).execute();
- }
-
- private long getRefreshedContactId() {
- Uri freshContactUri = Contacts.lookupContact(getContentResolver(), mLookupUri);
- if (freshContactUri != null) {
- return ContentUris.parseId(freshContactUri);
- }
- return -1;
- }
-
- /**
- * Read from the given {@link Cursor} and build a set of {@link DataStatus}
- * objects to match any valid statuses found.
- */
- private synchronized void readStatuses(Cursor cursor) {
- mStatuses.clear();
-
- // Walk found statuses, creating internal row for each
- while (cursor.moveToNext()) {
- final DataStatus status = new DataStatus(cursor);
- final long dataId = cursor.getLong(StatusQuery._ID);
- mStatuses.put(dataId, status);
- }
-
- mHasStatuses = true;
- }
-
- private static Cursor setupContactCursor(ContentResolver resolver, Uri lookupUri) {
- if (lookupUri == null) {
- return null;
- }
- final List<String> segments = lookupUri.getPathSegments();
- if (segments.size() != 4) {
- return null;
- }
-
- // Contains an Id.
- final long uriContactId = Long.parseLong(segments.get(3));
- final String uriLookupKey = Uri.encode(segments.get(2));
- final Uri dataUri = Uri.withAppendedPath(
- ContentUris.withAppendedId(Contacts.CONTENT_URI, uriContactId),
- Contacts.Data.CONTENT_DIRECTORY);
-
- // This cursor has several purposes:
- // - Fetch NAME_RAW_CONTACT_ID and DISPLAY_NAME_SOURCE
- // - Fetch the lookup-key to ensure we are looking at the right record
- // - Watcher for change events
- Cursor cursor = resolver.query(dataUri,
- new String[] {
- Contacts.NAME_RAW_CONTACT_ID,
- Contacts.DISPLAY_NAME_SOURCE,
- Contacts.LOOKUP_KEY
- }, null, null, null);
-
- if (cursor.moveToFirst()) {
- String lookupKey =
- cursor.getString(cursor.getColumnIndex(Contacts.LOOKUP_KEY));
- if (!lookupKey.equals(uriLookupKey)) {
- // ID and lookup key do not match
- cursor.close();
- return null;
- }
- return cursor;
- } else {
- cursor.close();
- return null;
- }
- }
-
- private synchronized void startEntityQuery() {
- closeCursor();
-
- // Interprete mLookupUri
- mCursor = setupContactCursor(mResolver, mLookupUri);
-
- // If mCursor is null now we did not succeed in using the Uri's Id (or it didn't contain
- // a Uri). Instead we now have to use the lookup key to find the record
- if (mCursor == null) {
- mLookupUri = Contacts.getLookupUri(getContentResolver(), mLookupUri);
- mCursor = setupContactCursor(mResolver, mLookupUri);
- }
-
- // If mCursor is still null, we were unsuccessful in finding the record
- if (mCursor == null) {
- mNameRawContactId = -1;
- mDisplayNameSource = DisplayNameSources.UNDEFINED;
- // TODO either figure out a way to prevent a flash of black background or
- // use some other UI than a toast
- Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "invalid contact uri: " + mLookupUri);
- finish();
- return;
- }
-
- final long contactId = ContentUris.parseId(mLookupUri);
-
- mNameRawContactId =
- mCursor.getLong(mCursor.getColumnIndex(Contacts.NAME_RAW_CONTACT_ID));
- mDisplayNameSource =
- mCursor.getInt(mCursor.getColumnIndex(Contacts.DISPLAY_NAME_SOURCE));
-
- mCursor.registerContentObserver(mObserver);
-
- // Clear flags and start queries to data and status
- mHasEntities = false;
- mHasStatuses = false;
-
- mHandler.startQuery(TOKEN_ENTITIES, null, RawContactsEntity.CONTENT_URI, null,
- RawContacts.CONTACT_ID + "=?", new String[] {
- String.valueOf(contactId)
- }, null);
- final Uri dataUri = Uri.withAppendedPath(
- ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
- Contacts.Data.CONTENT_DIRECTORY);
- mHandler.startQuery(TOKEN_STATUSES, null, dataUri, StatusQuery.PROJECTION,
- StatusUpdates.PRESENCE + " IS NOT NULL OR " + StatusUpdates.STATUS
- + " IS NOT NULL", null, null);
-
- mContactHeaderWidget.bindFromContactLookupUri(mLookupUri);
- }
-
- private void closeCursor() {
- if (mCursor != null) {
- mCursor.unregisterContentObserver(mObserver);
- mCursor.close();
- mCursor = null;
- }
- }
-
- /**
- * Consider binding views after any of several background queries has
- * completed. We check internal flags and only bind when all data has
- * arrived.
- */
- private void considerBindData() {
- if (mHasEntities && mHasStatuses) {
- bindData();
- }
- }
-
- private void bindData() {
-
- // Build up the contact entries
- buildEntries();
-
- // Collapse similar data items in select sections.
- Collapser.collapseList(mPhoneEntries);
- Collapser.collapseList(mSmsEntries);
- Collapser.collapseList(mEmailEntries);
- Collapser.collapseList(mPostalEntries);
- Collapser.collapseList(mImEntries);
-
- if (mAdapter == null) {
- mAdapter = new ViewAdapter(this, mSections);
- mListView.setAdapter(mAdapter);
- } else {
- mAdapter.setSections(mSections, SHOW_SEPARATORS);
- }
- mListView.setEmptyView(mEmptyView);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
-
- final MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.view, menu);
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- super.onPrepareOptionsMenu(menu);
-
- // Only allow edit when we have at least one raw_contact id
- final boolean hasRawContact = (mRawContactIds.size() > 0);
- menu.findItem(R.id.menu_edit).setEnabled(hasRawContact);
-
- // Only allow share when unrestricted contacts available
- menu.findItem(R.id.menu_share).setEnabled(!mAllRestricted);
-
- return true;
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
- AdapterView.AdapterContextMenuInfo info;
- try {
- info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- } catch (ClassCastException e) {
- Log.e(TAG, "bad menuInfo", e);
- return;
- }
-
- // This can be null sometimes, don't crash...
- if (info == null) {
- Log.e(TAG, "bad menuInfo");
- return;
- }
-
- ViewEntry entry = ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
- menu.setHeaderTitle(R.string.contactOptionsTitle);
- if (entry.mimetype.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
- menu.add(0, MENU_ITEM_CALL, 0, R.string.menu_call).setIntent(entry.intent);
- menu.add(0, 0, 0, R.string.menu_sendSMS).setIntent(entry.secondaryIntent);
- if (!entry.isPrimary) {
- menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultNumber);
- }
- } else if (entry.mimetype.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
- menu.add(0, 0, 0, R.string.menu_sendEmail).setIntent(entry.intent);
- if (!entry.isPrimary) {
- menu.add(0, MENU_ITEM_MAKE_DEFAULT, 0, R.string.menu_makeDefaultEmail);
- }
- } else if (entry.mimetype.equals(CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)) {
- menu.add(0, 0, 0, R.string.menu_viewAddress).setIntent(entry.intent);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_edit: {
- Long rawContactIdToEdit = null;
- if (mRawContactIds.size() > 0) {
- rawContactIdToEdit = mRawContactIds.get(0);
- } else {
- // There is no rawContact to edit.
- break;
- }
- Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
- rawContactIdToEdit);
- startActivityForResult(new Intent(Intent.ACTION_EDIT, rawContactUri),
- REQUEST_EDIT_CONTACT);
- break;
- }
- case R.id.menu_delete: {
- // Get confirmation
- if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
- showDialog(DIALOG_CONFIRM_READONLY_DELETE);
- } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
- showDialog(DIALOG_CONFIRM_READONLY_HIDE);
- } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
- showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
- } else {
- showDialog(DIALOG_CONFIRM_DELETE);
- }
- return true;
- }
- case R.id.menu_join: {
- showJoinAggregateActivity();
- return true;
- }
- case R.id.menu_options: {
- showOptionsActivity();
- return true;
- }
- case R.id.menu_share: {
- if (mAllRestricted) return false;
-
- // TODO: Keep around actual LOOKUP_KEY, or formalize method of extracting
- final String lookupKey = Uri.encode(mLookupUri.getPathSegments().get(2));
- final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
-
- final Intent intent = new Intent(Intent.ACTION_SEND);
- intent.setType(Contacts.CONTENT_VCARD_TYPE);
- intent.putExtra(Intent.EXTRA_STREAM, shareUri);
-
- // Launch chooser to share contact via
- final CharSequence chooseTitle = getText(R.string.share_via);
- final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
-
- try {
- startActivity(chooseIntent);
- } catch (ActivityNotFoundException ex) {
- Toast.makeText(this, R.string.share_error, Toast.LENGTH_SHORT).show();
- }
- return true;
- }
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case MENU_ITEM_MAKE_DEFAULT: {
- makeItemDefault(item);
- return true;
- }
- case MENU_ITEM_CALL: {
- StickyTabs.saveTab(this, getIntent());
- startActivity(item.getIntent());
- return true;
- }
- default: {
- return super.onContextItemSelected(item);
- }
- }
- }
-
- private boolean makeItemDefault(MenuItem item) {
- ViewEntry entry = getViewEntryForMenuItem(item);
- if (entry == null) {
- return false;
- }
-
- // Update the primary values in the data record.
- ContentValues values = new ContentValues(1);
- values.put(Data.IS_SUPER_PRIMARY, 1);
- getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, entry.id),
- values, null, null);
- startEntityQuery();
- return true;
- }
-
- /**
- * Shows a list of aggregates that can be joined into the currently viewed aggregate.
- */
- public void showJoinAggregateActivity() {
- long freshId = getRefreshedContactId();
- if (freshId > 0) {
- String displayName = null;
- if (mCursor.moveToFirst()) {
- displayName = mCursor.getString(0);
- }
- Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
- intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, freshId);
- if (displayName != null) {
- intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_NAME, displayName);
- }
- startActivityForResult(intent, REQUEST_JOIN_CONTACT);
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
- if (requestCode == REQUEST_JOIN_CONTACT) {
- if (resultCode == RESULT_OK && intent != null) {
- final long contactId = ContentUris.parseId(intent.getData());
- joinAggregate(contactId);
- }
- } else if (requestCode == REQUEST_EDIT_CONTACT) {
- if (resultCode == EditContactActivity.RESULT_CLOSE_VIEW_ACTIVITY) {
- finish();
- } else if (resultCode == Activity.RESULT_OK) {
- mLookupUri = intent.getData();
- if (mLookupUri == null) {
- finish();
- }
- }
- }
- }
-
- private void joinAggregate(final long contactId) {
- Cursor c = mResolver.query(RawContacts.CONTENT_URI, new String[] {RawContacts._ID},
- RawContacts.CONTACT_ID + "=" + contactId, null, null);
-
- try {
- while(c.moveToNext()) {
- long rawContactId = c.getLong(0);
- setAggregationException(rawContactId, AggregationExceptions.TYPE_KEEP_TOGETHER);
- }
- } finally {
- c.close();
- }
-
- Toast.makeText(this, R.string.contactsJoinedMessage, Toast.LENGTH_LONG).show();
- startEntityQuery();
- }
-
- /**
- * Given a contact ID sets an aggregation exception to either join the contact with the
- * current aggregate or split off.
- */
- protected void setAggregationException(long rawContactId, int exceptionType) {
- ContentValues values = new ContentValues(3);
- for (long aRawContactId : mRawContactIds) {
- if (aRawContactId != rawContactId) {
- values.put(AggregationExceptions.RAW_CONTACT_ID1, aRawContactId);
- values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId);
- values.put(AggregationExceptions.TYPE, exceptionType);
- mResolver.update(AggregationExceptions.CONTENT_URI, values, null, null);
- }
- }
- }
-
- private void showOptionsActivity() {
- final Intent intent = new Intent(this, ContactOptionsActivity.class);
- intent.setData(mLookupUri);
- startActivity(intent);
- }
-
- private ViewEntry getViewEntryForMenuItem(MenuItem item) {
- AdapterView.AdapterContextMenuInfo info;
- try {
- info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
- } catch (ClassCastException e) {
- Log.e(TAG, "bad menuInfo", e);
- return null;
- }
-
- return ContactEntryAdapter.getEntry(mSections, info.position, SHOW_SEPARATORS);
- }
-
- @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 = mListView.getSelectedItemPosition();
- if (index != -1) {
- ViewEntry entry = ViewAdapter.getEntry(mSections, index, SHOW_SEPARATORS);
- if (entry != null && entry.intent != null &&
- entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
- startActivity(entry.intent);
- StickyTabs.saveTab(this, getIntent());
- return true;
- }
- } else if (mPrimaryPhoneUri != null) {
- // There isn't anything selected, call the default number
- final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
- mPrimaryPhoneUri);
- startActivity(intent);
- StickyTabs.saveTab(this, getIntent());
- return true;
- }
- return false;
- }
-
- case KeyEvent.KEYCODE_DEL: {
- if (mReadOnlySourcesCnt > 0 & mWritableSourcesCnt > 0) {
- showDialog(DIALOG_CONFIRM_READONLY_DELETE);
- } else if (mReadOnlySourcesCnt > 0 && mWritableSourcesCnt == 0) {
- showDialog(DIALOG_CONFIRM_READONLY_HIDE);
- } else if (mReadOnlySourcesCnt == 0 && mWritableSourcesCnt > 1) {
- showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
- } else {
- showDialog(DIALOG_CONFIRM_DELETE);
- }
- return true;
- }
- }
-
- return super.onKeyDown(keyCode, event);
- }
-
- public void onItemClick(AdapterView parent, View v, int position, long id) {
- ViewEntry entry = ViewAdapter.getEntry(mSections, position, SHOW_SEPARATORS);
- if (entry != null) {
- Intent intent = entry.intent;
- if (intent != null) {
- if (Intent.ACTION_CALL_PRIVILEGED.equals(intent.getAction())) {
- StickyTabs.saveTab(this, getIntent());
- }
- 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 up the entries to display on the screen.
- *
- * @param personCursor the URI for the contact being displayed
- */
- private final void buildEntries() {
- // Clear out the old entries
- final int numSections = mSections.size();
- for (int i = 0; i < numSections; i++) {
- mSections.get(i).clear();
- }
-
- mRawContactIds.clear();
-
- mReadOnlySourcesCnt = 0;
- mWritableSourcesCnt = 0;
- mAllRestricted = true;
- mPrimaryPhoneUri = null;
-
- mWritableRawContactIds.clear();
-
- final Context context = this;
- final Sources sources = Sources.getInstance(context);
-
- // Build up method entries
- if (mLookupUri != null) {
- for (Entity entity: mEntities) {
- final ContentValues entValues = entity.getEntityValues();
- final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
- final long rawContactId = entValues.getAsLong(RawContacts._ID);
-
- // Mark when this contact has any unrestricted components
- final boolean isRestricted = entValues.getAsInteger(RawContacts.IS_RESTRICTED) != 0;
- if (!isRestricted) mAllRestricted = false;
-
- if (!mRawContactIds.contains(rawContactId)) {
- mRawContactIds.add(rawContactId);
- }
- ContactsSource contactsSource = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_SUMMARY);
- if (contactsSource != null && contactsSource.readOnly) {
- mReadOnlySourcesCnt += 1;
- } else {
- mWritableSourcesCnt += 1;
- mWritableRawContactIds.add(rawContactId);
- }
-
-
- for (NamedContentValues subValue : entity.getSubValues()) {
- final ContentValues entryValues = subValue.values;
- entryValues.put(Data.RAW_CONTACT_ID, rawContactId);
-
- final long dataId = entryValues.getAsLong(Data._ID);
- final String mimeType = entryValues.getAsString(Data.MIMETYPE);
- if (mimeType == null) continue;
-
- final DataKind kind = sources.getKindOrFallback(accountType, mimeType, this,
- ContactsSource.LEVEL_MIMETYPES);
- if (kind == null) continue;
-
- final ViewEntry entry = ViewEntry.fromValues(context, mimeType, kind,
- rawContactId, dataId, entryValues);
-
- final boolean hasData = !TextUtils.isEmpty(entry.data);
- final boolean isSuperPrimary = entryValues.getAsInteger(
- Data.IS_SUPER_PRIMARY) != 0;
-
- if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
- // Build phone entries
- mNumPhoneNumbers++;
-
- entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
- Uri.fromParts(Constants.SCHEME_TEL, entry.data, null));
- entry.secondaryIntent = new Intent(Intent.ACTION_SENDTO,
- Uri.fromParts(Constants.SCHEME_SMSTO, entry.data, null));
-
- // Remember super-primary phone
- if (isSuperPrimary) mPrimaryPhoneUri = entry.uri;
-
- entry.isPrimary = isSuperPrimary;
- mPhoneEntries.add(entry);
-
- if (entry.type == CommonDataKinds.Phone.TYPE_MOBILE
- || mShowSmsLinksForAllPhones) {
- // Add an SMS entry
- if (kind.iconAltRes > 0) {
- entry.secondaryActionIcon = kind.iconAltRes;
- }
- }
- } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
- // Build email entries
- entry.intent = new Intent(Intent.ACTION_SENDTO,
- Uri.fromParts(Constants.SCHEME_MAILTO, entry.data, null));
- entry.isPrimary = isSuperPrimary;
- mEmailEntries.add(entry);
-
- // When Email rows have status, create additional Im row
- final DataStatus status = mStatuses.get(entry.id);
- if (status != null) {
- final String imMime = Im.CONTENT_ITEM_TYPE;
- final DataKind imKind = sources.getKindOrFallback(accountType,
- imMime, this, ContactsSource.LEVEL_MIMETYPES);
- final ViewEntry imEntry = ViewEntry.fromValues(context,
- imMime, imKind, rawContactId, dataId, entryValues);
- imEntry.intent = ContactsUtils.buildImIntent(entryValues);
- imEntry.applyStatus(status, false);
- mImEntries.add(imEntry);
- }
- } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
- // Build postal entries
- entry.maxLines = 4;
- entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
- mPostalEntries.add(entry);
- } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
- // Build IM entries
- entry.intent = ContactsUtils.buildImIntent(entryValues);
- if (TextUtils.isEmpty(entry.label)) {
- entry.label = getString(R.string.chat).toLowerCase();
- }
-
- // Apply presence and status details when available
- final DataStatus status = mStatuses.get(entry.id);
- if (status != null) {
- entry.applyStatus(status, false);
- }
- mImEntries.add(entry);
- } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType) &&
- (hasData || !TextUtils.isEmpty(entry.label))) {
- // Build organization entries
- final boolean isNameRawContact = (mNameRawContactId == rawContactId);
-
- final boolean duplicatesTitle =
- isNameRawContact
- && mDisplayNameSource == DisplayNameSources.ORGANIZATION
- && (!hasData || TextUtils.isEmpty(entry.label));
-
- if (!duplicatesTitle) {
- entry.uri = null;
-
- if (TextUtils.isEmpty(entry.label)) {
- entry.label = entry.data;
- entry.data = "";
- }
-
- mOrganizationEntries.add(entry);
- }
- } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
- // Build nickname entries
- final boolean isNameRawContact = (mNameRawContactId == rawContactId);
-
- final boolean duplicatesTitle =
- isNameRawContact
- && mDisplayNameSource == DisplayNameSources.NICKNAME;
-
- if (!duplicatesTitle) {
- entry.uri = null;
- mNicknameEntries.add(entry);
- }
- } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
- // Build note entries
- entry.uri = null;
- entry.maxLines = 100;
- mOtherEntries.add(entry);
- } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
- // Build Website entries
- entry.uri = null;
- entry.maxLines = 10;
- try {
- WebAddress webAddress = new WebAddress(entry.data);
- entry.intent = new Intent(Intent.ACTION_VIEW,
- Uri.parse(webAddress.toString()));
- } catch (ParseException e) {
- Log.e(TAG, "Couldn't parse website: " + entry.data);
- }
- mOtherEntries.add(entry);
- } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
- // Build SipAddress entries
- entry.uri = null;
- entry.maxLines = 1;
- entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
- Uri.fromParts(Constants.SCHEME_SIP, entry.data, null));
- mOtherEntries.add(entry);
- // TODO: Consider moving the SipAddress into its own
- // section (rather than lumping it in with mOtherEntries)
- // so that we can reposition it right under the phone number.
- // (Then, we'd also update FallbackSource.java to set
- // secondary=false for this field, and tweak the weight
- // of its DataKind.)
- } else {
- // Handle showing custom rows
- entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
-
- // Use social summary when requested by external source
- final DataStatus status = mStatuses.get(entry.id);
- final boolean hasSocial = kind.actionBodySocial && status != null;
- if (hasSocial) {
- entry.applyStatus(status, true);
- }
-
- if (hasSocial || hasData) {
- mOtherEntries.add(entry);
- }
- }
- }
- }
- }
- }
-
- static String buildActionString(DataKind kind, ContentValues values, boolean lowerCase,
- Context context) {
- if (kind.actionHeader == null) {
- return null;
- }
- CharSequence actionHeader = kind.actionHeader.inflateUsing(context, values);
- if (actionHeader == null) {
- return null;
- }
- return lowerCase ? actionHeader.toString().toLowerCase() : actionHeader.toString();
- }
-
- static String buildDataString(DataKind kind, ContentValues values, Context context) {
- if (kind.actionBody == null) {
- return null;
- }
- CharSequence actionBody = kind.actionBody.inflateUsing(context, values);
- return actionBody == null ? null : actionBody.toString();
- }
-
- /**
- * A basic structure with the data for a contact entry in the list.
- */
- static class ViewEntry extends ContactEntryAdapter.Entry implements Collapsible<ViewEntry> {
- public Context context = null;
- public String resPackageName = null;
- public int actionIcon = -1;
- public boolean isPrimary = false;
- public int secondaryActionIcon = -1;
- public Intent intent;
- public Intent secondaryIntent = null;
- public int maxLabelLines = 1;
- public ArrayList<Long> ids = new ArrayList<Long>();
- public int collapseCount = 0;
-
- public int presence = -1;
-
- public CharSequence footerLine = null;
-
- private ViewEntry() {
- }
-
- /**
- * Build new {@link ViewEntry} and populate from the given values.
- */
- public static ViewEntry fromValues(Context context, String mimeType, DataKind kind,
- long rawContactId, long dataId, ContentValues values) {
- final ViewEntry entry = new ViewEntry();
- entry.context = context;
- entry.contactId = rawContactId;
- entry.id = dataId;
- entry.uri = ContentUris.withAppendedId(Data.CONTENT_URI, entry.id);
- entry.mimetype = mimeType;
- entry.label = buildActionString(kind, values, false, context);
- entry.data = buildDataString(kind, values, context);
-
- if (kind.typeColumn != null && values.containsKey(kind.typeColumn)) {
- entry.type = values.getAsInteger(kind.typeColumn);
- }
- if (kind.iconRes > 0) {
- entry.resPackageName = kind.resPackageName;
- entry.actionIcon = kind.iconRes;
- }
-
- return entry;
- }
-
- /**
- * Apply given {@link DataStatus} values over this {@link ViewEntry}
- *
- * @param fillData When true, the given status replaces {@link #data}
- * and {@link #footerLine}. Otherwise only {@link #presence}
- * is updated.
- */
- public ViewEntry applyStatus(DataStatus status, boolean fillData) {
- presence = status.getPresence();
- if (fillData && status.isValid()) {
- this.data = status.getStatus().toString();
- this.footerLine = status.getTimestampLabel(context);
- }
-
- return this;
- }
-
- public boolean collapseWith(ViewEntry entry) {
- // assert equal collapse keys
- if (!shouldCollapseWith(entry)) {
- return false;
- }
-
- // Choose the label associated with the highest type precedence.
- if (TypePrecedence.getTypePrecedence(mimetype, type)
- > TypePrecedence.getTypePrecedence(entry.mimetype, entry.type)) {
- type = entry.type;
- label = entry.label;
- }
-
- // Choose the max of the maxLines and maxLabelLines values.
- maxLines = Math.max(maxLines, entry.maxLines);
- maxLabelLines = Math.max(maxLabelLines, entry.maxLabelLines);
-
- // Choose the presence with the highest precedence.
- if (StatusUpdates.getPresencePrecedence(presence)
- < StatusUpdates.getPresencePrecedence(entry.presence)) {
- presence = entry.presence;
- }
-
- // If any of the collapsed entries are primary make the whole thing primary.
- isPrimary = entry.isPrimary ? true : isPrimary;
-
- // uri, and contactdId, shouldn't make a difference. Just keep the original.
-
- // Keep track of all the ids that have been collapsed with this one.
- ids.add(entry.id);
- collapseCount++;
- return true;
- }
-
- public boolean shouldCollapseWith(ViewEntry entry) {
- if (entry == null) {
- return false;
- }
-
- if (!ContactsUtils.shouldCollapse(context, mimetype, data, entry.mimetype,
- entry.data)) {
- return false;
- }
-
- if (!TextUtils.equals(mimetype, entry.mimetype)
- || !ContactsUtils.areIntentActionEqual(intent, entry.intent)
- || !ContactsUtils.areIntentActionEqual(secondaryIntent, entry.secondaryIntent)
- || actionIcon != entry.actionIcon) {
- return false;
- }
-
- return true;
- }
- }
-
- /** Cache of the children views of a row */
- static class ViewCache {
- public TextView label;
- public TextView data;
- public TextView footer;
- public ImageView actionIcon;
- public ImageView presenceIcon;
- public ImageView primaryIcon;
- public ImageView secondaryActionButton;
- public View secondaryActionDivider;
-
- // Need to keep track of this too
- ViewEntry entry;
- }
-
- private final class ViewAdapter extends ContactEntryAdapter<ViewEntry>
- implements View.OnClickListener {
-
-
- ViewAdapter(Context context, ArrayList<ArrayList<ViewEntry>> sections) {
- super(context, sections, SHOW_SEPARATORS);
- }
-
- public void onClick(View v) {
- Intent intent = (Intent) v.getTag();
- startActivity(intent);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ViewEntry entry = getEntry(mSections, position, false);
- View v;
-
- 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.footer = (TextView) v.findViewById(R.id.footer);
- views.actionIcon = (ImageView) v.findViewById(R.id.action_icon);
- views.primaryIcon = (ImageView) v.findViewById(R.id.primary_icon);
- views.presenceIcon = (ImageView) v.findViewById(R.id.presence_icon);
- views.secondaryActionButton = (ImageView) v.findViewById(
- R.id.secondary_action_button);
- views.secondaryActionButton.setOnClickListener(this);
- views.secondaryActionDivider = v.findViewById(R.id.divider);
- 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) {
- if (entry.mimetype.equals(Phone.CONTENT_ITEM_TYPE)
- || entry.mimetype.equals(Constants.MIME_SMS_ADDRESS)) {
- data.setText(PhoneNumberUtils.formatNumber(entry.data));
- } else {
- data.setText(entry.data);
- }
- setMaxLines(data, entry.maxLines);
- }
-
- // Set the footer
- if (!TextUtils.isEmpty(entry.footerLine)) {
- views.footer.setText(entry.footerLine);
- views.footer.setVisibility(View.VISIBLE);
- } else {
- views.footer.setVisibility(View.GONE);
- }
-
- // Set the primary icon
- views.primaryIcon.setVisibility(entry.isPrimary ? View.VISIBLE : View.GONE);
-
- // Set the action icon
- ImageView action = views.actionIcon;
- if (entry.actionIcon != -1) {
- Drawable actionIcon;
- if (entry.resPackageName != null) {
- // Load external resources through PackageManager
- actionIcon = mContext.getPackageManager().getDrawable(entry.resPackageName,
- entry.actionIcon, null);
- } else {
- actionIcon = resources.getDrawable(entry.actionIcon);
- }
- action.setImageDrawable(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 = ContactPresenceIconUtil.getPresenceIcon(
- mContext, entry.presence);
- ImageView presenceIconView = views.presenceIcon;
- if (presenceIcon != null) {
- presenceIconView.setImageDrawable(presenceIcon);
- presenceIconView.setVisibility(View.VISIBLE);
- } else {
- presenceIconView.setVisibility(View.GONE);
- }
-
- // Set the secondary action button
- ImageView secondaryActionView = views.secondaryActionButton;
- Drawable secondaryActionIcon = null;
- if (entry.secondaryActionIcon != -1) {
- secondaryActionIcon = resources.getDrawable(entry.secondaryActionIcon);
- }
- if (entry.secondaryIntent != null && secondaryActionIcon != null) {
- secondaryActionView.setImageDrawable(secondaryActionIcon);
- secondaryActionView.setTag(entry.secondaryIntent);
- secondaryActionView.setVisibility(View.VISIBLE);
- views.secondaryActionDivider.setVisibility(View.VISIBLE);
- } else {
- secondaryActionView.setVisibility(View.GONE);
- views.secondaryActionDivider.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);
- }
- }
- }
-
- private interface StatusQuery {
- final String[] PROJECTION = new String[] {
- Data._ID,
- Data.STATUS,
- Data.STATUS_RES_PACKAGE,
- Data.STATUS_ICON,
- Data.STATUS_LABEL,
- Data.STATUS_TIMESTAMP,
- Data.PRESENCE,
- };
-
- final int _ID = 0;
- }
-
- @Override
- public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
- boolean globalSearch) {
- if (globalSearch) {
- super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
- } else {
- ContactsSearchManager.startSearch(this, initialQuery);
- }
- }
-}
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
new file mode 100644
index 0000000..9943e2d
--- /dev/null
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2010 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.activities;
+
+import com.android.contacts.R;
+import com.android.contacts.list.ContactListFilterController;
+import com.android.contacts.list.ContactListFilterController.ContactListFilterListener;
+import com.android.contacts.list.ContactListFilterView;
+import com.android.contacts.list.ContactsRequest;
+
+import android.app.ActionBar;
+import android.app.ActionBar.LayoutParams;
+import android.content.Context;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.widget.SearchView;
+import android.widget.SearchView.OnCloseListener;
+import android.widget.SearchView.OnQueryTextListener;
+import android.widget.TextView;
+
+/**
+ * Adapter for the action bar at the top of the Contacts activity.
+ */
+public class ActionBarAdapter implements OnQueryTextListener, OnCloseListener,
+ ContactListFilterListener, OnFocusChangeListener {
+
+ public interface Listener {
+ void onAction();
+ }
+
+ private static final String EXTRA_KEY_SEARCH_MODE = "navBar.searchMode";
+ private static final String EXTRA_KEY_QUERY = "navBar.query";
+
+ private boolean mSearchMode;
+ private String mQueryString;
+
+ private View mNavigationBar;
+ private TextView mSearchLabel;
+ private SearchView mSearchView;
+
+ private final Context mContext;
+
+ private Listener mListener;
+ private ContactListFilterView mFilterView;
+ private ContactListFilterController mFilterController;
+
+ private boolean mEnabled;
+
+ public ActionBarAdapter(Context context) {
+ mContext = context;
+ }
+
+ public void onCreate(Bundle savedState, ContactsRequest request, ActionBar actionBar) {
+ mQueryString = null;
+ if (savedState != null) {
+ mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE);
+ mQueryString = savedState.getString(EXTRA_KEY_QUERY);
+ } else {
+ mSearchMode = request.isSearchMode();
+ mQueryString = request.getQueryString();
+ }
+
+ if (actionBar != null) {
+ actionBar.setDisplayOptions(
+ ActionBar.DISPLAY_SHOW_CUSTOM, ActionBar.DISPLAY_SHOW_CUSTOM);
+ }
+
+ mNavigationBar = LayoutInflater.from(mContext).inflate(R.layout.navigation_bar, null);
+ LayoutParams layoutParams = new LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+ if (actionBar != null) {
+ actionBar.setCustomView(mNavigationBar, layoutParams);
+ }
+
+ mFilterView = (ContactListFilterView) mNavigationBar.findViewById(R.id.filter_view);
+ mSearchLabel = (TextView) mNavigationBar.findViewById(R.id.search_label);
+ mSearchView = (SearchView) mNavigationBar.findViewById(R.id.search_view);
+
+ mSearchView.setOnQueryTextListener(this);
+ mSearchView.setOnCloseListener(this);
+ mSearchView.setOnQueryTextFocusChangeListener(this);
+ mSearchView.setQuery(mQueryString, false);
+ mSearchView.setQueryHint(mContext.getString(R.string.hint_findContacts));
+
+ update();
+ }
+
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ update();
+ }
+
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ public void setContactListFilterController(ContactListFilterController controller) {
+ mFilterController = controller;
+ mFilterController.setAnchor(mFilterView);
+ mFilterController.addListener(this);
+ }
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (v == mSearchView && hasFocus) {
+ setSearchMode(true);
+ }
+ }
+
+ public boolean isSearchMode() {
+ return mSearchMode;
+ }
+
+ public void setSearchMode(boolean flag) {
+ if (mSearchMode != flag) {
+ mSearchMode = flag;
+ update();
+ if (mSearchMode) {
+ mSearchView.requestFocus();
+ } else {
+ mSearchView.setQuery(null, false);
+ }
+ if (mListener != null) {
+ mListener.onAction();
+ }
+ }
+ }
+
+ public String getQueryString() {
+ return mQueryString;
+ }
+
+ public void setQueryString(String query) {
+ mQueryString = query;
+ mSearchView.setQuery(query, false);
+ }
+
+ public void update() {
+ if (!mEnabled) {
+ mNavigationBar.setVisibility(View.GONE);
+ } else if (mSearchMode) {
+ mNavigationBar.setVisibility(View.VISIBLE);
+ mSearchLabel.setVisibility(View.VISIBLE);
+ mFilterView.setVisibility(View.GONE);
+ if (mFilterController != null) {
+ mFilterController.setEnabled(false);
+ }
+ } else {
+ mNavigationBar.setVisibility(View.VISIBLE);
+ mSearchLabel.setVisibility(View.GONE);
+ mFilterView.setVisibility(View.VISIBLE);
+ if (mFilterController != null){
+ mFilterController.setEnabled(true);
+ if (mFilterController.isLoaded()) {
+ mFilterView.setContactListFilter(mFilterController.getFilter());
+ mFilterView.setSingleAccount(mFilterController.getAccountCount() == 1);
+ mFilterView.bindView(false);
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean onQueryTextChange(String queryString) {
+ mQueryString = queryString;
+ if (!mSearchMode) {
+ if (!TextUtils.isEmpty(queryString)) {
+ setSearchMode(true);
+ }
+ } else if (mListener != null) {
+ mListener.onAction();
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ return true;
+ }
+
+ @Override
+ public boolean onClose() {
+ setSearchMode(false);
+ return false;
+ }
+
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putBoolean(EXTRA_KEY_SEARCH_MODE, mSearchMode);
+ outState.putString(EXTRA_KEY_QUERY, mQueryString);
+ }
+
+ public void onRestoreInstanceState(Bundle savedState) {
+ mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE);
+ mQueryString = savedState.getString(EXTRA_KEY_QUERY);
+ }
+
+ @Override
+ public void onContactListFiltersLoaded() {
+ update();
+ }
+
+ @Override
+ public void onContactListFilterChanged() {
+ update();
+ }
+
+ @Override
+ public void onContactListFilterCustomizationRequest() {
+ }
+}
diff --git a/src/com/android/contacts/activities/AttachPhotoActivity.java b/src/com/android/contacts/activities/AttachPhotoActivity.java
new file mode 100644
index 0000000..65d24c3
--- /dev/null
+++ b/src/com/android/contacts/activities/AttachPhotoActivity.java
@@ -0,0 +1,268 @@
+/*
+ * 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.activities;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.R;
+import com.android.contacts.model.ExchangeAccountType;
+import com.android.contacts.model.GoogleAccountType;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+import android.widget.Toast;
+
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+
+/**
+ * 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 AttachPhotoActivity extends ContactsActivity {
+ private static final int REQUEST_PICK_CONTACT = 1;
+ private static final int REQUEST_CROP_PHOTO = 2;
+
+ private static final String RAW_CONTACT_URIS_KEY = "raw_contact_uris";
+
+ private Long[] mRawContactIds;
+
+ private ContentResolver mContentResolver;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ if (icicle != null) {
+ mRawContactIds = toClassArray(icicle.getLongArray(RAW_CONTACT_URIS_KEY));
+ } else {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType(Contacts.CONTENT_ITEM_TYPE);
+ startActivityForResult(intent, REQUEST_PICK_CONTACT);
+ }
+
+ mContentResolver = getContentResolver();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ if (mRawContactIds != null && mRawContactIds.length != 0) {
+ outState.putLongArray(RAW_CONTACT_URIS_KEY, toPrimativeArray(mRawContactIds));
+ }
+ }
+
+ private static long[] toPrimativeArray(Long[] in) {
+ if (in == null) {
+ return null;
+ }
+ long[] out = new long[in.length];
+ for (int i = 0; i < in.length; i++) {
+ out[i] = in[i];
+ }
+ return out;
+ }
+
+ private static Long[] toClassArray(long[] in) {
+ if (in == null) {
+ return null;
+ }
+ Long[] out = new Long[in.length];
+ for (int i = 0; i < in.length; i++) {
+ out[i] = in[i];
+ }
+ return out;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent result) {
+ if (resultCode != RESULT_OK) {
+ finish();
+ return;
+ }
+
+ if (requestCode == REQUEST_PICK_CONTACT) {
+ // 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);
+
+ // while they're cropping, convert the contact into a raw_contact
+ final long contactId = ContentUris.parseId(result.getData());
+ final ArrayList<Long> rawContactIdsList = queryForAllRawContactIds(
+ mContentResolver, contactId);
+ mRawContactIds = new Long[rawContactIdsList.size()];
+ mRawContactIds = rawContactIdsList.toArray(mRawContactIds);
+
+ if (mRawContactIds == null || rawContactIdsList.isEmpty()) {
+ Toast.makeText(this, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
+ }
+ } else if (requestCode == REQUEST_CROP_PHOTO) {
+ final Bundle extras = result.getExtras();
+ if (extras != null && mRawContactIds != null) {
+ Bitmap photo = extras.getParcelable("data");
+ if (photo != null) {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ photo.compress(Bitmap.CompressFormat.JPEG, 75, stream);
+
+ final ContentValues imageValues = new ContentValues();
+ imageValues.put(Photo.PHOTO, stream.toByteArray());
+ imageValues.put(RawContacts.Data.IS_SUPER_PRIMARY, 1);
+
+ // attach the photo to every raw contact
+ for (Long rawContactId : mRawContactIds) {
+
+ // exchange and google only allow one image, so do an update rather than insert
+ boolean shouldUpdate = false;
+
+ final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI,
+ rawContactId);
+ final Uri rawContactDataUri = Uri.withAppendedPath(rawContactUri,
+ RawContacts.Data.CONTENT_DIRECTORY);
+ insertPhoto(imageValues, rawContactDataUri, true);
+ }
+ }
+ }
+ finish();
+ }
+ }
+
+ // TODO: move to background
+ public static ArrayList<Long> queryForAllRawContactIds(ContentResolver cr, long contactId) {
+ Cursor rawContactIdCursor = null;
+ ArrayList<Long> rawContactIds = new ArrayList<Long>();
+ try {
+ rawContactIdCursor = cr.query(RawContacts.CONTENT_URI,
+ new String[] {RawContacts._ID},
+ RawContacts.CONTACT_ID + "=" + contactId, null, null);
+ if (rawContactIdCursor != null) {
+ while (rawContactIdCursor.moveToNext()) {
+ rawContactIds.add(rawContactIdCursor.getLong(0));
+ }
+ }
+ } finally {
+ if (rawContactIdCursor != null) {
+ rawContactIdCursor.close();
+ }
+ }
+ return rawContactIds;
+ }
+
+ /**
+ * Inserts a photo on the raw contact.
+ * @param values the photo values
+ * @param assertAccount if true, will check to verify that no photos exist for Google,
+ * Exchange and unsynced phone account types. These account types only take one picture,
+ * so if one exists, the account will be updated with the new photo.
+ */
+ private void insertPhoto(ContentValues values, Uri rawContactDataUri,
+ boolean assertAccount) {
+
+ ArrayList<ContentProviderOperation> operations =
+ new ArrayList<ContentProviderOperation>();
+
+ if (assertAccount) {
+ // Make sure no pictures exist for Google, Exchange and unsynced phone accounts.
+ operations.add(ContentProviderOperation.newAssertQuery(rawContactDataUri)
+ .withSelection(Photo.MIMETYPE + "=? AND ("
+ + RawContacts.ACCOUNT_TYPE + " IN (?,?) OR "
+ + RawContacts.ACCOUNT_TYPE + " IS NULL)",
+ new String[] {Photo.CONTENT_ITEM_TYPE, GoogleAccountType.ACCOUNT_TYPE,
+ ExchangeAccountType.ACCOUNT_TYPE})
+ .withExpectedCount(0).build());
+ }
+
+ // insert the photo
+ values.put(Photo.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
+ operations.add(ContentProviderOperation.newInsert(rawContactDataUri)
+ .withValues(values).build());
+
+ try {
+ mContentResolver.applyBatch(ContactsContract.AUTHORITY, operations);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Problem querying raw_contacts/data", e);
+ } catch (OperationApplicationException e) {
+ // the account doesn't allow multiple photos, so update
+ if (assertAccount) {
+ updatePhoto(values, rawContactDataUri, false);
+ } else {
+ throw new IllegalStateException("Problem inserting photo into raw_contacts/data", e);
+ }
+ }
+ }
+
+ /**
+ * Tries to update the photo on the raw_contact. If no photo exists, and allowInsert == true,
+ * then will try to {@link #updatePhoto(ContentValues, boolean)}
+ */
+ private void updatePhoto(ContentValues values, Uri rawContactDataUri,
+ boolean allowInsert) {
+ ArrayList<ContentProviderOperation> operations =
+ new ArrayList<ContentProviderOperation>();
+
+ values.remove(Photo.MIMETYPE);
+
+ // check that a photo exists
+ operations.add(ContentProviderOperation.newAssertQuery(rawContactDataUri)
+ .withSelection(Photo.MIMETYPE + "=?", new String[] {
+ Photo.CONTENT_ITEM_TYPE
+ }).withExpectedCount(1).build());
+
+ // update that photo
+ operations.add(ContentProviderOperation.newUpdate(rawContactDataUri)
+ .withSelection(Photo.MIMETYPE + "=?", new String[] {Photo.CONTENT_ITEM_TYPE})
+ .withValues(values).build());
+
+ try {
+ mContentResolver.applyBatch(ContactsContract.AUTHORITY, operations);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Problem querying raw_contacts/data", e);
+ } catch (OperationApplicationException e) {
+ if (allowInsert) {
+ // they deleted the photo between insert and update, so insert one
+ insertPhoto(values, rawContactDataUri, false);
+ } else {
+ throw new IllegalStateException("Problem inserting photo raw_contacts/data", e);
+ }
+ }
+ }
+}
diff --git a/src/com/android/contacts/activities/ContactBrowserActivity.java b/src/com/android/contacts/activities/ContactBrowserActivity.java
new file mode 100644
index 0000000..4c2411c
--- /dev/null
+++ b/src/com/android/contacts/activities/ContactBrowserActivity.java
@@ -0,0 +1,1013 @@
+/*
+ * Copyright (C) 2010 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.activities;
+
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.R;
+import com.android.contacts.detail.ContactDetailFragment;
+import com.android.contacts.interactions.ContactDeletionInteraction;
+import com.android.contacts.interactions.GroupDeletionDialogFragment;
+import com.android.contacts.interactions.GroupRenamingDialogFragment;
+import com.android.contacts.interactions.ImportExportInteraction;
+import com.android.contacts.interactions.PhoneNumberInteraction;
+import com.android.contacts.list.ContactBrowseListContextMenuAdapter;
+import com.android.contacts.list.ContactBrowseListFragment;
+import com.android.contacts.list.ContactEntryListFragment;
+import com.android.contacts.list.ContactListFilter;
+import com.android.contacts.list.ContactListFilterController;
+import com.android.contacts.list.ContactsIntentResolver;
+import com.android.contacts.list.ContactsRequest;
+import com.android.contacts.list.ContactsUnavailableFragment;
+import com.android.contacts.list.CustomContactListFilterActivity;
+import com.android.contacts.list.DirectoryListLoader;
+import com.android.contacts.list.OnContactBrowserActionListener;
+import com.android.contacts.list.OnContactsUnavailableActionListener;
+import com.android.contacts.list.ProviderStatusLoader;
+import com.android.contacts.list.ProviderStatusLoader.ProviderStatusListener;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.preference.ContactsPreferenceActivity;
+import com.android.contacts.util.AccountSelectionUtil;
+import com.android.contacts.util.AccountsListAdapter;
+import com.android.contacts.util.DialogManager;
+import com.android.contacts.widget.ContextMenuAdapter;
+
+import android.accounts.Account;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.Fragment;
+import android.content.ActivityNotFoundException;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Intents;
+import android.provider.ContactsContract.ProviderStatus;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListPopupWindow;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+
+/**
+ * Displays a list to browse contacts. For xlarge screens, this also displays a detail-pane on
+ * the right.
+ */
+public class ContactBrowserActivity extends ContactsActivity
+ implements View.OnCreateContextMenuListener, ActionBarAdapter.Listener,
+ DialogManager.DialogShowingViewActivity,
+ ContactListFilterController.ContactListFilterListener, ProviderStatusListener {
+
+ private static final String TAG = "ContactBrowserActivity";
+
+ private static final int SUBACTIVITY_NEW_CONTACT = 2;
+ private static final int SUBACTIVITY_SETTINGS = 3;
+ private static final int SUBACTIVITY_EDIT_CONTACT = 4;
+ private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 5;
+
+ private static final String KEY_SEARCH_MODE = "searchMode";
+
+ private DialogManager mDialogManager = new DialogManager(this);
+
+ private ContactsIntentResolver mIntentResolver;
+ private ContactsRequest mRequest;
+
+ private boolean mHasActionBar;
+ private ActionBarAdapter mActionBarAdapter;
+
+ private boolean mSearchMode;
+
+ private ContactBrowseListFragment mListFragment;
+ private boolean mContactContentDisplayed;
+ private ContactDetailFragment mDetailFragment;
+ private DetailFragmentListener mDetailFragmentListener = new DetailFragmentListener();
+
+ private PhoneNumberInteraction mPhoneNumberCallInteraction;
+ private PhoneNumberInteraction mSendTextMessageInteraction;
+ private ImportExportInteraction mImportExportInteraction;
+
+ private boolean mSearchInitiated;
+
+ private ContactListFilterController mContactListFilterController;
+
+ private View mAddContactImageView;
+
+ private ContactsUnavailableFragment mContactsUnavailableFragment;
+ private ProviderStatusLoader mProviderStatusLoader;
+ private int mProviderStatus = -1;
+
+ private boolean mOptionsMenuContactsAvailable;
+ private boolean mOptionsMenuGroupActionsEnabled;
+ private boolean mOptionsMenuCustomFilterChangeable;
+
+ public ContactBrowserActivity() {
+ mIntentResolver = new ContactsIntentResolver(this);
+ mContactListFilterController = new ContactListFilterController(this);
+ mContactListFilterController.addListener(this);
+ mProviderStatusLoader = new ProviderStatusLoader(this);
+ }
+
+ public boolean areContactsAvailable() {
+ return mProviderStatus == ProviderStatus.STATUS_NORMAL;
+ }
+
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ if (fragment instanceof ContactBrowseListFragment) {
+ mListFragment = (ContactBrowseListFragment)fragment;
+ mListFragment.setOnContactListActionListener(new ContactBrowserActionListener());
+ if (!getWindow().hasFeature(Window.FEATURE_ACTION_BAR)) {
+ mListFragment.setContextMenuAdapter(
+ new ContactBrowseListContextMenuAdapter(mListFragment));
+ }
+ } else if (fragment instanceof ContactDetailFragment) {
+ mDetailFragment = (ContactDetailFragment)fragment;
+ mDetailFragment.setListener(mDetailFragmentListener);
+ mContactContentDisplayed = true;
+ } else if (fragment instanceof ContactsUnavailableFragment) {
+ mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment;
+ mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader);
+ mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
+ new ContactsUnavailableFragmentListener());
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ mAddContactImageView = getLayoutInflater().inflate(
+ R.layout.add_contact_menu_item, null, false);
+ View item = mAddContactImageView.findViewById(R.id.menu_item);
+ item.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ createNewContact();
+ }
+ });
+
+ configureContentView(true, savedState);
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ setIntent(intent);
+ configureContentView(false, null);
+ }
+
+ private void configureContentView(boolean createContentView, Bundle savedState) {
+ // Extract relevant information from the intent
+ mRequest = mIntentResolver.resolveIntent(getIntent());
+ if (!mRequest.isValid()) {
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ Intent redirect = mRequest.getRedirectIntent();
+ if (redirect != null) {
+ // Need to start a different activity
+ startActivity(redirect);
+ finish();
+ return;
+ }
+
+ if (createContentView) {
+ setContentView(R.layout.contact_browser);
+ }
+
+ if (mRequest.getActionCode() == ContactsRequest.ACTION_VIEW_CONTACT
+ && !mContactContentDisplayed) {
+ redirect = new Intent(this, ContactDetailActivity.class);
+ redirect.setAction(Intent.ACTION_VIEW);
+ redirect.setData(mRequest.getContactUri());
+ startActivity(redirect);
+ finish();
+ return;
+ }
+
+ setTitle(mRequest.getActivityTitle());
+
+ mHasActionBar = getWindow().hasFeature(Window.FEATURE_ACTION_BAR);
+ if (mHasActionBar) {
+ ActionBar actionBar = getActionBar();
+
+ mActionBarAdapter = new ActionBarAdapter(this);
+ mActionBarAdapter.onCreate(savedState, mRequest, actionBar);
+ mActionBarAdapter.setContactListFilterController(mContactListFilterController);
+ }
+
+ configureFragments(savedState == null);
+ }
+
+ @Override
+ protected void onPause() {
+ if (mActionBarAdapter != null) {
+ mActionBarAdapter.setListener(null);
+ }
+
+ mOptionsMenuContactsAvailable = false;
+ mOptionsMenuCustomFilterChangeable = false;
+ mOptionsMenuGroupActionsEnabled = false;
+
+ mProviderStatus = -1;
+ mProviderStatusLoader.setProviderStatusListener(null);
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mActionBarAdapter != null) {
+ mActionBarAdapter.setListener(this);
+ }
+ mProviderStatusLoader.setProviderStatusListener(this);
+ updateFragmentVisibility();
+ }
+
+ @Override
+ protected void onStart() {
+ mContactListFilterController.onStart();
+ super.onStart();
+ }
+
+ @Override
+ protected void onStop() {
+ mContactListFilterController.onStop();
+ super.onStop();
+ }
+
+ private void configureFragments(boolean fromRequest) {
+ if (fromRequest) {
+ ContactListFilter filter = null;
+ int actionCode = mRequest.getActionCode();
+ switch (actionCode) {
+ case ContactsRequest.ACTION_ALL_CONTACTS:
+ filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+ break;
+ case ContactsRequest.ACTION_CONTACTS_WITH_PHONES:
+ filter = new ContactListFilter(
+ ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY);
+ break;
+
+ // TODO: handle FREQUENT and STREQUENT according to the spec
+ case ContactsRequest.ACTION_FREQUENT:
+ case ContactsRequest.ACTION_STREQUENT:
+ // For now they are treated the same as STARRED
+ case ContactsRequest.ACTION_STARRED:
+ filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_STARRED);
+ break;
+ }
+
+ mSearchMode = mRequest.isSearchMode();
+ if (filter != null) {
+ mContactListFilterController.setContactListFilter(filter, false);
+ mSearchMode = false;
+ } else if (mRequest.getActionCode() == ContactsRequest.ACTION_ALL_CONTACTS) {
+ mContactListFilterController.setContactListFilter(new ContactListFilter(
+ ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), false);
+ }
+
+ if (mRequest.getContactUri() != null) {
+ mSearchMode = false;
+ }
+
+ mListFragment.setContactsRequest(mRequest);
+ configureListFragmentForRequest();
+
+ } else if (mHasActionBar) {
+ mSearchMode = mActionBarAdapter.isSearchMode();
+ }
+
+ configureListFragment();
+
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onContactListFiltersLoaded() {
+ if (mListFragment == null || !mListFragment.isAdded()) {
+ return;
+ }
+
+ mListFragment.setFilter(mContactListFilterController.getFilter());
+
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onContactListFilterChanged() {
+ if (mListFragment == null || !mListFragment.isAdded()) {
+ return;
+ }
+
+ mListFragment.setFilter(mContactListFilterController.getFilter());
+
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onContactListFilterCustomizationRequest() {
+ startActivityForResult(new Intent(this, CustomContactListFilterActivity.class),
+ SUBACTIVITY_CUSTOMIZE_FILTER);
+ }
+
+ private void setupContactDetailFragment(final Uri contactLookupUri) {
+ mDetailFragment.loadUri(contactLookupUri);
+ }
+
+ /**
+ * Handler for action bar actions.
+ */
+ @Override
+ public void onAction() {
+ configureFragments(false /* from request */);
+ mListFragment.setQueryString(mActionBarAdapter.getQueryString(), true);
+ }
+
+ private void configureListFragmentForRequest() {
+ Uri contactUri = mRequest.getContactUri();
+ if (contactUri != null) {
+ mListFragment.setSelectedContactUri(contactUri);
+ }
+
+ mListFragment.setSearchMode(mRequest.isSearchMode());
+ mListFragment.setQueryString(mRequest.getQueryString(), false);
+
+ if (mRequest.isDirectorySearchEnabled()) {
+ mListFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT);
+ } else {
+ mListFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
+ }
+
+ if (mContactListFilterController.isLoaded()) {
+ mListFragment.setFilter(mContactListFilterController.getFilter());
+ }
+ }
+
+ private void configureListFragment() {
+ mListFragment.setSearchMode(mSearchMode);
+
+ mListFragment.setVisibleScrollbarEnabled(!mSearchMode);
+ mListFragment.setVerticalScrollbarPosition(
+ mContactContentDisplayed
+ ? View.SCROLLBAR_POSITION_LEFT
+ : View.SCROLLBAR_POSITION_RIGHT);
+ mListFragment.setSelectionVisible(mContactContentDisplayed);
+ mListFragment.setQuickContactEnabled(!mContactContentDisplayed);
+ }
+
+ @Override
+ public void onProviderStatusChange() {
+ updateFragmentVisibility();
+ }
+
+ private void updateFragmentVisibility() {
+ int providerStatus = mProviderStatusLoader.getProviderStatus();
+ if (providerStatus == mProviderStatus) {
+ return;
+ }
+
+ mProviderStatus = providerStatus;
+
+ View contactsUnavailableView = findViewById(R.id.contacts_unavailable_view);
+ View mainView = findViewById(R.id.main_view);
+
+ if (mProviderStatus == ProviderStatus.STATUS_NORMAL) {
+ contactsUnavailableView.setVisibility(View.GONE);
+ mainView.setVisibility(View.VISIBLE);
+ if (mListFragment != null) {
+ mListFragment.setEnabled(true);
+ }
+ if (mHasActionBar) {
+ mActionBarAdapter.setEnabled(true);
+ }
+ } else {
+ if (mHasActionBar) {
+ mActionBarAdapter.setEnabled(false);
+ }
+ if (mListFragment != null) {
+ mListFragment.setEnabled(false);
+ }
+ if (mContactsUnavailableFragment == null) {
+ mContactsUnavailableFragment = new ContactsUnavailableFragment();
+ mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader);
+ mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
+ new ContactsUnavailableFragmentListener());
+ getFragmentManager().beginTransaction()
+ .replace(R.id.contacts_unavailable_container, mContactsUnavailableFragment)
+ .commit();
+ } else {
+ mContactsUnavailableFragment.update();
+ }
+ contactsUnavailableView.setVisibility(View.VISIBLE);
+ mainView.setVisibility(View.INVISIBLE);
+ }
+
+ invalidateOptionsMenu();
+ }
+
+ private final class ContactBrowserActionListener implements OnContactBrowserActionListener {
+
+ @Override
+ public void onSelectionChange() {
+ if (mContactContentDisplayed) {
+ setupContactDetailFragment(mListFragment.getSelectedContactUri());
+ }
+ }
+
+ @Override
+ public void onViewContactAction(Uri contactLookupUri) {
+ if (mContactContentDisplayed) {
+ setupContactDetailFragment(contactLookupUri);
+ } else {
+ startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri));
+ }
+ }
+
+ @Override
+ public void onCreateNewContactAction() {
+ Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ intent.putExtras(extras);
+ }
+ startActivity(intent);
+ }
+
+ @Override
+ public void onEditContactAction(Uri contactLookupUri) {
+ Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri);
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ intent.putExtras(extras);
+ }
+ startActivityForResult(intent, SUBACTIVITY_EDIT_CONTACT);
+ }
+
+ @Override
+ public void onAddToFavoritesAction(Uri contactUri) {
+ ContentValues values = new ContentValues(1);
+ values.put(Contacts.STARRED, 1);
+ getContentResolver().update(contactUri, values, null, null);
+ }
+
+ @Override
+ public void onRemoveFromFavoritesAction(Uri contactUri) {
+ ContentValues values = new ContentValues(1);
+ values.put(Contacts.STARRED, 0);
+ getContentResolver().update(contactUri, values, null, null);
+ }
+
+ @Override
+ public void onCallContactAction(Uri contactUri) {
+ getPhoneNumberCallInteraction().startInteraction(contactUri);
+ }
+
+ @Override
+ public void onSmsContactAction(Uri contactUri) {
+ getSendTextMessageInteraction().startInteraction(contactUri);
+ }
+
+ @Override
+ public void onDeleteContactAction(Uri contactUri) {
+ ContactDeletionInteraction.start(ContactBrowserActivity.this, contactUri);
+ }
+
+ @Override
+ public void onFinishAction() {
+ onBackPressed();
+ }
+
+ @Override
+ public void onInvalidSelection() {
+ ContactListFilter filter;
+ ContactListFilter currentFilter = mListFragment.getFilter();
+ if (currentFilter != null
+ && currentFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
+ filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+ mListFragment.setFilter(filter);
+ } else {
+ filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_SINGLE_CONTACT);
+ mListFragment.setFilter(filter, false);
+ }
+ mContactListFilterController.setContactListFilter(filter, true);
+ }
+ }
+
+ private class DetailFragmentListener implements ContactDetailFragment.Listener {
+ @Override
+ public void onContactNotFound() {
+ // Nothing needs to be done here
+ }
+
+ @Override
+ public void onEditRequested(Uri contactLookupUri) {
+ startActivityForResult(
+ new Intent(Intent.ACTION_EDIT, contactLookupUri), SUBACTIVITY_EDIT_CONTACT);
+ }
+
+ @Override
+ public void onItemClicked(Intent intent) {
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found for intent: " + intent);
+ }
+ }
+
+ @Override
+ public void onDeleteRequested(Uri contactUri) {
+ ContactDeletionInteraction.start(ContactBrowserActivity.this, contactUri);
+ }
+
+ @Override
+ public void onCreateRawContactRequested(ArrayList<ContentValues> values, Account account) {
+ Toast.makeText(ContactBrowserActivity.this, R.string.toast_making_personal_copy,
+ Toast.LENGTH_LONG).show();
+ Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
+ ContactBrowserActivity.this, values, account,
+ ContactBrowserActivity.class, Intent.ACTION_VIEW);
+ startService(serviceIntent);
+ }
+ }
+
+ private class ContactsUnavailableFragmentListener
+ implements OnContactsUnavailableActionListener {
+
+ @Override
+ public void onCreateNewContactAction() {
+ startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
+ }
+
+ @Override
+ public void onAddAccountAction() {
+ Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ intent.putExtra(Settings.EXTRA_AUTHORITIES,
+ new String[] { ContactsContract.AUTHORITY });
+ startActivity(intent);
+ }
+
+ @Override
+ public void onImportContactsFromFileAction() {
+ AccountSelectionUtil.doImportFromSdCard(ContactBrowserActivity.this, null);
+ }
+
+ @Override
+ public void onFreeInternalStorageAction() {
+ startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS));
+ }
+ }
+
+ public void startActivityAndForwardResult(final Intent intent) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+
+ // Forward extras to the new activity
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ intent.putExtras(extras);
+ }
+ startActivity(intent);
+ finish();
+ }
+
+ @Override
+ public boolean onCreatePanelMenu(int featureId, Menu menu) {
+ // No menu if contacts are unavailable
+ if (!areContactsAvailable()) {
+ return false;
+ }
+
+ return super.onCreatePanelMenu(featureId, menu);
+ }
+
+ @Override
+ public boolean onPreparePanel(int featureId, View view, Menu menu) {
+ // No menu if contacts are unavailable
+ if (!areContactsAvailable()) {
+ return false;
+ }
+
+ return super.onPreparePanel(featureId, view, menu);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ if (!areContactsAvailable()) {
+ return false;
+ }
+
+ super.onCreateOptionsMenu(menu);
+
+ MenuInflater inflater = getMenuInflater();
+ if (mHasActionBar) {
+ inflater.inflate(R.menu.actions, menu);
+
+ // Change add contact button to button with a custom view
+ final MenuItem addContact = menu.findItem(R.id.menu_add);
+ addContact.setActionView(mAddContactImageView);
+ return true;
+ } else if (mRequest.getActionCode() == ContactsRequest.ACTION_DEFAULT ||
+ mRequest.getActionCode() == ContactsRequest.ACTION_STREQUENT) {
+ inflater.inflate(R.menu.list, menu);
+ return true;
+ } else if (!mListFragment.isSearchMode()) {
+ inflater.inflate(R.menu.search, menu);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void invalidateOptionsMenu() {
+ if (isOptionsMenuChanged()) {
+ super.invalidateOptionsMenu();
+ }
+ }
+
+ public boolean isOptionsMenuChanged() {
+ if (mOptionsMenuContactsAvailable != areContactsAvailable()) {
+ return true;
+ }
+
+ if (mOptionsMenuGroupActionsEnabled != areGroupActionsEnabled()) {
+ return true;
+ }
+
+ if (mOptionsMenuCustomFilterChangeable != isCustomFilterChangeable()) {
+ return true;
+ }
+
+ if (mListFragment != null && mListFragment.isOptionsMenuChanged()) {
+ return true;
+ }
+
+ if (mDetailFragment != null && mDetailFragment.isOptionsMenuChanged()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ mOptionsMenuContactsAvailable = areContactsAvailable();
+ if (!mOptionsMenuContactsAvailable) {
+ return false;
+ }
+
+ MenuItem settings = menu.findItem(R.id.menu_settings);
+ settings.setVisible(!ContactsPreferenceActivity.isEmpty(this));
+
+ mOptionsMenuCustomFilterChangeable = isCustomFilterChangeable();
+
+ MenuItem displayGroups = menu.findItem(R.id.menu_display_groups);
+ if (displayGroups != null) {
+ displayGroups.setVisible(mOptionsMenuCustomFilterChangeable);
+ }
+
+ mOptionsMenuGroupActionsEnabled = areGroupActionsEnabled();
+
+ MenuItem renameGroup = menu.findItem(R.id.menu_rename_group);
+ if (renameGroup != null) {
+ renameGroup.setVisible(mOptionsMenuGroupActionsEnabled);
+ }
+
+ MenuItem deleteGroup = menu.findItem(R.id.menu_delete_group);
+ if (deleteGroup != null) {
+ deleteGroup.setVisible(mOptionsMenuGroupActionsEnabled);
+ }
+
+ return true;
+ }
+
+ private boolean areGroupActionsEnabled() {
+ boolean groupActionsEnabled = false;
+ if (mListFragment != null) {
+ ContactListFilter filter = mListFragment.getFilter();
+ if (filter != null
+ && filter.filterType == ContactListFilter.FILTER_TYPE_GROUP
+ && !filter.groupReadOnly) {
+ groupActionsEnabled = true;
+ }
+ }
+ return groupActionsEnabled;
+ }
+
+ public boolean isCustomFilterChangeable() {
+ return mRequest != null && mRequest.getActionCode() == ContactsRequest.ACTION_DEFAULT;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_settings: {
+ final Intent intent = new Intent(this, ContactsPreferenceActivity.class);
+ startActivityForResult(intent, SUBACTIVITY_SETTINGS);
+ return true;
+ }
+ case R.id.menu_search: {
+ onSearchRequested();
+ return true;
+ }
+ case R.id.menu_add: {
+ final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+ startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
+ return true;
+ }
+ case R.id.menu_import_export: {
+ getImportExportInteraction().startInteraction();
+ return true;
+ }
+ case R.id.menu_accounts: {
+ final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
+ intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] {
+ ContactsContract.AUTHORITY
+ });
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ startActivity(intent);
+ return true;
+ }
+ case R.id.menu_rename_group: {
+ ContactListFilter filter = mListFragment.getFilter();
+ GroupRenamingDialogFragment.show(getFragmentManager(), filter.groupId,
+ filter.title);
+ return true;
+ }
+ case R.id.menu_delete_group: {
+ ContactListFilter filter = mListFragment.getFilter();
+ GroupDeletionDialogFragment.show(getFragmentManager(), filter.groupId,
+ filter.title);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void createNewContact() {
+ final ArrayList<Account> accounts =
+ AccountTypeManager.getInstance(this).getAccounts(true);
+ if (accounts.size() <= 1 || mAddContactImageView == null) {
+ // No account to choose or no control to anchor the popup-menu to
+ // ==> just go straight to the editor which will disambig if necessary
+ final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+ startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
+ return;
+ }
+
+ final ListPopupWindow popup = new ListPopupWindow(this, null);
+ popup.setWidth(getResources().getDimensionPixelSize(R.dimen.account_selector_popup_width));
+ popup.setAnchorView(mAddContactImageView);
+ final AccountsListAdapter adapter = new AccountsListAdapter(this, true);
+ popup.setAdapter(adapter);
+ popup.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ popup.dismiss();
+ final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+ intent.putExtra(Intents.Insert.ACCOUNT, adapter.getItem(position));
+ startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
+ }
+ });
+ popup.setModal(true);
+ popup.show();
+ }
+
+ @Override
+ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+ boolean globalSearch) {
+ if (mListFragment != null && mListFragment.isAdded() && !globalSearch) {
+ mListFragment.startSearch(initialQuery);
+ } else {
+ super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+ }
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id, Bundle bundle) {
+ if (DialogManager.isManagedId(id)) return mDialogManager.onCreateDialog(id, bundle);
+
+ Dialog dialog = getPhoneNumberCallInteraction().onCreateDialog(id, bundle);
+ if (dialog != null) return dialog;
+
+ dialog = getSendTextMessageInteraction().onCreateDialog(id, bundle);
+ if (dialog != null) return dialog;
+
+ dialog = getImportExportInteraction().onCreateDialog(id, bundle);
+ if (dialog != null) return dialog;
+
+ return super.onCreateDialog(id, bundle);
+ }
+
+ @Override
+ protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
+ if (getPhoneNumberCallInteraction().onPrepareDialog(id, dialog, bundle)) {
+ return;
+ }
+
+ if (getSendTextMessageInteraction().onPrepareDialog(id, dialog, bundle)) {
+ return;
+ }
+
+ super.onPrepareDialog(id, dialog, bundle);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case SUBACTIVITY_CUSTOMIZE_FILTER: {
+ if (resultCode == Activity.RESULT_OK) {
+ mContactListFilterController.selectCustomFilter();
+ }
+ break;
+ }
+
+ case SUBACTIVITY_EDIT_CONTACT:
+ case SUBACTIVITY_NEW_CONTACT: {
+ if (resultCode == RESULT_OK) {
+ mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
+ mListFragment.reloadDataAndSetSelectedUri(data.getData());
+ }
+ break;
+ }
+
+ case SUBACTIVITY_SETTINGS:
+ break;
+
+ // TODO: Using the new startActivityWithResultFromFragment API this should not be needed
+ // anymore
+ case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER:
+ if (resultCode == RESULT_OK) {
+ mListFragment.onPickerResult(data);
+ }
+
+// TODO fix or remove multipicker code
+// else if (resultCode == RESULT_CANCELED && mMode == MODE_PICK_MULTIPLE_PHONES) {
+// // Finish the activity if the sub activity was canceled as back key is used
+// // to confirm user selection in MODE_PICK_MULTIPLE_PHONES.
+// finish();
+// }
+// break;
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ ContextMenuAdapter menuAdapter = mListFragment.getContextMenuAdapter();
+ if (menuAdapter != null) {
+ return menuAdapter.onContextItemSelected(item);
+ }
+
+ return super.onContextItemSelected(item);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ // TODO move to the fragment
+ switch (keyCode) {
+// case KeyEvent.KEYCODE_CALL: {
+// if (callSelection()) {
+// return true;
+// }
+// break;
+// }
+
+ case KeyEvent.KEYCODE_DEL: {
+ if (deleteSelection()) {
+ return true;
+ }
+ break;
+ }
+ default: {
+ // Bring up the search UI if the user starts typing
+ final int unicodeChar = event.getUnicodeChar();
+
+ if (unicodeChar != 0) {
+ String query = new String(new int[]{ unicodeChar }, 0, 1);
+ if (mHasActionBar) {
+ if (!mActionBarAdapter.isSearchMode()) {
+ mActionBarAdapter.setQueryString(query);
+ mActionBarAdapter.setSearchMode(true);
+ return true;
+ }
+ } else if (!mRequest.isSearchMode()) {
+ if (!mSearchInitiated) {
+ mSearchInitiated = true;
+ startSearch(query, false, null, false);
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mSearchMode && mActionBarAdapter != null) {
+ mActionBarAdapter.setSearchMode(false);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ private boolean deleteSelection() {
+ // TODO move to the fragment
+// if (mActionCode == ContactsRequest.ACTION_DEFAULT) {
+// final int position = mListView.getSelectedItemPosition();
+// if (position != ListView.INVALID_POSITION) {
+// Uri contactUri = getContactUri(position);
+// if (contactUri != null) {
+// doContactDelete(contactUri);
+// return true;
+// }
+// }
+// }
+ return false;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_SEARCH_MODE, mSearchMode);
+ if (mActionBarAdapter != null) {
+ mActionBarAdapter.onSaveInstanceState(outState);
+ }
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle inState) {
+ super.onRestoreInstanceState(inState);
+ mSearchMode = inState.getBoolean(KEY_SEARCH_MODE);
+ if (mActionBarAdapter != null) {
+ mActionBarAdapter.onRestoreInstanceState(inState);
+ }
+ }
+
+ private PhoneNumberInteraction getPhoneNumberCallInteraction() {
+ if (mPhoneNumberCallInteraction == null) {
+ mPhoneNumberCallInteraction = new PhoneNumberInteraction(this, false, null);
+ }
+ return mPhoneNumberCallInteraction;
+ }
+
+ private PhoneNumberInteraction getSendTextMessageInteraction() {
+ if (mSendTextMessageInteraction == null) {
+ mSendTextMessageInteraction = new PhoneNumberInteraction(this, true, null);
+ }
+ return mSendTextMessageInteraction;
+ }
+
+ private ImportExportInteraction getImportExportInteraction() {
+ if (mImportExportInteraction == null) {
+ mImportExportInteraction = new ImportExportInteraction(this);
+ }
+ return mImportExportInteraction;
+ }
+
+ @Override
+ public DialogManager getDialogManager() {
+ return mDialogManager;
+ }
+
+ // Visible for testing
+ public ContactBrowseListFragment getListFragment() {
+ return mListFragment;
+ }
+
+ // Visible for testing
+ public ContactDetailFragment getDetailFragment() {
+ return mDetailFragment;
+ }
+}
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
new file mode 100644
index 0000000..ca26280
--- /dev/null
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2010 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.activities;
+
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.ContactsSearchManager;
+import com.android.contacts.R;
+import com.android.contacts.detail.ContactDetailFragment;
+import com.android.contacts.interactions.ContactDeletionInteraction;
+
+import android.accounts.Account;
+import android.content.ActivityNotFoundException;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+
+public class ContactDetailActivity extends ContactsActivity {
+ private static final String TAG = "ContactDetailActivity";
+
+ private ContactDetailFragment mFragment;
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ setContentView(R.layout.contact_detail_activity);
+
+ mFragment = (ContactDetailFragment) getFragmentManager().findFragmentById(
+ R.id.contact_detail_fragment);
+ mFragment.setListener(mFragmentListener);
+ mFragment.loadUri(getIntent().getData());
+
+ Log.i(TAG, getIntent().getData().toString());
+ }
+
+ @Override
+ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+ boolean globalSearch) {
+ if (globalSearch) {
+ super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+ } else {
+ ContactsSearchManager.startSearch(this, initialQuery);
+ }
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (mFragment.handleKeyDown(keyCode)) return true;
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private final ContactDetailFragment.Listener mFragmentListener =
+ new ContactDetailFragment.Listener() {
+ @Override
+ public void onContactNotFound() {
+ finish();
+ }
+
+ @Override
+ public void onEditRequested(Uri contactLookupUri) {
+ startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri));
+ }
+
+ @Override
+ public void onItemClicked(Intent intent) {
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found for intent: " + intent);
+ }
+ }
+
+ @Override
+ public void onDeleteRequested(Uri contactUri) {
+ ContactDeletionInteraction.start(ContactDetailActivity.this, contactUri);
+ }
+
+ @Override
+ public void onCreateRawContactRequested(
+ ArrayList<ContentValues> values, Account account) {
+ Toast.makeText(ContactDetailActivity.this, R.string.toast_making_personal_copy,
+ Toast.LENGTH_LONG).show();
+ Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
+ ContactDetailActivity.this, values, account,
+ ContactDetailActivity.class, Intent.ACTION_VIEW);
+ startService(serviceIntent);
+
+ }
+ };
+}
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
new file mode 100644
index 0000000..00e0ab0
--- /dev/null
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2010 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.activities;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.ContactsSearchManager;
+import com.android.contacts.R;
+import com.android.contacts.editor.ContactEditorFragment;
+import com.android.contacts.editor.ContactEditorFragment.SaveMode;
+import com.android.contacts.interactions.ContactDeletionInteraction;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.util.DialogManager;
+
+import android.accounts.Account;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import java.util.ArrayList;
+
+public class ContactEditorActivity extends ContactsActivity
+ implements DialogManager.DialogShowingViewActivity {
+ private static final String TAG = "ContactEditorActivity";
+
+ public static final String ACTION_JOIN_COMPLETED = "joinCompleted";
+
+ private ContactEditorFragment mFragment;
+ private Button mDoneButton;
+ private Button mRevertButton;
+
+ private DialogManager mDialogManager = new DialogManager(this);
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+ String action = getIntent().getAction();
+
+ // The only situation where action could be ACTION_JOIN_COMPLETED is if the
+ // user joined the contact with another and closed the activity before
+ // the save operation was completed. The activity should remain closed then.
+ if (ACTION_JOIN_COMPLETED.equals(action)) {
+ finish();
+ return;
+ }
+
+ setContentView(R.layout.contact_editor_activity);
+
+ // This Activity will always fall back to the "top" Contacts screen when touched on the
+ // app up icon, regardless of launch context.
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
+ }
+
+ mFragment = (ContactEditorFragment) getFragmentManager().findFragmentById(
+ R.id.contact_editor_fragment);
+ mFragment.setListener(mFragmentListener);
+ Uri uri = Intent.ACTION_EDIT.equals(action) ? getIntent().getData() : null;
+ mFragment.load(action, uri, getIntent().getExtras());
+
+ // Depending on the use-case, this activity has Done and Revert buttons or not.
+ mDoneButton = (Button) findViewById(R.id.done);
+ mRevertButton = (Button) findViewById(R.id.revert);
+ if (mDoneButton != null) mDoneButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mFragment.save(SaveMode.CLOSE);
+ }
+ });
+ if (mRevertButton != null) mRevertButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ if (mFragment == null) {
+ return;
+ }
+
+ String action = intent.getAction();
+ if (Intent.ACTION_EDIT.equals(action)) {
+ mFragment.setIntentExtras(intent.getExtras());
+ } else if (ACTION_JOIN_COMPLETED.equals(action)) {
+ mFragment.onJoinCompleted(intent.getData());
+ }
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id, Bundle args) {
+ if (DialogManager.isManagedId(id)) return mDialogManager.onCreateDialog(id, args);
+
+ // Nobody knows about the Dialog
+ Log.w(TAG, "Unknown dialog requested, id: " + id + ", args: " + args);
+ return null;
+ }
+
+ @Override
+ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+ boolean globalSearch) {
+ if (globalSearch) {
+ super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+ } else {
+ ContactsSearchManager.startSearch(this, initialQuery);
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ mFragment.save(SaveMode.CLOSE);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home: {
+ mFragment.save(SaveMode.HOME);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private final ContactEditorFragment.Listener mFragmentListener =
+ new ContactEditorFragment.Listener() {
+ @Override
+ public void onReverted() {
+ finish();
+ }
+
+ @Override
+ public void onSaveFinished(int resultCode, Intent resultIntent, boolean navigateHome) {
+ setResult(resultCode, resultIntent);
+ if (navigateHome) {
+ Intent intent = new Intent(ContactEditorActivity.this,
+ ContactBrowserActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ }
+ finish();
+ }
+
+ @Override
+ public void onContactSplit(Uri newLookupUri) {
+ finish();
+ }
+
+ @Override
+ public void onAccountSelectorAborted() {
+ finish();
+ }
+
+ @Override
+ public void onContactNotFound() {
+ setResult(Activity.RESULT_CANCELED, null);
+ finish();
+ }
+
+ @Override
+ public void setTitleTo(int resourceId) {
+ setTitle(resourceId);
+ }
+
+ @Override
+ public void onDeleteRequested(Uri contactUri) {
+ ContactDeletionInteraction.start(ContactEditorActivity.this, contactUri);
+ }
+
+ @Override
+ public void onEditOtherContactRequested(
+ Uri contactLookupUri, ArrayList<ContentValues> values) {
+ Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri);
+ intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ intent.putExtra(ContactEditorFragment.INTENT_EXTRA_ADD_TO_DEFAULT_DIRECTORY, "");
+
+ // Pass on all the data that has been entered so far
+ if (values != null && values.size() != 0) {
+ intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, values);
+ }
+
+ startActivity(intent);
+ finish();
+ }
+
+ @Override
+ public void onCustomCreateContactActivityRequested(Account account, Bundle intentExtras) {
+ final AccountTypeManager accountTypes =
+ AccountTypeManager.getInstance(ContactEditorActivity.this);
+ final AccountType accountType = accountTypes.getAccountType(account.type);
+
+ Intent intent = new Intent();
+ intent.setClassName(accountType.resPackageName,
+ accountType.getCreateContactActivityClassName());
+ intent.setAction(Intent.ACTION_INSERT);
+ intent.setType(Contacts.CONTENT_ITEM_TYPE);
+ if (intentExtras != null) {
+ intent.putExtras(intentExtras);
+ }
+ intent.putExtra(RawContacts.ACCOUNT_NAME, account.name);
+ intent.putExtra(RawContacts.ACCOUNT_TYPE, account.type);
+ intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ startActivity(intent);
+ finish();
+ }
+
+ @Override
+ public void onCustomEditContactActivityRequested(Account account, Uri rawContactUri,
+ Bundle intentExtras, boolean redirect) {
+ final AccountTypeManager accountTypes =
+ AccountTypeManager.getInstance(ContactEditorActivity.this);
+ final AccountType accountType = accountTypes.getAccountType(account.type);
+
+ Intent intent = new Intent();
+ intent.setClassName(accountType.resPackageName,
+ accountType.getEditContactActivityClassName());
+ intent.setAction(Intent.ACTION_EDIT);
+ intent.setData(rawContactUri);
+ if (intentExtras != null) {
+ intent.putExtras(intentExtras);
+ }
+
+ if (redirect) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ startActivity(intent);
+ finish();
+ } else {
+ startActivity(intent);
+ }
+ }
+ };
+
+ @Override
+ public DialogManager getDialogManager() {
+ return mDialogManager;
+ }
+}
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
new file mode 100644
index 0000000..4efc43f
--- /dev/null
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -0,0 +1,390 @@
+/*
+ * 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.activities;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.R;
+import com.android.contacts.list.ContactEntryListFragment;
+import com.android.contacts.list.ContactPickerFragment;
+import com.android.contacts.list.ContactsIntentResolver;
+import com.android.contacts.list.ContactsRequest;
+import com.android.contacts.list.DirectoryListLoader;
+import com.android.contacts.list.OnContactPickerActionListener;
+import com.android.contacts.list.OnPhoneNumberPickerActionListener;
+import com.android.contacts.list.OnPostalAddressPickerActionListener;
+import com.android.contacts.list.PhoneNumberPickerFragment;
+import com.android.contacts.list.PostalAddressPickerFragment;
+import com.android.contacts.widget.ContextMenuAdapter;
+
+import android.app.Fragment;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+import android.text.TextUtils;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.SearchView;
+import android.widget.SearchView.OnQueryTextListener;
+
+/**
+ * Displays a list of contacts (or phone numbers or postal addresses) for the
+ * purposes of selecting one.
+ */
+public class ContactSelectionActivity extends ContactsActivity
+ implements View.OnCreateContextMenuListener, OnQueryTextListener, OnClickListener {
+ private static final String TAG = "ContactSelectionActivity";
+
+ private static final String KEY_ACTION_CODE = "actionCode";
+ private static final int DEFAULT_DIRECTORY_RESULT_LIMIT = 20;
+
+ // Delay to allow the UI to settle before making search view visible
+ private static final int FOCUS_DELAY = 200;
+
+ private ContactsIntentResolver mIntentResolver;
+ protected ContactEntryListFragment<?> mListFragment;
+
+ private int mActionCode = -1;
+
+ private ContactsRequest mRequest;
+ private SearchView mSearchView;
+
+ public ContactSelectionActivity() {
+ mIntentResolver = new ContactsIntentResolver(this);
+ }
+
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ if (fragment instanceof ContactEntryListFragment<?>) {
+ mListFragment = (ContactEntryListFragment<?>) fragment;
+ setupActionListener();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ if (savedState != null) {
+ mActionCode = savedState.getInt(KEY_ACTION_CODE);
+ }
+
+ // Extract relevant information from the intent
+ mRequest = mIntentResolver.resolveIntent(getIntent());
+ if (!mRequest.isValid()) {
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ Intent redirect = mRequest.getRedirectIntent();
+ if (redirect != null) {
+ // Need to start a different activity
+ startActivity(redirect);
+ finish();
+ return;
+ }
+
+ configureActivityTitle();
+
+ setContentView(R.layout.contact_picker);
+
+ configureListFragment();
+
+ mSearchView = (SearchView)findViewById(R.id.search_view);
+ mSearchView.setQueryHint(getString(R.string.hint_findContacts));
+ mSearchView.setOnQueryTextListener(this);
+
+ // TODO: re-enable search for postal addresses
+ if (mRequest.getActionCode() == ContactsRequest.ACTION_PICK_POSTAL) {
+ mSearchView.setVisibility(View.GONE);
+ } else {
+ // This is a hack to prevent the search view from grabbing focus
+ // at this point. If search view were visible, it would always grabs focus
+ // because it is the first focusable widget in the window.
+ mSearchView.setVisibility(View.INVISIBLE);
+ mSearchView.postDelayed(new Runnable() {
+
+ @Override
+ public void run() {
+ mSearchView.setVisibility(View.VISIBLE);
+ }
+ }, FOCUS_DELAY);
+ }
+
+ Button cancel = (Button) findViewById(R.id.cancel);
+ cancel.setOnClickListener(this);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(KEY_ACTION_CODE, mActionCode);
+ }
+
+ private void configureActivityTitle() {
+ if (mRequest.getActivityTitle() != null) {
+ setTitle(mRequest.getActivityTitle());
+ return;
+ }
+
+ int actionCode = mRequest.getActionCode();
+ switch (actionCode) {
+ case ContactsRequest.ACTION_INSERT_OR_EDIT_CONTACT: {
+ setTitle(R.string.contactPickerActivityTitle);
+ break;
+ }
+
+ case ContactsRequest.ACTION_PICK_CONTACT: {
+ setTitle(R.string.contactPickerActivityTitle);
+ break;
+ }
+
+ case ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT: {
+ setTitle(R.string.contactPickerActivityTitle);
+ break;
+ }
+
+ case ContactsRequest.ACTION_CREATE_SHORTCUT_CONTACT: {
+ setTitle(R.string.shortcutActivityTitle);
+ break;
+ }
+
+ case ContactsRequest.ACTION_PICK_PHONE: {
+ setTitle(R.string.contactPickerActivityTitle);
+ break;
+ }
+
+ case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
+ setTitle(R.string.callShortcutActivityTitle);
+ break;
+ }
+
+ case ContactsRequest.ACTION_CREATE_SHORTCUT_SMS: {
+ setTitle(R.string.messageShortcutActivityTitle);
+ break;
+ }
+
+ case ContactsRequest.ACTION_PICK_POSTAL: {
+ setTitle(R.string.contactPickerActivityTitle);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Creates the fragment based on the current request.
+ */
+ public void configureListFragment() {
+ if (mActionCode == mRequest.getActionCode()) {
+ return;
+ }
+
+ mActionCode = mRequest.getActionCode();
+ switch (mActionCode) {
+ case ContactsRequest.ACTION_INSERT_OR_EDIT_CONTACT: {
+ ContactPickerFragment fragment = new ContactPickerFragment();
+ fragment.setCreateContactEnabled(true);
+ fragment.setEditMode(true);
+ fragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
+ mListFragment = fragment;
+ break;
+ }
+
+ case ContactsRequest.ACTION_PICK_CONTACT: {
+ ContactPickerFragment fragment = new ContactPickerFragment();
+ fragment.setSearchMode(mRequest.isSearchMode());
+ mListFragment = fragment;
+ break;
+ }
+
+ case ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT: {
+ ContactPickerFragment fragment = new ContactPickerFragment();
+ fragment.setCreateContactEnabled(!mRequest.isSearchMode());
+ mListFragment = fragment;
+ break;
+ }
+
+ case ContactsRequest.ACTION_CREATE_SHORTCUT_CONTACT: {
+ ContactPickerFragment fragment = new ContactPickerFragment();
+ fragment.setSearchMode(mRequest.isSearchMode());
+ fragment.setQueryString(mRequest.getQueryString(), false);
+ fragment.setShortcutRequested(true);
+ mListFragment = fragment;
+ break;
+ }
+
+ case ContactsRequest.ACTION_PICK_PHONE: {
+ PhoneNumberPickerFragment fragment = new PhoneNumberPickerFragment();
+ mListFragment = fragment;
+ break;
+ }
+
+ case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
+ PhoneNumberPickerFragment fragment = new PhoneNumberPickerFragment();
+ fragment.setShortcutAction(Intent.ACTION_CALL);
+ fragment.setSearchMode(mRequest.isSearchMode());
+
+ mListFragment = fragment;
+ break;
+ }
+
+ case ContactsRequest.ACTION_CREATE_SHORTCUT_SMS: {
+ PhoneNumberPickerFragment fragment = new PhoneNumberPickerFragment();
+ fragment.setShortcutAction(Intent.ACTION_SENDTO);
+
+ mListFragment = fragment;
+ break;
+ }
+
+ case ContactsRequest.ACTION_PICK_POSTAL: {
+ PostalAddressPickerFragment fragment = new PostalAddressPickerFragment();
+ mListFragment = fragment;
+ break;
+ }
+
+ default:
+ throw new IllegalStateException("Invalid action code: " + mActionCode);
+ }
+
+ mListFragment.setLegacyCompatibilityMode(mRequest.isLegacyCompatibilityMode());
+ mListFragment.setContactsRequest(mRequest);
+ mListFragment.setSearchMode(mRequest.isSearchMode());
+ mListFragment.setQueryString(mRequest.getQueryString(), false);
+ mListFragment.setDirectoryResultLimit(DEFAULT_DIRECTORY_RESULT_LIMIT);
+
+ getFragmentManager().beginTransaction()
+ .replace(R.id.list_container, mListFragment)
+ .commit();
+ }
+
+ public void setupActionListener() {
+ if (mListFragment instanceof ContactPickerFragment) {
+ ((ContactPickerFragment) mListFragment).setOnContactPickerActionListener(
+ new ContactPickerActionListener());
+ } else if (mListFragment instanceof PhoneNumberPickerFragment) {
+ ((PhoneNumberPickerFragment) mListFragment).setOnPhoneNumberPickerActionListener(
+ new PhoneNumberPickerActionListener());
+ } else if (mListFragment instanceof PostalAddressPickerFragment) {
+ ((PostalAddressPickerFragment) mListFragment).setOnPostalAddressPickerActionListener(
+ new PostalAddressPickerActionListener());
+ } else {
+ throw new IllegalStateException("Unsupported list fragment type: " + mListFragment);
+ }
+ }
+
+ private final class ContactPickerActionListener implements OnContactPickerActionListener {
+ @Override
+ public void onCreateNewContactAction() {
+ Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+ startActivityAndForwardResult(intent);
+ }
+
+ @Override
+ public void onEditContactAction(Uri contactLookupUri) {
+ Intent intent = new Intent(Intent.ACTION_EDIT, contactLookupUri);
+ startActivityAndForwardResult(intent);
+ }
+
+ @Override
+ public void onPickContactAction(Uri contactUri) {
+ returnPickerResult(contactUri);
+ }
+
+ @Override
+ public void onShortcutIntentCreated(Intent intent) {
+ returnPickerResult(intent);
+ }
+ }
+
+ private final class PhoneNumberPickerActionListener implements
+ OnPhoneNumberPickerActionListener {
+ @Override
+ public void onPickPhoneNumberAction(Uri dataUri) {
+ returnPickerResult(dataUri);
+ }
+
+ @Override
+ public void onShortcutIntentCreated(Intent intent) {
+ returnPickerResult(intent);
+ }
+ }
+
+ private final class PostalAddressPickerActionListener implements
+ OnPostalAddressPickerActionListener {
+ @Override
+ public void onPickPostalAddressAction(Uri dataUri) {
+ returnPickerResult(dataUri);
+ }
+ }
+
+ public void startActivityAndForwardResult(final Intent intent) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+
+ // Forward extras to the new activity
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ intent.putExtras(extras);
+ }
+ startActivity(intent);
+ finish();
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ ContextMenuAdapter menuAdapter = mListFragment.getContextMenuAdapter();
+ if (menuAdapter != null) {
+ return menuAdapter.onContextItemSelected(item);
+ }
+
+ return super.onContextItemSelected(item);
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ mListFragment.setQueryString(newText, true);
+ mListFragment.setSearchMode(!TextUtils.isEmpty(newText));
+ return false;
+ }
+
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ return false;
+ }
+
+ public void returnPickerResult(Uri data) {
+ Intent intent = new Intent();
+ intent.setData(data);
+ returnPickerResult(intent);
+ }
+
+ public void returnPickerResult(Intent intent) {
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.cancel) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+}
diff --git a/src/com/android/contacts/activities/ContactsFrontDoor.java b/src/com/android/contacts/activities/ContactsFrontDoor.java
new file mode 100644
index 0000000..3677cce
--- /dev/null
+++ b/src/com/android/contacts/activities/ContactsFrontDoor.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 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.activities;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.DialtactsActivity;
+import com.android.contacts.util.PhoneCapabilityTester;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+public class ContactsFrontDoor extends ContactsActivity {
+ public static final String EXTRA_FRONT_DOOR = "front_door";
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ Intent originalIntent = getIntent();
+ Intent intent = new Intent();
+ intent.setAction(originalIntent.getAction());
+ intent.setDataAndType(originalIntent.getData(), originalIntent.getType());
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_FORWARD_RESULT
+ | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(EXTRA_FRONT_DOOR, true);
+
+ if (PhoneCapabilityTester.isPhone(this)) {
+ // Default to the normal dialtacts layout
+ intent.setClass(this, DialtactsActivity.class);
+ } else {
+ // No tabs, just a contact list
+ intent.setClass(this, ContactBrowserActivity.class);
+ }
+
+ startActivity(intent);
+ finish();
+ }
+}
diff --git a/src/com/android/contacts/activities/JoinContactActivity.java b/src/com/android/contacts/activities/JoinContactActivity.java
new file mode 100644
index 0000000..5ee6de2
--- /dev/null
+++ b/src/com/android/contacts/activities/JoinContactActivity.java
@@ -0,0 +1,180 @@
+/*
+ * 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.activities;
+
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.R;
+import com.android.contacts.list.ContactEntryListFragment;
+import com.android.contacts.list.JoinContactListFragment;
+import com.android.contacts.list.OnContactPickerActionListener;
+
+import android.app.Fragment;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+/**
+ * An activity that shows a list of contacts that can be joined with the target contact.
+ */
+public class JoinContactActivity extends ContactsActivity implements OnClickListener {
+
+ private static final String TAG = "JoinContactActivity";
+
+ /**
+ * The action for the join contact activity.
+ * <p>
+ * Input: extra field {@link #EXTRA_TARGET_CONTACT_ID} is the aggregate ID.
+ * TODO: move to {@link ContactsContract}.
+ */
+ public static final String JOIN_CONTACT = "com.android.contacts.action.JOIN_CONTACT";
+
+ /**
+ * Used with {@link #JOIN_CONTACT} to give it the target for aggregation.
+ * <p>
+ * Type: LONG
+ */
+ public static final String EXTRA_TARGET_CONTACT_ID = "com.android.contacts.action.CONTACT_ID";
+
+ private static final String KEY_TARGET_CONTACT_ID = "targetContactId";
+
+ private long mTargetContactId;
+
+ private JoinContactListFragment mListFragment;
+
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ if (fragment instanceof JoinContactListFragment) {
+ mListFragment = (JoinContactListFragment) fragment;
+ setupActionListener();
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ mTargetContactId = intent.getLongExtra(EXTRA_TARGET_CONTACT_ID, -1);
+ if (mTargetContactId == -1) {
+ Log.e(TAG, "Intent " + intent.getAction() + " is missing required extra: "
+ + EXTRA_TARGET_CONTACT_ID);
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ setContentView(R.layout.join_contact_picker);
+ setTitle(R.string.titleJoinContactDataWith);
+
+ findViewById(R.id.cancel).setOnClickListener(this);
+
+ if (mListFragment == null) {
+ mListFragment = new JoinContactListFragment();
+
+ getFragmentManager().beginTransaction()
+ .replace(R.id.list_container, mListFragment)
+ .commit();
+ }
+ }
+
+ public void setupActionListener() {
+ mListFragment.setTargetContactId(mTargetContactId);
+ mListFragment.setOnContactPickerActionListener(new OnContactPickerActionListener() {
+ @Override
+ public void onPickContactAction(Uri contactUri) {
+ Intent intent = new Intent(null, contactUri);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+
+ @Override
+ public void onShortcutIntentCreated(Intent intent) {
+ }
+
+ @Override
+ public void onCreateNewContactAction() {
+ }
+
+ @Override
+ public void onEditContactAction(Uri contactLookupUri) {
+ }
+ });
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putLong(KEY_TARGET_CONTACT_ID, mTargetContactId);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ mTargetContactId = savedInstanceState.getLong(KEY_TARGET_CONTACT_ID);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.search, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_search: {
+ onSearchRequested();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+ boolean globalSearch) {
+ if (globalSearch) {
+ super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+ } else {
+ mListFragment.startSearch(initialQuery);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER
+ && resultCode == RESULT_OK) {
+ mListFragment.onPickerResult(data);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.cancel) {
+ finish();
+ }
+ }
+}
diff --git a/src/com/android/contacts/activities/NonPhoneActivity.java b/src/com/android/contacts/activities/NonPhoneActivity.java
new file mode 100644
index 0000000..26eed7c
--- /dev/null
+++ b/src/com/android/contacts/activities/NonPhoneActivity.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 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.activities;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Intents.Insert;
+import android.text.TextUtils;
+
+/**
+ * Activity that intercepts DIAL and VIEW intents for phone numbers for devices that can not
+ * be used as a phone. This allows the user to see the phone number
+ */
+public class NonPhoneActivity extends ContactsActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final String phoneNumber = getPhoneNumber();
+ if (TextUtils.isEmpty(phoneNumber)) {
+ finish();
+ return;
+ }
+
+ final NonPhoneDialogFragment fragment = new NonPhoneDialogFragment();
+ fragment.setArguments(Bundle.forPair("PHONE_NUMBER", phoneNumber));
+ getFragmentManager().beginTransaction().add(fragment, "Fragment").commit();
+ }
+
+ private String getPhoneNumber() {
+ if (getIntent() == null) return null;
+ final Uri data = getIntent().getData();
+ if (data == null) return null;
+ final String scheme = data.getScheme();
+ if (!"tel".equals(scheme)) return null;
+ return getIntent().getData().getSchemeSpecificPart();
+ }
+
+ public static final class NonPhoneDialogFragment extends DialogFragment
+ implements OnClickListener {
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final AlertDialog alertDialog;
+ alertDialog = new AlertDialog.Builder(getActivity(), R.style.NonPhoneDialogTheme)
+ .create();
+ alertDialog.setTitle(R.string.non_phone_caption);
+ alertDialog.setMessage(getArgumentPhoneNumber());
+ alertDialog.setButton(DialogInterface.BUTTON_POSITIVE,
+ getActivity().getString(R.string.non_phone_add_to_contacts), this);
+ alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
+ getActivity().getString(R.string.non_phone_close), this);
+ return alertDialog;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ intent.setType(Contacts.CONTENT_ITEM_TYPE);
+ intent.putExtra(Insert.PHONE, getArgumentPhoneNumber());
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_NO_HISTORY);
+ startActivity(intent);
+ }
+ dismiss();
+ }
+
+ private String getArgumentPhoneNumber() {
+ return getArguments().getPairValue();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ // During screen rotation, getActivity returns null. In this case we do not
+ // want to close the Activity anyway
+ final Activity activity = getActivity();
+ if (activity != null) activity.finish();
+ }
+ }
+}
diff --git a/src/com/android/contacts/activities/ShowOrCreateActivity.java b/src/com/android/contacts/activities/ShowOrCreateActivity.java
new file mode 100755
index 0000000..7341430
--- /dev/null
+++ b/src/com/android/contacts/activities/ShowOrCreateActivity.java
@@ -0,0 +1,271 @@
+/*
+ * 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.activities;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.ContactsSearchManager;
+import com.android.contacts.R;
+import com.android.contacts.util.Constants;
+import com.android.contacts.util.NotifyingAsyncQueryHandler;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.SearchManager;
+import android.content.ComponentName;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Intents;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.RawContacts;
+import android.util.Log;
+
+/**
+ * Handle several edge cases around showing or possibly creating contacts in
+ * connected with a specific E-mail address or phone number. Will search based
+ * on incoming {@link Intent#getData()} as described by
+ * {@link Intents#SHOW_OR_CREATE_CONTACT}.
+ * <ul>
+ * <li>If no matching contacts found, will prompt user with dialog to add to a
+ * contact, then will use {@link Intent#ACTION_INSERT_OR_EDIT} to let create new
+ * contact or edit new data into an existing one.
+ * <li>If one matching contact found, directly show {@link Intent#ACTION_VIEW}
+ * that specific contact.
+ * <li>If more than one matching found, show list of matching contacts using
+ * {@link Intent#ACTION_SEARCH}.
+ * </ul>
+ */
+public final class ShowOrCreateActivity extends ContactsActivity
+ implements NotifyingAsyncQueryHandler.AsyncQueryListener {
+ static final String TAG = "ShowOrCreateActivity";
+ static final boolean LOGD = false;
+
+ static final String[] PHONES_PROJECTION = new String[] {
+ PhoneLookup._ID,
+ PhoneLookup.LOOKUP_KEY,
+ };
+
+ static final String[] CONTACTS_PROJECTION = new String[] {
+ Email.CONTACT_ID,
+ Email.LOOKUP_KEY,
+ };
+
+ static final int CONTACT_ID_INDEX = 0;
+ static final int LOOKUP_KEY_INDEX = 1;
+
+ static final int CREATE_CONTACT_DIALOG = 1;
+
+ static final int QUERY_TOKEN = 42;
+
+ private NotifyingAsyncQueryHandler mQueryHandler;
+
+ private Bundle mCreateExtras;
+ private String mCreateDescrip;
+ private boolean mCreateForce;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Create handler if doesn't exist, otherwise cancel any running
+ if (mQueryHandler == null) {
+ mQueryHandler = new NotifyingAsyncQueryHandler(this, this);
+ } else {
+ mQueryHandler.cancelOperation(QUERY_TOKEN);
+ }
+
+ final Intent intent = getIntent();
+ final Uri data = intent.getData();
+
+ // Unpack scheme and target data from intent
+ String scheme = null;
+ String ssp = null;
+ if (data != null) {
+ scheme = data.getScheme();
+ ssp = data.getSchemeSpecificPart();
+ }
+
+ // Build set of extras for possible use when creating contact
+ mCreateExtras = new Bundle();
+ Bundle originalExtras = intent.getExtras();
+ if (originalExtras != null) {
+ mCreateExtras.putAll(originalExtras);
+ }
+
+ // Read possible extra with specific title
+ mCreateDescrip = intent.getStringExtra(Intents.EXTRA_CREATE_DESCRIPTION);
+ if (mCreateDescrip == null) {
+ mCreateDescrip = ssp;
+ }
+
+ // Allow caller to bypass dialog prompt
+ mCreateForce = intent.getBooleanExtra(Intents.EXTRA_FORCE_CREATE, false);
+
+ // Handle specific query request
+ if (Constants.SCHEME_MAILTO.equals(scheme)) {
+ mCreateExtras.putString(Intents.Insert.EMAIL, ssp);
+ mCreateExtras.putString(SearchManager.QUERY, ssp);
+
+ Uri uri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, Uri.encode(ssp));
+ mQueryHandler.startQuery(QUERY_TOKEN, null, uri, CONTACTS_PROJECTION, null, null, null);
+
+ } else if (Constants.SCHEME_TEL.equals(scheme)) {
+ mCreateExtras.putString(Intents.Insert.PHONE, ssp);
+ mCreateExtras.putString(SearchManager.QUERY, ssp);
+
+ Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, ssp);
+ mQueryHandler.startQuery(QUERY_TOKEN, null, uri, PHONES_PROJECTION, null, null, null);
+
+ } else {
+ Log.w(TAG, "Invalid intent:" + getIntent());
+ finish();
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (mQueryHandler != null) {
+ mQueryHandler.cancelOperation(QUERY_TOKEN);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ if (cursor == null) {
+ // Bail when problem running query in background
+ finish();
+ return;
+ }
+
+ // Count contacts found by query
+ int count = 0;
+ long contactId = -1;
+ String lookupKey = null;
+ try {
+ count = cursor.getCount();
+ if (count == 1 && cursor.moveToFirst()) {
+ // Try reading ID if only one contact returned
+ contactId = cursor.getLong(CONTACT_ID_INDEX);
+ lookupKey = cursor.getString(LOOKUP_KEY_INDEX);
+ }
+ } finally {
+ cursor.close();
+ }
+
+ if (count == 1 && contactId != -1) {
+ // If we only found one item, jump right to viewing it
+ final Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
+ final Intent viewIntent = new Intent(Intent.ACTION_VIEW, contactUri);
+ startActivity(viewIntent);
+ finish();
+
+ } else if (count > 1) {
+ // If more than one, show pick list
+ Intent listIntent = new Intent(Intent.ACTION_SEARCH);
+ listIntent.setComponent(new ComponentName(this, ContactBrowserActivity.class));
+ listIntent.putExtras(mCreateExtras);
+ startActivity(listIntent);
+ finish();
+
+ } else {
+ // No matching contacts found
+ if (mCreateForce) {
+ // Forced to create new contact
+ Intent createIntent = new Intent(Intent.ACTION_INSERT, RawContacts.CONTENT_URI);
+ createIntent.putExtras(mCreateExtras);
+ createIntent.setType(RawContacts.CONTENT_TYPE);
+
+ startActivity(createIntent);
+ finish();
+
+ } else {
+ showDialog(CREATE_CONTACT_DIALOG);
+ }
+ }
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch(id) {
+ case CREATE_CONTACT_DIALOG:
+ // Prompt user to insert or edit contact
+ final Intent createIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ createIntent.putExtras(mCreateExtras);
+ createIntent.setType(RawContacts.CONTENT_ITEM_TYPE);
+
+ final CharSequence message = getResources().getString(
+ R.string.add_contact_dlg_message_fmt, mCreateDescrip);
+
+ return new AlertDialog.Builder(this, AlertDialog.THEME_HOLO_LIGHT)
+ .setTitle(R.string.add_contact_dlg_title)
+ .setMessage(message)
+ .setPositiveButton(android.R.string.ok,
+ new IntentClickListener(this, createIntent))
+ .setNegativeButton(android.R.string.cancel,
+ new IntentClickListener(this, null))
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish(); // Close the activity.
+ }})
+ .create();
+ }
+ return super.onCreateDialog(id);
+ }
+
+ /**
+ * Listener for {@link DialogInterface} that launches a given {@link Intent}
+ * when clicked. When clicked, this also closes the parent using
+ * {@link Activity#finish()}.
+ */
+ private static class IntentClickListener implements DialogInterface.OnClickListener {
+ private Activity mParent;
+ private Intent mIntent;
+
+ /**
+ * @param parent {@link Activity} to use for launching target.
+ * @param intent Target {@link Intent} to launch when clicked.
+ */
+ public IntentClickListener(Activity parent, Intent intent) {
+ mParent = parent;
+ mIntent = intent;
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (mIntent != null) {
+ mParent.startActivity(mIntent);
+ }
+ mParent.finish();
+ }
+ }
+
+ @Override
+ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+ boolean globalSearch) {
+ if (globalSearch) {
+ super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+ } else {
+ ContactsSearchManager.startSearch(this, initialQuery);
+ }
+ }
+}
diff --git a/src/com/android/contacts/datepicker/DatePicker.java b/src/com/android/contacts/datepicker/DatePicker.java
new file mode 100644
index 0000000..7ea9641
--- /dev/null
+++ b/src/com/android/contacts/datepicker/DatePicker.java
@@ -0,0 +1,481 @@
+/*
+ * 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.datepicker;
+
+// This is a fork of the standard Android DatePicker that additionally allows toggling the year
+// on/off. It uses some private API so that not everything has to be copied.
+
+import com.android.contacts.R;
+
+import android.annotation.Widget;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.format.DateFormat;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.NumberPicker;
+import android.widget.NumberPicker.OnValueChangeListener;
+
+import java.text.DateFormatSymbols;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
+/**
+ * A view for selecting a month / year / day based on a calendar like layout.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
+ * tutorial</a>.</p>
+ *
+ * For a dialog using this view, see {@link android.app.DatePickerDialog}.
+ */
+@Widget
+public class DatePicker extends FrameLayout {
+
+ private static final int DEFAULT_START_YEAR = 1900;
+ private static final int DEFAULT_END_YEAR = 2100;
+
+ /* UI Components */
+ private final CheckBox mYearToggle;
+ private final NumberPicker mDayPicker;
+ private final NumberPicker mMonthPicker;
+ private final NumberPicker mYearPicker;
+
+ /**
+ * How we notify users the date has changed.
+ */
+ private OnDateChangedListener mOnDateChangedListener;
+
+ private int mDay;
+ private int mMonth;
+ private int mYear;
+ private boolean mYearOptional;
+ private boolean mHasYear;
+
+ /**
+ * The callback used to indicate the user changes the date.
+ */
+ public interface OnDateChangedListener {
+
+ /**
+ * @param view The view associated with this listener.
+ * @param year The year that was set.
+ * @param monthOfYear The month that was set (0-11) for compatibility
+ * with {@link java.util.Calendar}.
+ * @param dayOfMonth The day of the month that was set.
+ */
+ void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth);
+ }
+
+ public DatePicker(Context context) {
+ this(context, null);
+ }
+
+ public DatePicker(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DatePicker(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ LayoutInflater inflater = (LayoutInflater) context.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.date_picker, this, true);
+
+ mDayPicker = (NumberPicker) findViewById(R.id.day);
+ mDayPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
+ mDayPicker.setOnLongPressUpdateInterval(100);
+ mDayPicker.setOnValueChangedListener(new OnValueChangeListener() {
+ public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+ mDay = newVal;
+ notifyDateChanged();
+ }
+ });
+ mMonthPicker = (NumberPicker) findViewById(R.id.month);
+ mMonthPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
+ DateFormatSymbols dfs = new DateFormatSymbols();
+ String[] months = dfs.getShortMonths();
+
+ /*
+ * If the user is in a locale where the month names are numeric,
+ * use just the number instead of the "month" character for
+ * consistency with the other fields.
+ */
+ if (months[0].startsWith("1")) {
+ for (int i = 0; i < months.length; i++) {
+ months[i] = String.valueOf(i + 1);
+ }
+ mMonthPicker.setMinValue(1);
+ mMonthPicker.setMaxValue(12);
+ } else {
+ mMonthPicker.setMinValue(1);
+ mMonthPicker.setMaxValue(12);
+ mMonthPicker.setDisplayedValues(months);
+ }
+
+ mMonthPicker.setOnLongPressUpdateInterval(200);
+ mMonthPicker.setOnValueChangedListener(new OnValueChangeListener() {
+ public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+
+ /* We display the month 1-12 but store it 0-11 so always
+ * subtract by one to ensure our internal state is always 0-11
+ */
+ mMonth = newVal - 1;
+ // Adjust max day of the month
+ adjustMaxDay();
+ notifyDateChanged();
+ updateDaySpinner();
+ }
+ });
+ mYearPicker = (NumberPicker) findViewById(R.id.year);
+ mYearPicker.setOnLongPressUpdateInterval(100);
+ mYearPicker.setOnValueChangedListener(new OnValueChangeListener() {
+ public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+ mYear = newVal;
+ // Adjust max day for leap years if needed
+ adjustMaxDay();
+ notifyDateChanged();
+ updateDaySpinner();
+ }
+ });
+
+ mYearToggle = (CheckBox) findViewById(R.id.yearToggle);
+ mYearToggle.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ mHasYear = isChecked;
+ adjustMaxDay();
+ notifyDateChanged();
+ updateSpinners();
+ }
+ });
+
+ // attributes
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.DatePicker);
+
+ int mStartYear =
+ a.getInt(com.android.internal.R.styleable.DatePicker_startYear, DEFAULT_START_YEAR);
+ int mEndYear =
+ a.getInt(com.android.internal.R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
+ mYearPicker.setMinValue(mStartYear);
+ mYearPicker.setMaxValue(mEndYear);
+
+ a.recycle();
+
+ // initialize to current date
+ Calendar cal = Calendar.getInstance();
+ init(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), null);
+
+ // re-order the number pickers to match the current date format
+ reorderPickers(months);
+
+ if (!isEnabled()) {
+ setEnabled(false);
+ }
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ mDayPicker.setEnabled(enabled);
+ mMonthPicker.setEnabled(enabled);
+ mYearPicker.setEnabled(enabled);
+ }
+
+ private void reorderPickers(String[] months) {
+ java.text.DateFormat format;
+ String order;
+
+ /*
+ * If the user is in a locale where the medium date format is
+ * still numeric (Japanese and Czech, for example), respect
+ * the date format order setting. Otherwise, use the order
+ * that the locale says is appropriate for a spelled-out date.
+ */
+
+ if (months[0].startsWith("1")) {
+ format = DateFormat.getDateFormat(getContext());
+ } else {
+ format = DateFormat.getMediumDateFormat(getContext());
+ }
+
+ if (format instanceof SimpleDateFormat) {
+ order = ((SimpleDateFormat) format).toPattern();
+ } else {
+ // Shouldn't happen, but just in case.
+ order = new String(DateFormat.getDateFormatOrder(getContext()));
+ }
+
+ /* Remove the 3 pickers from their parent and then add them back in the
+ * required order.
+ */
+ LinearLayout parent = (LinearLayout) findViewById(R.id.parent);
+ parent.removeAllViews();
+
+ boolean quoted = false;
+ boolean didDay = false, didMonth = false, didYear = false;
+
+ for (int i = 0; i < order.length(); i++) {
+ char c = order.charAt(i);
+
+ if (c == '\'') {
+ quoted = !quoted;
+ }
+
+ if (!quoted) {
+ if (c == DateFormat.DATE && !didDay) {
+ parent.addView(mDayPicker);
+ didDay = true;
+ } else if ((c == DateFormat.MONTH || c == 'L') && !didMonth) {
+ parent.addView(mMonthPicker);
+ didMonth = true;
+ } else if (c == DateFormat.YEAR && !didYear) {
+ parent.addView (mYearPicker);
+ didYear = true;
+ }
+ }
+ }
+
+ // Shouldn't happen, but just in case.
+ if (!didMonth) {
+ parent.addView(mMonthPicker);
+ }
+ if (!didDay) {
+ parent.addView(mDayPicker);
+ }
+ if (!didYear) {
+ parent.addView(mYearPicker);
+ }
+ }
+
+ public void updateDate(int year, int monthOfYear, int dayOfMonth) {
+ if (mYear != year || mMonth != monthOfYear || mDay != dayOfMonth) {
+ mYear = (mYearOptional && year == 0) ? getCurrentYear() : year;
+ mMonth = monthOfYear;
+ mDay = dayOfMonth;
+ updateSpinners();
+ reorderPickers(new DateFormatSymbols().getShortMonths());
+ notifyDateChanged();
+ }
+ }
+
+ private int getCurrentYear() {
+ return Calendar.getInstance().get(Calendar.YEAR);
+ }
+
+ private static class SavedState extends BaseSavedState {
+
+ private final int mYear;
+ private final int mMonth;
+ private final int mDay;
+ private final boolean mHasYear;
+ private final boolean mYearOptional;
+
+ /**
+ * Constructor called from {@link DatePicker#onSaveInstanceState()}
+ */
+ private SavedState(Parcelable superState, int year, int month, int day, boolean hasYear,
+ boolean yearOptional) {
+ super(superState);
+ mYear = year;
+ mMonth = month;
+ mDay = day;
+ mHasYear = hasYear;
+ mYearOptional = yearOptional;
+ }
+
+ /**
+ * Constructor called from {@link #CREATOR}
+ */
+ private SavedState(Parcel in) {
+ super(in);
+ mYear = in.readInt();
+ mMonth = in.readInt();
+ mDay = in.readInt();
+ mHasYear = in.readInt() != 0;
+ mYearOptional = in.readInt() != 0;
+ }
+
+ public int getYear() {
+ return mYear;
+ }
+
+ public int getMonth() {
+ return mMonth;
+ }
+
+ public int getDay() {
+ return mDay;
+ }
+
+ public boolean hasYear() {
+ return mHasYear;
+ }
+
+ public boolean isYearOptional() {
+ return mYearOptional;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mYear);
+ dest.writeInt(mMonth);
+ dest.writeInt(mDay);
+ dest.writeInt(mHasYear ? 1 : 0);
+ dest.writeInt(mYearOptional ? 1 : 0);
+ }
+
+ @SuppressWarnings("unused")
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Creator<SavedState>() {
+
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
+
+ /**
+ * Override so we are in complete control of save / restore for this widget.
+ */
+ @Override
+ protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+ dispatchThawSelfOnly(container);
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+
+ return new SavedState(superState, mYear, mMonth, mDay, mHasYear, mYearOptional);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ mYear = ss.getYear();
+ mMonth = ss.getMonth();
+ mDay = ss.getDay();
+ mHasYear = ss.hasYear();
+ mYearOptional = ss.isYearOptional();
+ updateSpinners();
+ }
+
+ /**
+ * Initialize the state.
+ * @param year The initial year.
+ * @param monthOfYear The initial month.
+ * @param dayOfMonth The initial day of the month.
+ * @param onDateChangedListener How user is notified date is changed by user, can be null.
+ */
+ public void init(int year, int monthOfYear, int dayOfMonth,
+ OnDateChangedListener onDateChangedListener) {
+ init(year, monthOfYear, dayOfMonth, false, onDateChangedListener);
+ }
+
+ /**
+ * Initialize the state.
+ * @param year The initial year or 0 if no year has been specified
+ * @param monthOfYear The initial month.
+ * @param dayOfMonth The initial day of the month.
+ * @param yearOptional True if the user can toggle the year
+ * @param onDateChangedListener How user is notified date is changed by user, can be null.
+ */
+ public void init(int year, int monthOfYear, int dayOfMonth, boolean yearOptional,
+ OnDateChangedListener onDateChangedListener) {
+ mYear = (yearOptional && year == 0) ? getCurrentYear() : year;
+ mMonth = monthOfYear;
+ mDay = dayOfMonth;
+ mYearOptional = yearOptional;
+ mHasYear = yearOptional ? (year != 0) : true;
+ mOnDateChangedListener = onDateChangedListener;
+ updateSpinners();
+ }
+
+ private void updateSpinners() {
+ updateDaySpinner();
+ mYearToggle.setChecked(mHasYear);
+ mYearToggle.setVisibility(mYearOptional ? View.VISIBLE : View.GONE);
+ mYearPicker.setValue(mYear);
+ mYearPicker.setVisibility(mHasYear ? View.VISIBLE : View.GONE);
+
+ /* The month display uses 1-12 but our internal state stores it
+ * 0-11 so add one when setting the display.
+ */
+ mMonthPicker.setValue(mMonth + 1);
+ }
+
+ private void updateDaySpinner() {
+ Calendar cal = Calendar.getInstance();
+ // if year was not set, use 2000 as it was a leap year
+ cal.set(mHasYear ? mYear : 2000, mMonth, 1);
+ int max = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+ mDayPicker.setMinValue(1);
+ mDayPicker.setMaxValue(max);
+ mDayPicker.setValue(mDay);
+ }
+
+ public int getYear() {
+ return (mYearOptional && !mHasYear) ? 0 : mYear;
+ }
+
+ public boolean isYearOptional() {
+ return mYearOptional;
+ }
+
+ public int getMonth() {
+ return mMonth;
+ }
+
+ public int getDayOfMonth() {
+ return mDay;
+ }
+
+ private void adjustMaxDay(){
+ Calendar cal = Calendar.getInstance();
+ // if year was not set, use 2000 as it was a leap year
+ cal.set(Calendar.YEAR, mHasYear ? mYear : 2000);
+ cal.set(Calendar.MONTH, mMonth);
+ int max = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
+ if (mDay > max) {
+ mDay = max;
+ }
+ }
+
+ private void notifyDateChanged() {
+ if (mOnDateChangedListener != null) {
+ int year = (mYearOptional && !mHasYear) ? 0 : mYear;
+ mOnDateChangedListener.onDateChanged(DatePicker.this, year, mMonth, mDay);
+ }
+ }
+}
diff --git a/src/com/android/contacts/datepicker/DatePickerDialog.java b/src/com/android/contacts/datepicker/DatePickerDialog.java
new file mode 100644
index 0000000..112b96e
--- /dev/null
+++ b/src/com/android/contacts/datepicker/DatePickerDialog.java
@@ -0,0 +1,232 @@
+/*
+ * 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.datepicker;
+
+// This is a fork of the standard Android DatePicker that additionally allows toggling the year
+// on/off. It uses some private API so that not everything has to be copied.
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.TextUtils.TruncateAt;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.contacts.R;
+import com.android.contacts.datepicker.DatePicker.OnDateChangedListener;
+
+import java.text.DateFormatSymbols;
+import java.util.Calendar;
+
+/**
+ * A simple dialog containing an {@link DatePicker}.
+ *
+ * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date Picker
+ * tutorial</a>.</p>
+ */
+public class DatePickerDialog extends AlertDialog implements OnClickListener,
+ OnDateChangedListener {
+
+ private static final String YEAR = "year";
+ private static final String MONTH = "month";
+ private static final String DAY = "day";
+ private static final String YEAR_OPTIONAL = "year_optional";
+
+ private final DatePicker mDatePicker;
+ private final OnDateSetListener mCallBack;
+ private final Calendar mCalendar;
+ private final java.text.DateFormat mTitleDateFormat;
+ private final String[] mWeekDays;
+
+ private int mInitialYear;
+ private int mInitialMonth;
+ private int mInitialDay;
+
+ /**
+ * The callback used to indicate the user is done filling in the date.
+ */
+ public interface OnDateSetListener {
+ /**
+ * @param view The view associated with this listener.
+ * @param year The year that was set or 0 if the user has not specified a year
+ * @param monthOfYear The month that was set (0-11) for compatibility
+ * with {@link java.util.Calendar}.
+ * @param dayOfMonth The day of the month that was set.
+ */
+ void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth);
+ }
+
+ /**
+ * @param context The context the dialog is to run in.
+ * @param callBack How the parent is notified that the date is set.
+ * @param year The initial year of the dialog
+ * @param monthOfYear The initial month of the dialog.
+ * @param dayOfMonth The initial day of the dialog.
+ */
+ public DatePickerDialog(Context context,
+ OnDateSetListener callBack,
+ int year,
+ int monthOfYear,
+ int dayOfMonth) {
+ this(context, callBack, year, monthOfYear, dayOfMonth, false);
+ }
+
+ /**
+ * @param context The context the dialog is to run in.
+ * @param callBack How the parent is notified that the date is set.
+ * @param year The initial year of the dialog or 0 if no year has been specified
+ * @param monthOfYear The initial month of the dialog.
+ * @param dayOfMonth The initial day of the dialog.
+ * @param yearOptional Whether the year can be toggled by the user
+ */
+ public DatePickerDialog(Context context,
+ OnDateSetListener callBack,
+ int year,
+ int monthOfYear,
+ int dayOfMonth,
+ boolean yearOptional) {
+ this(context, context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB
+ ? com.android.internal.R.style.Theme_Holo_Light_Dialog_Alert
+ : com.android.internal.R.style.Theme_Dialog_Alert,
+ callBack, year, monthOfYear, dayOfMonth, yearOptional);
+ }
+
+ /**
+ * @param context The context the dialog is to run in.
+ * @param theme the theme to apply to this dialog
+ * @param callBack How the parent is notified that the date is set.
+ * @param year The initial year of the dialog or 0 if no year has been specified
+ * @param monthOfYear The initial month of the dialog.
+ * @param dayOfMonth The initial day of the dialog.
+ */
+ public DatePickerDialog(Context context,
+ int theme,
+ OnDateSetListener callBack,
+ int year,
+ int monthOfYear,
+ int dayOfMonth) {
+ this(context, theme, callBack, year, monthOfYear, dayOfMonth, false);
+ }
+
+ /**
+ * @param context The context the dialog is to run in.
+ * @param theme the theme to apply to this dialog
+ * @param callBack How the parent is notified that the date is set.
+ * @param year The initial year of the dialog.
+ * @param monthOfYear The initial month of the dialog.
+ * @param dayOfMonth The initial day of the dialog.
+ * @param yearOptional Whether the year can be toggled by the user
+ */
+ public DatePickerDialog(Context context,
+ int theme,
+ OnDateSetListener callBack,
+ int year,
+ int monthOfYear,
+ int dayOfMonth,
+ boolean yearOptional) {
+ super(context, theme);
+
+ mCallBack = callBack;
+ mInitialYear = year;
+ mInitialMonth = monthOfYear;
+ mInitialDay = dayOfMonth;
+ DateFormatSymbols symbols = new DateFormatSymbols();
+ mWeekDays = symbols.getShortWeekdays();
+
+ mTitleDateFormat = java.text.DateFormat.
+ getDateInstance(java.text.DateFormat.FULL);
+ mCalendar = Calendar.getInstance();
+ updateTitle(mInitialYear, mInitialMonth, mInitialDay);
+
+ setButton(BUTTON_POSITIVE, context.getText(com.android.internal.R.string.date_time_set),
+ this);
+ setButton(BUTTON_NEGATIVE, context.getText(android.R.string.cancel),
+ (OnClickListener) null);
+
+ LayoutInflater inflater =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View view = inflater.inflate(R.layout.date_picker_dialog, null);
+ setView(view);
+ mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
+ mDatePicker.init(mInitialYear, mInitialMonth, mInitialDay, yearOptional, this);
+ }
+
+ @Override
+ public void show() {
+ super.show();
+
+ /* Sometimes the full month is displayed causing the title
+ * to be very long, in those cases ensure it doesn't wrap to
+ * 2 lines (as that looks jumpy) and ensure we ellipsize the end.
+ */
+ TextView title = (TextView) findViewById(com.android.internal.R.id.alertTitle);
+ title.setSingleLine();
+ title.setEllipsize(TruncateAt.END);
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (mCallBack != null) {
+ mDatePicker.clearFocus();
+ mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
+ mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
+ }
+ }
+
+ public void onDateChanged(DatePicker view, int year,
+ int month, int day) {
+ updateTitle(year, month, day);
+ }
+
+ public void updateDate(int year, int monthOfYear, int dayOfMonth) {
+ mInitialYear = year;
+ mInitialMonth = monthOfYear;
+ mInitialDay = dayOfMonth;
+ mDatePicker.updateDate(year, monthOfYear, dayOfMonth);
+ }
+
+ private void updateTitle(int year, int month, int day) {
+ mCalendar.set(Calendar.YEAR, year);
+ mCalendar.set(Calendar.MONTH, month);
+ mCalendar.set(Calendar.DAY_OF_MONTH, day);
+ setTitle(mTitleDateFormat.format(mCalendar.getTime()));
+ }
+
+ @Override
+ public Bundle onSaveInstanceState() {
+ Bundle state = super.onSaveInstanceState();
+ state.putInt(YEAR, mDatePicker.getYear());
+ state.putInt(MONTH, mDatePicker.getMonth());
+ state.putInt(DAY, mDatePicker.getDayOfMonth());
+ state.putBoolean(YEAR_OPTIONAL, mDatePicker.isYearOptional());
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ int year = savedInstanceState.getInt(YEAR);
+ int month = savedInstanceState.getInt(MONTH);
+ int day = savedInstanceState.getInt(DAY);
+ boolean yearOptional = savedInstanceState.getBoolean(YEAR_OPTIONAL);
+ mDatePicker.init(year, month, day, yearOptional, this);
+ updateTitle(year, month, day);
+ }
+}
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
new file mode 100644
index 0000000..5dde900
--- /dev/null
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -0,0 +1,1322 @@
+/*
+ * Copyright (C) 2010 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.detail;
+
+import com.android.contacts.Collapser;
+import com.android.contacts.Collapser.Collapsible;
+import com.android.contacts.ContactLoader;
+import com.android.contacts.ContactOptionsActivity;
+import com.android.contacts.ContactPresenceIconUtil;
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.GroupMetaData;
+import com.android.contacts.R;
+import com.android.contacts.TypePrecedence;
+import com.android.contacts.editor.SelectAccountDialogFragment;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.AccountType.EditType;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.util.Constants;
+import com.android.contacts.util.DataStatus;
+import com.android.contacts.util.DateUtils;
+import com.android.contacts.util.PhoneCapabilityTester;
+import com.android.contacts.widget.TransitionAnimationView;
+import com.android.internal.telephony.ITelephony;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.ClipboardManager;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Entity;
+import android.content.Entity.NamedContentValues;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.net.ParseException;
+import android.net.Uri;
+import android.net.WebAddress;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.DisplayNameSources;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.StatusUpdates;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ContactDetailFragment extends Fragment implements
+ OnItemClickListener, OnItemLongClickListener, SelectAccountDialogFragment.Listener {
+
+ private static final String TAG = "ContactDetailFragment";
+
+ private static final int LOADER_DETAILS = 1;
+
+ private static final String KEY_CONTACT_URI = "contactUri";
+ private static final String LOADER_ARG_CONTACT_URI = "contactUri";
+
+ private Context mContext;
+ private View mView;
+ private Uri mLookupUri;
+ private Listener mListener;
+
+ private ContactLoader.Result mContactData;
+ private ContactDetailHeaderView mHeaderView;
+ private ListView mListView;
+ private ViewAdapter mAdapter;
+ private Uri mPrimaryPhoneUri = null;
+
+ private Button mCopyGalToLocalButton;
+ private boolean mAllRestricted;
+ private final ArrayList<Long> mWritableRawContactIds = new ArrayList<Long>();
+ private int mNumPhoneNumbers = 0;
+ private String mDefaultCountryIso;
+ private boolean mContactDataDisplayed;
+
+ private boolean mOptionsMenuOptions;
+ private boolean mOptionsMenuEditable;
+ private boolean mOptionsMenuShareable;
+
+ /**
+ * Device capability: Set during buildEntries and used in the long-press context menu
+ */
+ private boolean mHasPhone;
+
+ /**
+ * Device capability: Set during buildEntries and used in the long-press context menu
+ */
+ private boolean mHasSms;
+
+ /**
+ * Device capability: Set during buildEntries and used in the long-press context menu
+ */
+ private boolean mHasSip;
+
+ /**
+ * The view shown if the detail list is empty.
+ * We set this to the list view when first bind the adapter, so that it won't be shown while
+ * we're loading data.
+ */
+ private View mEmptyView;
+
+ /**
+ * A list of distinct contact IDs included in the current contact.
+ */
+ private ArrayList<Long> mRawContactIds = new ArrayList<Long>();
+ private ArrayList<ViewEntry> mPhoneEntries = new ArrayList<ViewEntry>();
+ private ArrayList<ViewEntry> mSmsEntries = new ArrayList<ViewEntry>();
+ private ArrayList<ViewEntry> mEmailEntries = new ArrayList<ViewEntry>();
+ private ArrayList<ViewEntry> mPostalEntries = new ArrayList<ViewEntry>();
+ private ArrayList<ViewEntry> mImEntries = new ArrayList<ViewEntry>();
+ private ArrayList<ViewEntry> mNicknameEntries = new ArrayList<ViewEntry>();
+ private ArrayList<ViewEntry> mGroupEntries = new ArrayList<ViewEntry>();
+ private ArrayList<ViewEntry> mRelationEntries = new ArrayList<ViewEntry>();
+ private ArrayList<ViewEntry> mOtherEntries = new ArrayList<ViewEntry>();
+ private ArrayList<ArrayList<ViewEntry>> mSections = new ArrayList<ArrayList<ViewEntry>>();
+ private LayoutInflater mInflater;
+
+ private boolean mTransitionAnimationRequested;
+
+ public ContactDetailFragment() {
+ // Explicit constructor for inflation
+
+ // 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(mNicknameEntries);
+ mSections.add(mOtherEntries);
+ mSections.add(mRelationEntries);
+ mSections.add(mGroupEntries);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mLookupUri = savedInstanceState.getParcelable(KEY_CONTACT_URI);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(KEY_CONTACT_URI, mLookupUri);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mContext = activity;
+ mDefaultCountryIso = ContactsUtils.getCurrentCountryIso(mContext);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ mView = inflater.inflate(R.layout.contact_detail_fragment, container, false);
+
+ setHasOptionsMenu(true);
+
+ mInflater = inflater;
+
+ mHeaderView = (ContactDetailHeaderView) mView.findViewById(R.id.contact_header_widget);
+ mHeaderView.setListener(mHeaderViewListener);
+
+ mListView = (ListView) mView.findViewById(android.R.id.list);
+ mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
+ mListView.setOnItemClickListener(this);
+ mListView.setOnItemLongClickListener(this);
+
+ // Don't set it to mListView yet. We do so later when we bind the adapter.
+ mEmptyView = mView.findViewById(android.R.id.empty);
+
+ mCopyGalToLocalButton = (Button) mView.findViewById(R.id.copyLocal);
+ mCopyGalToLocalButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ makePersonalCopy();
+ }
+ });
+
+ mView.setVisibility(View.INVISIBLE);
+ return mView;
+ }
+
+ public void setListener(Listener value) {
+ mListener = value;
+ }
+
+ public Uri getUri() {
+ return mLookupUri;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ if (mLookupUri != null) {
+ Bundle args = new Bundle();
+ args.putParcelable(LOADER_ARG_CONTACT_URI, mLookupUri);
+ getLoaderManager().initLoader(LOADER_DETAILS, args, mDetailLoaderListener);
+ }
+ }
+
+ public void loadUri(Uri lookupUri) {
+ if ((lookupUri != null && lookupUri.equals(mLookupUri))
+ || (lookupUri == null && mLookupUri == null)) {
+ return;
+ }
+
+ mLookupUri = lookupUri;
+ mTransitionAnimationRequested = mContactDataDisplayed;
+ mContactDataDisplayed = true;
+ if (mLookupUri == null) {
+ getLoaderManager().destroyLoader(LOADER_DETAILS);
+ mContactData = null;
+ bindData();
+ } else if (getActivity() != null) {
+ Bundle args = new Bundle();
+ args.putParcelable(LOADER_ARG_CONTACT_URI, mLookupUri);
+ getLoaderManager().restartLoader(LOADER_DETAILS, args, mDetailLoaderListener);
+ }
+ }
+
+ private void bindData() {
+ if (mView == null) {
+ return;
+ }
+
+ if (isAdded()) {
+ getActivity().invalidateOptionsMenu();
+ }
+
+ if (mTransitionAnimationRequested) {
+ TransitionAnimationView.startAnimation(mView, mContactData == null);
+ mTransitionAnimationRequested = false;
+ }
+
+ if (mContactData == null) {
+ mView.setVisibility(View.INVISIBLE);
+ return;
+ }
+
+ // Set the header
+ mHeaderView.loadData(mContactData);
+
+ // Build up the contact entries
+ buildEntries();
+
+ // Collapse similar data items in select sections.
+ Collapser.collapseList(mPhoneEntries);
+ Collapser.collapseList(mSmsEntries);
+ Collapser.collapseList(mEmailEntries);
+ Collapser.collapseList(mPostalEntries);
+ Collapser.collapseList(mImEntries);
+
+ if (mAdapter == null) {
+ mAdapter = new ViewAdapter();
+ mListView.setAdapter(mAdapter);
+ } else {
+ mAdapter.notifyDataSetChanged();
+ }
+ mListView.setEmptyView(mEmptyView);
+
+ // Configure copy gal button
+ if (mContactData.isDirectoryEntry()) {
+ final int exportSupport = mContactData.getDirectoryExportSupport();
+ if (exportSupport == Directory.EXPORT_SUPPORT_ANY_ACCOUNT
+ || exportSupport == Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY) {
+ mCopyGalToLocalButton.setVisibility(View.VISIBLE);
+ } else {
+ mCopyGalToLocalButton.setVisibility(View.GONE);
+ }
+ } else {
+ mCopyGalToLocalButton.setVisibility(View.GONE);
+ }
+
+ mView.setVisibility(View.VISIBLE);
+ }
+
+ /**
+ * Build up the entries to display on the screen.
+ */
+ private final void buildEntries() {
+ mHasPhone = PhoneCapabilityTester.isPhone(mContext);
+ mHasSms = PhoneCapabilityTester.isSmsIntentRegistered(mContext);
+ mHasSip = PhoneCapabilityTester.isSipPhone(mContext);
+
+ // Clear out the old entries
+ final int numSections = mSections.size();
+ for (int i = 0; i < numSections; i++) {
+ mSections.get(i).clear();
+ }
+
+ mRawContactIds.clear();
+
+ mAllRestricted = true;
+ mPrimaryPhoneUri = null;
+ mNumPhoneNumbers = 0;
+
+ mWritableRawContactIds.clear();
+
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+
+ // Build up method entries
+ if (mContactData == null) {
+ return;
+ }
+
+ ArrayList<String> groups = new ArrayList<String>();
+ for (Entity entity: mContactData.getEntities()) {
+ final ContentValues entValues = entity.getEntityValues();
+ final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
+ final long rawContactId = entValues.getAsLong(RawContacts._ID);
+
+ // Mark when this contact has any unrestricted components
+ Integer restricted = entValues.getAsInteger(RawContacts.IS_RESTRICTED);
+ final boolean isRestricted = restricted != null && restricted != 0;
+ if (!isRestricted) mAllRestricted = false;
+
+ if (!mRawContactIds.contains(rawContactId)) {
+ mRawContactIds.add(rawContactId);
+ }
+ AccountType type = accountTypes.getAccountType(accountType);
+ if (type == null || !type.readOnly) {
+ mWritableRawContactIds.add(rawContactId);
+ }
+
+ for (NamedContentValues subValue : entity.getSubValues()) {
+ final ContentValues entryValues = subValue.values;
+ entryValues.put(Data.RAW_CONTACT_ID, rawContactId);
+
+ final long dataId = entryValues.getAsLong(Data._ID);
+ final String mimeType = entryValues.getAsString(Data.MIMETYPE);
+ if (mimeType == null) continue;
+
+ if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ Long groupId = entryValues.getAsLong(GroupMembership.GROUP_ROW_ID);
+ if (groupId != null) {
+ handleGroupMembership(groups, mContactData.getGroupMetaData(), groupId);
+ }
+ continue;
+ }
+
+ final DataKind kind = accountTypes.getKindOrFallback(
+ accountType, mimeType);
+ if (kind == null) continue;
+
+ final ViewEntry entry = ViewEntry.fromValues(mContext, mimeType, kind, dataId,
+ entryValues);
+
+ final boolean hasData = !TextUtils.isEmpty(entry.data);
+ Integer superPrimary = entryValues.getAsInteger(Data.IS_SUPER_PRIMARY);
+ final boolean isSuperPrimary = superPrimary != null && superPrimary != 0;
+
+ if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ // Always ignore the name. It is shown in the header if set
+ } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
+ // Build phone entries
+ mNumPhoneNumbers++;
+ String phoneNumberE164 =
+ entryValues.getAsString(PhoneLookup.NORMALIZED_NUMBER);
+ entry.data = PhoneNumberUtils.formatNumber(
+ entry.data, phoneNumberE164, mDefaultCountryIso);
+ final Intent phoneIntent = mHasPhone ? new Intent(Intent.ACTION_CALL_PRIVILEGED,
+ Uri.fromParts(Constants.SCHEME_TEL, entry.data, null)) : null;
+ final Intent smsIntent = mHasSms ? new Intent(Intent.ACTION_SENDTO,
+ Uri.fromParts(Constants.SCHEME_SMSTO, entry.data, null)) : null;
+
+ // Configure Icons and Intents. Notice actionIcon is already set to the phone
+ if (mHasPhone && mHasSms) {
+ entry.intent = phoneIntent;
+ entry.secondaryIntent = smsIntent;
+ entry.secondaryActionIcon = kind.iconAltRes;
+ } else if (mHasPhone) {
+ entry.intent = phoneIntent;
+ } else if (mHasSms) {
+ entry.intent = smsIntent;
+ entry.actionIcon = kind.iconAltRes;
+ } else {
+ entry.intent = null;
+ entry.actionIcon = -1;
+ }
+
+
+ // Remember super-primary phone
+ if (isSuperPrimary) mPrimaryPhoneUri = entry.uri;
+
+ entry.isPrimary = isSuperPrimary;
+ mPhoneEntries.add(entry);
+ } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
+ // Build email entries
+ entry.intent = new Intent(Intent.ACTION_SENDTO,
+ Uri.fromParts(Constants.SCHEME_MAILTO, entry.data, null));
+ entry.isPrimary = isSuperPrimary;
+ mEmailEntries.add(entry);
+
+ // When Email rows have status, create additional Im row
+ final DataStatus status = mContactData.getStatuses().get(entry.id);
+ if (status != null) {
+ final String imMime = Im.CONTENT_ITEM_TYPE;
+ final DataKind imKind = accountTypes.getKindOrFallback(accountType,
+ imMime);
+ final ViewEntry imEntry = ViewEntry.fromValues(mContext,
+ imMime, imKind, dataId, entryValues);
+ buildImActions(imEntry, entryValues);
+ imEntry.applyStatus(status, false);
+ mImEntries.add(imEntry);
+ }
+ } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
+ // Build postal entries
+ entry.maxLines = 4;
+ entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
+ mPostalEntries.add(entry);
+ } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
+ // Build IM entries
+ buildImActions(entry, entryValues);
+
+ // Apply presence and status details when available
+ final DataStatus status = mContactData.getStatuses().get(entry.id);
+ if (status != null) {
+ entry.applyStatus(status, false);
+ }
+ mImEntries.add(entry);
+ } else if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ // Organizations are not shown. The first one is shown in the header
+ // and subsequent ones are not supported anymore
+ } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
+ // Build nickname entries
+ final boolean isNameRawContact =
+ (mContactData.getNameRawContactId() == rawContactId);
+
+ final boolean duplicatesTitle =
+ isNameRawContact
+ && mContactData.getDisplayNameSource() == DisplayNameSources.NICKNAME;
+
+ if (!duplicatesTitle) {
+ entry.uri = null;
+ mNicknameEntries.add(entry);
+ }
+ } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
+ // Build note entries
+ entry.uri = null;
+ entry.maxLines = 100;
+ mOtherEntries.add(entry);
+ } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
+ // Build Website entries
+ entry.uri = null;
+ entry.maxLines = 10;
+ try {
+ WebAddress webAddress = new WebAddress(entry.data);
+ entry.intent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse(webAddress.toString()));
+ } catch (ParseException e) {
+ Log.e(TAG, "Couldn't parse website: " + entry.data);
+ }
+ mOtherEntries.add(entry);
+ } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
+ // Build SipAddress entries
+ entry.uri = null;
+ entry.maxLines = 1;
+ if (mHasSip) {
+ entry.intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+ Uri.fromParts(Constants.SCHEME_SIP, entry.data, null));
+ } else {
+ entry.intent = null;
+ entry.actionIcon = -1;
+ }
+ mOtherEntries.add(entry);
+ // TODO: Consider moving the SipAddress into its own
+ // section (rather than lumping it in with mOtherEntries)
+ // so that we can reposition it right under the phone number.
+ // (Then, we'd also update FallbackAccountType.java to set
+ // secondary=false for this field, and tweak the weight
+ // of its DataKind.)
+ } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
+ entry.data = DateUtils.formatDate(mContext, entry.data);
+ entry.uri = null;
+ mOtherEntries.add(entry);
+ } else if (Relation.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
+ entry.intent = new Intent(Intent.ACTION_SEARCH);
+ entry.intent.putExtra(SearchManager.QUERY, entry.data);
+ entry.intent.setType(Contacts.CONTENT_TYPE);
+ mRelationEntries.add(entry);
+ } else {
+ // Handle showing custom rows
+ entry.intent = new Intent(Intent.ACTION_VIEW, entry.uri);
+
+ // Use social summary when requested by external source
+ final DataStatus status = mContactData.getStatuses().get(entry.id);
+ final boolean hasSocial = kind.actionBodySocial && status != null;
+ if (hasSocial) {
+ entry.applyStatus(status, true);
+ }
+
+ if (hasSocial || hasData) {
+ mOtherEntries.add(entry);
+ }
+ }
+ }
+ }
+
+ if (!groups.isEmpty()) {
+ ViewEntry entry = new ViewEntry();
+ Collections.sort(groups);
+ StringBuilder sb = new StringBuilder();
+ int size = groups.size();
+ for (int i = 0; i < size; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(groups.get(i));
+ }
+ entry.mimetype = GroupMembership.MIMETYPE;
+ entry.kind = mContext.getString(R.string.groupsLabel);
+ entry.data = sb.toString();
+ mGroupEntries.add(entry);
+ }
+ }
+
+ /**
+ * Maps group ID to the corresponding group name, collapses all synonymous groups.
+ * Ignores default groups (e.g. My Contacts) and favorites groups.
+ */
+ private void handleGroupMembership(
+ ArrayList<String> groups, List<GroupMetaData> groupMetaData, long groupId) {
+ if (groupMetaData == null) {
+ return;
+ }
+
+ for (GroupMetaData group : groupMetaData) {
+ if (group.getGroupId() == groupId) {
+ if (!group.isDefaultGroup() && !group.isFavorites()) {
+ String title = group.getTitle();
+ if (!groups.contains(title)) {
+ groups.add(title);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ private static String buildDataString(DataKind kind, ContentValues values,
+ Context context) {
+ if (kind.actionBody == null) {
+ return null;
+ }
+ CharSequence actionBody = kind.actionBody.inflateUsing(context, values);
+ return actionBody == null ? null : actionBody.toString();
+ }
+
+ /**
+ * Build {@link Intent} to launch an action for the given {@link Im} or
+ * {@link Email} row. If the result is non-null, it either contains one or two Intents
+ * (e.g. [Text, Videochat] or just [Text])
+ */
+ public static void buildImActions(ViewEntry entry, ContentValues values) {
+ final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(values.getAsString(Data.MIMETYPE));
+
+ if (!isEmail && !isProtocolValid(values)) {
+ return;
+ }
+
+ final String data = values.getAsString(isEmail ? Email.DATA : Im.DATA);
+ if (TextUtils.isEmpty(data)) {
+ return;
+ }
+
+ final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : values.getAsInteger(Im.PROTOCOL);
+
+ if (protocol == Im.PROTOCOL_GOOGLE_TALK) {
+ final Integer chatCapabilityObj = values.getAsInteger(Im.CHAT_CAPABILITY);
+ final int chatCapability = chatCapabilityObj == null ? 0 : chatCapabilityObj;
+ entry.chatCapability = chatCapability;
+ if ((chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) {
+ entry.actionIcon = R.drawable.sym_action_talk_holo_light;
+ entry.intent =
+ new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message"));
+ entry.secondaryIntent =
+ new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call"));
+ } else if ((chatCapability & Im.CAPABILITY_HAS_VOICE) != 0) {
+ // Allow Talking and Texting
+ entry.actionIcon = R.drawable.sym_action_talk_holo_light;
+ entry.intent =
+ new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message"));
+ entry.secondaryIntent =
+ new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call"));
+ } else {
+ entry.actionIcon = R.drawable.sym_action_talk_holo_light;
+ entry.intent =
+ new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message"));
+ }
+ } else {
+ // Build an IM Intent
+ String host = values.getAsString(Im.CUSTOM_PROTOCOL);
+
+ if (protocol != Im.PROTOCOL_CUSTOM) {
+ // Try bringing in a well-known host for specific protocols
+ host = ContactsUtils.lookupProviderNameFromId(protocol);
+ }
+
+ if (!TextUtils.isEmpty(host)) {
+ final String authority = host.toLowerCase();
+ final Uri imUri = new Uri.Builder().scheme(Constants.SCHEME_IMTO).authority(
+ authority).appendPath(data).build();
+ entry.actionIcon = R.drawable.sym_action_talk_holo_light;
+ entry.intent = new Intent(Intent.ACTION_SENDTO, imUri);
+ }
+ }
+ }
+
+ private static boolean isProtocolValid(ContentValues values) {
+ String protocolString = values.getAsString(Im.PROTOCOL);
+ if (protocolString == null) {
+ return false;
+ }
+ try {
+ Integer.valueOf(protocolString);
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * A basic structure with the data for a contact entry in the list.
+ */
+ static class ViewEntry implements Collapsible<ViewEntry> {
+ public int type = -1;
+ public String kind;
+ public String typeString;
+ public String data;
+ public Uri uri;
+ public long id = 0;
+ public int maxLines = 1;
+ public String mimetype;
+
+ public Context context = null;
+ public String resPackageName = null;
+ public int actionIcon = -1;
+ public boolean isPrimary = false;
+ public int secondaryActionIcon = -1;
+ public Intent intent;
+ public Intent secondaryIntent = null;
+ public ArrayList<Long> ids = new ArrayList<Long>();
+ public int collapseCount = 0;
+
+ public int presence = -1;
+ public int chatCapability = 0;
+
+ public CharSequence footerLine = null;
+
+ ViewEntry() {
+ }
+
+ /**
+ * Build new {@link ViewEntry} and populate from the given values.
+ */
+ public static ViewEntry fromValues(Context context, String mimeType, DataKind kind,
+ long dataId, ContentValues values) {
+ final ViewEntry entry = new ViewEntry();
+ entry.context = context;
+ entry.id = dataId;
+ entry.uri = ContentUris.withAppendedId(Data.CONTENT_URI, entry.id);
+ entry.mimetype = mimeType;
+ entry.kind = (kind.titleRes == -1 || kind.titleRes == 0) ? ""
+ : context.getString(kind.titleRes);
+ entry.data = buildDataString(kind, values, context);
+
+ if (kind.typeColumn != null && values.containsKey(kind.typeColumn)) {
+ entry.type = values.getAsInteger(kind.typeColumn);
+
+ // get type string
+ entry.typeString = "";
+ for (EditType type : kind.typeList) {
+ if (type.rawValue == entry.type) {
+ if (!type.unspecifiedType) {
+ if (type.customColumn == null) {
+ // Non-custom type. Get its description from the resource
+ entry.typeString = context.getString(type.labelRes);
+ } else {
+ // Custom type. Read it from the database
+ entry.typeString = values.getAsString(type.customColumn);
+ }
+ }
+ break;
+ }
+ }
+ } else {
+ entry.typeString = "";
+ }
+
+ if (kind.iconRes > 0) {
+ entry.resPackageName = kind.resPackageName;
+ entry.actionIcon = kind.iconRes;
+ }
+
+ return entry;
+ }
+
+ /**
+ * Apply given {@link DataStatus} values over this {@link ViewEntry}
+ *
+ * @param fillData When true, the given status replaces {@link #data}
+ * and {@link #footerLine}. Otherwise only {@link #presence}
+ * is updated.
+ */
+ public ViewEntry applyStatus(DataStatus status, boolean fillData) {
+ presence = status.getPresence();
+ if (fillData && status.isValid()) {
+ this.data = status.getStatus().toString();
+ this.footerLine = status.getTimestampLabel(context);
+ }
+
+ return this;
+ }
+
+ @Override
+ public boolean collapseWith(ViewEntry entry) {
+ // assert equal collapse keys
+ if (!shouldCollapseWith(entry)) {
+ return false;
+ }
+
+ // Choose the label associated with the highest type precedence.
+ if (TypePrecedence.getTypePrecedence(mimetype, type)
+ > TypePrecedence.getTypePrecedence(entry.mimetype, entry.type)) {
+ type = entry.type;
+ kind = entry.kind;
+ typeString = entry.typeString;
+ }
+
+ // Choose the max of the maxLines and maxLabelLines values.
+ maxLines = Math.max(maxLines, entry.maxLines);
+
+ // Choose the presence with the highest precedence.
+ if (StatusUpdates.getPresencePrecedence(presence)
+ < StatusUpdates.getPresencePrecedence(entry.presence)) {
+ presence = entry.presence;
+ }
+
+ // If any of the collapsed entries are primary make the whole thing primary.
+ isPrimary = entry.isPrimary ? true : isPrimary;
+
+ // uri, and contactdId, shouldn't make a difference. Just keep the original.
+
+ // Keep track of all the ids that have been collapsed with this one.
+ ids.add(entry.id);
+ collapseCount++;
+ return true;
+ }
+
+ @Override
+ public boolean shouldCollapseWith(ViewEntry entry) {
+ if (entry == null) {
+ return false;
+ }
+
+ if (!ContactsUtils.shouldCollapse(context, mimetype, data, entry.mimetype,
+ entry.data)) {
+ return false;
+ }
+
+ if (!TextUtils.equals(mimetype, entry.mimetype)
+ || !ContactsUtils.areIntentActionEqual(intent, entry.intent)
+ || !ContactsUtils.areIntentActionEqual(secondaryIntent, entry.secondaryIntent)
+ || actionIcon != entry.actionIcon) {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ /** Cache of the children views of a row */
+ private static class ViewCache {
+ public View kindDivider;
+ public View inKindDivider;
+ public View lineBelowLast;
+ public TextView kind;
+ public TextView type;
+ public TextView data;
+ public TextView footer;
+ public ImageView actionIcon;
+ public ImageView presenceIcon;
+ public ImageView secondaryActionButton;
+ public View secondaryActionDivider;
+ }
+
+ private final class ViewAdapter extends BaseAdapter {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final ViewEntry entry = getEntry(position);
+ final View v;
+ final ViewCache viewCache;
+
+ // Check to see if we can reuse convertView
+ if (convertView != null) {
+ v = convertView;
+ viewCache = (ViewCache) v.getTag();
+ } else {
+ // Create a new view if needed
+ v = mInflater.inflate(R.layout.contact_detail_list_item, parent, false);
+
+ // Cache the children
+ viewCache = new ViewCache();
+ viewCache.kind = (TextView) v.findViewById(R.id.kind);
+ viewCache.kindDivider = v.findViewById(R.id.kind_divider);
+ viewCache.lineBelowLast = v.findViewById(R.id.line_below_last);
+ viewCache.inKindDivider = v.findViewById(R.id.in_kind_divider);
+ viewCache.type = (TextView) v.findViewById(R.id.type);
+ viewCache.data = (TextView) v.findViewById(R.id.data);
+ viewCache.footer = (TextView) v.findViewById(R.id.footer);
+ viewCache.actionIcon = (ImageView) v.findViewById(R.id.action_icon);
+ viewCache.presenceIcon = (ImageView) v.findViewById(R.id.presence_icon);
+ viewCache.secondaryActionButton = (ImageView) v.findViewById(
+ R.id.secondary_action_button);
+ viewCache.secondaryActionButton.setOnClickListener(mSecondaryActionClickListener);
+ viewCache.secondaryActionDivider = v.findViewById(R.id.divider);
+ v.setTag(viewCache);
+ }
+
+ final ViewEntry previousEntry = position == 0 ? null : getEntry(position - 1);
+ final boolean isFirstOfItsKind =
+ previousEntry == null ? true : !previousEntry.kind.equals(entry.kind);
+ final boolean isLast = position == getCount() - 1;
+
+ // Bind the data to the view
+ bindView(v, entry, isFirstOfItsKind, isLast);
+ return v;
+ }
+
+ protected void bindView(View view, ViewEntry entry, boolean isFirstOfItsKind,
+ boolean isLast) {
+ final Resources resources = mContext.getResources();
+ ViewCache views = (ViewCache) view.getTag();
+
+ views.kind.setText(isFirstOfItsKind ? entry.kind : "");
+ views.kindDivider.setVisibility(isFirstOfItsKind ? View.VISIBLE : View.GONE);
+ views.inKindDivider.setVisibility(isFirstOfItsKind ? View.GONE : View.VISIBLE);
+ if (views.lineBelowLast != null) {
+ views.lineBelowLast.setVisibility(isLast ? View.VISIBLE : View.GONE);
+ }
+
+ views.type.setText(entry.typeString);
+ views.type.setVisibility(
+ TextUtils.isEmpty(entry.typeString) ? View.GONE : View.VISIBLE);
+
+ views.data.setText(entry.data);
+ setMaxLines(views.data, entry.maxLines);
+
+ // Set the footer
+ if (!TextUtils.isEmpty(entry.footerLine)) {
+ views.footer.setText(entry.footerLine);
+ views.footer.setVisibility(View.VISIBLE);
+ } else {
+ views.footer.setVisibility(View.GONE);
+ }
+
+ // Set the action icon
+ final ImageView action = views.actionIcon;
+ if (entry.actionIcon != -1) {
+ Drawable actionIcon;
+ if (entry.resPackageName != null) {
+ // Load external resources through PackageManager
+ actionIcon = mContext.getPackageManager().getDrawable(entry.resPackageName,
+ entry.actionIcon, null);
+ } else {
+ actionIcon = resources.getDrawable(entry.actionIcon);
+ }
+ action.setImageDrawable(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
+ final Drawable presenceIcon = ContactPresenceIconUtil.getPresenceIcon(
+ mContext, entry.presence);
+ final ImageView presenceIconView = views.presenceIcon;
+ if (presenceIcon != null) {
+ presenceIconView.setImageDrawable(presenceIcon);
+ presenceIconView.setVisibility(View.VISIBLE);
+ } else {
+ presenceIconView.setVisibility(View.GONE);
+ }
+
+ // Set the secondary action button
+ final ImageView secondaryActionView = views.secondaryActionButton;
+ Drawable secondaryActionIcon = null;
+ if (entry.secondaryActionIcon != -1) {
+ secondaryActionIcon = resources.getDrawable(entry.secondaryActionIcon);
+ } else if ((entry.chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) {
+ secondaryActionIcon =
+ resources.getDrawable(R.drawable.sym_action_videochat_holo_light);
+ } else if ((entry.chatCapability & Im.CAPABILITY_HAS_VOICE) != 0) {
+ secondaryActionIcon =
+ resources.getDrawable(R.drawable.sym_action_audiochat_holo_light);
+ }
+
+ if (entry.secondaryIntent != null && secondaryActionIcon != null) {
+ secondaryActionView.setImageDrawable(secondaryActionIcon);
+ secondaryActionView.setTag(entry);
+ secondaryActionView.setVisibility(View.VISIBLE);
+ views.secondaryActionDivider.setVisibility(View.VISIBLE);
+ } else {
+ secondaryActionView.setVisibility(View.GONE);
+ views.secondaryActionDivider.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);
+ }
+ }
+
+ private OnClickListener mSecondaryActionClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mListener == null) return;
+ if (v == null) return;
+ final ViewEntry entry = (ViewEntry) v.getTag();
+ if (entry == null) return;
+ final Intent intent = entry.secondaryIntent;
+ if (intent == null) return;
+ mListener.onItemClicked(intent);
+ }
+ };
+
+ @Override
+ public int getCount() {
+ int count = 0;
+ final int numSections = mSections.size();
+ for (int i = 0; i < numSections; i++) {
+ final ArrayList<ViewEntry> section = mSections.get(i);
+ count += section.size();
+ }
+ return count;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return getEntry(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ final ViewEntry entry = getEntry(position);
+ if (entry != null) {
+ return entry.id;
+ }
+ return -1;
+ }
+
+ private ViewEntry getEntry(int position) {
+ final int numSections = mSections.size();
+ for (int i = 0; i < numSections; i++) {
+ final ArrayList<ViewEntry> section = mSections.get(i);
+ final int sectionSize = section.size();
+ if (position < sectionSize) {
+ return section.get(position);
+ }
+ position -= sectionSize;
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
+ inflater.inflate(R.menu.view, menu);
+ }
+
+ public boolean isOptionsMenuChanged() {
+ return mOptionsMenuOptions != isContactOptionsChangeEnabled()
+ || mOptionsMenuEditable != isContactEditable()
+ || mOptionsMenuShareable != isContactShareable();
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ mOptionsMenuOptions = isContactOptionsChangeEnabled();
+ mOptionsMenuEditable = isContactEditable();
+ mOptionsMenuShareable = isContactShareable();
+
+ // Options only shows telephony-related settings (ringtone, send to voicemail).
+ // ==> Hide if we don't have a telephone
+ final MenuItem optionsMenu = menu.findItem(R.id.menu_options);
+ optionsMenu.setVisible(mOptionsMenuOptions);
+
+ final MenuItem editMenu = menu.findItem(R.id.menu_edit);
+ editMenu.setVisible(mOptionsMenuEditable);
+
+ final MenuItem deleteMenu = menu.findItem(R.id.menu_delete);
+ deleteMenu.setVisible(mOptionsMenuEditable);
+
+ final MenuItem shareMenu = menu.findItem(R.id.menu_share);
+ shareMenu.setVisible(mOptionsMenuShareable);
+ }
+
+ public boolean isContactOptionsChangeEnabled() {
+ return mContactData != null && !mContactData.isDirectoryEntry()
+ && PhoneCapabilityTester.isPhone(mContext);
+ }
+
+ public boolean isContactEditable() {
+ return mContactData != null && !mContactData.isDirectoryEntry();
+ }
+
+ public boolean isContactShareable() {
+ return mContactData != null && !mContactData.isDirectoryEntry() && !mAllRestricted;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_edit: {
+ if (mListener != null) mListener.onEditRequested(mLookupUri);
+ break;
+ }
+ case R.id.menu_delete: {
+ if (mListener != null) mListener.onDeleteRequested(mLookupUri);
+ return true;
+ }
+ case R.id.menu_options: {
+ if (mContactData == null) return false;
+ final Intent intent = new Intent(mContext, ContactOptionsActivity.class);
+ intent.setData(mContactData.getLookupUri());
+ mContext.startActivity(intent);
+ return true;
+ }
+ case R.id.menu_share: {
+ if (mAllRestricted) return false;
+ if (mContactData == null) return false;
+
+ final String lookupKey = mContactData.getLookupKey();
+ final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
+
+ final Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType(Contacts.CONTENT_VCARD_TYPE);
+ intent.putExtra(Intent.EXTRA_STREAM, shareUri);
+
+ // Launch chooser to share contact via
+ final CharSequence chooseTitle = mContext.getText(R.string.share_via);
+ final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
+
+ try {
+ mContext.startActivity(chooseIntent);
+ } catch (ActivityNotFoundException ex) {
+ Toast.makeText(mContext, R.string.share_error, Toast.LENGTH_SHORT).show();
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void makePersonalCopy() {
+ if (mListener == null) {
+ return;
+ }
+
+ int exportSupport = mContactData.getDirectoryExportSupport();
+ switch (exportSupport) {
+ case Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY: {
+ createCopy(new Account(mContactData.getDirectoryAccountName(),
+ mContactData.getDirectoryAccountType()));
+ break;
+ }
+ case Directory.EXPORT_SUPPORT_ANY_ACCOUNT: {
+ final ArrayList<Account> accounts =
+ AccountTypeManager.getInstance(mContext).getAccounts(true);
+ if (accounts.isEmpty()) {
+ createCopy(null);
+ return; // Don't show a dialog.
+ }
+
+ // In the common case of a single writable account, auto-select
+ // it without showing a dialog.
+ if (accounts.size() == 1) {
+ createCopy(accounts.get(0));
+ return; // Don't show a dialog.
+ }
+
+ final SelectAccountDialogFragment dialog = new SelectAccountDialogFragment();
+ dialog.setTargetFragment(this, 0);
+ dialog.show(getFragmentManager(), SelectAccountDialogFragment.TAG);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onAccountSelectorCancelled() {
+ }
+
+ @Override
+ public void onAccountChosen(Account account) {
+ createCopy(account);
+ }
+
+ private void createCopy(Account account) {
+ if (mListener != null) {
+ mListener.onCreateRawContactRequested(mContactData.getContentValues(), account);
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (mListener == null) return;
+ final ViewEntry entry = mAdapter.getEntry(position);
+ if (entry == null) return;
+ final Intent intent = entry.intent;
+ if (intent == null) return;
+ mListener.onItemClicked(intent);
+ }
+
+ @Override
+ public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
+ if (mListener == null) return false;
+ final ViewCache cache = (ViewCache) view.getTag();
+ if (cache == null) return false;
+ CharSequence text = cache.data.getText();
+ if (TextUtils.isEmpty(text)) return false;
+
+ ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
+ Context.CLIPBOARD_SERVICE);
+ cm.setText(text);
+ Toast.makeText(getActivity(), R.string.toast_text_copied, Toast.LENGTH_SHORT).show();
+ return true;
+ }
+
+ public boolean handleKeyDown(int keyCode) {
+ 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 = mListView.getSelectedItemPosition();
+ if (index != -1) {
+ final ViewEntry entry = mAdapter.getEntry(index);
+ if (entry != null && entry.intent != null &&
+ entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
+ mContext.startActivity(entry.intent);
+ return true;
+ }
+ } else if (mPrimaryPhoneUri != null) {
+ // There isn't anything selected, call the default number
+ final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
+ mPrimaryPhoneUri);
+ mContext.startActivity(intent);
+ return true;
+ }
+ return false;
+ }
+
+ case KeyEvent.KEYCODE_DEL: {
+ if (mListener != null) mListener.onDeleteRequested(mLookupUri);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * The listener for the detail loader
+ */
+ private final LoaderManager.LoaderCallbacks<ContactLoader.Result> mDetailLoaderListener =
+ new LoaderCallbacks<ContactLoader.Result>() {
+ @Override
+ public Loader<ContactLoader.Result> onCreateLoader(int id, Bundle args) {
+ Uri lookupUri = args.getParcelable(LOADER_ARG_CONTACT_URI);
+ return new ContactLoader(mContext, lookupUri, true /* loadGroupMetaData */);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<ContactLoader.Result> loader, ContactLoader.Result data) {
+ if (!((ContactLoader)loader).getLookupUri().equals(mLookupUri)) {
+ return;
+ }
+
+ if (data != ContactLoader.Result.NOT_FOUND && data != ContactLoader.Result.ERROR) {
+ mContactData = data;
+ } else {
+ Log.i(TAG, "No contact found: " + ((ContactLoader)loader).getLookupUri());
+ mContactData = null;
+ }
+
+ bindData();
+
+ if (mContactData == null && mListener != null) {
+ mListener.onContactNotFound();
+ }
+ }
+
+ public void onLoaderReset(Loader<ContactLoader.Result> loader) {
+ mContactData = null;
+ bindData();
+ }
+ };
+
+ private ContactDetailHeaderView.Listener mHeaderViewListener =
+ new ContactDetailHeaderView.Listener() {
+ @Override
+ public void onDisplayNameClick(View view) {
+ }
+
+ @Override
+ public void onPhotoClick(View view) {
+ }
+ };
+
+ public static interface Listener {
+ /**
+ * Contact was not found, so somehow close this fragment. This is raised after a contact
+ * is removed via Menu/Delete
+ */
+ public void onContactNotFound();
+
+ /**
+ * User decided to go to Edit-Mode
+ */
+ public void onEditRequested(Uri lookupUri);
+
+ /**
+ * User clicked a single item (e.g. mail)
+ */
+ public void onItemClicked(Intent intent);
+
+ /**
+ * User decided to delete the contact
+ */
+ public void onDeleteRequested(Uri lookupUri);
+
+ /**
+ * User requested creation of a new contact with the specified values.
+ *
+ * @param values ContentValues containing data rows for the new contact.
+ * @param account Account where the new contact should be created
+ */
+ public void onCreateRawContactRequested(ArrayList<ContentValues> values, Account account);
+ }
+}
diff --git a/src/com/android/contacts/detail/ContactDetailHeaderView.java b/src/com/android/contacts/detail/ContactDetailHeaderView.java
new file mode 100644
index 0000000..4b211b6
--- /dev/null
+++ b/src/com/android/contacts/detail/ContactDetailHeaderView.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2010 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.detail;
+
+import com.android.contacts.ContactLoader;
+import com.android.contacts.ContactLoader.Result;
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.R;
+import com.android.contacts.util.ContactBadgeUtil;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Entity;
+import android.content.Entity.NamedContentValues;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.DisplayNameSources;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AlphaAnimation;
+import android.widget.CheckBox;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Header for displaying a title bar with contact info. You
+ * can bind specific values by calling
+ * {@link ContactDetailHeaderView#loadData(com.android.contacts.ContactLoader.Result)}
+ */
+public class ContactDetailHeaderView extends FrameLayout
+ implements View.OnClickListener, View.OnLongClickListener {
+ private static final String TAG = "ContactDetailHeaderView";
+
+ private static final int PHOTO_FADE_IN_ANIMATION_DURATION_MILLIS = 100;
+
+ private TextView mDisplayNameView;
+ private TextView mPhoneticNameView;
+ private TextView mOrganizationTextView;
+ private CheckBox mStarredView;
+ private ImageView mPhotoView;
+ private View mStatusContainerView;
+ private TextView mStatusView;
+ private TextView mStatusDateView;
+ private TextView mAttributionView;
+
+ private Uri mContactUri;
+ private Listener mListener;
+
+ /**
+ * Interface for callbacks invoked when the user interacts with a header.
+ */
+ public interface Listener {
+ public void onPhotoClick(View view);
+ public void onDisplayNameClick(View view);
+ }
+
+ public ContactDetailHeaderView(Context context) {
+ this(context, null);
+ }
+
+ public ContactDetailHeaderView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ContactDetailHeaderView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ final LayoutInflater inflater =
+ (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.contact_detail_header_view, this);
+
+ mDisplayNameView = (TextView) findViewById(R.id.name);
+ mDisplayNameView.setOnLongClickListener(this);
+
+ mPhoneticNameView = (TextView) findViewById(R.id.phonetic_name);
+ mPhoneticNameView.setOnLongClickListener(this);
+
+ mOrganizationTextView = (TextView) findViewById(R.id.organization);
+ mOrganizationTextView.setOnLongClickListener(this);
+
+ mStarredView = (CheckBox)findViewById(R.id.star);
+ mStarredView.setOnClickListener(this);
+
+ mPhotoView = (ImageView) findViewById(R.id.photo);
+
+ mStatusContainerView = findViewById(R.id.status_container);
+ mStatusView = (TextView)findViewById(R.id.status);
+ mStatusDateView = (TextView)findViewById(R.id.status_date);
+
+ mAttributionView = (TextView) findViewById(R.id.attribution);
+ }
+
+ /**
+ * Loads the data from the Loader-Result. This is the only function that has to be called
+ * from the outside to fully setup the View
+ */
+ public void loadData(ContactLoader.Result contactData) {
+ mContactUri = contactData.getLookupUri();
+
+ setDisplayName(contactData.getDisplayName(), contactData.getPhoneticName());
+ setCompany(contactData);
+ if (contactData.isLoadingPhoto()) {
+ setPhoto(null, false);
+ } else {
+ byte[] photo = contactData.getPhotoBinaryData();
+ setPhoto(photo != null ? BitmapFactory.decodeByteArray(photo, 0, photo.length)
+ : ContactBadgeUtil.loadPlaceholderPhoto(mContext),
+ contactData.isDirectoryEntry());
+ }
+
+ setStared(!contactData.isDirectoryEntry(), contactData.getStarred());
+ setSocialSnippet(contactData.getSocialSnippet());
+ setSocialDate(ContactBadgeUtil.getSocialDate(contactData, getContext()));
+ setAttribution(contactData.getEntities().size() > 1, contactData.isDirectoryEntry(),
+ contactData.getDirectoryDisplayName(), contactData.getDirectoryType());
+ }
+
+ /**
+ * Set the given {@link Listener} to handle header events.
+ */
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ private void performPhotoClick() {
+ if (mListener != null) {
+ mListener.onPhotoClick(mPhotoView);
+ }
+ }
+
+ private void performDisplayNameClick() {
+ if (mListener != null) {
+ mListener.onDisplayNameClick(mDisplayNameView);
+ }
+ }
+
+ /**
+ * Set the starred state of this header widget.
+ */
+ private void setStared(boolean visible, boolean starred) {
+ if (visible) {
+ mStarredView.setVisibility(View.VISIBLE);
+ mStarredView.setChecked(starred);
+ } else {
+ mStarredView.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Set the photo to display in the header. If bitmap is null, the default placeholder
+ * image is shown
+ */
+ private void setPhoto(Bitmap bitmap, boolean fadeIn) {
+ if (mPhotoView.getDrawable() == null && fadeIn) {
+ AlphaAnimation animation = new AlphaAnimation(0, 1);
+ animation.setDuration(PHOTO_FADE_IN_ANIMATION_DURATION_MILLIS);
+ animation.setInterpolator(new AccelerateInterpolator());
+ mPhotoView.startAnimation(animation);
+ }
+ mPhotoView.setImageBitmap(bitmap);
+ }
+
+ /**
+ * Set the display name and phonetic name to show in the header.
+ */
+ private void setDisplayName(CharSequence displayName, CharSequence phoneticName) {
+ mDisplayNameView.setText(displayName);
+ if (TextUtils.isEmpty(phoneticName)) {
+ mPhoneticNameView.setVisibility(View.GONE);
+ } else {
+ mPhoneticNameView.setText(phoneticName);
+ mPhoneticNameView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /**
+ * Sets the organization info. If several organizations are given, the first one is used
+ */
+ private void setCompany(Result contactData) {
+ final boolean displayNameIsOrganization =
+ contactData.getDisplayNameSource() == DisplayNameSources.ORGANIZATION;
+ for (Entity entity : contactData.getEntities()) {
+ for (NamedContentValues subValue : entity.getSubValues()) {
+ final ContentValues entryValues = subValue.values;
+ final String mimeType = entryValues.getAsString(Data.MIMETYPE);
+
+ if (Organization.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ final String company = entryValues.getAsString(Organization.COMPANY);
+ final String title = entryValues.getAsString(Organization.TITLE);
+ final String combined;
+ // We need to show company and title in a combined string. However, if the
+ // DisplayName is already the organization, it mirrors company or (if company
+ // is empty title). Make sure we don't show what's already shown as DisplayName
+ if (TextUtils.isEmpty(company)) {
+ combined = displayNameIsOrganization ? null : title;
+ } else {
+ if (TextUtils.isEmpty(title)) {
+ combined = displayNameIsOrganization ? null : company;
+ } else {
+ if (displayNameIsOrganization) {
+ combined = title;
+ } else {
+ combined = getResources().getString(
+ R.string.organization_company_and_title,
+ company, title);
+ }
+ }
+ }
+
+ if (TextUtils.isEmpty(combined)) {
+ mOrganizationTextView.setVisibility(GONE);
+ } else {
+ mOrganizationTextView.setVisibility(VISIBLE);
+ mOrganizationTextView.setText(combined);
+ }
+
+ return;
+ }
+ }
+ }
+ mOrganizationTextView.setVisibility(GONE);
+ }
+
+ /**
+ * Set the social snippet text to display in the header.
+ */
+ private void setSocialSnippet(CharSequence snippet) {
+ if (TextUtils.isEmpty(snippet)) {
+ // No status info. Hide everything
+ if (mStatusContainerView != null) mStatusContainerView.setVisibility(View.GONE);
+ mStatusView.setVisibility(View.GONE);
+ mStatusDateView.setVisibility(View.GONE);
+ } else {
+ // We have status info. Show the bubble
+ if (mStatusContainerView != null) mStatusContainerView.setVisibility(View.VISIBLE);
+ mStatusView.setVisibility(View.VISIBLE);
+ mStatusView.setText(snippet);
+ }
+ }
+
+ /**
+ * Set the status attribution text to display in the header.
+ */
+
+ private void setSocialDate(CharSequence dateText) {
+ if (TextUtils.isEmpty(dateText)) {
+ mStatusDateView.setVisibility(View.GONE);
+ } else {
+ mStatusDateView.setText(dateText);
+ mStatusDateView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void setAttribution(boolean isJoinedContact, boolean isDirectoryEntry,
+ String directoryDisplayName, String directoryType) {
+ if (isJoinedContact) {
+ mAttributionView.setText(R.string.indicator_joined_contact);
+ mAttributionView.setVisibility(View.VISIBLE);
+ } else if (isDirectoryEntry) {
+ String displayName = !TextUtils.isEmpty(directoryDisplayName)
+ ? directoryDisplayName
+ : directoryType;
+ String text = getContext().getString(
+ R.string.contact_directory_description, displayName);
+ mAttributionView.setText(text);
+ mAttributionView.setVisibility(View.VISIBLE);
+ } else {
+ mAttributionView.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.star: {
+ // Toggle "starred" state
+ // Make sure there is a contact
+ if (mContactUri != null) {
+ Intent intent = ContactSaveService.createSetStarredIntent(
+ getContext(), mContactUri, mStarredView.isChecked());
+ getContext().startService(intent);
+ }
+ break;
+ }
+ case R.id.photo: {
+ performPhotoClick();
+ break;
+ }
+ case R.id.name: {
+ performDisplayNameClick();
+ break;
+ }
+ }
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (!(v instanceof TextView)) {
+ return false;
+ }
+
+ CharSequence text = ((TextView)v).getText();
+
+ if (TextUtils.isEmpty(text)) {
+ return false;
+ }
+
+ ClipboardManager cm = (ClipboardManager) getContext().getSystemService(
+ Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(ClipData.newPlainText(null, text));
+ Toast.makeText(getContext(), R.string.toast_text_copied, Toast.LENGTH_SHORT).show();
+ return true;
+ }
+}
diff --git a/src/com/android/contacts/editor/AggregationSuggestionEngine.java b/src/com/android/contacts/editor/AggregationSuggestionEngine.java
new file mode 100644
index 0000000..bb17bbb
--- /dev/null
+++ b/src/com/android/contacts/editor/AggregationSuggestionEngine.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2010 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.editor;
+
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.google.android.collect.Lists;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.Process;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Contacts.AggregationSuggestions;
+import android.provider.ContactsContract.Contacts.AggregationSuggestions.Builder;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Runs asynchronous queries to obtain aggregation suggestions in the as-you-type mode.
+ */
+public class AggregationSuggestionEngine extends HandlerThread {
+ public static final String TAG = "AggregationSuggestionEngine";
+
+ public interface Listener {
+ void onAggregationSuggestionChange();
+ }
+
+ public static final class RawContact {
+ public long rawContactId;
+ public String accountType;
+ public String accountName;
+
+ @Override
+ public String toString() {
+ return "ID: " + rawContactId + " account: " + accountType + "/" + accountName;
+ }
+ }
+
+ public static final class Suggestion {
+
+ public long contactId;
+ public String lookupKey;
+ public String name;
+ public String phoneNumber;
+ public String emailAddress;
+ public String nickname;
+ public byte[] photo;
+ public List<RawContact> rawContacts;
+
+ @Override
+ public String toString() {
+ return "ID: " + contactId + " rawContacts: " + rawContacts + " name: " + name
+ + " phone: " + phoneNumber + " email: " + emailAddress + " nickname: "
+ + nickname + (photo != null ? " [has photo]" : "");
+ }
+ }
+
+ private final class SuggestionContentObserver extends ContentObserver {
+ private SuggestionContentObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ scheduleSuggestionLookup();
+ }
+ }
+
+ private static final int MESSAGE_RESET = 0;
+ private static final int MESSAGE_NAME_CHANGE = 1;
+ private static final int MESSAGE_DATA_CURSOR = 2;
+
+ private static final long SUGGESTION_LOOKUP_DELAY_MILLIS = 300;
+
+ private static final int MAX_SUGGESTION_COUNT = 3;
+
+ private final Context mContext;
+
+ private long[] mSuggestedContactIds = new long[0];
+
+ private Handler mMainHandler;
+ private Handler mHandler;
+ private long mContactId;
+ private Listener mListener;
+ private Cursor mDataCursor;
+ private ContentObserver mContentObserver;
+ private Uri mSuggestionsUri;
+
+ public AggregationSuggestionEngine(Context context) {
+ super("AggregationSuggestions", Process.THREAD_PRIORITY_BACKGROUND);
+ mContext = context;
+ mMainHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ AggregationSuggestionEngine.this.deliverNotification((Cursor) msg.obj);
+ }
+ };
+ }
+
+ protected Handler getHandler() {
+ if (mHandler == null) {
+ mHandler = new Handler(getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ AggregationSuggestionEngine.this.handleMessage(msg);
+ }
+ };
+ }
+ return mHandler;
+ }
+
+ public void setContactId(long contactId) {
+ if (contactId != mContactId) {
+ mContactId = contactId;
+ reset();
+ }
+ }
+
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public boolean quit() {
+ if (mDataCursor != null) {
+ mDataCursor.close();
+ }
+ mDataCursor = null;
+ if (mContentObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+ mContentObserver = null;
+ }
+ return super.quit();
+ }
+
+ public void reset() {
+ Handler handler = getHandler();
+ handler.removeMessages(MESSAGE_NAME_CHANGE);
+ handler.sendEmptyMessage(MESSAGE_RESET);
+ }
+
+ public void onNameChange(ValuesDelta values) {
+ mSuggestionsUri = buildAggregationSuggestionUri(values);
+ if (mSuggestionsUri != null) {
+ if (mContentObserver == null) {
+ mContentObserver = new SuggestionContentObserver(getHandler());
+ mContext.getContentResolver().registerContentObserver(
+ Contacts.CONTENT_URI, true, mContentObserver);
+ }
+ } else if (mContentObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+ mContentObserver = null;
+ }
+ scheduleSuggestionLookup();
+ }
+
+ protected void scheduleSuggestionLookup() {
+ Handler handler = getHandler();
+ handler.removeMessages(MESSAGE_NAME_CHANGE);
+
+ if (mSuggestionsUri == null) {
+ return;
+ }
+
+ Message msg = handler.obtainMessage(MESSAGE_NAME_CHANGE, mSuggestionsUri);
+ handler.sendMessageDelayed(msg, SUGGESTION_LOOKUP_DELAY_MILLIS);
+ }
+
+ private Uri buildAggregationSuggestionUri(ValuesDelta values) {
+ StringBuilder nameSb = new StringBuilder();
+ appendValue(nameSb, values, StructuredName.PREFIX);
+ appendValue(nameSb, values, StructuredName.GIVEN_NAME);
+ appendValue(nameSb, values, StructuredName.MIDDLE_NAME);
+ appendValue(nameSb, values, StructuredName.FAMILY_NAME);
+ appendValue(nameSb, values, StructuredName.SUFFIX);
+
+ if (nameSb.length() == 0) {
+ appendValue(nameSb, values, StructuredName.DISPLAY_NAME);
+ }
+
+ StringBuilder phoneticNameSb = new StringBuilder();
+ appendValue(phoneticNameSb, values, StructuredName.PHONETIC_FAMILY_NAME);
+ appendValue(phoneticNameSb, values, StructuredName.PHONETIC_MIDDLE_NAME);
+ appendValue(phoneticNameSb, values, StructuredName.PHONETIC_GIVEN_NAME);
+
+ if (nameSb.length() == 0 && phoneticNameSb.length() == 0) {
+ return null;
+ }
+
+ Builder builder = AggregationSuggestions.builder()
+ .setLimit(MAX_SUGGESTION_COUNT)
+ .setContactId(mContactId);
+
+ if (nameSb.length() != 0) {
+ builder.addParameter(AggregationSuggestions.PARAMETER_MATCH_NAME, nameSb.toString());
+ }
+
+ if (phoneticNameSb.length() != 0) {
+ builder.addParameter(
+ AggregationSuggestions.PARAMETER_MATCH_NAME, phoneticNameSb.toString());
+ }
+
+ return builder.build();
+ }
+
+ private void appendValue(StringBuilder sb, ValuesDelta values, String column) {
+ String value = values.getAsString(column);
+ if (!TextUtils.isEmpty(value)) {
+ if (sb.length() > 0) {
+ sb.append(' ');
+ }
+ sb.append(value);
+ }
+ }
+
+ protected void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_RESET:
+ mSuggestedContactIds = new long[0];
+ break;
+ case MESSAGE_NAME_CHANGE:
+ loadAggregationSuggestions((Uri) msg.obj);
+ break;
+ }
+ }
+
+ private static final class DataQuery {
+
+ public static final String SELECTION_PREFIX =
+ Data.MIMETYPE + " IN ('"
+ + Phone.CONTENT_ITEM_TYPE + "','"
+ + Email.CONTENT_ITEM_TYPE + "','"
+ + StructuredName.CONTENT_ITEM_TYPE + "','"
+ + Nickname.CONTENT_ITEM_TYPE + "','"
+ + Photo.CONTENT_ITEM_TYPE + "')"
+ + " AND " + Data.CONTACT_ID + " IN (";
+
+ public static final String[] COLUMNS = {
+ Data._ID,
+ Data.CONTACT_ID,
+ Data.LOOKUP_KEY,
+ Data.PHOTO_ID,
+ Data.DISPLAY_NAME,
+ Data.RAW_CONTACT_ID,
+ Data.MIMETYPE,
+ Data.DATA1,
+ Data.IS_SUPER_PRIMARY,
+ Photo.PHOTO,
+ RawContacts.ACCOUNT_TYPE,
+ RawContacts.ACCOUNT_NAME,
+ };
+
+ public static final int ID = 0;
+ public static final int CONTACT_ID = 1;
+ public static final int LOOKUP_KEY = 2;
+ public static final int PHOTO_ID = 3;
+ public static final int DISPLAY_NAME = 4;
+ public static final int RAW_CONTACT_ID = 5;
+ public static final int MIMETYPE = 6;
+ public static final int DATA1 = 7;
+ public static final int IS_SUPERPRIMARY = 8;
+ public static final int PHOTO = 9;
+ public static final int ACCOUNT_TYPE = 10;
+ public static final int ACCOUNT_NAME = 11;
+ }
+
+ private void loadAggregationSuggestions(Uri uri) {
+ ContentResolver contentResolver = mContext.getContentResolver();
+ Cursor cursor = contentResolver.query(uri, new String[]{Contacts._ID}, null, null, null);
+ try {
+ // If a new request is pending, chuck the result of the previous request
+ if (getHandler().hasMessages(MESSAGE_NAME_CHANGE)) {
+ return;
+ }
+
+ boolean changed = updateSuggestedContactIds(cursor);
+ if (!changed) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder(DataQuery.SELECTION_PREFIX);
+ int count = mSuggestedContactIds.length;
+ for (int i = 0; i < count; i++) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(mSuggestedContactIds[i]);
+ }
+ sb.append(')');
+ sb.toString();
+
+ Cursor dataCursor = contentResolver.query(Data.CONTENT_URI,
+ DataQuery.COLUMNS, sb.toString(), null, Data.CONTACT_ID);
+ mMainHandler.sendMessage(mMainHandler.obtainMessage(MESSAGE_DATA_CURSOR, dataCursor));
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private boolean updateSuggestedContactIds(Cursor cursor) {
+ int count = cursor.getCount();
+ boolean changed = count != mSuggestedContactIds.length;
+ if (!changed) {
+ while (cursor.moveToNext()) {
+ long contactId = cursor.getLong(0);
+ if (Arrays.binarySearch(mSuggestedContactIds, contactId) < 0) {
+ changed = true;
+ break;
+ }
+ }
+ }
+
+ if (changed) {
+ mSuggestedContactIds = new long[count];
+ cursor.moveToPosition(-1);
+ for (int i = 0; i < count; i++) {
+ cursor.moveToNext();
+ mSuggestedContactIds[i] = cursor.getLong(0);
+ }
+ Arrays.sort(mSuggestedContactIds);
+ }
+
+ return changed;
+ }
+
+ protected void deliverNotification(Cursor dataCursor) {
+ if (mDataCursor != null) {
+ mDataCursor.close();
+ }
+ mDataCursor = dataCursor;
+ if (mListener != null) {
+ mListener.onAggregationSuggestionChange();
+ }
+ }
+
+ public int getSuggestedContactCount() {
+ return mDataCursor != null ? mDataCursor.getCount() : 0;
+ }
+
+ public List<Suggestion> getSuggestions() {
+ ArrayList<Suggestion> list = Lists.newArrayList();
+ if (mDataCursor != null) {
+ Suggestion suggestion = null;
+ long currentContactId = -1;
+ mDataCursor.moveToPosition(-1);
+ while (mDataCursor.moveToNext()) {
+ long contactId = mDataCursor.getLong(DataQuery.CONTACT_ID);
+ if (contactId != currentContactId) {
+ suggestion = new Suggestion();
+ suggestion.contactId = contactId;
+ suggestion.name = mDataCursor.getString(DataQuery.DISPLAY_NAME);
+ suggestion.lookupKey = mDataCursor.getString(DataQuery.LOOKUP_KEY);
+ suggestion.rawContacts = Lists.newArrayList();
+ list.add(suggestion);
+ currentContactId = contactId;
+ }
+
+ long rawContactId = mDataCursor.getLong(DataQuery.RAW_CONTACT_ID);
+ if (!containsRawContact(suggestion, rawContactId)) {
+ RawContact rawContact = new RawContact();
+ rawContact.rawContactId = rawContactId;
+ rawContact.accountName = mDataCursor.getString(DataQuery.ACCOUNT_NAME);
+ rawContact.accountType = mDataCursor.getString(DataQuery.ACCOUNT_TYPE);
+ suggestion.rawContacts.add(rawContact);
+ }
+
+ String mimetype = mDataCursor.getString(DataQuery.MIMETYPE);
+ if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
+ String data = mDataCursor.getString(DataQuery.DATA1);
+ int superprimary = mDataCursor.getInt(DataQuery.IS_SUPERPRIMARY);
+ if (!TextUtils.isEmpty(data)
+ && (superprimary != 0 || suggestion.phoneNumber == null)) {
+ suggestion.phoneNumber = data;
+ }
+ } else if (Email.CONTENT_ITEM_TYPE.equals(mimetype)) {
+ String data = mDataCursor.getString(DataQuery.DATA1);
+ int superprimary = mDataCursor.getInt(DataQuery.IS_SUPERPRIMARY);
+ if (!TextUtils.isEmpty(data)
+ && (superprimary != 0 || suggestion.emailAddress == null)) {
+ suggestion.emailAddress = data;
+ }
+ } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimetype)) {
+ String data = mDataCursor.getString(DataQuery.DATA1);
+ if (!TextUtils.isEmpty(data)) {
+ suggestion.nickname = data;
+ }
+ } else if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) {
+ long dataId = mDataCursor.getLong(DataQuery.ID);
+ long photoId = mDataCursor.getLong(DataQuery.PHOTO_ID);
+ if (dataId == photoId && !mDataCursor.isNull(DataQuery.PHOTO)) {
+ suggestion.photo = mDataCursor.getBlob(DataQuery.PHOTO);
+ }
+ }
+ }
+ }
+ return list;
+ }
+
+ public boolean containsRawContact(Suggestion suggestion, long rawContactId) {
+ if (suggestion.rawContacts != null) {
+ int count = suggestion.rawContacts.size();
+ for (int i = 0; i < count; i++) {
+ if (suggestion.rawContacts.get(i).rawContactId == rawContactId) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/contacts/editor/AggregationSuggestionView.java b/src/com/android/contacts/editor/AggregationSuggestionView.java
new file mode 100644
index 0000000..5403731
--- /dev/null
+++ b/src/com/android/contacts/editor/AggregationSuggestionView.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2010 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.editor;
+
+import com.android.contacts.R;
+import com.android.contacts.editor.AggregationSuggestionEngine.RawContact;
+import com.android.contacts.editor.AggregationSuggestionEngine.Suggestion;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+import com.google.android.collect.Lists;
+
+import android.content.Context;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A view that contains a name, picture and other data for a contact aggregation suggestion.
+ */
+public class AggregationSuggestionView extends RelativeLayout {
+
+ public interface Listener {
+
+ /**
+ * Callback that passes the contact ID to join with and, for convenience,
+ * also the list of constituent raw contact IDs to avoid a separate query
+ * for those.
+ */
+ public void onJoinAction(long contactId, List<Long> rawContacIds);
+
+ /**
+ * Callback that passes the contact ID to edit instead of the current contact.
+ */
+ public void onEditAction(Uri contactLookupUri);
+ }
+
+ private Listener mListener;
+ private long mContactId;
+ private String mLookupKey;
+ private List<RawContact> mRawContacts = Lists.newArrayList();
+ private boolean mNewContact;
+
+ public AggregationSuggestionView(Context context) {
+ super(context);
+ setClickable(true);
+ }
+
+ public AggregationSuggestionView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setClickable(true);
+ }
+
+ public AggregationSuggestionView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setClickable(true);
+ }
+
+ public void setNewContact(boolean flag) {
+ mNewContact = flag;
+ }
+
+ public void bindSuggestion(Suggestion suggestion) {
+ mContactId = suggestion.contactId;
+ mLookupKey = suggestion.lookupKey;
+ mRawContacts = suggestion.rawContacts;
+ ImageView photo = (ImageView) findViewById(R.id.aggregation_suggestion_photo);
+ if (suggestion.photo != null) {
+ photo.setImageBitmap(BitmapFactory.decodeByteArray(
+ suggestion.photo, 0, suggestion.photo.length));
+ } else {
+ photo.setImageResource(R.drawable.ic_contact_picture);
+ }
+
+ TextView name = (TextView) findViewById(R.id.aggregation_suggestion_name);
+ name.setText(suggestion.name);
+
+ TextView data = (TextView) findViewById(R.id.aggregation_suggestion_data);
+ String dataText = null;
+ if (suggestion.nickname != null) {
+ dataText = suggestion.nickname;
+ } else if (suggestion.emailAddress != null) {
+ dataText = suggestion.emailAddress;
+ } else if (suggestion.phoneNumber != null) {
+ dataText = suggestion.phoneNumber;
+ }
+ data.setText(dataText);
+ }
+
+ /**
+ * Returns true if the suggested contact can be edited.
+ */
+ private boolean canEditSuggestedContact() {
+ if (!mNewContact) {
+ return false;
+ }
+
+ AccountTypeManager accountTypes = AccountTypeManager.getInstance(getContext());
+ for (RawContact rawContact : mRawContacts) {
+ String accountType = rawContact.accountType;
+ if (accountType == null) {
+ return true;
+ }
+ AccountType type = accountTypes.getAccountType(accountType);
+ if (!type.readOnly) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public boolean performClick() {
+ if (mListener != null) {
+ if (canEditSuggestedContact()) {
+ mListener.onEditAction(Contacts.getLookupUri(mContactId, mLookupKey));
+ } else {
+ ArrayList<Long> rawContactIds = Lists.newArrayList();
+ for (RawContact rawContact : mRawContacts) {
+ rawContactIds.add(rawContact.rawContactId);
+ }
+ mListener.onJoinAction(mContactId, rawContactIds);
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/contacts/editor/BaseRawContactEditorView.java b/src/com/android/contacts/editor/BaseRawContactEditorView.java
new file mode 100644
index 0000000..49ca863
--- /dev/null
+++ b/src/com/android/contacts/editor/BaseRawContactEditorView.java
@@ -0,0 +1,145 @@
+/*
+ * 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.editor;
+
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountType.EditType;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.model.EntityModifier;
+
+import android.content.Context;
+import android.content.Entity;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+/**
+ * Base view that provides common code for the editor interaction for a specific
+ * RawContact represented through an {@link EntityDelta}.
+ * <p>
+ * Internal updates are performed against {@link ValuesDelta} so that the
+ * source {@link Entity} can be swapped out. Any state-based changes, such as
+ * adding {@link Data} rows or changing {@link EditType}, are performed through
+ * {@link EntityModifier} to ensure that {@link AccountType} are enforced.
+ */
+public abstract class BaseRawContactEditorView extends LinearLayout {
+
+ private PhotoEditorView mPhoto;
+ private boolean mHasPhotoEditor = false;
+
+ private View mHeader;
+ private View mBody;
+ private View mDivider;
+
+ private boolean mExpanded = true;
+
+ public BaseRawContactEditorView(Context context) {
+ super(context);
+ }
+
+ public BaseRawContactEditorView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mHeader = findViewById(R.id.header);
+ mBody = findViewById(R.id.body);
+ mDivider = findViewById(R.id.divider);
+ mHeader.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setExpanded(!mExpanded);
+ }
+ });
+
+ mPhoto = (PhotoEditorView)findViewById(R.id.edit_photo);
+ mPhoto.setEnabled(isEnabled());
+ }
+
+ public void setGroupMetaData(Cursor groupMetaData) {
+ }
+
+ /**
+ * Assign the given {@link Bitmap} to the internal {@link PhotoEditorView}
+ * for the {@link EntityDelta} currently being edited.
+ */
+ public void setPhotoBitmap(Bitmap bitmap) {
+ mPhoto.setPhotoBitmap(bitmap);
+ }
+
+ protected void setHasPhotoEditor(boolean hasPhotoEditor) {
+ mHasPhotoEditor = hasPhotoEditor;
+ mPhoto.setVisibility(hasPhotoEditor ? View.VISIBLE : View.GONE);
+ }
+
+ /**
+ * Return true if the current {@link RawContacts} supports {@link Photo},
+ * which means that {@link PhotoEditorView} is enabled.
+ */
+ public boolean hasPhotoEditor() {
+ return mHasPhotoEditor;
+ }
+
+ /**
+ * Return true if internal {@link PhotoEditorView} has a {@link Photo} set.
+ */
+ public boolean hasSetPhoto() {
+ return mPhoto.hasSetPhoto();
+ }
+
+ public PhotoEditorView getPhotoEditor() {
+ return mPhoto;
+ }
+
+ /**
+ * @return the RawContact ID that this editor is editing.
+ */
+ public abstract long getRawContactId();
+
+ /**
+ * Set the internal state for this view, given a current
+ * {@link EntityDelta} state and the {@link AccountType} that
+ * apply to that state.
+ */
+ public abstract void setState(EntityDelta state, AccountType source, ViewIdGenerator vig);
+
+ /* package */ void setExpanded(boolean value) {
+ // only allow collapsing if we are one of several children
+ final boolean newValue;
+ if (getParent() instanceof ViewGroup && ((ViewGroup) getParent()).getChildCount() == 1) {
+ newValue = true;
+ } else {
+ newValue = value;
+ }
+
+ if (newValue == mExpanded) return;
+ mExpanded = newValue;
+ mBody.setVisibility(newValue ? View.VISIBLE : View.GONE);
+ mDivider.setVisibility(newValue ? View.GONE : View.VISIBLE);
+ }
+}
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
new file mode 100644
index 0000000..b117d77
--- /dev/null
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -0,0 +1,1853 @@
+/*
+ * Copyright (C) 2010 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.editor;
+
+import com.android.contacts.ContactLoader;
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.GroupMetaDataLoader;
+import com.android.contacts.R;
+import com.android.contacts.activities.ContactEditorActivity;
+import com.android.contacts.activities.JoinContactActivity;
+import com.android.contacts.editor.AggregationSuggestionEngine.Suggestion;
+import com.android.contacts.editor.Editor.EditorListener;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.model.EntityDeltaList;
+import com.android.contacts.model.EntityModifier;
+import com.android.contacts.model.GoogleAccountType;
+import com.android.contacts.util.EmptyService;
+import com.android.contacts.util.WeakAsyncTask;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ActivityNotFoundException;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.DialogInterface;
+import android.content.Entity;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Intents;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewStub;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+public class ContactEditorFragment extends Fragment implements
+ SplitContactConfirmationDialogFragment.Listener, SelectAccountDialogFragment.Listener,
+ AggregationSuggestionEngine.Listener, AggregationSuggestionView.Listener,
+ ExternalRawContactEditorView.Listener {
+
+ private static final String TAG = "ContactEditorFragment";
+
+ private static final int LOADER_DATA = 1;
+ private static final int LOADER_GROUPS = 2;
+
+ private static final String KEY_URI = "uri";
+ private static final String KEY_ACTION = "action";
+ private static final String KEY_EDIT_STATE = "state";
+ private static final String KEY_RAW_CONTACT_ID_REQUESTING_PHOTO = "photorequester";
+ private static final String KEY_VIEW_ID_GENERATOR = "viewidgenerator";
+ private static final String KEY_CURRENT_PHOTO_FILE = "currentphotofile";
+ private static final String KEY_QUERY_SELECTION = "queryselection";
+ private static final String KEY_CONTACT_ID_FOR_JOIN = "contactidforjoin";
+ private static final String KEY_SHOW_JOIN_SUGGESTIONS = "showJoinSuggestions";
+ private static final String KEY_ENABLED = "enabled";
+
+ /**
+ * An intent extra that forces the editor to add the edited contact
+ * to the default group (e.g. "My Contacts").
+ */
+ public static final String INTENT_EXTRA_ADD_TO_DEFAULT_DIRECTORY = "addToDefaultDirectory";
+
+ /**
+ * Modes that specify what the AsyncTask has to perform after saving
+ */
+ public interface SaveMode {
+ /**
+ * Close the editor after saving
+ */
+ public static final int CLOSE = 0;
+
+ /**
+ * Reload the data so that the user can continue editing
+ */
+ public static final int RELOAD = 1;
+
+ /**
+ * Split the contact after saving
+ */
+ public static final int SPLIT = 2;
+
+ /**
+ * Join another contact after saving
+ */
+ public static final int JOIN = 3;
+
+ /**
+ * Navigate to Contacts Home activity after saving.
+ */
+ public static final int HOME = 4;
+ }
+
+ private interface Status {
+ /**
+ * The loader is fetching data
+ */
+ public static final int LOADING = 0;
+
+ /**
+ * Not currently busy. We are waiting for the user to enter data
+ */
+ public static final int EDITING = 1;
+
+ /**
+ * The data is currently being saved. This is used to prevent more auto-saves (they shouldn't
+ * overlap)
+ */
+ public static final int SAVING = 2;
+
+ /**
+ * Prevents any more savings (this is used if Save/Close or Revert was executed by the user)
+ */
+ public static final int CLOSING = 3;
+ }
+
+ private static final int REQUEST_CODE_JOIN = 0;
+ private static final int REQUEST_CODE_CAMERA_WITH_DATA = 1;
+ private static final int REQUEST_CODE_PHOTO_PICKED_WITH_DATA = 2;
+
+ private Bitmap mPhoto = null;
+ private long mRawContactIdRequestingPhoto = -1;
+ private long mRawContactIdRequestingPhotoAfterLoad = -1;
+
+ private final EntityDeltaComparator mComparator = new EntityDeltaComparator();
+
+ private static final int ICON_SIZE = 96;
+
+ private static final File PHOTO_DIR = new File(
+ Environment.getExternalStorageDirectory() + "/DCIM/Camera");
+
+ private Cursor mGroupMetaData;
+
+ /**
+ * A delay in milliseconds used for bringing aggregation suggestions to
+ * the visible part of the screen. The reason this has to be done after
+ * a delay is a race condition with the soft keyboard. The keyboard
+ * may expand to display its own autocomplete suggestions, which will
+ * reduce the visible area of the screen. We will yield to the keyboard
+ * hoping that the delay is sufficient. If not - part of the
+ * suggestion will be hidden, which is not fatal.
+ */
+ private static final int AGGREGATION_SUGGESTION_SCROLL_DELAY = 200;
+
+ private File mCurrentPhotoFile;
+
+ private Context mContext;
+ private String mAction;
+ private Uri mLookupUri;
+ private Bundle mIntentExtras;
+ private Listener mListener;
+
+ private String mQuerySelection;
+
+ private long mContactIdForJoin;
+
+ private LinearLayout mContent;
+ private EntityDeltaList mState;
+
+ private ViewIdGenerator mViewIdGenerator;
+
+ private long mLoaderStartTime;
+
+ private int mStatus;
+ private boolean mSaveOnStop = true;
+
+ private AggregationSuggestionEngine mAggregationSuggestionEngine;
+ private long mAggregationSuggestionsRawContactId;
+ private View mAggregationSuggestionView;
+
+ private boolean mAutoAddToDefaultGroup;
+
+ private boolean mEnabled = true;
+
+ public ContactEditorFragment() {
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (mEnabled != enabled) {
+ mEnabled = enabled;
+ if (mContent != null) {
+ int count = mContent.getChildCount();
+ for (int i = 0; i < count; i++) {
+ mContent.getChildAt(i).setEnabled(enabled);
+ }
+ }
+ final Activity activity = getActivity();
+ if (activity != null) activity.invalidateOptionsMenu();
+ }
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mContext = activity;
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (mAggregationSuggestionEngine != null) {
+ mAggregationSuggestionEngine.quit();
+ }
+
+ // If anything was left unsaved, save it now but keep the editor open.
+ if (!getActivity().isChangingConfigurations() && mStatus == Status.EDITING && mSaveOnStop) {
+ save(SaveMode.RELOAD);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ final View view = inflater.inflate(R.layout.contact_editor_fragment, container, false);
+
+ mContent = (LinearLayout) view.findViewById(R.id.editors);
+
+ setHasOptionsMenu(true);
+
+ // If we are in an orientation change, we already have mState (it was loaded by onCreate)
+ if (mState != null) {
+ bindEditors();
+ }
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ Log.d(TAG, "onActivityCreated(" + savedInstanceState + ")");
+
+ // Handle initial actions only when existing state missing
+ final boolean hasIncomingState = savedInstanceState != null;
+
+ if (!hasIncomingState) {
+ if (Intent.ACTION_EDIT.equals(mAction)) {
+ if (mListener != null) mListener.setTitleTo(R.string.editContact_title_edit);
+ getLoaderManager().initLoader(LOADER_DATA, null, mDataLoaderListener);
+ } else if (Intent.ACTION_INSERT.equals(mAction)) {
+ if (mListener != null) mListener.setTitleTo(R.string.editContact_title_insert);
+
+ final Account account = mIntentExtras == null ? null :
+ (Account) mIntentExtras.getParcelable(Intents.Insert.ACCOUNT);
+
+ if (account != null) {
+ // Account specified in Intent
+ createContact(account);
+ } else {
+ // No Account specified. Let the user choose
+ // Load Accounts async so that we can present them
+ selectAccountAndCreateContact();
+ }
+ } else throw new IllegalArgumentException("Unknown Action String " + mAction +
+ ". Only support " + Intent.ACTION_EDIT + " or " + Intent.ACTION_INSERT);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ mSaveOnStop = true;
+ getLoaderManager().initLoader(LOADER_GROUPS, null, mGroupLoaderListener);
+ super.onStart();
+ }
+
+ public void load(String action, Uri lookupUri, Bundle intentExtras) {
+ mAction = action;
+ mLookupUri = lookupUri;
+ mIntentExtras = intentExtras;
+ mAutoAddToDefaultGroup = mIntentExtras != null
+ && mIntentExtras.containsKey(INTENT_EXTRA_ADD_TO_DEFAULT_DIRECTORY);
+ }
+
+ public void setListener(Listener value) {
+ mListener = value;
+ }
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ if (savedState != null) {
+ // Restore mUri before calling super.onCreate so that onInitializeLoaders
+ // would already have a uri and an action to work with
+ mLookupUri = savedState.getParcelable(KEY_URI);
+ mAction = savedState.getString(KEY_ACTION);
+ }
+
+ super.onCreate(savedState);
+
+ if (savedState == null) {
+ // If savedState is non-null, onRestoreInstanceState() will restore the generator.
+ mViewIdGenerator = new ViewIdGenerator();
+ } else {
+ // Read state from savedState. No loading involved here
+ mState = savedState.<EntityDeltaList> getParcelable(KEY_EDIT_STATE);
+ mRawContactIdRequestingPhoto = savedState.getLong(
+ KEY_RAW_CONTACT_ID_REQUESTING_PHOTO);
+ mViewIdGenerator = savedState.getParcelable(KEY_VIEW_ID_GENERATOR);
+ String fileName = savedState.getString(KEY_CURRENT_PHOTO_FILE);
+ if (fileName != null) {
+ mCurrentPhotoFile = new File(fileName);
+ }
+ mQuerySelection = savedState.getString(KEY_QUERY_SELECTION);
+ mContactIdForJoin = savedState.getLong(KEY_CONTACT_ID_FOR_JOIN);
+ mAggregationSuggestionsRawContactId = savedState.getLong(KEY_SHOW_JOIN_SUGGESTIONS);
+ mEnabled = savedState.getBoolean(KEY_ENABLED);
+ mStatus = Status.EDITING;
+ }
+ }
+
+ public void setData(ContactLoader.Result data) {
+ // If we have already loaded data, we do not want to change it here to not confuse the user
+ if (mState != null) {
+ Log.v(TAG, "Ignoring background change. This will have to be rebased later");
+ return;
+ }
+
+ // See if this edit operation needs to be redirected to a custom editor
+ ArrayList<Entity> entities = data.getEntities();
+ if (entities.size() == 1) {
+ Entity entity = entities.get(0);
+ ContentValues entityValues = entity.getEntityValues();
+ String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
+ AccountType accountType = AccountTypeManager.getInstance(mContext).getAccountType(type);
+ if (accountType.getEditContactActivityClassName() != null) {
+ if (mListener != null) {
+ String name = entityValues.getAsString(RawContacts.ACCOUNT_NAME);
+ long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
+ mListener.onCustomEditContactActivityRequested(new Account(name, type),
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
+ mIntentExtras, true);
+ }
+ return;
+ }
+ }
+
+ bindEditorsForExistingContact(data);
+ }
+
+ @Override
+ public void onExternalEditorRequest(Account account, Uri uri) {
+ mListener.onCustomEditContactActivityRequested(account, uri, null, false);
+ }
+
+ private void bindEditorsForExistingContact(ContactLoader.Result data) {
+ setEnabled(true);
+
+ // Build Filter mQuerySelection
+ final ArrayList<Entity> entities = data.getEntities();
+ final StringBuilder sb = new StringBuilder(RawContacts._ID + " IN(");
+ final int count = entities.size();
+ for (int i = 0; i < count; i++) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(entities.get(i).getEntityValues().get(RawContacts._ID));
+ }
+ sb.append(")");
+ mQuerySelection = sb.toString();
+ mState = EntityDeltaList.fromIterator(entities.iterator());
+ setIntentExtras(mIntentExtras);
+ mIntentExtras = null;
+
+ bindEditors();
+ }
+
+ /**
+ * Merges extras from the intent.
+ */
+ public void setIntentExtras(Bundle extras) {
+ if (extras == null || extras.size() == 0) {
+ return;
+ }
+
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+ for (EntityDelta state : mState) {
+ final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ final AccountType type = accountTypes.getAccountType(accountType);
+ if (!type.readOnly) {
+ // Apply extras to the first writable raw contact only
+ EntityModifier.parseExtras(mContext, type, state, extras);
+ break;
+ }
+ }
+ }
+
+ private void selectAccountAndCreateContact() {
+ final ArrayList<Account> accounts =
+ AccountTypeManager.getInstance(mContext).getAccounts(true);
+ // No Accounts available. Create a phone-local contact.
+ if (accounts.isEmpty()) {
+ createContact(null);
+ return; // Don't show a dialog.
+ }
+
+ // In the common case of a single account being writable, auto-select
+ // it without showing a dialog.
+ if (accounts.size() == 1) {
+ createContact(accounts.get(0));
+ return; // Don't show a dialog.
+ }
+
+ final SelectAccountDialogFragment dialog = new SelectAccountDialogFragment();
+ dialog.setTargetFragment(this, 0);
+ dialog.show(getFragmentManager(), SelectAccountDialogFragment.TAG);
+ }
+
+ /**
+ * @param account may be null to signal a device-local contact should
+ * be created.
+ */
+ private void createContact(Account account) {
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+ final AccountType accountType =
+ accountTypes.getAccountType(account != null ? account.type : null);
+
+ if (accountType.getCreateContactActivityClassName() != null) {
+ if (mListener != null) {
+ mListener.onCustomCreateContactActivityRequested(account, mIntentExtras);
+ }
+ } else {
+ bindEditorsForNewContact(account, accountType);
+ }
+ }
+
+ private void bindEditorsForNewContact(Account account, final AccountType accountType) {
+ final ContentValues values = new ContentValues();
+ if (account != null) {
+ values.put(RawContacts.ACCOUNT_NAME, account.name);
+ values.put(RawContacts.ACCOUNT_TYPE, account.type);
+ } else {
+ values.putNull(RawContacts.ACCOUNT_NAME);
+ values.putNull(RawContacts.ACCOUNT_TYPE);
+ }
+
+ // Parse any values from incoming intent
+ EntityDelta insert = new EntityDelta(ValuesDelta.fromAfter(values));
+ EntityModifier.parseExtras(mContext, accountType, insert, mIntentExtras);
+
+ // Ensure we have some default fields (if the account type does not support a field,
+ // ensureKind will not add it, so it is safe to add e.g. Event)
+ EntityModifier.ensureKindExists(insert, accountType, Phone.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, accountType, Email.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, accountType, Note.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, accountType, Organization.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, accountType, Event.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, accountType, Website.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, accountType, StructuredPostal.CONTENT_ITEM_TYPE);
+
+ if (mState == null) {
+ // Create state if none exists yet
+ mState = EntityDeltaList.fromSingle(insert);
+ } else {
+ // Add contact onto end of existing state
+ mState.add(insert);
+ }
+
+ bindEditors();
+ }
+
+ private void bindEditors() {
+ // Sort the editors
+ Collections.sort(mState, mComparator);
+
+ // Remove any existing editors and rebuild any visible
+ mContent.removeAllViews();
+
+ final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+ int size = mState.size();
+ for (int i = 0; i < size; i++) {
+ // TODO ensure proper ordering of entities in the list
+ final EntityDelta entity = mState.get(i);
+ final ValuesDelta values = entity.getValues();
+ if (!values.isVisible()) continue;
+
+ final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
+ final AccountType type = accountTypes.getAccountType(accountType);
+ final long rawContactId = values.getAsLong(RawContacts._ID);
+
+ final BaseRawContactEditorView editor;
+ if (type.isExternal()) {
+ editor = (BaseRawContactEditorView) inflater.inflate(
+ R.layout.external_raw_contact_editor_view, mContent, false);
+ ((ExternalRawContactEditorView) editor).setListener(this);
+ } else {
+ editor = (BaseRawContactEditorView)
+ inflater.inflate(R.layout.raw_contact_editor_view, mContent, false);
+ }
+ editor.setEnabled(mEnabled);
+
+ mContent.addView(editor);
+
+ editor.setState(entity, type, mViewIdGenerator);
+
+ editor.getPhotoEditor().setEditorListener(
+ new PhotoEditorListener(editor, type.readOnly));
+ if (editor instanceof RawContactEditorView) {
+ final RawContactEditorView rawContactEditor = (RawContactEditorView) editor;
+ final TextFieldsEditorView nameEditor = rawContactEditor.getNameEditor();
+ nameEditor.setEditorListener(new EditorListener() {
+
+ @Override
+ public void onRequest(int request) {
+ onContactNameChange(request, rawContactEditor, nameEditor);
+ }
+
+ @Override
+ public void onDeleted(Editor removedEditor) {
+ }
+ });
+
+ rawContactEditor.setAutoAddToDefaultGroup(mAutoAddToDefaultGroup);
+
+ if (rawContactId == mAggregationSuggestionsRawContactId) {
+ acquireAggregationSuggestions(rawContactEditor);
+ }
+ }
+ }
+
+ bindGroupMetaData();
+
+ // Show editor now that we've loaded state
+ mContent.setVisibility(View.VISIBLE);
+
+ // Refresh Action Bar as the visibility of the join command
+ // Activity can be null if we have been detached from the Activity
+ final Activity activity = getActivity();
+ if (activity != null) activity.invalidateOptionsMenu();
+ }
+
+ private void bindGroupMetaData() {
+ if (mGroupMetaData == null) {
+ return;
+ }
+
+ int editorCount = mContent.getChildCount();
+ for (int i = 0; i < editorCount; i++) {
+ BaseRawContactEditorView editor = (BaseRawContactEditorView) mContent.getChildAt(i);
+ editor.setGroupMetaData(mGroupMetaData);
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
+ inflater.inflate(R.menu.edit, menu);
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ menu.findItem(R.id.menu_split).setVisible(mState != null && mState.size() > 1);
+ int size = menu.size();
+ for (int i = 0; i < size; i++) {
+ menu.getItem(i).setEnabled(mEnabled);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_done:
+ return save(SaveMode.CLOSE);
+ case R.id.menu_discard:
+ return revert();
+ case R.id.menu_delete:
+ return doDeleteAction();
+ case R.id.menu_split:
+ return doSplitContactAction();
+ case R.id.menu_join:
+ return doJoinContactAction();
+ }
+ return false;
+ }
+
+ /**
+ * Delete the entire contact currently being edited, which usually asks for
+ * user confirmation before continuing.
+ */
+ private boolean doDeleteAction() {
+ if (!hasValidState())
+ return false;
+
+ // TODO: Make sure Insert turns into Edit if/once it is autosaved
+ if (Intent.ACTION_INSERT.equals(mAction)) {
+ if (mListener != null) mListener.onReverted();
+ } else {
+ if (mListener != null) mListener.onDeleteRequested(mLookupUri);
+ }
+ return true;
+ }
+
+ private boolean doSplitContactAction() {
+ if (!hasValidState()) return false;
+
+ final SplitContactConfirmationDialogFragment dialog =
+ new SplitContactConfirmationDialogFragment();
+ dialog.setTargetFragment(this, 0);
+ dialog.show(getFragmentManager(), SplitContactConfirmationDialogFragment.TAG);
+ return true;
+ }
+
+ private boolean doJoinContactAction() {
+ if (!hasValidState()) {
+ return false;
+ }
+
+ // If we just started creating a new contact and haven't added any data, it's too
+ // early to do a join
+ if (mState.size() == 1 && mState.get(0).isContactInsert()) {
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+ EntityModifier.trimEmpty(mState, accountTypes);
+ if (mState.buildDiff().isEmpty()) {
+ Toast.makeText(getActivity(), R.string.toast_join_with_empty_contact,
+ Toast.LENGTH_LONG).show();
+ return true;
+ }
+ }
+
+ return save(SaveMode.JOIN);
+ }
+
+ /**
+ * Constructs an intent for picking a photo from Gallery, cropping it and returning the bitmap.
+ */
+ public static Intent getPhotoPickIntent() {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
+ intent.setType("image/*");
+ intent.putExtra("crop", "true");
+ intent.putExtra("aspectX", 1);
+ intent.putExtra("aspectY", 1);
+ intent.putExtra("outputX", ICON_SIZE);
+ intent.putExtra("outputY", ICON_SIZE);
+ intent.putExtra("return-data", true);
+ return intent;
+ }
+
+ /**
+ * Check if our internal {@link #mState} is valid, usually checked before
+ * performing user actions.
+ */
+ private boolean hasValidState() {
+ return mState != null && mState.size() > 0;
+ }
+
+ /**
+ * Create a file name for the icon photo using current time.
+ */
+ private String getPhotoFileName() {
+ Date date = new Date(System.currentTimeMillis());
+ SimpleDateFormat dateFormat = new SimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");
+ return dateFormat.format(date) + ".jpg";
+ }
+
+ /**
+ * Constructs an intent for capturing a photo and storing it in a temporary file.
+ */
+ public static Intent getTakePickIntent(File f) {
+ Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null);
+ intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
+ return intent;
+ }
+
+ /**
+ * Sends a newly acquired photo to Gallery for cropping
+ */
+ protected void doCropPhoto(File f) {
+ try {
+ // Add the image to the media store
+ MediaScannerConnection.scanFile(
+ mContext,
+ new String[] { f.getAbsolutePath() },
+ new String[] { null },
+ null);
+
+ // Launch gallery to crop the photo
+ final Intent intent = getCropImageIntent(Uri.fromFile(f));
+ mSaveOnStop = false;
+ startActivityForResult(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA);
+ } catch (Exception e) {
+ Log.e(TAG, "Cannot crop image", e);
+ Toast.makeText(mContext, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ /**
+ * Constructs an intent for image cropping.
+ */
+ public static Intent getCropImageIntent(Uri photoUri) {
+ Intent intent = new Intent("com.android.camera.action.CROP");
+ intent.setDataAndType(photoUri, "image/*");
+ intent.putExtra("crop", "true");
+ intent.putExtra("aspectX", 1);
+ intent.putExtra("aspectY", 1);
+ intent.putExtra("outputX", ICON_SIZE);
+ intent.putExtra("outputY", ICON_SIZE);
+ intent.putExtra("return-data", true);
+ return intent;
+ }
+
+ /**
+ * Saves or creates the contact based on the mode, and if successful
+ * finishes the activity.
+ */
+ public boolean save(int saveMode) {
+ if (!hasValidState()) {
+ return false;
+ }
+
+ // If we are about to close the editor - there is no need to refresh the data
+ if (saveMode == SaveMode.CLOSE) {
+ getLoaderManager().destroyLoader(LOADER_DATA);
+ }
+
+ mStatus = Status.SAVING;
+
+ // Trim any empty fields, and RawContacts, before persisting
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+ EntityModifier.trimEmpty(mState, accountTypes);
+
+ if (mState.buildDiff().isEmpty()) {
+ onSaveCompleted(true, saveMode, mLookupUri);
+ return true;
+ }
+
+ final PersistTask task = new PersistTask(this, saveMode);
+ task.execute(mState);
+
+ return true;
+ }
+
+ public static class CancelEditDialogFragment extends DialogFragment {
+
+ public static void show(ContactEditorFragment fragment) {
+ CancelEditDialogFragment dialog = new CancelEditDialogFragment();
+ dialog.setTargetFragment(fragment, 0);
+ dialog.show(fragment.getFragmentManager(), "cancelEditor");
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog dialog = new AlertDialog.Builder(getActivity())
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setTitle(R.string.cancel_confirmation_dialog_title)
+ .setMessage(R.string.cancel_confirmation_dialog_message)
+ .setPositiveButton(R.string.discard,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ ((ContactEditorFragment)getTargetFragment()).doRevertAction();
+ }
+ }
+ )
+ .setNegativeButton(android.R.string.cancel, null)
+ .create();
+ return dialog;
+ }
+ }
+
+ private boolean revert() {
+ if (mState == null || mState.buildDiff().isEmpty()) {
+ doRevertAction();
+ } else {
+ CancelEditDialogFragment.show(this);
+ }
+ return true;
+ }
+
+ private void doRevertAction() {
+ // When this Fragment is closed we don't want it to auto-save
+ mStatus = Status.CLOSING;
+ if (mListener != null) mListener.onReverted();
+ }
+
+ public void onJoinCompleted(Uri uri) {
+ onSaveCompleted(uri != null, SaveMode.RELOAD, uri);
+ }
+
+ public void onSaveCompleted(boolean success, int saveMode, Uri contactLookupUri) {
+ Log.d(TAG, "onSaveCompleted(" + success + ", " + saveMode + ", " + contactLookupUri);
+ switch (saveMode) {
+ case SaveMode.CLOSE:
+ case SaveMode.HOME:
+ final Intent resultIntent;
+ final int resultCode;
+ if (success && contactLookupUri != null) {
+ final String requestAuthority =
+ mLookupUri == null ? null : mLookupUri.getAuthority();
+
+ final String legacyAuthority = "contacts";
+
+ resultIntent = new Intent();
+ if (legacyAuthority.equals(requestAuthority)) {
+ // Build legacy Uri when requested by caller
+ final long contactId = ContentUris.parseId(Contacts.lookupContact(
+ mContext.getContentResolver(), contactLookupUri));
+ final Uri legacyContentUri = Uri.parse("content://contacts/people");
+ final Uri legacyUri = ContentUris.withAppendedId(
+ legacyContentUri, contactId);
+ resultIntent.setData(legacyUri);
+ } else {
+ // Otherwise pass back a lookup-style Uri
+ resultIntent.setData(contactLookupUri);
+ }
+
+ resultCode = Activity.RESULT_OK;
+ } else {
+ resultCode = Activity.RESULT_CANCELED;
+ resultIntent = null;
+ }
+ // It is already saved, so prevent that it is saved again
+ mStatus = Status.CLOSING;
+ if (mListener != null) mListener.onSaveFinished(resultCode, resultIntent,
+ saveMode == SaveMode.HOME);
+ break;
+ case SaveMode.RELOAD:
+ case SaveMode.JOIN:
+ if (success && contactLookupUri != null) {
+ // If this was in INSERT, we are changing into an EDIT now.
+ // If it already was an EDIT, we are changing to the new Uri now
+ mState = null;
+ load(Intent.ACTION_EDIT, contactLookupUri, null);
+ mStatus = Status.LOADING;
+ getLoaderManager().restartLoader(LOADER_DATA, null, mDataLoaderListener);
+
+ // If it was a JOIN, we are now ready to bring up the join activity.
+ if (saveMode == SaveMode.JOIN) {
+ showJoinAggregateActivity(contactLookupUri);
+ }
+ }
+ break;
+ case SaveMode.SPLIT:
+ setEnabled(true);
+ if (mListener != null) {
+ mListener.onContactSplit(contactLookupUri);
+ } else {
+ Log.d(TAG, "No listener registered, can not call onSplitFinished");
+ }
+ mStatus = Status.EDITING;
+ break;
+ }
+ }
+
+ /**
+ * Shows a list of aggregates that can be joined into the currently viewed aggregate.
+ *
+ * @param contactLookupUri the fresh URI for the currently edited contact (after saving it)
+ */
+ private void showJoinAggregateActivity(Uri contactLookupUri) {
+ if (contactLookupUri == null || !isAdded()) {
+ return;
+ }
+
+ mContactIdForJoin = ContentUris.parseId(contactLookupUri);
+ final Intent intent = new Intent(JoinContactActivity.JOIN_CONTACT);
+ intent.putExtra(JoinContactActivity.EXTRA_TARGET_CONTACT_ID, mContactIdForJoin);
+ startActivityForResult(intent, REQUEST_CODE_JOIN);
+ }
+
+ /**
+ * Performs aggregation with the contact selected by the user from suggestions or A-Z list.
+ */
+ private void joinAggregate(final long contactId) {
+ Intent intent = ContactSaveService.createJoinContactsIntent(mContext, mContactIdForJoin,
+ contactId, isContactWritable(),
+ ContactEditorActivity.class, ContactEditorActivity.ACTION_JOIN_COMPLETED);
+ mContext.startService(intent);
+ }
+
+ /**
+ * Returns true if there is at least one writable raw contact in the current contact.
+ */
+ private boolean isContactWritable() {
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+ int size = mState.size();
+ for (int i = 0; i < size; i++) {
+ ValuesDelta values = mState.get(i).getValues();
+ final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
+ final AccountType type = accountTypes.getAccountType(accountType);
+ if (!type.readOnly) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static interface Listener {
+ /**
+ * Contact was not found, so somehow close this fragment. This is raised after a contact
+ * is removed via Menu/Delete (unless it was a new contact)
+ */
+ void onContactNotFound();
+
+ /**
+ * Contact was split, so we can close now.
+ * @param newLookupUri The lookup uri of the new contact that should be shown to the user.
+ * The editor tries best to chose the most natural contact here.
+ */
+ void onContactSplit(Uri newLookupUri);
+
+ /**
+ * User was presented with an account selection and couldn't decide.
+ */
+ void onAccountSelectorAborted();
+
+ /**
+ * User has tapped Revert, close the fragment now.
+ */
+ void onReverted();
+
+ /**
+ * Set the Title (e.g. of the Activity)
+ */
+ void setTitleTo(int resourceId);
+
+ /**
+ * Contact was saved and the Fragment can now be closed safely.
+ */
+ void onSaveFinished(int resultCode, Intent resultIntent, boolean navigateHome);
+
+ /**
+ * User decided to delete the contact.
+ */
+ void onDeleteRequested(Uri lookupUri);
+
+ /**
+ * User switched to editing a different contact (a suggestion from the
+ * aggregation engine).
+ */
+ void onEditOtherContactRequested(
+ Uri contactLookupUri, ArrayList<ContentValues> contentValues);
+
+ /**
+ * Contact is being created for an external account that provides its own
+ * new contact activity.
+ */
+ void onCustomCreateContactActivityRequested(Account account, Bundle intentExtras);
+
+ /**
+ * The edited raw contact belongs to an external account that provides
+ * its own edit activity.
+ *
+ * @param redirect indicates that the current editor should be closed
+ * before the custom editor is shown.
+ */
+ void onCustomEditContactActivityRequested(Account account, Uri rawContactUri,
+ Bundle intentExtras, boolean redirect);
+ }
+
+ private class EntityDeltaComparator implements Comparator<EntityDelta> {
+ /**
+ * Compare EntityDeltas for sorting the stack of editors.
+ */
+ @Override
+ public int compare(EntityDelta one, EntityDelta two) {
+ // Check direct equality
+ if (one.equals(two)) {
+ return 0;
+ }
+
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+ String accountType2 = one.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ final AccountType type1 = accountTypes.getAccountType(accountType2);
+ accountType2 = two.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ final AccountType type2 = accountTypes.getAccountType(accountType2);
+
+ // Check read-only
+ if (type1.readOnly && !type2.readOnly) {
+ return 1;
+ } else if (!type1.readOnly && type2.readOnly) {
+ return -1;
+ }
+
+ // Check account type
+ boolean skipAccountTypeCheck = false;
+ boolean isGoogleAccount1 = type1 instanceof GoogleAccountType;
+ boolean isGoogleAccount2 = type2 instanceof GoogleAccountType;
+ if (isGoogleAccount1 && !isGoogleAccount2) {
+ return -1;
+ } else if (!isGoogleAccount1 && isGoogleAccount2) {
+ return 1;
+ } else if (isGoogleAccount1 && isGoogleAccount2){
+ skipAccountTypeCheck = true;
+ }
+
+ int value;
+ if (!skipAccountTypeCheck) {
+ if (type1.accountType == null) {
+ return 1;
+ }
+ value = type1.accountType.compareTo(type2.accountType);
+ if (value != 0) {
+ return value;
+ }
+ }
+
+ // Check account name
+ ValuesDelta oneValues = one.getValues();
+ String oneAccount = oneValues.getAsString(RawContacts.ACCOUNT_NAME);
+ if (oneAccount == null) oneAccount = "";
+ ValuesDelta twoValues = two.getValues();
+ String twoAccount = twoValues.getAsString(RawContacts.ACCOUNT_NAME);
+ if (twoAccount == null) twoAccount = "";
+ value = oneAccount.compareTo(twoAccount);
+ if (value != 0) {
+ return value;
+ }
+
+ // Both are in the same account, fall back to contact ID
+ Long oneId = oneValues.getAsLong(RawContacts._ID);
+ Long twoId = twoValues.getAsLong(RawContacts._ID);
+ if (oneId == null) {
+ return -1;
+ } else if (twoId == null) {
+ return 1;
+ }
+
+ return (int)(oneId - twoId);
+ }
+ }
+
+ /**
+ * Returns the contact ID for the currently edited contact or 0 if the contact is new.
+ */
+ protected long getContactId() {
+ for (EntityDelta rawContact : mState) {
+ Long contactId = rawContact.getValues().getAsLong(RawContacts.CONTACT_ID);
+ if (contactId != null) {
+ return contactId;
+ }
+ }
+ return 0;
+ }
+
+
+ private void onContactNameChange(int request, final RawContactEditorView rawContactEditor,
+ TextFieldsEditorView nameEditor) {
+
+ switch (request) {
+ case EditorListener.EDITOR_FORM_CHANGED:
+ if (nameEditor.hasShortAndLongForms()) {
+ if (nameEditor.areOptionalFieldsVisible()) {
+ switchFromFullNameToStructuredName(nameEditor);
+ } else {
+ switchFromStructuredNameToFullName(nameEditor);
+ }
+ }
+ break;
+
+ case EditorListener.FIELD_CHANGED:
+ if (nameEditor.hasShortAndLongForms()) {
+ if (nameEditor.areOptionalFieldsVisible()) {
+ eraseFullName(nameEditor.getValues());
+ } else {
+ eraseStructuredName(nameEditor.getValues());
+ }
+ }
+ acquireAggregationSuggestions(rawContactEditor);
+ break;
+ }
+ }
+
+ private void switchFromFullNameToStructuredName(LabeledEditorView nameEditor) {
+ ValuesDelta values = nameEditor.getValues();
+
+ String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
+ if (displayName == null) {
+ displayName = "";
+ }
+
+ Uri uri = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name")
+ .appendQueryParameter(StructuredName.DISPLAY_NAME, displayName).build();
+ Cursor cursor = getActivity().getContentResolver().query(uri, new String[]{
+ StructuredName.PREFIX,
+ StructuredName.GIVEN_NAME,
+ StructuredName.MIDDLE_NAME,
+ StructuredName.FAMILY_NAME,
+ StructuredName.SUFFIX,
+ }, null, null, null);
+
+ try {
+ if (cursor.moveToFirst()) {
+ eraseFullName(values);
+ values.put(StructuredName.PREFIX, cursor.getString(0));
+ values.put(StructuredName.GIVEN_NAME, cursor.getString(1));
+ values.put(StructuredName.MIDDLE_NAME, cursor.getString(2));
+ values.put(StructuredName.FAMILY_NAME, cursor.getString(3));
+ values.put(StructuredName.SUFFIX, cursor.getString(4));
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void switchFromStructuredNameToFullName(LabeledEditorView nameEditor) {
+ ValuesDelta values = nameEditor.getValues();
+
+ Uri.Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath(
+ "complete_name");
+ appendQueryParameter(builder, values, StructuredName.PREFIX);
+ appendQueryParameter(builder, values, StructuredName.GIVEN_NAME);
+ appendQueryParameter(builder, values, StructuredName.MIDDLE_NAME);
+ appendQueryParameter(builder, values, StructuredName.FAMILY_NAME);
+ appendQueryParameter(builder, values, StructuredName.SUFFIX);
+ Uri uri = builder.build();
+ Cursor cursor = getActivity().getContentResolver().query(uri, new String[]{
+ StructuredName.DISPLAY_NAME,
+ }, null, null, null);
+
+ try {
+ if (cursor.moveToFirst()) {
+ eraseStructuredName(values);
+ values.put(StructuredName.DISPLAY_NAME, cursor.getString(0));
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void eraseFullName(ValuesDelta values) {
+ values.putNull(StructuredName.DISPLAY_NAME);
+ }
+
+ private void eraseStructuredName(ValuesDelta values) {
+ values.putNull(StructuredName.PREFIX);
+ values.putNull(StructuredName.GIVEN_NAME);
+ values.putNull(StructuredName.MIDDLE_NAME);
+ values.putNull(StructuredName.FAMILY_NAME);
+ values.putNull(StructuredName.SUFFIX);
+ }
+
+ private void appendQueryParameter(Uri.Builder builder, ValuesDelta values, String field) {
+ String value = values.getAsString(field);
+ if (!TextUtils.isEmpty(value)) {
+ builder.appendQueryParameter(field, value);
+ }
+ }
+
+ /**
+ * Triggers an asynchronous search for aggregation suggestions.
+ */
+ public void acquireAggregationSuggestions(RawContactEditorView rawContactEditor) {
+ long rawContactId = rawContactEditor.getRawContactId();
+ if (mAggregationSuggestionsRawContactId != rawContactId
+ && mAggregationSuggestionView != null) {
+ mAggregationSuggestionView.setVisibility(View.GONE);
+ mAggregationSuggestionView = null;
+ mAggregationSuggestionEngine.reset();
+ }
+
+ mAggregationSuggestionsRawContactId = rawContactId;
+
+ if (mAggregationSuggestionEngine == null) {
+ mAggregationSuggestionEngine = new AggregationSuggestionEngine(getActivity());
+ mAggregationSuggestionEngine.setListener(this);
+ mAggregationSuggestionEngine.start();
+ }
+
+ mAggregationSuggestionEngine.setContactId(getContactId());
+
+ LabeledEditorView nameEditor = rawContactEditor.getNameEditor();
+ mAggregationSuggestionEngine.onNameChange(nameEditor.getValues());
+ }
+
+ @Override
+ public void onAggregationSuggestionChange() {
+ if (!isAdded() || mState == null) {
+ return;
+ }
+
+ RawContactEditorView rawContactView =
+ (RawContactEditorView)getRawContactEditorView(mAggregationSuggestionsRawContactId);
+ if (rawContactView == null) {
+ return;
+ }
+
+ ViewStub stub = (ViewStub)rawContactView.findViewById(R.id.aggregation_suggestion_stub);
+ if (stub != null) {
+ stub.inflate();
+ }
+
+ // Only request the view on screen when it is first displayed
+ boolean requestOnScreen = mAggregationSuggestionView == null;
+ mAggregationSuggestionView = rawContactView.findViewById(R.id.aggregation_suggestion);
+
+ int count = mAggregationSuggestionEngine.getSuggestedContactCount();
+ if (count == 0) {
+ mAggregationSuggestionView.setVisibility(View.GONE);
+ return;
+ }
+
+ List<Suggestion> suggestions = mAggregationSuggestionEngine.getSuggestions();
+
+ LinearLayout itemList = (LinearLayout) mAggregationSuggestionView.findViewById(
+ R.id.aggregation_suggestions);
+ itemList.removeAllViews();
+
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+
+ for (Suggestion suggestion : suggestions) {
+ AggregationSuggestionView suggestionView =
+ (AggregationSuggestionView) inflater.inflate(
+ R.layout.aggregation_suggestions_item, null);
+ suggestionView.setLayoutParams(
+ new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ suggestionView.setNewContact(mState.size() == 1 && mState.get(0).isContactInsert());
+ suggestionView.setListener(this);
+ suggestionView.bindSuggestion(suggestion);
+ itemList.addView(suggestionView);
+ }
+
+ adjustAggregationSuggestionViewLayout(rawContactView);
+ mAggregationSuggestionView.setVisibility(View.VISIBLE);
+
+ if (requestOnScreen) {
+ mContent.postDelayed(new Runnable() {
+
+ @Override
+ public void run() {
+ requestAggregationSuggestionOnScreen(mAggregationSuggestionView);
+ }
+ }, AGGREGATION_SUGGESTION_SCROLL_DELAY);
+ }
+ }
+
+ /**
+ * Adjusts the layout of the aggregation suggestion view so that it is placed directly
+ * underneath and have the same width as the last text editor of the contact name editor.
+ */
+ private void adjustAggregationSuggestionViewLayout(RawContactEditorView rawContactView) {
+ TextFieldsEditorView nameEditor = rawContactView.getNameEditor();
+ Rect rect = new Rect();
+ nameEditor.acquireEditorBounds(rect);
+ MarginLayoutParams layoutParams =
+ (MarginLayoutParams) mAggregationSuggestionView.getLayoutParams();
+ layoutParams.leftMargin = rect.left;
+ layoutParams.width = rect.width();
+ mAggregationSuggestionView.setLayoutParams(layoutParams);
+ }
+
+ @Override
+ public void onJoinAction(long contactId, List<Long> rawContactIdList) {
+ long rawContactIds[] = new long[rawContactIdList.size()];
+ for (int i = 0; i < rawContactIds.length; i++) {
+ rawContactIds[i] = rawContactIdList.get(i);
+ }
+ JoinSuggestedContactDialogFragment dialog =
+ new JoinSuggestedContactDialogFragment();
+ Bundle args = new Bundle();
+ args.putLongArray("rawContactIds", rawContactIds);
+ dialog.setArguments(args);
+ dialog.setTargetFragment(this, 0);
+ try {
+ dialog.show(getFragmentManager(), "join");
+ } catch (Exception ex) {
+ // No problem - the activity is no longer available to display the dialog
+ }
+ }
+
+ public static class JoinSuggestedContactDialogFragment extends DialogFragment {
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setTitle(R.string.aggregation_suggestion_join_dialog_title)
+ .setMessage(R.string.aggregation_suggestion_join_dialog_message)
+ .setPositiveButton(android.R.string.yes,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ ContactEditorFragment targetFragment =
+ (ContactEditorFragment) getTargetFragment();
+ long rawContactIds[] =
+ getArguments().getLongArray("rawContactIds");
+ targetFragment.doJoinSuggestedContact(rawContactIds);
+ }
+ }
+ )
+ .setNegativeButton(android.R.string.no, null)
+ .create();
+ }
+ }
+
+ /**
+ * Joins the suggested contact (specified by the id's of constituent raw
+ * contacts), save all changes, and stay in the editor.
+ */
+ protected void doJoinSuggestedContact(long[] rawContactIds) {
+ mState.setJoinWithRawContacts(rawContactIds);
+ save(SaveMode.RELOAD);
+ }
+
+ @Override
+ public void onEditAction(Uri contactLookupUri) {
+ SuggestionEditConfirmationDialogFragment dialog =
+ new SuggestionEditConfirmationDialogFragment();
+ Bundle args = new Bundle();
+ args.putParcelable("contactUri", contactLookupUri);
+ dialog.setArguments(args);
+ dialog.setTargetFragment(this, 0);
+ dialog.show(getFragmentManager(), "edit");
+ }
+
+ public static class SuggestionEditConfirmationDialogFragment extends DialogFragment {
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setTitle(R.string.aggregation_suggestion_edit_dialog_title)
+ .setMessage(R.string.aggregation_suggestion_edit_dialog_message)
+ .setPositiveButton(android.R.string.yes,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ ContactEditorFragment targetFragment =
+ (ContactEditorFragment) getTargetFragment();
+ Uri contactUri =
+ getArguments().getParcelable("contactUri");
+ targetFragment.doEditSuggestedContact(contactUri);
+ }
+ }
+ )
+ .setNegativeButton(android.R.string.no, null)
+ .create();
+ }
+ }
+
+ /**
+ * Abandons the currently edited contact and switches to editing the suggested
+ * one, transferring all the data there
+ */
+ protected void doEditSuggestedContact(Uri contactUri) {
+ if (mListener != null) {
+ mListener.onEditOtherContactRequested(
+ contactUri, mState.get(0).getContentValues());
+ }
+ }
+
+ /**
+ * Scrolls the editor if necessary to reveal the aggregation suggestion that is
+ * shown below the name editor. Makes sure that the currently focused field
+ * remains visible.
+ */
+ private void requestAggregationSuggestionOnScreen(final View view) {
+ Rect rect = getRelativeBounds(mContent, view);
+ View focused = mContent.findFocus();
+ if (focused != null) {
+ rect.union(getRelativeBounds(mContent, focused));
+ }
+ mContent.requestRectangleOnScreen(rect);
+ }
+
+ /**
+ * Computes bounds of the supplied view relative to its ascendant.
+ */
+ private Rect getRelativeBounds(View ascendant, View view) {
+ Rect rect = new Rect();
+ rect.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+
+ View parent = (View) view.getParent();
+ while (parent != ascendant) {
+ rect.offset(parent.getLeft(), parent.getTop());
+ parent = (View) parent.getParent();
+ }
+ return rect;
+ }
+
+ // TODO: There has to be a nicer way than this WeakAsyncTask...? Maybe call a service?
+ /**
+ * Background task for persisting edited contact data, using the changes
+ * defined by a set of {@link EntityDelta}. This task starts
+ * {@link EmptyService} to make sure the background thread can finish
+ * persisting in cases where the system wants to reclaim our process.
+ */
+ public static class PersistTask extends
+ WeakAsyncTask<EntityDeltaList, Void, Integer, ContactEditorFragment> {
+ private static final int PERSIST_TRIES = 3;
+
+ private static final int RESULT_UNCHANGED = 0;
+ private static final int RESULT_SUCCESS = 1;
+ private static final int RESULT_FAILURE = 2;
+
+ private final Context mContext;
+
+ private int mSaveMode;
+ private Uri mContactLookupUri = null;
+
+ public PersistTask(ContactEditorFragment target, int saveMode) {
+ super(target);
+ mSaveMode = saveMode;
+ mContext = target.mContext;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onPreExecute(ContactEditorFragment target) {
+ target.setEnabled(false);
+
+ // Before starting this task, start an empty service to protect our
+ // process from being reclaimed by the system.
+ mContext.startService(new Intent(mContext, EmptyService.class));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected Integer doInBackground(ContactEditorFragment target, EntityDeltaList... params) {
+ final ContentResolver resolver = mContext.getContentResolver();
+
+ EntityDeltaList state = params[0];
+
+ // Attempt to persist changes
+ int tries = 0;
+ Integer result = RESULT_FAILURE;
+ while (tries++ < PERSIST_TRIES) {
+ try {
+ // Build operations and try applying
+ final ArrayList<ContentProviderOperation> diff = state.buildDiff();
+ ContentProviderResult[] results = null;
+ if (!diff.isEmpty()) {
+ results = resolver.applyBatch(ContactsContract.AUTHORITY, diff);
+ }
+
+ final long rawContactId = getRawContactId(state, diff, results);
+ if (rawContactId != -1) {
+ final Uri rawContactUri = ContentUris.withAppendedId(
+ RawContacts.CONTENT_URI, rawContactId);
+
+ // convert the raw contact URI to a contact URI
+ mContactLookupUri = RawContacts.getContactLookupUri(resolver,
+ rawContactUri);
+ Log.d(TAG, "Looked up RawContact Uri " + rawContactUri +
+ " into ContactLookupUri " + mContactLookupUri);
+ } else {
+ Log.w(TAG, "Could not determine RawContact ID after save");
+ }
+ result = (diff.size() > 0) ? RESULT_SUCCESS : RESULT_UNCHANGED;
+ break;
+
+ } catch (RemoteException e) {
+ // Something went wrong, bail without success
+ Log.e(TAG, "Problem persisting user edits", e);
+ break;
+
+ } catch (OperationApplicationException e) {
+ // Version consistency failed, re-parent change and try again
+ Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
+ final EntityDeltaList newState = EntityDeltaList.fromQuery(resolver,
+ target.mQuerySelection, null, null);
+ state = EntityDeltaList.mergeAfter(newState, state);
+ }
+ }
+
+ return result;
+ }
+
+ private long getRawContactId(EntityDeltaList state,
+ final ArrayList<ContentProviderOperation> diff,
+ final ContentProviderResult[] results) {
+ long rawContactId = state.findRawContactId();
+ if (rawContactId != -1) {
+ return rawContactId;
+ }
+
+
+ // we gotta do some searching for the id
+ final int diffSize = diff.size();
+ for (int i = 0; i < diffSize; i++) {
+ ContentProviderOperation operation = diff.get(i);
+ if (operation.getType() == ContentProviderOperation.TYPE_INSERT
+ && operation.getUri().getEncodedPath().contains(
+ RawContacts.CONTENT_URI.getEncodedPath())) {
+ return ContentUris.parseId(results[i].uri);
+ }
+ }
+ return -1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onPostExecute(ContactEditorFragment target, Integer result) {
+ Log.d(TAG, "onPostExecute(something," + result + "). mSaveMode=" + mSaveMode);
+ if (result == RESULT_SUCCESS && mSaveMode != SaveMode.JOIN) {
+ Toast.makeText(mContext, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
+ } else if (result == RESULT_FAILURE) {
+ Toast.makeText(mContext, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
+ }
+
+ // Stop the service that was protecting us
+ mContext.stopService(new Intent(mContext, EmptyService.class));
+
+ target.onSaveCompleted(result != RESULT_FAILURE, mSaveMode, mContactLookupUri);
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putParcelable(KEY_URI, mLookupUri);
+ outState.putString(KEY_ACTION, mAction);
+
+ if (hasValidState()) {
+ // Store entities with modifications
+ outState.putParcelable(KEY_EDIT_STATE, mState);
+ }
+
+ outState.putLong(KEY_RAW_CONTACT_ID_REQUESTING_PHOTO, mRawContactIdRequestingPhoto);
+ outState.putParcelable(KEY_VIEW_ID_GENERATOR, mViewIdGenerator);
+ if (mCurrentPhotoFile != null) {
+ outState.putString(KEY_CURRENT_PHOTO_FILE, mCurrentPhotoFile.toString());
+ }
+ outState.putString(KEY_QUERY_SELECTION, mQuerySelection);
+ outState.putLong(KEY_CONTACT_ID_FOR_JOIN, mContactIdForJoin);
+ outState.putLong(KEY_SHOW_JOIN_SUGGESTIONS, mAggregationSuggestionsRawContactId);
+ outState.putBoolean(KEY_ENABLED, mEnabled);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // Ignore failed requests
+ if (resultCode != Activity.RESULT_OK) return;
+ switch (requestCode) {
+ case REQUEST_CODE_PHOTO_PICKED_WITH_DATA: {
+ // As we are coming back to this view, the editor will be reloaded automatically,
+ // which will cause the photo that is set here to disappear. To prevent this,
+ // we remember to set a flag which is interpreted after loading.
+ // This photo is set here already to reduce flickering.
+ mPhoto = data.getParcelableExtra("data");
+ setPhoto(mRawContactIdRequestingPhoto, mPhoto);
+ mRawContactIdRequestingPhotoAfterLoad = mRawContactIdRequestingPhoto;
+ mRawContactIdRequestingPhoto = -1;
+
+ break;
+ }
+ case REQUEST_CODE_CAMERA_WITH_DATA: {
+ doCropPhoto(mCurrentPhotoFile);
+ break;
+ }
+ case REQUEST_CODE_JOIN: {
+ if (data != null) {
+ final long contactId = ContentUris.parseId(data.getData());
+ joinAggregate(contactId);
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Sets the photo stored in mPhoto and writes it to the RawContact with the given id
+ */
+ private void setPhoto(long rawContact, Bitmap photo) {
+ BaseRawContactEditorView requestingEditor = getRawContactEditorView(rawContact);
+ if (requestingEditor != null) {
+ requestingEditor.setPhotoBitmap(photo);
+ } else {
+ Log.w(TAG, "The contact that requested the photo is no longer present.");
+ }
+ }
+
+ /**
+ * Finds raw contact editor view for the given rawContactId.
+ */
+ public BaseRawContactEditorView getRawContactEditorView(long rawContactId) {
+ for (int i = 0; i < mContent.getChildCount(); i++) {
+ final View childView = mContent.getChildAt(i);
+ if (childView instanceof BaseRawContactEditorView) {
+ final BaseRawContactEditorView editor = (BaseRawContactEditorView) childView;
+ if (editor.getRawContactId() == rawContactId) {
+ return editor;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if there is currently more than one photo on screen.
+ */
+ private boolean hasMoreThanOnePhoto() {
+ int count = mContent.getChildCount();
+ int countWithPicture = 0;
+ for (int i = 0; i < count; i++) {
+ final View childView = mContent.getChildAt(i);
+ if (childView instanceof BaseRawContactEditorView) {
+ final BaseRawContactEditorView editor = (BaseRawContactEditorView) childView;
+ if (editor.hasSetPhoto()) {
+ countWithPicture++;
+ if (countWithPicture > 1) return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * The listener for the data loader
+ */
+ private final LoaderManager.LoaderCallbacks<ContactLoader.Result> mDataLoaderListener =
+ new LoaderCallbacks<ContactLoader.Result>() {
+ @Override
+ public Loader<ContactLoader.Result> onCreateLoader(int id, Bundle args) {
+ mLoaderStartTime = SystemClock.elapsedRealtime();
+ return new ContactLoader(mContext, mLookupUri);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<ContactLoader.Result> loader, ContactLoader.Result data) {
+ final long loaderCurrentTime = SystemClock.elapsedRealtime();
+ Log.v(TAG, "Time needed for loading: " + (loaderCurrentTime-mLoaderStartTime));
+ if (data == ContactLoader.Result.NOT_FOUND || data == ContactLoader.Result.ERROR) {
+ // Item has been deleted
+ Log.i(TAG, "No contact found. Closing activity");
+ if (mListener != null) mListener.onContactNotFound();
+ return;
+ }
+
+ mStatus = Status.EDITING;
+ mLookupUri = data.getLookupUri();
+ final long setDataStartTime = SystemClock.elapsedRealtime();
+ setData(data);
+ final long setDataEndTime = SystemClock.elapsedRealtime();
+
+ // If we are coming back from the photo trimmer, this will be set.
+ if (mRawContactIdRequestingPhotoAfterLoad != -1) {
+ setPhoto(mRawContactIdRequestingPhotoAfterLoad, mPhoto);
+ mRawContactIdRequestingPhotoAfterLoad = -1;
+ mPhoto = null;
+ }
+ Log.v(TAG, "Time needed for setting UI: " + (setDataEndTime-setDataStartTime));
+ }
+
+ public void onLoaderReset(Loader<ContactLoader.Result> loader) {
+ }
+ };
+
+ /**
+ * The listener for the group meta data loader
+ */
+ private final LoaderManager.LoaderCallbacks<Cursor> mGroupLoaderListener =
+ new LoaderCallbacks<Cursor>() {
+
+ @Override
+ public CursorLoader onCreateLoader(int id, Bundle args) {
+ return new GroupMetaDataLoader(mContext);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mGroupMetaData = data;
+ bindGroupMetaData();
+ }
+
+ public void onLoaderReset(Loader<Cursor> loader) {
+ }
+ };
+
+ @Override
+ public void onSplitContactConfirmed() {
+ mState.markRawContactsForSplitting();
+ save(SaveMode.SPLIT);
+ }
+
+ /**
+ * Account was chosen in the selector. Create a RawContact for this account now
+ */
+ @Override
+ public void onAccountChosen(Account account) {
+ createContact(account);
+ }
+
+ /**
+ * The account selector has been aborted. If we are in "New" mode, we have to close now
+ */
+ @Override
+ public void onAccountSelectorCancelled() {
+ if (!hasValidState() && mListener != null) {
+ mListener.onAccountSelectorAborted();
+ }
+ }
+
+ private final class PhotoEditorListener
+ implements EditorListener, PhotoActionPopup.Listener {
+ private final BaseRawContactEditorView mEditor;
+ private final boolean mAccountReadOnly;
+
+ private PhotoEditorListener(BaseRawContactEditorView editor, boolean accountReadOnly) {
+ mEditor = editor;
+ mAccountReadOnly = accountReadOnly;
+ }
+
+ @Override
+ public void onRequest(int request) {
+ if (!hasValidState()) return;
+
+ if (request == EditorListener.REQUEST_PICK_PHOTO) {
+ // Determine mode
+ final int mode;
+ if (mAccountReadOnly) {
+ if (mEditor.hasSetPhoto() && hasMoreThanOnePhoto()) {
+ mode = PhotoActionPopup.MODE_READ_ONLY_ALLOW_PRIMARY;
+ } else {
+ // Read-only and either no photo or the only photo ==> no options
+ return;
+ }
+ } else {
+ if (mEditor.hasSetPhoto()) {
+ if (hasMoreThanOnePhoto()) {
+ mode = PhotoActionPopup.MODE_PHOTO_ALLOW_PRIMARY;
+ } else {
+ mode = PhotoActionPopup.MODE_PHOTO_DISALLOW_PRIMARY;
+ }
+ } else {
+ mode = PhotoActionPopup.MODE_NO_PHOTO;
+ }
+ }
+ PhotoActionPopup.createPopupMenu(mContext, mEditor.getPhotoEditor(), this, mode)
+ .show();
+ }
+ }
+
+ @Override
+ public void onDeleted(Editor removedEditor) {
+ }
+
+ /**
+ * User has chosen to set the selected photo as the (super) primary photo
+ */
+ @Override
+ public void onUseAsPrimaryChosen() {
+ // Set the IsSuperPrimary for each editor
+ int count = mContent.getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View childView = mContent.getChildAt(i);
+ if (childView instanceof BaseRawContactEditorView) {
+ final BaseRawContactEditorView editor = (BaseRawContactEditorView) childView;
+ final PhotoEditorView photoEditor = editor.getPhotoEditor();
+ photoEditor.setSuperPrimary(editor == mEditor);
+ }
+ }
+ }
+
+ /**
+ * User has chosen to remove a picture
+ */
+ @Override
+ public void onRemovePictureChose() {
+ mEditor.setPhotoBitmap(null);
+ }
+
+ /**
+ * Launches Camera to take a picture and store it in a file.
+ */
+ @Override
+ public void onTakePhotoChosen() {
+ mRawContactIdRequestingPhoto = mEditor.getRawContactId();
+ try {
+ // Launch camera to take photo for selected contact
+ PHOTO_DIR.mkdirs();
+ mCurrentPhotoFile = new File(PHOTO_DIR, getPhotoFileName());
+ final Intent intent = getTakePickIntent(mCurrentPhotoFile);
+
+ mSaveOnStop = false;
+ startActivityForResult(intent, REQUEST_CODE_CAMERA_WITH_DATA);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(mContext, R.string.photoPickerNotFoundText,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ /**
+ * Launches Gallery to pick a photo.
+ */
+ @Override
+ public void onPickFromGalleryChosen() {
+ mRawContactIdRequestingPhoto = mEditor.getRawContactId();
+ try {
+ // Launch picker to choose photo for selected contact
+ final Intent intent = getPhotoPickIntent();
+ mSaveOnStop = false;
+ startActivityForResult(intent, REQUEST_CODE_PHOTO_PICKED_WITH_DATA);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(mContext, R.string.photoPickerNotFoundText,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+}
diff --git a/src/com/android/contacts/editor/Editor.java b/src/com/android/contacts/editor/Editor.java
new file mode 100644
index 0000000..d733e68
--- /dev/null
+++ b/src/com/android/contacts/editor/Editor.java
@@ -0,0 +1,73 @@
+/*
+ * 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.editor;
+
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+
+import android.provider.ContactsContract.Data;
+
+/**
+ * Generic definition of something that edits a {@link Data} row through an
+ * {@link ValuesDelta} object.
+ */
+public interface Editor {
+ /**
+ * Listener for an {@link Editor}, usually to handle deleted items.
+ */
+ public interface EditorListener {
+ /**
+ * Called when the given {@link Editor} has been deleted.
+ */
+ public void onDeleted(Editor editor);
+
+ /**
+ * Called when the given {@link Editor} has a request, for example it
+ * wants to select a photo.
+ */
+ public void onRequest(int request);
+
+ public static final int REQUEST_PICK_PHOTO = 1;
+ public static final int FIELD_CHANGED = 2;
+
+ // The editor has switched between different representations of the same
+ // data, e.g. from full name to structured name
+ public static final int EDITOR_FORM_CHANGED = 3;
+ }
+
+ /**
+ * Prepare this editor for the given {@link ValuesDelta}, which
+ * builds any needed views. Any changes performed by the user will be
+ * written back to that same object.
+ */
+ public void setValues(DataKind kind, ValuesDelta values, EntityDelta state, boolean readOnly,
+ ViewIdGenerator vig);
+
+ public void setDeletable(boolean deletable);
+
+ /**
+ * Add a specific {@link EditorListener} to this {@link Editor}.
+ */
+ public void setEditorListener(EditorListener listener);
+
+ /**
+ * Called internally when the contents of a specific field have changed,
+ * allowing advanced editors to persist data in a specific way.
+ */
+ public void onFieldChanged(String column, String value);
+}
diff --git a/src/com/android/contacts/editor/EventFieldEditorView.java b/src/com/android/contacts/editor/EventFieldEditorView.java
new file mode 100644
index 0000000..7345dbb
--- /dev/null
+++ b/src/com/android/contacts/editor/EventFieldEditorView.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2010 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.editor;
+
+import com.android.contacts.R;
+import com.android.contacts.datepicker.DatePicker;
+import com.android.contacts.datepicker.DatePickerDialog;
+import com.android.contacts.datepicker.DatePickerDialog.OnDateSetListener;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.AccountType.EditField;
+import com.android.contacts.model.AccountType.EventEditType;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.util.DateUtils;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * Editor that allows editing Events using a {@link DatePickerDialog}
+ */
+public class EventFieldEditorView extends LabeledEditorView {
+ /**
+ * Exchange requires 8:00 for birthdays
+ */
+ private final int DEFAULT_HOUR = 8;
+
+ private Button mDateView;
+
+ public EventFieldEditorView(Context context) {
+ super(context);
+ }
+
+ public EventFieldEditorView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public EventFieldEditorView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public int getBaseline(int row) {
+ int baseline = super.getBaseline(row);
+ if (mDateView != null) {
+ // The date view will be centered vertically in the corresponding line item
+ int lineItemHeight = getLineItemHeight(row);
+ int offset = (lineItemHeight - mDateView.getMeasuredHeight()) / 2;
+ baseline = Math.max(baseline, offset + mDateView.getBaseline());
+ }
+ return baseline;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ int l1 = getPaddingLeft();
+ int t1 = getPaddingTop();
+ int r1 = getMeasuredWidth() - getPaddingRight();
+
+ // Fields
+ // Subtract buttons left and right if necessary
+ final int labelWidth = (getLabel() != null) ? getLabel().getMeasuredWidth() : 0;
+ final int deleteWidth = (getDelete() != null) ? getDelete().getMeasuredWidth() : 0;
+ final int r2 = r1 - deleteWidth - labelWidth;
+ if (mDateView != null) {
+ int height = mDateView.getMeasuredHeight();
+ int baseline = getBaseline(0);
+ int top = t1 + baseline - mDateView.getBaseline();
+ mDateView.layout(
+ l1, top,
+ r2, top + height);
+ }
+ }
+
+ @Override
+ protected int getLineItemHeight(int row) {
+ int height = mDateView == null ? 0 : mDateView.getMeasuredHeight();
+ return Math.max(height, super.getLineItemHeight(row));
+ }
+
+ @Override
+ protected void requestFocusForFirstEditField() {
+ if (mDateView != null) mDateView.requestFocus();
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+
+ if (mDateView != null) mDateView.setEnabled(!isReadOnly() && enabled);
+ }
+
+ @Override
+ public void setValues(DataKind kind, ValuesDelta entry, EntityDelta state, boolean readOnly,
+ ViewIdGenerator vig) {
+ if (kind.fieldList.size() != 1) throw new IllegalStateException("kind must have 1 field");
+ super.setValues(kind, entry, state, readOnly, vig);
+
+ if (mDateView == null) {
+
+ mDateView = new Button(getContext(), null, android.R.attr.spinnerStyle);
+ mDateView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT));
+ mDateView.setEnabled(isEnabled() && !readOnly);
+ mDateView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showDialog(R.id.dialog_event_date_picker);
+ }
+ });
+ addView(mDateView);
+ }
+
+ rebuildDateView();
+ }
+
+ private void rebuildDateView() {
+ final EditField editField = getKind().fieldList.get(0);
+ final String column = editField.column;
+ String data = DateUtils.formatDate(getContext(), getEntry().getAsString(column));
+ if (TextUtils.isEmpty(data)) {
+ data = " ";
+ }
+ mDateView.setText(data);
+ }
+
+ @Override
+ public Dialog createDialog(Bundle bundle) {
+ if (bundle == null) throw new IllegalArgumentException("bundle must not be null");
+ int dialogId = bundle.getInt(DIALOG_ID_KEY);
+ switch (dialogId) {
+ case R.id.dialog_event_date_picker:
+ return createDatePickerDialog();
+ default:
+ return super.createDialog(bundle);
+ }
+ }
+
+ @Override
+ protected EventEditType getType() {
+ return (EventEditType) super.getType();
+ }
+
+ @Override
+ protected void onLabelRebuilt() {
+ // if we changed to a type that requires a year, ensure that it is actually set
+ final String column = getKind().fieldList.get(0).column;
+ final String oldValue = getEntry().getAsString(column);
+ final DataKind kind = getKind();
+
+ final Calendar calendar = Calendar.getInstance();
+ final int defaultYear = calendar.get(Calendar.YEAR);
+
+ // Check whether the year is optional
+ final boolean isYearOptional = getType().isYearOptional();
+
+ if (!isYearOptional && !TextUtils.isEmpty(oldValue)) {
+ final ParsePosition position = new ParsePosition(0);
+ final Date date2 = kind.dateFormatWithoutYear.parse(oldValue, position);
+
+ // Don't understand the date, lets not change it
+ if (date2 == null) return;
+
+ // This value is missing the year. Add it now
+ calendar.setTime(date2);
+ calendar.set(defaultYear, calendar.get(Calendar.MONTH),
+ calendar.get(Calendar.DAY_OF_MONTH), DEFAULT_HOUR, 0, 0);
+
+ onFieldChanged(column, kind.dateFormatWithYear.format(calendar.getTime()));
+ rebuildDateView();
+ }
+ }
+
+ /**
+ * Prepare dialog for entering a date
+ */
+ private Dialog createDatePickerDialog() {
+ final String column = getKind().fieldList.get(0).column;
+ final String oldValue = getEntry().getAsString(column);
+ final DataKind kind = getKind();
+
+ final Calendar calendar = Calendar.getInstance();
+ final int defaultYear = calendar.get(Calendar.YEAR);
+
+ // Check whether the year is optional
+ final boolean isYearOptional = getType().isYearOptional();
+
+ final int oldYear, oldMonth, oldDay;
+ if (TextUtils.isEmpty(oldValue)) {
+ // Default to January first, 30 years ago
+ oldYear = defaultYear;
+ oldMonth = 0;
+ oldDay = 1;
+ } else {
+ final ParsePosition position = new ParsePosition(0);
+ // Try parsing with year
+ Date date1 = kind.dateFormatWithYear.parse(oldValue, position);
+ if (date1 == null) {
+ // If that format does not fit, try guessing the right format
+ date1 = DateUtils.parseDate(oldValue);
+ }
+ if (date1 != null) {
+ calendar.setTime(date1);
+ oldYear = calendar.get(Calendar.YEAR);
+ oldMonth = calendar.get(Calendar.MONTH);
+ oldDay = calendar.get(Calendar.DAY_OF_MONTH);
+ } else {
+ final Date date2 = kind.dateFormatWithoutYear.parse(oldValue, position);
+ // Don't understand the date, lets not change it
+ if (date2 == null) return null;
+ calendar.setTime(date2);
+ oldYear = isYearOptional ? 0 : defaultYear;
+ oldMonth = calendar.get(Calendar.MONTH);
+ oldDay = calendar.get(Calendar.DAY_OF_MONTH);
+ }
+ }
+ final OnDateSetListener callBack = new OnDateSetListener() {
+ @Override
+ public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
+ if (year == 0 && !isYearOptional) throw new IllegalStateException();
+ final Calendar outCalendar = Calendar.getInstance();
+
+ // If no year specified, set it to 1900. The format string will ignore that year
+ // For formats other than Exchange, the time of the day is ignored
+ outCalendar.clear();
+ outCalendar.set(year == 0 ? 1900 : year, monthOfYear, dayOfMonth,
+ DEFAULT_HOUR, 0, 0);
+
+ final String resultString;
+ if (year == 0) {
+ resultString = kind.dateFormatWithoutYear.format(outCalendar.getTime());
+ } else {
+ resultString = kind.dateFormatWithYear.format(outCalendar.getTime());
+ }
+ onFieldChanged(column, resultString);
+ rebuildDateView();
+ }
+ };
+ final DatePickerDialog resultDialog = new DatePickerDialog(getContext(), callBack,
+ oldYear, oldMonth, oldDay, isYearOptional);
+ return resultDialog;
+ }
+}
diff --git a/src/com/android/contacts/editor/ExternalRawContactEditorView.java b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
new file mode 100644
index 0000000..aaa1e44
--- /dev/null
+++ b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
@@ -0,0 +1,229 @@
+/*
+ * 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.editor;
+
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.R;
+import com.android.contacts.editor.ExternalRawContactEditorView.Listener;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.model.EntityModifier;
+
+import android.accounts.Account;
+import android.content.ContentUris;
+import android.content.Context;
+import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.RawContacts;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * Custom view that displays external contacts in the edit screen.
+ */
+public class ExternalRawContactEditorView extends BaseRawContactEditorView
+ implements OnClickListener {
+ private LayoutInflater mInflater;
+
+ private View mPhotoStub;
+ private TextView mName;
+ private TextView mReadOnlyWarning;
+ private Button mEditExternallyButton;
+ private ViewGroup mGeneral;
+
+ private ImageView mHeaderIcon;
+ private TextView mHeaderAccountType;
+ private TextView mHeaderAccountName;
+
+ private String mAccountName;
+ private String mAccountType;
+ private long mRawContactId = -1;
+
+ private Listener mListener;
+
+ public interface Listener {
+ void onExternalEditorRequest(Account account, Uri uri);
+ }
+
+ public ExternalRawContactEditorView(Context context) {
+ super(context);
+ }
+
+ public ExternalRawContactEditorView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mInflater = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ mPhotoStub = findViewById(R.id.stub_photo);
+
+ mName = (TextView) findViewById(R.id.read_only_name);
+ mReadOnlyWarning = (TextView) findViewById(R.id.read_only_warning);
+ mEditExternallyButton = (Button) findViewById(R.id.button_edit_externally);
+ mEditExternallyButton.setOnClickListener(this);
+ mGeneral = (ViewGroup)findViewById(R.id.sect_general);
+
+ mHeaderIcon = (ImageView) findViewById(R.id.header_icon);
+ mHeaderAccountType = (TextView) findViewById(R.id.header_account_type);
+ mHeaderAccountName = (TextView) findViewById(R.id.header_account_name);
+ }
+
+ /**
+ * Set the internal state for this view, given a current
+ * {@link EntityDelta} state and the {@link AccountType} that
+ * apply to that state.
+ */
+ @Override
+ public void setState(EntityDelta state, AccountType type, ViewIdGenerator vig) {
+ // Remove any existing sections
+ mGeneral.removeAllViews();
+
+ // Bail if invalid state or source
+ if (state == null || type == null) return;
+
+ // Make sure we have StructuredName
+ EntityModifier.ensureKindExists(state, type, StructuredName.CONTENT_ITEM_TYPE);
+
+ // Fill in the header info
+ ValuesDelta values = state.getValues();
+ mAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
+ mAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
+ CharSequence accountType = type.getDisplayLabel(mContext);
+ if (TextUtils.isEmpty(accountType)) {
+ accountType = mContext.getString(R.string.account_phone);
+ }
+ if (!TextUtils.isEmpty(mAccountName)) {
+ mHeaderAccountName.setText(
+ mContext.getString(R.string.from_account_format, mAccountName));
+ }
+ mHeaderAccountType.setText(mContext.getString(R.string.account_type_format, accountType));
+ mHeaderIcon.setImageDrawable(type.getDisplayIcon(mContext));
+
+ mRawContactId = values.getAsLong(RawContacts._ID);
+
+ ValuesDelta primary;
+
+ // Photo
+ DataKind kind = type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
+ if (kind != null) {
+ EntityModifier.ensureKindExists(state, type, Photo.CONTENT_ITEM_TYPE);
+ boolean hasPhotoEditor = type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null;
+ setHasPhotoEditor(hasPhotoEditor);
+ primary = state.getPrimaryEntry(Photo.CONTENT_ITEM_TYPE);
+ getPhotoEditor().setValues(kind, primary, state, type.readOnly, vig);
+ if (!hasPhotoEditor || !getPhotoEditor().hasSetPhoto()) {
+ mPhotoStub.setVisibility(View.GONE);
+ } else {
+ mPhotoStub.setVisibility(View.VISIBLE);
+ }
+ } else {
+ mPhotoStub.setVisibility(View.VISIBLE);
+ }
+
+ // Name
+ primary = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
+ mName.setText(primary.getAsString(StructuredName.DISPLAY_NAME));
+
+ if (type.readOnly) {
+ mReadOnlyWarning.setText(mContext.getString(R.string.contact_read_only, accountType));
+ mReadOnlyWarning.setVisibility(View.VISIBLE);
+ mEditExternallyButton.setVisibility(View.GONE);
+ } else {
+ mReadOnlyWarning.setVisibility(View.GONE);
+ mEditExternallyButton.setVisibility(View.VISIBLE);
+ }
+
+ // Phones
+ ArrayList<ValuesDelta> phones = state.getMimeEntries(Phone.CONTENT_ITEM_TYPE);
+ if (phones != null) {
+ for (ValuesDelta phone : phones) {
+ View field = mInflater.inflate(
+ R.layout.item_read_only_field, mGeneral, false);
+ TextView v;
+ v = (TextView) field.findViewById(R.id.label);
+ v.setText(mContext.getText(R.string.phoneLabelsGroup));
+ v = (TextView) field.findViewById(R.id.data);
+ v.setText(PhoneNumberUtils.formatNumber(phone.getAsString(Phone.NUMBER),
+ phone.getAsString(Phone.NORMALIZED_NUMBER),
+ ContactsUtils.getCurrentCountryIso(getContext())));
+ mGeneral.addView(field);
+ }
+ }
+
+ // Emails
+ ArrayList<ValuesDelta> emails = state.getMimeEntries(Email.CONTENT_ITEM_TYPE);
+ if (emails != null) {
+ for (ValuesDelta email : emails) {
+ View field = mInflater.inflate(
+ R.layout.item_read_only_field, mGeneral, false);
+ TextView v;
+ v = (TextView) field.findViewById(R.id.label);
+ v.setText(mContext.getText(R.string.emailLabelsGroup));
+ v = (TextView) field.findViewById(R.id.data);
+ v.setText(email.getAsString(Email.DATA));
+ mGeneral.addView(field);
+ }
+ }
+
+ // Hide mGeneral if it's empty
+ if (mGeneral.getChildCount() > 0) {
+ mGeneral.setVisibility(View.VISIBLE);
+ } else {
+ mGeneral.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public long getRawContactId() {
+ return mRawContactId;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.button_edit_externally) {
+ if (mListener != null) {
+ mListener.onExternalEditorRequest(new Account(mAccountName, mAccountType),
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactId));
+ }
+ }
+ }
+}
diff --git a/src/com/android/contacts/editor/GroupMembershipView.java b/src/com/android/contacts/editor/GroupMembershipView.java
new file mode 100644
index 0000000..8845aa3
--- /dev/null
+++ b/src/com/android/contacts/editor/GroupMembershipView.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2010 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.editor;
+
+import com.android.contacts.GroupMetaDataLoader;
+import com.android.contacts.R;
+import com.android.contacts.interactions.GroupCreationDialogFragment;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.model.EntityModifier;
+
+import android.app.Activity;
+import android.content.Context;
+import android.database.Cursor;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.RawContacts;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListPopupWindow;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * An editor for group membership. Displays the current group membership list and
+ * brings up a dialog to change it.
+ */
+public class GroupMembershipView extends LinearLayout
+ implements OnClickListener, OnItemClickListener {
+
+ private static final int CREATE_NEW_GROUP_GROUP_ID = 133;
+
+ public static final class GroupSelectionItem {
+ private final long mGroupId;
+ private final String mTitle;
+ private boolean mChecked;
+
+ public GroupSelectionItem(long groupId, String title, boolean checked) {
+ this.mGroupId = groupId;
+ this.mTitle = title;
+ mChecked = checked;
+ }
+
+ public long getGroupId() {
+ return mGroupId;
+ }
+
+ public boolean isChecked() {
+ return mChecked;
+ }
+
+ public void setChecked(boolean checked) {
+ mChecked = checked;
+ }
+
+ @Override
+ public String toString() {
+ return mTitle;
+ }
+ }
+
+ private EntityDelta mState;
+ private Cursor mGroupMetaData;
+ private String mAccountName;
+ private String mAccountType;
+ private TextView mGroupList;
+ private ArrayAdapter<GroupSelectionItem> mAdapter;
+ private long mDefaultGroupId;
+ private long mFavoritesGroupId;
+ private ListPopupWindow mPopup;
+ private DataKind mKind;
+ private boolean mDefaultGroupVisibilityKnown;
+ private boolean mDefaultGroupVisible;
+
+ public GroupMembershipView(Context context) {
+ super(context);
+ }
+
+ public GroupMembershipView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ if (mGroupList != null) {
+ mGroupList.setEnabled(enabled);
+ }
+ }
+
+ public void setKind(DataKind kind) {
+ mKind = kind;
+ TextView kindTitle = (TextView) findViewById(R.id.kind_title);
+ kindTitle.setText(getResources().getString(kind.titleRes));
+ }
+
+ public void setGroupMetaData(Cursor groupMetaData) {
+ this.mGroupMetaData = groupMetaData;
+ updateView();
+ }
+
+ public void setState(EntityDelta state) {
+ mState = state;
+ ValuesDelta values = state.getValues();
+ mAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
+ mAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
+ mDefaultGroupVisibilityKnown = false;
+ updateView();
+ }
+
+ private void updateView() {
+ if (mGroupMetaData == null || mGroupMetaData.isClosed() || mAccountType == null
+ || mAccountName == null) {
+ setVisibility(GONE);
+ return;
+ }
+
+ boolean accountHasGroups = false;
+ mFavoritesGroupId = 0;
+ mDefaultGroupId = 0;
+
+ StringBuilder sb = new StringBuilder();
+ mGroupMetaData.moveToPosition(-1);
+ while (mGroupMetaData.moveToNext()) {
+ String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
+ String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+ if (accountName.equals(mAccountName) && accountType.equals(mAccountType)) {
+ long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
+ if (!mGroupMetaData.isNull(GroupMetaDataLoader.FAVORITES)
+ && mGroupMetaData.getInt(GroupMetaDataLoader.FAVORITES) != 0) {
+ mFavoritesGroupId = groupId;
+ } else if (!mGroupMetaData.isNull(GroupMetaDataLoader.AUTO_ADD)
+ && mGroupMetaData.getInt(GroupMetaDataLoader.AUTO_ADD) != 0) {
+ mDefaultGroupId = groupId;
+ } else {
+ accountHasGroups = true;
+ }
+
+ // Exclude favorites from the list - they are handled with special UI (star)
+ // Also exclude the default group.
+ if (groupId != mFavoritesGroupId && groupId != mDefaultGroupId
+ && hasMembership(groupId)) {
+ String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE);
+ if (sb.length() != 0) {
+ sb.append(", ");
+ }
+ sb.append(title);
+ }
+ }
+ }
+
+ if (!accountHasGroups) {
+ setVisibility(GONE);
+ return;
+ }
+
+ if (mGroupList == null) {
+ mGroupList = (TextView) findViewById(R.id.group_list);
+ mGroupList.setOnClickListener(this);
+ }
+
+ mGroupList.setEnabled(isEnabled());
+ if (sb.length() == 0) {
+ mGroupList.setText(" ");
+ } else {
+ mGroupList.setText(sb);
+ }
+ setVisibility(VISIBLE);
+
+ if (!mDefaultGroupVisibilityKnown) {
+ // Only show the default group (My Contacts) if the contact is NOT in it
+ mDefaultGroupVisible = mDefaultGroupId != 0 && !hasMembership(mDefaultGroupId);
+ mDefaultGroupVisibilityKnown = true;
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mPopup != null && mPopup.isShowing()) {
+ mPopup.dismiss();
+ return;
+ }
+
+ mAdapter = new ArrayAdapter<GroupSelectionItem>(
+ getContext(), R.layout.group_membership_list_item);
+
+ mGroupMetaData.moveToPosition(-1);
+ while (mGroupMetaData.moveToNext()) {
+ String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
+ String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+ if (accountName.equals(mAccountName) && accountType.equals(mAccountType)) {
+ long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
+ if (groupId != mFavoritesGroupId
+ && (groupId != mDefaultGroupId || mDefaultGroupVisible)) {
+ String title = mGroupMetaData.getString(GroupMetaDataLoader.TITLE);
+ boolean checked = hasMembership(groupId);
+ mAdapter.add(new GroupSelectionItem(groupId, title, checked));
+ }
+ }
+ }
+
+ mAdapter.add(new GroupSelectionItem(CREATE_NEW_GROUP_GROUP_ID,
+ getContext().getString(R.string.create_group_item_label), false));
+
+ mPopup = new ListPopupWindow(getContext(), null);
+ mPopup.setAnchorView(mGroupList);
+ mPopup.setAdapter(mAdapter);
+ mPopup.setModal(true);
+ mPopup.show();
+
+ ListView listView = mPopup.getListView();
+ listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ int count = mAdapter.getCount();
+ for (int i = 0; i < count; i++) {
+ listView.setItemChecked(i, mAdapter.getItem(i).isChecked());
+ }
+
+ listView.setOnItemClickListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mPopup != null) {
+ mPopup.dismiss();
+ mPopup = null;
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ ListView list = (ListView) parent;
+ int count = mAdapter.getCount();
+
+ if (list.isItemChecked(count - 1)) {
+ list.setItemChecked(count - 1, false);
+ createNewGroup();
+ return;
+ }
+
+ for (int i = 0; i < count; i++) {
+ mAdapter.getItem(i).setChecked(list.isItemChecked(i));
+ }
+
+ // First remove the memberships that have been unchecked
+ ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
+ if (entries != null) {
+ for (ValuesDelta entry : entries) {
+ if (!entry.isDelete()) {
+ Long groupId = entry.getAsLong(GroupMembership.GROUP_ROW_ID);
+ if (groupId != null && groupId != mFavoritesGroupId
+ && (groupId != mDefaultGroupId || mDefaultGroupVisible)
+ && !isGroupChecked(groupId)) {
+ entry.markDeleted();
+ }
+ }
+ }
+ }
+
+ // Now add the newly selected items
+ for (int i = 0; i < count; i++) {
+ GroupSelectionItem item = mAdapter.getItem(i);
+ long groupId = item.getGroupId();
+ if (item.isChecked() && !hasMembership(groupId)) {
+ ValuesDelta entry = EntityModifier.insertChild(mState, mKind);
+ entry.put(GroupMembership.GROUP_ROW_ID, groupId);
+ }
+ }
+
+ updateView();
+ }
+
+ private boolean isGroupChecked(long groupId) {
+ int count = mAdapter.getCount();
+ for (int i = 0; i < count; i++) {
+ GroupSelectionItem item = mAdapter.getItem(i);
+ if (groupId == item.getGroupId()) {
+ return item.isChecked();
+ }
+ }
+ return false;
+ }
+
+ private boolean hasMembership(long groupId) {
+ if (groupId == mDefaultGroupId && mState.isContactInsert()) {
+ return true;
+ }
+
+ ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
+ if (entries != null) {
+ for (ValuesDelta values : entries) {
+ if (!values.isDelete()) {
+ Long id = values.getAsLong(GroupMembership.GROUP_ROW_ID);
+ if (id != null && id == groupId) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private void createNewGroup() {
+ if (mPopup != null) {
+ mPopup.dismiss();
+ mPopup = null;
+ }
+
+ GroupCreationDialogFragment.show(
+ ((Activity) getContext()).getFragmentManager(), mAccountType, mAccountName);
+ }
+}
diff --git a/src/com/android/contacts/editor/KindSectionView.java b/src/com/android/contacts/editor/KindSectionView.java
new file mode 100644
index 0000000..4ce8dda
--- /dev/null
+++ b/src/com/android/contacts/editor/KindSectionView.java
@@ -0,0 +1,289 @@
+/*
+ * 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.editor;
+
+import com.android.contacts.R;
+import com.android.contacts.editor.Editor.EditorListener;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.model.EntityModifier;
+
+import android.content.Context;
+import android.os.Handler;
+import android.provider.ContactsContract.Data;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * Custom view for an entire section of data as segmented by
+ * {@link DataKind} around a {@link Data#MIMETYPE}. This view shows a
+ * section header and a trigger for adding new {@link Data} rows.
+ */
+public class KindSectionView extends LinearLayout implements EditorListener {
+ private static final String TAG = "KindSectionView";
+
+ private ViewGroup mEditors;
+ private View mAddPlusButtonContainer;
+ private ImageButton mAddPlusButton;
+ private TextView mTitle;
+ private String mTitleString;
+
+ private DataKind mKind;
+ private EntityDelta mState;
+ private boolean mReadOnly;
+
+ private ViewIdGenerator mViewIdGenerator;
+
+ private int mMinLineItemHeight;
+
+ public KindSectionView(Context context) {
+ this(context, null);
+ }
+
+ public KindSectionView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mMinLineItemHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.editor_min_line_item_height);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ if (mEditors != null) {
+ int childCount = mEditors.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ mEditors.getChildAt(i).setEnabled(enabled);
+ }
+ }
+
+ if (mAddPlusButton != null) {
+ mAddPlusButton.setEnabled(enabled && !mReadOnly);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ if (mAddPlusButton == null || mEditors == null || mEditors.getChildCount() < 2) {
+ return;
+ }
+
+ // Align the "+" button with the "-" button in the last editor
+ View lastEditor = mEditors.getChildAt(mEditors.getChildCount() - 1);
+ int top = lastEditor.getTop();
+ mAddPlusButtonContainer.layout(mAddPlusButtonContainer.getLeft(), top,
+ mAddPlusButtonContainer.getRight(), top + mAddPlusButtonContainer.getHeight());
+ }
+
+ public boolean isReadOnly() {
+ return mReadOnly;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onFinishInflate() {
+ setDrawingCacheEnabled(true);
+ setAlwaysDrawnWithCacheEnabled(true);
+
+ mEditors = (ViewGroup)findViewById(R.id.kind_editors);
+
+ mAddPlusButtonContainer = findViewById(R.id.kind_plus_container);
+ mAddPlusButton = (ImageButton) findViewById(R.id.kind_plus);
+ mAddPlusButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // defer action so that the pressed state of the button is visible shortly
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ addItem();
+ }
+ });
+ }
+ });
+
+ mTitle = (TextView)findViewById(R.id.kind_title);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onDeleted(Editor editor) {
+ updateAddVisible();
+ updateVisible();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onRequest(int request) {
+ // Ignore requests
+ }
+
+ public void setState(DataKind kind, EntityDelta state, boolean readOnly, ViewIdGenerator vig) {
+ mKind = kind;
+ mState = state;
+ mReadOnly = readOnly;
+ mViewIdGenerator = vig;
+
+ setId(mViewIdGenerator.getId(state, kind, null, ViewIdGenerator.NO_VIEW_INDEX));
+
+ // TODO: handle resources from remote packages
+ mTitleString = (kind.titleRes == -1 || kind.titleRes == 0)
+ ? ""
+ : getResources().getString(kind.titleRes);
+ mTitle.setText(mTitleString);
+
+ rebuildFromState();
+ updateAddVisible();
+ updateVisible();
+ }
+
+ public String getTitle() {
+ return mTitleString;
+ }
+
+ /**
+ * Build editors for all current {@link #mState} rows.
+ */
+ public void rebuildFromState() {
+ // Remove any existing editors
+ mEditors.removeAllViews();
+
+ // Check if we are displaying anything here
+ boolean hasEntries = mState.hasMimeEntries(mKind.mimeType);
+
+ if (hasEntries) {
+ for (ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)) {
+ // Skip entries that aren't visible
+ if (!entry.isVisible()) continue;
+ if (isEmptyNoop(entry)) continue;
+
+ createEditorView(entry);
+ }
+ }
+ }
+
+
+ /**
+ * Creates an EditorView for the given entry. This function must be used while constructing
+ * the views corresponding to the the object-model. The resulting EditorView is also added
+ * to the end of mEditors
+ */
+ private View createEditorView(ValuesDelta entry) {
+ final View view;
+ if (mKind.editorClass == null) {
+ view = new TextFieldsEditorView(mContext);
+ } else {
+ try {
+ view = mKind.editorClass.getConstructor(Context.class).newInstance(
+ mContext);
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Cannot allocate editor for " + mKind.editorClass);
+ }
+ }
+
+ view.setEnabled(isEnabled());
+
+ if (view instanceof Editor) {
+ Editor editor = (Editor) view;
+ editor.setValues(mKind, entry, mState, mReadOnly, mViewIdGenerator);
+ editor.setEditorListener(this);
+ editor.setDeletable(true);
+ }
+ mEditors.addView(view);
+ return view;
+ }
+
+ /**
+ * Tests whether the given item has no changes (so it exists in the database) but is empty
+ */
+ private boolean isEmptyNoop(ValuesDelta item) {
+ if (!item.isNoop()) return false;
+ final int fieldCount = mKind.fieldList.size();
+ for (int i = 0; i < fieldCount; i++) {
+ final String column = mKind.fieldList.get(i).column;
+ final String value = item.getAsString(column);
+ if (!TextUtils.isEmpty(value)) return false;
+ }
+ return true;
+ }
+
+ private void updateVisible() {
+ setVisibility(getEditorCount() != 0 ? VISIBLE : GONE);
+ }
+
+
+ protected void updateAddVisible() {
+ final boolean isVisible;
+ if (!mKind.isList) {
+ isVisible = false;
+ } else {
+ // Set enabled state on the "add" view
+ final boolean canInsert = EntityModifier.canInsert(mState, mKind);
+ isVisible = !mReadOnly && canInsert;
+ }
+ mAddPlusButton.setVisibility(isVisible ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ public void addItem() {
+ ValuesDelta values = null;
+ // if this is a list, we can freely add. if not, only allow adding the first
+ if (!mKind.isList) {
+ if (getEditorCount() == 1) {
+ return;
+ }
+
+ // If we already have an item, just make it visible
+ ArrayList<ValuesDelta> entries = mState.getMimeEntries(mKind.mimeType);
+ if (entries != null && entries.size() > 0) {
+ values = entries.get(0);
+ }
+ }
+
+ // Insert a new child, create its view and set its focus
+ if (values == null) {
+ values = EntityModifier.insertChild(mState, mKind);
+ }
+
+ final View newField = createEditorView(values);
+ newField.requestFocus();
+
+ // For non-lists (e.g. Notes we can only have one field. in that case we need to disable
+ // the add button
+ updateAddVisible();
+
+ // Ensure we are visible
+ updateVisible();
+ }
+
+ public int getEditorCount() {
+ return mEditors.getChildCount();
+ }
+
+ public DataKind getKind() {
+ return mKind;
+ }
+}
diff --git a/src/com/android/contacts/editor/LabeledEditorView.java b/src/com/android/contacts/editor/LabeledEditorView.java
new file mode 100644
index 0000000..971a023
--- /dev/null
+++ b/src/com/android/contacts/editor/LabeledEditorView.java
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2010 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.editor;
+
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.AccountType.EditType;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.model.EntityModifier;
+import com.android.contacts.util.DialogManager;
+import com.android.contacts.util.DialogManager.DialogShowingView;
+import com.android.contacts.util.ThemeUtils;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Entity;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * Base class for editors that handles labels and values.
+ * Uses {@link ValuesDelta} to read any existing
+ * {@link Entity} values, and to correctly write any changes values.
+ */
+public abstract class LabeledEditorView extends ViewGroup implements Editor, DialogShowingView {
+ protected static final String DIALOG_ID_KEY = "dialog_id";
+ private static final int DIALOG_ID_CUSTOM = 1;
+
+ private static final int INPUT_TYPE_CUSTOM = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
+
+ private Spinner mLabel;
+ private EditTypeAdapter mEditTypeAdapter;
+ private ImageButton mDelete;
+
+ private DataKind mKind;
+ private ValuesDelta mEntry;
+ private EntityDelta mState;
+ private boolean mReadOnly;
+
+ private EditType mType;
+
+ private ViewIdGenerator mViewIdGenerator;
+ private DialogManager mDialogManager = null;
+ private EditorListener mListener;
+ protected int mMinLineItemHeight;
+
+ /**
+ * A marker in the spinner adapter of the currently selected custom type.
+ */
+ public static final EditType CUSTOM_SELECTION = new EditType(0, 0);
+
+ private OnItemSelectedListener mSpinnerListener = new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(
+ AdapterView<?> parent, View view, int position, long id) {
+ onTypeSelectionChange(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ };
+
+ public LabeledEditorView(Context context) {
+ super(context);
+ init(context);
+ }
+
+ public LabeledEditorView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ public LabeledEditorView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context);
+ }
+
+ private void init(Context context) {
+ mMinLineItemHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.editor_min_line_item_height);
+ }
+
+ public boolean isReadOnly() {
+ return mReadOnly;
+ }
+
+ public int getBaseline(int row) {
+ if (row == 0 && mLabel != null) {
+ return mLabel.getBaseline();
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the number of rows in this editor, including the invisible ones.
+ */
+ protected int getLineItemCount() {
+ return 1;
+ }
+
+ protected boolean isLineItemVisible(int row) {
+ return true;
+ }
+
+ protected int getLineItemHeight(int row) {
+ int fieldHeight = 0;
+ int buttonHeight = 0;
+ if (row == 0) {
+ // summarize the EditText heights
+ if (mLabel != null) {
+ fieldHeight = mLabel.getMeasuredHeight();
+ }
+
+ // Ensure there is enough space for the minus button
+ View deleteButton = getDelete();
+ final int deleteHeight = (deleteButton != null) ? deleteButton.getMeasuredHeight() : 0;
+ buttonHeight += deleteHeight;
+ }
+
+ return Math.max(Math.max(buttonHeight, fieldHeight), mMinLineItemHeight);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+
+ int height = 0;
+ height += getPaddingTop() + getPaddingBottom();
+
+ int count = getLineItemCount();
+ for (int i = 0; i < count; i++) {
+ if (isLineItemVisible(i)) {
+ height += getLineItemHeight(i);
+ }
+ }
+
+ setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
+ resolveSize(height, heightMeasureSpec));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // Subtract padding from the borders ==> x1 variables
+ int t1 = getPaddingTop();
+ int r1 = getMeasuredWidth() - getPaddingRight();
+ int b1 = getMeasuredHeight() - getPaddingBottom();
+
+ final int r2;
+ if (mDelete != null) {
+ r2 = r1 - mDelete.getMeasuredWidth();
+ // Vertically center the delete button in the first line item
+ int height = mDelete.getMeasuredHeight();
+ int top = t1 + (mMinLineItemHeight - height) / 2;
+ mDelete.layout(
+ r2, top,
+ r1, top + height);
+ } else {
+ r2 = r1;
+ }
+
+ if (mLabel != null) {
+ int baseline = getBaseline(0);
+ int y = t1 + baseline - mLabel.getBaseline();
+ mLabel.layout(
+ r2 - mLabel.getMeasuredWidth(), y,
+ r2, y + mLabel.getMeasuredHeight());
+ }
+ }
+
+ /**
+ * Creates or removes the type/label button. Doesn't do anything if already correctly configured
+ */
+ private void setupLabelButton(boolean shouldExist) {
+ if (shouldExist && mLabel == null) {
+ mLabel = new Spinner(mContext);
+ final int width =
+ mContext.getResources().getDimensionPixelSize(R.dimen.editor_type_label_width);
+ mLabel.setLayoutParams(new LayoutParams(width, LayoutParams.WRAP_CONTENT));
+ mLabel.setOnItemSelectedListener(mSpinnerListener);
+ mLabel.setEnabled(!mReadOnly && isEnabled());
+ addView(mLabel);
+ } else if (!shouldExist && mLabel != null) {
+ removeView(mLabel);
+ mLabel = null;
+ }
+ }
+
+ /**
+ * Creates or removes the remove button. Doesn't do anything if already correctly configured
+ */
+ private void setupDeleteButton(boolean shouldExist) {
+ if (shouldExist && mDelete == null) {
+ mDelete = new ImageButton(mContext);
+ mDelete.setImageResource(R.drawable.ic_menu_remove_field_holo_light);
+ mDelete.setBackgroundResource(
+ ThemeUtils.getSelectableItemBackground(mContext.getTheme()));
+ final Resources resources = mContext.getResources();
+ mDelete.setPadding(
+ resources.getDimensionPixelOffset(R.dimen.editor_round_button_padding_left),
+ resources.getDimensionPixelOffset(R.dimen.editor_round_button_padding_top),
+ resources.getDimensionPixelOffset(R.dimen.editor_round_button_padding_right),
+ resources.getDimensionPixelOffset(R.dimen.editor_round_button_padding_bottom));
+ mDelete.setContentDescription(
+ getResources().getText(R.string.description_minus_button));
+ mDelete.setLayoutParams(
+ new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+ mDelete.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // defer removal of this button so that the pressed state is visible shortly
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ // Keep around in model, but mark as deleted
+ mEntry.markDeleted();
+
+ ((ViewGroup) getParent()).removeView(LabeledEditorView.this);
+
+ if (mListener != null) {
+ // Notify listener when present
+ mListener.onDeleted(LabeledEditorView.this);
+ }
+ }
+ });
+ }
+ });
+ mDelete.setEnabled(!mReadOnly && isEnabled());
+ addView(mDelete);
+ } else if (!shouldExist && mDelete != null) {
+ removeView(mDelete);
+ mDelete = null;
+ }
+ }
+
+ protected void onOptionalFieldVisibilityChange() {
+ if (mListener != null) {
+ mListener.onRequest(EditorListener.EDITOR_FORM_CHANGED);
+ }
+ }
+
+ @Override
+ public void setEditorListener(EditorListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void setDeletable(boolean deletable) {
+ setupDeleteButton(deletable);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ if (mLabel != null) mLabel.setEnabled(!mReadOnly && enabled);
+ if (mDelete != null) mDelete.setEnabled(!mReadOnly && enabled);
+ }
+
+ public Spinner getLabel() {
+ return mLabel;
+ }
+
+ public ImageButton getDelete() {
+ return mDelete;
+ }
+
+ protected DataKind getKind() {
+ return mKind;
+ }
+
+ protected ValuesDelta getEntry() {
+ return mEntry;
+ }
+
+ protected EditType getType() {
+ return mType;
+ }
+
+ /**
+ * Build the current label state based on selected {@link EditType} and
+ * possible custom label string.
+ */
+ private void rebuildLabel() {
+ if (mLabel == null) return;
+ mEditTypeAdapter = new EditTypeAdapter(mContext);
+ mLabel.setAdapter(mEditTypeAdapter);
+ if (mEditTypeAdapter.hasCustomSelection()) {
+ mLabel.setSelection(mEditTypeAdapter.getPosition(CUSTOM_SELECTION));
+ } else {
+ mLabel.setSelection(mEditTypeAdapter.getPosition(mType));
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onFieldChanged(String column, String value) {
+ String oldValue = mEntry.getAsString(column);
+ if (oldValue == null && value.equals("") || oldValue != null && oldValue.equals(value)) {
+ return;
+ }
+
+ // Field changes are saved directly
+ mEntry.put(column, value);
+ if (mListener != null) {
+ mListener.onRequest(EditorListener.FIELD_CHANGED);
+ }
+ }
+
+ protected void rebuildValues() {
+ setValues(mKind, mEntry, mState, mReadOnly, mViewIdGenerator);
+ }
+
+ /**
+ * Prepare this editor using the given {@link DataKind} for defining
+ * structure and {@link ValuesDelta} describing the content to edit.
+ */
+ @Override
+ public void setValues(DataKind kind, ValuesDelta entry, EntityDelta state, boolean readOnly,
+ ViewIdGenerator vig) {
+ mKind = kind;
+ mEntry = entry;
+ mState = state;
+ mReadOnly = readOnly;
+ mViewIdGenerator = vig;
+ setId(vig.getId(state, kind, entry, ViewIdGenerator.NO_VIEW_INDEX));
+
+ if (!entry.isVisible()) {
+ // Hide ourselves entirely if deleted
+ setVisibility(View.GONE);
+ return;
+ }
+ setVisibility(View.VISIBLE);
+
+ // Display label selector if multiple types available
+ final boolean hasTypes = EntityModifier.hasEditTypes(kind);
+ setupLabelButton(hasTypes);
+ if (mLabel != null) mLabel.setEnabled(!readOnly && isEnabled());
+ if (hasTypes) {
+ mType = EntityModifier.getCurrentType(entry, kind);
+ rebuildLabel();
+ }
+ }
+
+ public ValuesDelta getValues() {
+ return mEntry;
+ }
+
+ /**
+ * Prepare dialog for entering a custom label. The input value is trimmed: white spaces before
+ * and after the input text is removed.
+ * <p>
+ * If the final value is empty, this change request is ignored;
+ * no empty text is allowed in any custom label.
+ */
+ private Dialog createCustomDialog() {
+ final EditText customType = new EditText(mContext);
+ customType.setId(R.id.custom_dialog_content);
+ customType.setInputType(INPUT_TYPE_CUSTOM);
+ customType.setSaveEnabled(true);
+ customType.requestFocus();
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ builder.setTitle(R.string.customLabelPickerTitle);
+ builder.setView(customType);
+
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final String customText = customType.getText().toString().trim();
+ if (ContactsUtils.isGraphic(customText)) {
+ final List<EditType> allTypes =
+ EntityModifier.getValidTypes(mState, mKind, null);
+ mType = null;
+ for (EditType editType : allTypes) {
+ if (editType.customColumn != null) {
+ mType = editType;
+ break;
+ }
+ }
+ if (mType == null) return;
+
+ mEntry.put(mKind.typeColumn, mType.rawValue);
+ mEntry.put(mType.customColumn, customText);
+ rebuildLabel();
+ requestFocusForFirstEditField();
+ onLabelRebuilt();
+ }
+ }
+ });
+
+ builder.setNegativeButton(android.R.string.cancel, null);
+
+ return builder.create();
+ }
+
+ /**
+ * Called after the label has changed (either chosen from the list or entered in the Dialog)
+ */
+ protected void onLabelRebuilt() {
+ }
+
+ protected void onTypeSelectionChange(int position) {
+ EditType selected = mEditTypeAdapter.getItem(position);
+ // See if the selection has in fact changed
+ if (mEditTypeAdapter.hasCustomSelection() && selected == CUSTOM_SELECTION) {
+ return;
+ }
+
+ if (mType == selected && mType.customColumn == null) {
+ return;
+ }
+
+ if (selected.customColumn != null) {
+ showDialog(DIALOG_ID_CUSTOM);
+ } else {
+ // User picked type, and we're sure it's ok to actually write the entry.
+ mType = selected;
+ mEntry.put(mKind.typeColumn, mType.rawValue);
+ rebuildLabel();
+ requestFocusForFirstEditField();
+ onLabelRebuilt();
+ }
+ }
+
+ /* package */
+ void showDialog(int bundleDialogId) {
+ Bundle bundle = new Bundle();
+ bundle.putInt(DIALOG_ID_KEY, bundleDialogId);
+ getDialogManager().showDialogInView(this, bundle);
+ }
+
+ private DialogManager getDialogManager() {
+ if (mDialogManager == null) {
+ Context context = getContext();
+ if (!(context instanceof DialogManager.DialogShowingViewActivity)) {
+ throw new IllegalStateException(
+ "View must be hosted in an Activity that implements " +
+ "DialogManager.DialogShowingViewActivity");
+ }
+ mDialogManager = ((DialogManager.DialogShowingViewActivity)context).getDialogManager();
+ }
+ return mDialogManager;
+ }
+
+ @Override
+ public Dialog createDialog(Bundle bundle) {
+ if (bundle == null) throw new IllegalArgumentException("bundle must not be null");
+ int dialogId = bundle.getInt(DIALOG_ID_KEY);
+ switch (dialogId) {
+ case DIALOG_ID_CUSTOM:
+ return createCustomDialog();
+ default:
+ throw new IllegalArgumentException("Invalid dialogId: " + dialogId);
+ }
+ }
+
+ protected abstract void requestFocusForFirstEditField();
+
+ private class EditTypeAdapter extends ArrayAdapter<EditType> {
+ private final LayoutInflater mInflater;
+ private boolean mHasCustomSelection;
+
+ public EditTypeAdapter(Context context) {
+ super(context, 0);
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ if (mType != null && mType.customColumn != null) {
+
+ // Use custom label string when present
+ final String customText = mEntry.getAsString(mType.customColumn);
+ if (customText != null) {
+ add(CUSTOM_SELECTION);
+ mHasCustomSelection = true;
+ }
+ }
+
+ addAll(EntityModifier.getValidTypes(mState, mKind, mType));
+ }
+
+ public boolean hasCustomSelection() {
+ return mHasCustomSelection;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return createViewFromResource(
+ position, convertView, parent, android.R.layout.simple_spinner_item);
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ return createViewFromResource(
+ position, convertView, parent, android.R.layout.simple_spinner_dropdown_item);
+ }
+
+ private View createViewFromResource(int position, View convertView, ViewGroup parent,
+ int resource) {
+ View view;
+ TextView textView;
+
+ if (convertView == null) {
+ view = mInflater.inflate(resource, parent, false);
+ } else {
+ view = convertView;
+ }
+
+ textView = (TextView) view;
+
+ EditType type = getItem(position);
+ String text;
+ if (type == CUSTOM_SELECTION) {
+ text = mEntry.getAsString(mType.customColumn);
+ } else {
+ text = getContext().getString(type.labelRes);
+ }
+ textView.setText(text);
+ return view;
+ }
+ }
+}
diff --git a/src/com/android/contacts/editor/PhotoActionPopup.java b/src/com/android/contacts/editor/PhotoActionPopup.java
new file mode 100644
index 0000000..ae76afe
--- /dev/null
+++ b/src/com/android/contacts/editor/PhotoActionPopup.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2010 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.editor;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.ListPopupWindow;
+
+import java.util.ArrayList;
+
+/**
+ * Shows a popup asking the user what to do for a photo. The result is pased back to the Listener
+ */
+public class PhotoActionPopup {
+ public static final String TAG = "PhotoActionPopup";
+
+ public static final int MODE_NO_PHOTO = 0;
+ public static final int MODE_READ_ONLY_ALLOW_PRIMARY = 1;
+ public static final int MODE_PHOTO_DISALLOW_PRIMARY = 2;
+ public static final int MODE_PHOTO_ALLOW_PRIMARY = 3;
+
+ public static ListPopupWindow createPopupMenu(Context context, View anchorView,
+ final Listener listener, int mode) {
+ // Build choices, depending on the current mode. We assume this Dialog is never called
+ // if there are NO choices (e.g. a read-only picture is already super-primary)
+ final ArrayList<ChoiceListItem> choices = new ArrayList<ChoiceListItem>(4);
+ // Use as Primary
+ if (mode == MODE_PHOTO_ALLOW_PRIMARY || mode == MODE_READ_ONLY_ALLOW_PRIMARY) {
+ choices.add(new ChoiceListItem(ChoiceListItem.ID_USE_AS_PRIMARY,
+ context.getString(R.string.use_photo_as_primary)));
+ }
+ // Remove
+ if (mode == MODE_PHOTO_DISALLOW_PRIMARY || mode == MODE_PHOTO_ALLOW_PRIMARY) {
+ choices.add(new ChoiceListItem(ChoiceListItem.ID_REMOVE,
+ context.getString(R.string.removePhoto)));
+ }
+ // Take photo (if there is already a photo, it says "Take new photo")
+ if (mode == MODE_NO_PHOTO || mode == MODE_PHOTO_ALLOW_PRIMARY
+ || mode == MODE_PHOTO_DISALLOW_PRIMARY) {
+ final int resId = mode == MODE_NO_PHOTO ? R.string.take_photo :R.string.take_new_photo;
+ choices.add(new ChoiceListItem(ChoiceListItem.ID_TAKE_PHOTO,
+ context.getString(resId)));
+ }
+ // Select from Gallery (or "Select new from Gallery")
+ if (mode == MODE_NO_PHOTO || mode == MODE_PHOTO_ALLOW_PRIMARY
+ || mode == MODE_PHOTO_DISALLOW_PRIMARY) {
+ final int resId = mode == MODE_NO_PHOTO ? R.string.pick_photo :R.string.pick_new_photo;
+ choices.add(new ChoiceListItem(ChoiceListItem.ID_PICK_PHOTO,
+ context.getString(resId)));
+ }
+ final ListAdapter adapter = new ArrayAdapter<ChoiceListItem>(context,
+ android.R.layout.select_dialog_item, choices);
+
+ final ListPopupWindow listPopupWindow = new ListPopupWindow(context);
+ final OnItemClickListener clickListener = new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final ChoiceListItem choice = choices.get(position);
+ listPopupWindow.dismiss();
+
+ switch (choice.getId()) {
+ case ChoiceListItem.ID_USE_AS_PRIMARY:
+ listener.onUseAsPrimaryChosen();
+ break;
+ case ChoiceListItem.ID_REMOVE:
+ listener.onRemovePictureChose();
+ break;
+ case ChoiceListItem.ID_TAKE_PHOTO:
+ listener.onTakePhotoChosen();
+ break;
+ case ChoiceListItem.ID_PICK_PHOTO:
+ listener.onPickFromGalleryChosen();
+ break;
+ }
+ }
+ };
+
+ listPopupWindow.setAnchorView(anchorView);
+ listPopupWindow.setAdapter(adapter);
+ listPopupWindow.setOnItemClickListener(clickListener);
+ listPopupWindow.setWidth(context.getResources().getDimensionPixelSize(
+ R.dimen.photo_action_popup_width));
+ listPopupWindow.setModal(true);
+ return listPopupWindow;
+ }
+
+ private static final class ChoiceListItem {
+ private final int mId;
+ private final String mCaption;
+
+ public static final int ID_USE_AS_PRIMARY = 0;
+ public static final int ID_TAKE_PHOTO = 1;
+ public static final int ID_PICK_PHOTO = 2;
+ public static final int ID_REMOVE = 3;
+
+ public ChoiceListItem(int id, String caption) {
+ mId = id;
+ mCaption = caption;
+ }
+
+ @Override
+ public String toString() {
+ return mCaption;
+ }
+
+ public int getId() {
+ return mId;
+ }
+ }
+
+ public interface Listener {
+ void onUseAsPrimaryChosen();
+ void onRemovePictureChose();
+ void onTakePhotoChosen();
+ void onPickFromGalleryChosen();
+ }
+}
diff --git a/src/com/android/contacts/editor/PhotoEditorView.java b/src/com/android/contacts/editor/PhotoEditorView.java
new file mode 100644
index 0000000..89633fd
--- /dev/null
+++ b/src/com/android/contacts/editor/PhotoEditorView.java
@@ -0,0 +1,181 @@
+/*
+ * 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.editor;
+
+import com.android.contacts.R;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Simple editor for {@link Photo}.
+ */
+public class PhotoEditorView extends FrameLayout implements Editor {
+ private static final String TAG = "PhotoEditorView";
+
+ private ImageView mPhotoImageView;
+ private View mFrameView;
+
+ private ValuesDelta mEntry;
+ private EditorListener mListener;
+
+ private boolean mHasSetPhoto = false;
+ private boolean mReadOnly;
+
+ public PhotoEditorView(Context context) {
+ super(context);
+ }
+
+ public PhotoEditorView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ mFrameView.setEnabled(enabled);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mPhotoImageView = (ImageView) findViewById(R.id.photo);
+ mFrameView = findViewById(R.id.frame);
+ mFrameView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mListener != null) {
+ mListener.onRequest(EditorListener.REQUEST_PICK_PHOTO);
+ }
+ }
+ });
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onFieldChanged(String column, String value) {
+ throw new UnsupportedOperationException("Photos don't support direct field changes");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setValues(DataKind kind, ValuesDelta values, EntityDelta state, boolean readOnly,
+ ViewIdGenerator vig) {
+ mEntry = values;
+ mReadOnly = readOnly;
+
+ setId(vig.getId(state, kind, values, 0));
+
+ if (values != null) {
+ // Try decoding photo if actual entry
+ final byte[] photoBytes = values.getAsByteArray(Photo.PHOTO);
+ if (photoBytes != null) {
+ final Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0,
+ photoBytes.length);
+
+ mPhotoImageView.setImageBitmap(photo);
+ mFrameView.setEnabled(isEnabled());
+ mHasSetPhoto = true;
+ mEntry.setFromTemplate(false);
+ } else {
+ resetDefault();
+ }
+ } else {
+ resetDefault();
+ }
+ }
+
+ /**
+ * Return true if a valid {@link Photo} has been set.
+ */
+ public boolean hasSetPhoto() {
+ return mHasSetPhoto;
+ }
+
+ /**
+ * Assign the given {@link Bitmap} as the new value, updating UI and
+ * readying for persisting through {@link ValuesDelta}.
+ */
+ public void setPhotoBitmap(Bitmap photo) {
+ if (photo == null) {
+ // Clear any existing photo and return
+ mEntry.put(Photo.PHOTO, (byte[])null);
+ resetDefault();
+ return;
+ }
+
+ final int size = photo.getWidth() * photo.getHeight() * 4;
+ final ByteArrayOutputStream out = new ByteArrayOutputStream(size);
+
+ try {
+ photo.compress(Bitmap.CompressFormat.PNG, 100, out);
+ out.flush();
+ out.close();
+
+ mEntry.put(Photo.PHOTO, out.toByteArray());
+ mPhotoImageView.setImageBitmap(photo);
+ mFrameView.setEnabled(isEnabled());
+ mHasSetPhoto = true;
+ mEntry.setFromTemplate(false);
+
+ // When the user chooses a new photo mark it as super primary
+ mEntry.put(Photo.IS_SUPER_PRIMARY, 1);
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to serialize photo: " + e.toString());
+ }
+ }
+
+ /**
+ * Set the super primary bit on the photo.
+ */
+ public void setSuperPrimary(boolean superPrimary) {
+ mEntry.put(Photo.IS_SUPER_PRIMARY, superPrimary ? 1 : 0);
+ }
+
+ protected void resetDefault() {
+ // Invalid photo, show default "add photo" place-holder
+ mPhotoImageView.setImageResource(R.drawable.ic_contact_picture);
+ mFrameView.setEnabled(!mReadOnly && isEnabled());
+ mHasSetPhoto = false;
+ mEntry.setFromTemplate(true);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void setEditorListener(EditorListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void setDeletable(boolean deletable) {
+ // Photo is not deletable
+ }
+}
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
new file mode 100644
index 0000000..496144b
--- /dev/null
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -0,0 +1,344 @@
+/*
+ * 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.editor;
+
+import com.android.contacts.GroupMetaDataLoader;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.AccountType.EditType;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.model.EntityModifier;
+
+import android.content.Context;
+import android.content.Entity;
+import android.database.Cursor;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.PopupMenu;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * Custom view that provides all the editor interaction for a specific
+ * {@link Contacts} represented through an {@link EntityDelta}. Callers can
+ * reuse this view and quickly rebuild its contents through
+ * {@link #setState(EntityDelta, AccountType, ViewIdGenerator)}.
+ * <p>
+ * Internal updates are performed against {@link ValuesDelta} so that the
+ * source {@link Entity} can be swapped out. Any state-based changes, such as
+ * adding {@link Data} rows or changing {@link EditType}, are performed through
+ * {@link EntityModifier} to ensure that {@link AccountType} are enforced.
+ */
+public class RawContactEditorView extends BaseRawContactEditorView {
+ private LayoutInflater mInflater;
+
+ private TextFieldsEditorView mName;
+ private GroupMembershipView mGroupMembershipView;
+
+ private ViewGroup mFields;
+
+ private ImageView mHeaderIcon;
+ private TextView mHeaderAccountType;
+ private TextView mHeaderAccountName;
+
+ private Button mAddFieldButton;
+
+ private long mRawContactId = -1;
+ private boolean mAutoAddToDefaultGroup = true;
+ private Cursor mGroupMetaData;
+ private DataKind mGroupMembershipKind;
+ private EntityDelta mState;
+
+ public RawContactEditorView(Context context) {
+ super(context);
+ }
+
+ public RawContactEditorView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+
+ View view = getPhotoEditor();
+ if (view != null) {
+ view.setEnabled(isEnabled());
+ }
+
+ if (mName != null) {
+ mName.setEnabled(isEnabled());
+ }
+
+ if (mFields != null) {
+ int count = mFields.getChildCount();
+ for (int i = 0; i < count; i++) {
+ mFields.getChildAt(i).setEnabled(enabled);
+ }
+ }
+
+ if (mGroupMembershipView != null) {
+ mGroupMembershipView.setEnabled(enabled);
+ }
+
+ mAddFieldButton.setEnabled(isEnabled());
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mInflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ final int photoSize = getResources().getDimensionPixelSize(R.dimen.edit_photo_size);
+
+ mName = (TextFieldsEditorView)findViewById(R.id.edit_name);
+ mName.setMinimumHeight(photoSize);
+ mName.setDeletable(false);
+
+ mFields = (ViewGroup)findViewById(R.id.sect_fields);
+
+ mHeaderIcon = (ImageView) findViewById(R.id.header_icon);
+ mHeaderAccountType = (TextView) findViewById(R.id.header_account_type);
+ mHeaderAccountName = (TextView) findViewById(R.id.header_account_name);
+
+ mAddFieldButton = (Button) findViewById(R.id.button_add_field);
+ mAddFieldButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showAddInformationPopupWindow();
+ }
+ });
+ }
+
+ /**
+ * Set the internal state for this view, given a current
+ * {@link EntityDelta} state and the {@link AccountType} that
+ * apply to that state.
+ */
+ @Override
+ public void setState(EntityDelta state, AccountType type, ViewIdGenerator vig) {
+ mState = state;
+
+ // Remove any existing sections
+ mFields.removeAllViews();
+
+ // Bail if invalid state or account type
+ if (state == null || type == null) return;
+
+ setId(vig.getId(state, null, null, ViewIdGenerator.NO_VIEW_INDEX));
+
+ // Make sure we have StructuredName
+ EntityModifier.ensureKindExists(state, type, StructuredName.CONTENT_ITEM_TYPE);
+
+ // Fill in the header info
+ ValuesDelta values = state.getValues();
+ String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
+ CharSequence accountType = type.getDisplayLabel(mContext);
+ if (TextUtils.isEmpty(accountType)) {
+ accountType = mContext.getString(R.string.account_phone);
+ }
+ if (!TextUtils.isEmpty(accountName)) {
+ mHeaderAccountName.setText(
+ mContext.getString(R.string.from_account_format, accountName));
+ }
+ mHeaderAccountType.setText(mContext.getString(R.string.account_type_format, accountType));
+ mHeaderIcon.setImageDrawable(type.getDisplayIcon(mContext));
+
+ mRawContactId = values.getAsLong(RawContacts._ID);
+
+ // Show photo editor when supported
+ EntityModifier.ensureKindExists(state, type, Photo.CONTENT_ITEM_TYPE);
+ setHasPhotoEditor((type.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null));
+ getPhotoEditor().setEnabled(isEnabled());
+ mName.setEnabled(isEnabled());
+ mName.setEditorTextSize(
+ mContext.getResources().getDimensionPixelSize(R.dimen.contact_name_text_size));
+
+ // Show and hide the appropriate views
+ mFields.setVisibility(View.VISIBLE);
+ mName.setVisibility(View.VISIBLE);
+
+ mGroupMembershipKind = type.getKindForMimetype(GroupMembership.CONTENT_ITEM_TYPE);
+ if (mGroupMembershipKind != null) {
+ mGroupMembershipView = (GroupMembershipView)mInflater.inflate(
+ R.layout.item_group_membership, mFields, false);
+ mGroupMembershipView.setKind(mGroupMembershipKind);
+ mGroupMembershipView.setEnabled(isEnabled());
+ }
+
+ // Create editor sections for each possible data kind
+ for (DataKind kind : type.getSortedDataKinds()) {
+ // Skip kind of not editable
+ if (!kind.editable) continue;
+
+ final String mimeType = kind.mimeType;
+ if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ // Handle special case editor for structured name
+ final ValuesDelta primary = state.getPrimaryEntry(mimeType);
+ mName.setValues(kind, primary, state, false, vig);
+ } else if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ // Handle special case editor for photos
+ final ValuesDelta primary = state.getPrimaryEntry(mimeType);
+ getPhotoEditor().setValues(kind, primary, state, false, vig);
+ } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ if (mGroupMembershipView != null) {
+ mGroupMembershipView.setState(state);
+ }
+ } else {
+ // Otherwise use generic section-based editors
+ if (kind.fieldList == null) continue;
+ final KindSectionView section = (KindSectionView)mInflater.inflate(
+ R.layout.item_kind_section, mFields, false);
+ section.setEnabled(isEnabled());
+ section.setState(kind, state, false, vig);
+ mFields.addView(section);
+ }
+ }
+
+ if (mGroupMembershipView != null) {
+ mFields.addView(mGroupMembershipView);
+ }
+
+ addToDefaultGroupIfNeeded();
+
+ mAddFieldButton.setEnabled(isEnabled());
+ }
+
+ @Override
+ public void setGroupMetaData(Cursor groupMetaData) {
+ mGroupMetaData = groupMetaData;
+ addToDefaultGroupIfNeeded();
+ if (mGroupMembershipView != null) {
+ mGroupMembershipView.setGroupMetaData(groupMetaData);
+ }
+ }
+
+ public void setAutoAddToDefaultGroup(boolean flag) {
+ this.mAutoAddToDefaultGroup = flag;
+ }
+
+ /**
+ * If automatic addition to the default group was requested (see
+ * {@link #setAutoAddToDefaultGroup}, checks if the raw contact is in any
+ * group and if it is not adds it to the default group (in case of Google
+ * contacts that's "My Contacts").
+ */
+ private void addToDefaultGroupIfNeeded() {
+ if (!mAutoAddToDefaultGroup || mGroupMetaData == null || mGroupMetaData.isClosed()
+ || mState == null) {
+ return;
+ }
+
+ boolean hasGroupMembership = false;
+ ArrayList<ValuesDelta> entries = mState.getMimeEntries(GroupMembership.CONTENT_ITEM_TYPE);
+ if (entries != null) {
+ for (ValuesDelta values : entries) {
+ Long id = values.getAsLong(GroupMembership.GROUP_ROW_ID);
+ if (id != null && id.longValue() != 0) {
+ hasGroupMembership = true;
+ break;
+ }
+ }
+ }
+
+ if (!hasGroupMembership) {
+ long defaultGroupId = getDefaultGroupId();
+ if (defaultGroupId != -1) {
+ ValuesDelta entry = EntityModifier.insertChild(mState, mGroupMembershipKind);
+ entry.put(GroupMembership.GROUP_ROW_ID, defaultGroupId);
+ }
+ }
+ }
+
+ /**
+ * Returns the default group (e.g. "My Contacts") for the current raw contact's
+ * account. Returns -1 if there is no such group.
+ */
+ private long getDefaultGroupId() {
+ String accountType = mState.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ String accountName = mState.getValues().getAsString(RawContacts.ACCOUNT_NAME);
+ mGroupMetaData.moveToPosition(-1);
+ while (mGroupMetaData.moveToNext()) {
+ String name = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
+ String type = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+ if (name.equals(accountName) && type.equals(accountType)) {
+ long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
+ if (!mGroupMetaData.isNull(GroupMetaDataLoader.AUTO_ADD)
+ && mGroupMetaData.getInt(GroupMetaDataLoader.AUTO_ADD) != 0) {
+ return groupId;
+ }
+ }
+ }
+ return -1;
+ }
+
+ public TextFieldsEditorView getNameEditor() {
+ return mName;
+ }
+
+ @Override
+ public long getRawContactId() {
+ return mRawContactId;
+ }
+
+ private void showAddInformationPopupWindow() {
+ final ArrayList<KindSectionView> fields =
+ new ArrayList<KindSectionView>(mFields.getChildCount());
+
+ final PopupMenu popupMenu = new PopupMenu(getContext(), mAddFieldButton);
+ final Menu menu = popupMenu.getMenu();
+ for (int i = 0; i < mFields.getChildCount(); i++) {
+ View child = mFields.getChildAt(i);
+ if (child instanceof KindSectionView) {
+ final KindSectionView sectionView = (KindSectionView) child;
+ // not a list and already exists? ignore
+ if (!sectionView.getKind().isList && sectionView.getEditorCount() != 0) {
+ continue;
+ }
+ menu.add(Menu.NONE, fields.size(), Menu.NONE, sectionView.getTitle());
+ fields.add(sectionView);
+ }
+ }
+ popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ final KindSectionView view = fields.get(item.getItemId());
+ view.addItem();
+ return true;
+ }
+ });
+
+ popupMenu.show();
+ }
+}
diff --git a/src/com/android/contacts/editor/SelectAccountDialogFragment.java b/src/com/android/contacts/editor/SelectAccountDialogFragment.java
new file mode 100644
index 0000000..67c5f6e
--- /dev/null
+++ b/src/com/android/contacts/editor/SelectAccountDialogFragment.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 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.editor;
+
+import com.android.contacts.R;
+import com.android.contacts.util.AccountsListAdapter;
+
+import android.accounts.Account;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+/**
+ * Shows a dialog asking the user which account to chose.
+ * The result is passed back to the Fragment that is configured by
+ * {@link Fragment#setTargetFragment(Fragment, int)}, which has to implement
+ * {@link SelectAccountDialogFragment.Listener}.
+ * Does not perform any action by itself.
+ */
+public class SelectAccountDialogFragment extends DialogFragment {
+ public static final String TAG = "SelectAccountDialogFragment";
+
+ public SelectAccountDialogFragment() {
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ final AccountsListAdapter accountAdapter = new AccountsListAdapter(builder.getContext(),
+ true);
+
+ final DialogInterface.OnClickListener clickListener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+
+ final Listener target = (Listener) getTargetFragment();
+ target.onAccountChosen(accountAdapter.getItem(which));
+ }
+ };
+
+ builder.setTitle(R.string.dialog_new_contact_account);
+ builder.setSingleChoiceItems(accountAdapter, 0, clickListener);
+ final AlertDialog result = builder.create();
+ return result;
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ final Listener target = (Listener) getTargetFragment();
+ target.onAccountSelectorCancelled();
+ }
+
+ public interface Listener {
+ void onAccountChosen(Account account);
+ void onAccountSelectorCancelled();
+ }
+}
diff --git a/src/com/android/contacts/editor/SplitContactConfirmationDialogFragment.java b/src/com/android/contacts/editor/SplitContactConfirmationDialogFragment.java
new file mode 100644
index 0000000..3259fd4
--- /dev/null
+++ b/src/com/android/contacts/editor/SplitContactConfirmationDialogFragment.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 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.editor;
+
+import com.android.contacts.R;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+/**
+ * Shows a dialog asking the user whether to split the contact. The result is passed back
+ * to the Fragment that is configured by {@link Fragment#setTargetFragment(Fragment, int)}, which
+ * has to implement {@link SplitContactConfirmationDialogFragment.Listener}.
+ * Does not split the contact itself.
+ */
+public class SplitContactConfirmationDialogFragment extends DialogFragment {
+ public static final String TAG = "SplitContactConfirmationDialog";
+
+ public SplitContactConfirmationDialogFragment() {
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setTitle(R.string.splitConfirmation_title);
+ builder.setIconAttribute(android.R.attr.alertDialogIcon);
+ builder.setMessage(R.string.splitConfirmation);
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final Listener targetListener = (Listener) getTargetFragment();
+ targetListener.onSplitContactConfirmed();
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.setCancelable(false);
+ return builder.create();
+ }
+
+ public interface Listener {
+ void onSplitContactConfirmed();
+ }
+}
diff --git a/src/com/android/contacts/editor/TextFieldsEditorView.java b/src/com/android/contacts/editor/TextFieldsEditorView.java
new file mode 100644
index 0000000..1a930a1
--- /dev/null
+++ b/src/com/android/contacts/editor/TextFieldsEditorView.java
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2010 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.editor;
+
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.AccountType.EditField;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.util.ThemeUtils;
+
+import android.content.Context;
+import android.content.Entity;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.PhoneNumberFormattingTextWatcher;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+/**
+ * Simple editor that handles labels and any {@link EditField} defined for
+ * the entry. Uses {@link ValuesDelta} to read any existing
+ * {@link Entity} values, and to correctly write any changes values.
+ */
+public class TextFieldsEditorView extends LabeledEditorView {
+ private EditText[] mFieldEditTexts = null;
+ private ImageButton mMoreOrLess;
+ private boolean mHideOptional = true;
+ private boolean mHasShortAndLongForms;
+ private int mEditorTextSize;
+
+ public TextFieldsEditorView(Context context) {
+ super(context);
+ }
+
+ public TextFieldsEditorView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public TextFieldsEditorView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setEditorTextSize(int textSize) {
+ this.mEditorTextSize = textSize;
+ }
+
+ @Override
+ protected int getLineItemCount() {
+ int count = mFieldEditTexts == null ? 0 : mFieldEditTexts.length;
+ return Math.max(count, super.getLineItemCount());
+ }
+
+ @Override
+ protected boolean isLineItemVisible(int row) {
+ return mFieldEditTexts != null && mFieldEditTexts[row].getVisibility() != View.GONE;
+ }
+
+ @Override
+ public int getBaseline(int row) {
+ int baseline = super.getBaseline(row);
+ if (mFieldEditTexts != null) {
+ EditText editText = mFieldEditTexts[row];
+ // The text field will be centered vertically in the corresponding line item
+ int lineItemHeight = getLineItemHeight(row);
+ int offset = (lineItemHeight - editText.getMeasuredHeight()) / 2;
+ baseline = Math.max(baseline, offset + editText.getBaseline());
+ }
+ return baseline;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ int l1 = getPaddingLeft();
+ int t1 = getPaddingTop();
+ int r1 = getMeasuredWidth() - getPaddingRight();
+
+ if ((mMoreOrLess != null)) {
+ mMoreOrLess.layout(
+ r1 - mMoreOrLess.getMeasuredWidth(), t1,
+ r1, t1 + mMoreOrLess.getMeasuredHeight());
+ }
+
+ // Subtract buttons if necessary
+ final int labelWidth = (getLabel() != null) ? getLabel().getMeasuredWidth() : 0;
+ final int deleteWidth = (getDelete() != null) ? getDelete().getMeasuredWidth() : 0;
+ final int moreOrLessWidth = mMoreOrLess != null ? mMoreOrLess.getMeasuredWidth() : 0;
+ final int r2 = r1 - Math.max(deleteWidth, moreOrLessWidth) - labelWidth;
+
+ // Layout text fields
+ int y = t1;
+ if (mFieldEditTexts != null) {
+ for (int i = 0; i < mFieldEditTexts.length; i++) {
+ int baseline = getBaseline(i);
+ EditText editText = mFieldEditTexts[i];
+ if (editText.getVisibility() != View.GONE) {
+ int height = editText.getMeasuredHeight();
+ int top = t1 + y + baseline - editText.getBaseline();
+ editText.layout(
+ l1, top,
+ r2, top + height);
+ y += getLineItemHeight(i);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected int getLineItemHeight(int row) {
+ int fieldHeight = 0;
+ int buttonHeight = 0;
+
+ boolean lastLineItem = true;
+ if (mFieldEditTexts != null) {
+ fieldHeight = mFieldEditTexts[row].getMeasuredHeight();
+ lastLineItem = (row == mFieldEditTexts.length - 1);
+ }
+
+ // Ensure there is enough space for the more/less button
+ if (row == 0) {
+ final int moreOrLessHeight = mMoreOrLess != null ? mMoreOrLess.getMeasuredHeight() : 0;
+ buttonHeight += moreOrLessHeight;
+ }
+
+ // Ensure there is enough space for the minus button
+ if (lastLineItem) {
+ View deleteButton = getDelete();
+ final int deleteHeight = (deleteButton != null) ? deleteButton.getMeasuredHeight() : 0;
+ buttonHeight += deleteHeight;
+ }
+
+ return Math.max(Math.max(buttonHeight, fieldHeight), super.getLineItemHeight(row));
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+
+ if (mFieldEditTexts != null) {
+ for (int index = 0; index < mFieldEditTexts.length; index++) {
+ mFieldEditTexts[index].setEnabled(!isReadOnly() && enabled);
+ }
+ }
+ if (mMoreOrLess != null) mMoreOrLess.setEnabled(!isReadOnly() && enabled);
+ }
+
+ /**
+ * Creates or removes the type/label button. Doesn't do anything if already correctly configured
+ */
+ private void setupMoreOrLessButton(boolean shouldExist, boolean collapsed) {
+ if (shouldExist) {
+ if (mMoreOrLess == null) {
+ mMoreOrLess = new ImageButton(mContext);
+ mMoreOrLess.setBackgroundResource(
+ ThemeUtils.getSelectableItemBackground(mContext.getTheme()));
+ final Resources resources = mContext.getResources();
+ mMoreOrLess.setPadding(
+ resources.getDimensionPixelOffset(
+ R.dimen.editor_round_button_padding_left),
+ resources.getDimensionPixelOffset(
+ R.dimen.editor_round_button_padding_top),
+ resources.getDimensionPixelOffset(
+ R.dimen.editor_round_button_padding_right),
+ resources.getDimensionPixelOffset(
+ R.dimen.editor_round_button_padding_bottom));
+ mMoreOrLess.setLayoutParams(
+ new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+ mMoreOrLess.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Save focus
+ final View focusedChild = getFocusedChild();
+ final int focusedViewId = focusedChild == null ? -1 : focusedChild.getId();
+
+ // Reconfigure GUI
+ mHideOptional = !mHideOptional;
+ onOptionalFieldVisibilityChange();
+ rebuildValues();
+
+ // Restore focus
+ View newFocusView = findViewById(focusedViewId);
+ if (newFocusView == null || newFocusView.getVisibility() == GONE) {
+ // find first visible child
+ newFocusView = TextFieldsEditorView.this;
+ }
+ newFocusView.requestFocus();
+ }
+ });
+ addView(mMoreOrLess);
+ }
+ mMoreOrLess.setImageResource(collapsed
+ ? R.drawable.ic_menu_expander_minimized_holo_light
+ : R.drawable.ic_menu_expander_maximized_holo_light);
+ } else if (mMoreOrLess != null) {
+ removeView(mMoreOrLess);
+ mMoreOrLess = null;
+ }
+ }
+
+ @Override
+ protected void requestFocusForFirstEditField() {
+ if (mFieldEditTexts != null && mFieldEditTexts.length != 0) {
+ EditText firstField = null;
+ boolean anyFieldHasFocus = false;
+ for (EditText editText : mFieldEditTexts) {
+ if (firstField == null && editText.getVisibility() == View.VISIBLE) {
+ firstField = editText;
+ }
+ if (editText.hasFocus()) {
+ anyFieldHasFocus = true;
+ break;
+ }
+ }
+ if (!anyFieldHasFocus && firstField != null) {
+ firstField.requestFocus();
+ }
+ }
+ }
+
+ @Override
+ public void setValues(DataKind kind, ValuesDelta entry, EntityDelta state, boolean readOnly,
+ ViewIdGenerator vig) {
+ super.setValues(kind, entry, state, readOnly, vig);
+ // Remove edit texts that we currently have
+ if (mFieldEditTexts != null) {
+ for (EditText fieldEditText : mFieldEditTexts) {
+ removeView(fieldEditText);
+ }
+ }
+ boolean hidePossible = false;
+
+ int fieldCount = kind.fieldList.size();
+ mFieldEditTexts = new EditText[fieldCount];
+ for (int index = 0; index < fieldCount; index++) {
+ final EditField field = kind.fieldList.get(index);
+ final EditText fieldView = new EditText(mContext);
+ fieldView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.WRAP_CONTENT));
+ fieldView.setGravity(Gravity.TOP);
+ if (mEditorTextSize != 0) {
+ fieldView.setTextSize(mEditorTextSize);
+ }
+ mFieldEditTexts[index] = fieldView;
+ fieldView.setId(vig.getId(state, kind, entry, index));
+ if (field.titleRes > 0) {
+ fieldView.setHint(field.titleRes);
+ }
+ int inputType = field.inputType;
+ fieldView.setInputType(inputType);
+ if (inputType == InputType.TYPE_CLASS_PHONE) {
+ fieldView.addTextChangedListener(new PhoneNumberFormattingTextWatcher(
+ ContactsUtils.getCurrentCountryIso(mContext)));
+ }
+ fieldView.setMinLines(field.minLines);
+
+ // Read current value from state
+ final String column = field.column;
+ final String value = entry.getAsString(column);
+ fieldView.setText(value);
+
+ // Prepare listener for writing changes
+ fieldView.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ // Trigger event for newly changed value
+ onFieldChanged(column, s.toString());
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+ });
+
+ fieldView.setEnabled(isEnabled() && !readOnly);
+
+ if (field.shortForm) {
+ hidePossible = true;
+ mHasShortAndLongForms = true;
+ fieldView.setVisibility(mHideOptional ? View.VISIBLE : View.GONE);
+ } else if (field.longForm) {
+ hidePossible = true;
+ mHasShortAndLongForms = true;
+ fieldView.setVisibility(mHideOptional ? View.GONE : View.VISIBLE);
+ } else {
+ // Hide field when empty and optional value
+ final boolean couldHide = (!ContactsUtils.isGraphic(value) && field.optional);
+ final boolean willHide = (mHideOptional && couldHide);
+ fieldView.setVisibility(willHide ? View.GONE : View.VISIBLE);
+ hidePossible = hidePossible || couldHide;
+ }
+
+ addView(fieldView);
+ }
+
+ // When hiding fields, place expandable
+ setupMoreOrLessButton(hidePossible, mHideOptional);
+ if (mMoreOrLess != null) mMoreOrLess.setEnabled(!readOnly && isEnabled());
+ }
+
+ /**
+ * Returns true if the editor is currently configured to show optional fields.
+ */
+ public boolean areOptionalFieldsVisible() {
+ return !mHideOptional;
+ }
+
+ public boolean hasShortAndLongForms() {
+ return mHasShortAndLongForms;
+ }
+
+ /**
+ * Populates the bound rectangle with the bounds of the last editor field inside this view.
+ */
+ public void acquireEditorBounds(Rect bounds) {
+ if (mFieldEditTexts != null) {
+ for (int i = mFieldEditTexts.length; --i >= 0;) {
+ EditText editText = mFieldEditTexts[i];
+ if (editText.getVisibility() == View.VISIBLE) {
+ bounds.set(editText.getLeft(), editText.getTop(), editText.getRight(),
+ editText.getBottom());
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Saves the visibility of the child EditTexts, and mHideOptional.
+ */
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+ SavedState ss = new SavedState(superState);
+
+ ss.mHideOptional = mHideOptional;
+
+ final int numChildren = mFieldEditTexts.length;
+ ss.mVisibilities = new int[numChildren];
+ for (int i = 0; i < numChildren; i++) {
+ ss.mVisibilities[i] = mFieldEditTexts[i].getVisibility();
+ }
+
+ return ss;
+ }
+
+ /**
+ * Restores the visibility of the child EditTexts, and mHideOptional.
+ */
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+
+ mHideOptional = ss.mHideOptional;
+
+ int numChildren = Math.min(mFieldEditTexts.length, ss.mVisibilities.length);
+ for (int i = 0; i < numChildren; i++) {
+ mFieldEditTexts[i].setVisibility(ss.mVisibilities[i]);
+ }
+ }
+
+ private static class SavedState extends BaseSavedState {
+ public boolean mHideOptional;
+ public int[] mVisibilities;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ mVisibilities = new int[in.readInt()];
+ in.readIntArray(mVisibilities);
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(mVisibilities.length);
+ out.writeIntArray(mVisibilities);
+ }
+
+ @SuppressWarnings({"unused", "hiding" })
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+}
diff --git a/src/com/android/contacts/editor/ViewIdGenerator.java b/src/com/android/contacts/editor/ViewIdGenerator.java
new file mode 100644
index 0000000..93bb002
--- /dev/null
+++ b/src/com/android/contacts/editor/ViewIdGenerator.java
@@ -0,0 +1,131 @@
+/*
+ * 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.editor;
+
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class that provides unique view ids for {@link ContentEditorView}, {@link KindSectionView},
+ * {@link LabeledEditorView} and {@link EditView} on {@link EditContactActivity}.
+ * It is used to assign a unique but consistent id to each view across {@link EditContactActivity}'s
+ * lifecycle, so that we can re-construct view state (e.g. focused view) when the screen rotates.
+ *
+ * <p>This class is not thread safe.
+ */
+public final class ViewIdGenerator implements Parcelable {
+ private static final int INVALID_VIEW_ID = 0;
+ private static final int INITIAL_VIEW_ID = 1;
+
+ public static final int NO_VIEW_INDEX = -1;
+
+ private int mNextId;
+
+ /**
+ * Used as a map from the "key" of the views to actual ids. {@link #getId()} generates keys for
+ * the views.
+ */
+ private Bundle mIdMap = new Bundle();
+
+ private static final char KEY_SEPARATOR = '*';
+
+ private final static StringBuilder sWorkStringBuilder = new StringBuilder();
+
+ public ViewIdGenerator() {
+ mNextId = INITIAL_VIEW_ID;
+ }
+
+ /** {@inheritDoc} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns an id for a view associated with specified contact field.
+ *
+ * @param entity {@link EntityDelta} associated with the view
+ * @param kind {@link DataKind} associated with the view, or null if none exists.
+ * @param values {@link ValuesDelta} associated with the view, or null if none exists.
+ * @param viewIndex index of the view in the parent {@link Editor}, if it's a leave view.
+ * Otherwise, pass {@link #NO_VIEW_INDEX}.
+ */
+ public int getId(EntityDelta entity, DataKind kind, ValuesDelta values,
+ int viewIndex) {
+ final String k = getMapKey(entity, kind, values, viewIndex);
+
+ int id = mIdMap.getInt(k, INVALID_VIEW_ID);
+ if (id == INVALID_VIEW_ID) {
+ // Make sure the new id won't conflict with auto-generated ids by masking with 0xffff.
+ id = (mNextId++) & 0xFFFF;
+ mIdMap.putInt(k, id);
+ }
+ return id;
+ }
+
+ private static String getMapKey(EntityDelta entity, DataKind kind, ValuesDelta values,
+ int viewIndex) {
+ sWorkStringBuilder.setLength(0);
+ if (entity != null) {
+ sWorkStringBuilder.append(entity.getValues().getId());
+
+ if (kind != null) {
+ sWorkStringBuilder.append(KEY_SEPARATOR);
+ sWorkStringBuilder.append(kind.mimeType);
+
+ if (values != null) {
+ sWorkStringBuilder.append(KEY_SEPARATOR);
+ sWorkStringBuilder.append(values.getId());
+
+ if (viewIndex != NO_VIEW_INDEX) {
+ sWorkStringBuilder.append(KEY_SEPARATOR);
+ sWorkStringBuilder.append(viewIndex);
+ }
+ }
+ }
+ }
+ return sWorkStringBuilder.toString();
+ }
+
+ /** {@Override} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mNextId);
+ dest.writeBundle(mIdMap);
+ }
+
+ private void readFromParcel(Parcel src) {
+ mNextId = src.readInt();
+ mIdMap = src.readBundle();
+ }
+
+ public static final Parcelable.Creator<ViewIdGenerator> CREATOR =
+ new Parcelable.Creator<ViewIdGenerator>() {
+ public ViewIdGenerator createFromParcel(Parcel in) {
+ final ViewIdGenerator vig = new ViewIdGenerator();
+ vig.readFromParcel(in);
+ return vig;
+ }
+
+ public ViewIdGenerator[] newArray(int size) {
+ return new ViewIdGenerator[size];
+ }
+ };
+}
diff --git a/src/com/android/contacts/interactions/ContactDeletionInteraction.java b/src/com/android/contacts/interactions/ContactDeletionInteraction.java
new file mode 100644
index 0000000..bc78e87
--- /dev/null
+++ b/src/com/android/contacts/interactions/ContactDeletionInteraction.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2010 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.interactions;
+
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+import com.google.android.collect.Sets;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.Loader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Contacts.Entity;
+
+import java.util.HashSet;
+
+/**
+ * An interaction invoked to delete a contact.
+ */
+public class ContactDeletionInteraction extends Fragment
+ implements LoaderCallbacks<Cursor>, OnDismissListener {
+
+ private static final String FRAGMENT_TAG = "deleteContact";
+
+ private static final String KEY_ACTIVE = "active";
+ private static final String KEY_CONTACT_URI = "contactUri";
+ public static final String ARG_CONTACT_URI = "contactUri";
+
+ private static final String[] ENTITY_PROJECTION = new String[] {
+ Entity.RAW_CONTACT_ID, //0
+ Entity.ACCOUNT_TYPE, //1
+ Entity.CONTACT_ID, // 2
+ Entity.LOOKUP_KEY, // 3
+ };
+
+ private static final int COLUMN_INDEX_RAW_CONTACT_ID = 0;
+ private static final int COLUMN_INDEX_ACCOUNT_TYPE = 1;
+ private static final int COLUMN_INDEX_CONTACT_ID = 2;
+ private static final int COLUMN_INDEX_LOOKUP_KEY = 3;
+
+ private boolean mActive;
+ private Uri mContactUri;
+ private Context mContext;
+
+ private AlertDialog mDialog;
+
+ // Visible for testing
+ int mMessageId;
+
+ public static ContactDeletionInteraction start(Activity activity, Uri contactUri) {
+ FragmentManager fragmentManager = activity.getFragmentManager();
+ ContactDeletionInteraction fragment =
+ (ContactDeletionInteraction) fragmentManager.findFragmentByTag(FRAGMENT_TAG);
+ if (fragment == null) {
+ fragment = new ContactDeletionInteraction();
+ fragment.setContactUri(contactUri);
+ fragmentManager.beginTransaction().add(fragment, FRAGMENT_TAG).commit();
+ } else {
+ fragment.setContactUri(contactUri);
+ }
+ return fragment;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mContext = activity;
+ }
+
+ public void setContactUri(Uri contactUri) {
+ mContactUri = contactUri;
+ mActive = true;
+ if (isStarted()) {
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_CONTACT_URI, mContactUri);
+ getLoaderManager().restartLoader(R.id.dialog_delete_contact_loader_id, args, this);
+ }
+ }
+
+ /* Visible for testing */
+ boolean isStarted() {
+ return isAdded();
+ }
+
+ @Override
+ public void onStart() {
+ if (mActive) {
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_CONTACT_URI, mContactUri);
+ getLoaderManager().initLoader(R.id.dialog_delete_contact_loader_id, args, this);
+ }
+ super.onStart();
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ Uri contactUri = args.getParcelable(ARG_CONTACT_URI);
+ return new CursorLoader(mContext,
+ Uri.withAppendedPath(contactUri, Entity.CONTENT_DIRECTORY), ENTITY_PROJECTION,
+ null, null, null);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+
+ if (!mActive) {
+ return;
+ }
+
+ long contactId = 0;
+ String lookupKey = null;
+
+ // This cursor may contain duplicate raw contacts, so we need to de-dupe them first
+ HashSet<Long> readOnlyRawContacts = Sets.newHashSet();
+ HashSet<Long> writableRawContacts = Sets.newHashSet();
+
+ AccountTypeManager accountTypes = AccountTypeManager.getInstance(getActivity());
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ final long rawContactId = cursor.getLong(COLUMN_INDEX_RAW_CONTACT_ID);
+ final String accountType = cursor.getString(COLUMN_INDEX_ACCOUNT_TYPE);
+ contactId = cursor.getLong(COLUMN_INDEX_CONTACT_ID);
+ lookupKey = cursor.getString(COLUMN_INDEX_LOOKUP_KEY);
+ AccountType type = accountTypes.getAccountType(accountType);
+ boolean readonly = type != null && type.readOnly;
+ if (readonly) {
+ readOnlyRawContacts.add(rawContactId);
+ } else {
+ writableRawContacts.add(rawContactId);
+ }
+ }
+
+ int readOnlyCount = readOnlyRawContacts.size();
+ int writableCount = writableRawContacts.size();
+ if (readOnlyCount > 0 && writableCount > 0) {
+ mMessageId = R.string.readOnlyContactDeleteConfirmation;
+ } else if (readOnlyCount > 0 && writableCount == 0) {
+ mMessageId = R.string.readOnlyContactWarning;
+ } else if (readOnlyCount == 0 && writableCount > 1) {
+ mMessageId = R.string.multipleContactDeleteConfirmation;
+ } else {
+ mMessageId = R.string.deleteConfirmation;
+ }
+
+ final Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
+ showDialog(mMessageId, contactUri);
+ }
+
+ public void onLoaderReset(Loader<Cursor> loader) {
+ }
+
+ private void showDialog(int messageId, final Uri contactUri) {
+ mDialog = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.deleteConfirmation_title)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setMessage(messageId)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ doDeleteContact(contactUri);
+ }
+ }
+ )
+ .create();
+
+ mDialog.setOnDismissListener(this);
+ mDialog.show();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mActive = false;
+ mDialog = null;
+ getLoaderManager().destroyLoader(R.id.dialog_delete_contact_loader_id);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_ACTIVE, mActive);
+ outState.putParcelable(KEY_CONTACT_URI, mContactUri);
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ if (savedInstanceState != null) {
+ mActive = savedInstanceState.getBoolean(KEY_ACTIVE);
+ mContactUri = savedInstanceState.getParcelable(KEY_CONTACT_URI);
+ }
+ }
+
+ protected void doDeleteContact(Uri contactUri) {
+ mContext.startService(ContactSaveService.createDeleteContactIntent(mContext, contactUri));
+ }
+}
diff --git a/src/com/android/contacts/interactions/GroupCreationDialogFragment.java b/src/com/android/contacts/interactions/GroupCreationDialogFragment.java
new file mode 100644
index 0000000..8991928
--- /dev/null
+++ b/src/com/android/contacts/interactions/GroupCreationDialogFragment.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 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.interactions;
+
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.R;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.app.FragmentManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.EditText;
+
+/**
+ * A dialog for creating a new group.
+ */
+public class GroupCreationDialogFragment extends GroupNameDialogFragment {
+ private static final String ARG_ACCOUNT_TYPE = "accountType";
+ private static final String ARG_ACCOUNT_NAME = "accountName";
+
+ public static void show(
+ FragmentManager fragmentManager, String accountType, String accountName) {
+ GroupCreationDialogFragment dialog = new GroupCreationDialogFragment();
+ Bundle args = new Bundle();
+ args.putString(ARG_ACCOUNT_TYPE, accountType);
+ args.putString(ARG_ACCOUNT_NAME, accountName);
+ dialog.setArguments(args);
+ dialog.show(fragmentManager, "createGroup");
+ }
+
+ @Override
+ protected void initializeGroupLabelEditText(EditText editText) {
+ }
+
+ @Override
+ protected int getTitleResourceId() {
+ return R.string.create_group_dialog_title;
+ }
+
+ @Override
+ protected void onCompleted(String groupLabel) {
+ Bundle arguments = getArguments();
+ String accountType = arguments.getString(ARG_ACCOUNT_TYPE);
+ String accountName = arguments.getString(ARG_ACCOUNT_NAME);
+
+ Activity activity = getActivity();
+ activity.startService(ContactSaveService.createNewGroupIntent(activity,
+ new Account(accountName, accountType), groupLabel,
+ activity.getClass(), Intent.ACTION_EDIT));
+ }
+}
diff --git a/src/com/android/contacts/interactions/GroupDeletionDialogFragment.java b/src/com/android/contacts/interactions/GroupDeletionDialogFragment.java
new file mode 100644
index 0000000..44d31be
--- /dev/null
+++ b/src/com/android/contacts/interactions/GroupDeletionDialogFragment.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 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.interactions;
+
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.R;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+/**
+ * A dialog for deleting a group.
+ */
+public class GroupDeletionDialogFragment extends DialogFragment {
+
+ private static final String ARG_GROUP_ID = "groupId";
+ private static final String ARG_LABEL = "label";
+
+ public static void show(FragmentManager fragmentManager, long groupId, String label) {
+ GroupDeletionDialogFragment dialog = new GroupDeletionDialogFragment();
+ Bundle args = new Bundle();
+ args.putLong(ARG_GROUP_ID, groupId);
+ args.putString(ARG_LABEL, label);
+ dialog.setArguments(args);
+ dialog.show(fragmentManager, "deleteGroup");
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ String label = getArguments().getString(ARG_LABEL);
+ String message = getActivity().getString(R.string.delete_group_dialog_message, label);
+
+ return new AlertDialog.Builder(getActivity())
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setTitle(R.string.delete_group_dialog_title)
+ .setMessage(message)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ deleteGroup();
+ }
+ }
+ )
+ .setNegativeButton(android.R.string.cancel, null)
+ .create();
+ }
+
+ protected void deleteGroup() {
+ Bundle arguments = getArguments();
+ long groupId = arguments.getLong(ARG_GROUP_ID);
+
+ getActivity().startService(ContactSaveService.createGroupDeletionIntent(
+ getActivity(), groupId));
+ }
+}
diff --git a/src/com/android/contacts/interactions/GroupNameDialogFragment.java b/src/com/android/contacts/interactions/GroupNameDialogFragment.java
new file mode 100644
index 0000000..93500be
--- /dev/null
+++ b/src/com/android/contacts/interactions/GroupNameDialogFragment.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 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.interactions;
+
+import com.android.contacts.R;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnShowListener;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+
+/**
+ * A common superclass for creating and renaming groups.
+ */
+public abstract class GroupNameDialogFragment extends DialogFragment
+ implements TextWatcher, OnShowListener {
+ private EditText mEdit;
+
+ protected abstract int getTitleResourceId();
+ protected abstract void initializeGroupLabelEditText(EditText editText);
+ protected abstract void onCompleted(String groupLabel);
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ View view = LayoutInflater.from(getActivity()).inflate(R.layout.group_name_dialog, null);
+ mEdit = (EditText) view.findViewById(R.id.group_label);
+ initializeGroupLabelEditText(mEdit);
+
+ mEdit.addTextChangedListener(this);
+
+ AlertDialog dialog = new AlertDialog.Builder(getActivity())
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setTitle(getTitleResourceId())
+ .setView(view)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int whichButton) {
+ onCompleted(mEdit.getText().toString().trim());
+ }
+ }
+ )
+ .setNegativeButton(android.R.string.cancel, null)
+ .create();
+
+ dialog.setOnShowListener(this);
+ return dialog;
+ }
+
+ public void onShow(DialogInterface dialog) {
+ updateOkButtonState((AlertDialog) dialog);
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ AlertDialog dialog = (AlertDialog) getDialog();
+ updateOkButtonState(dialog);
+ }
+
+ private void updateOkButtonState(AlertDialog dialog) {
+ Button okButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ okButton.setEnabled(!TextUtils.isEmpty(mEdit.getText().toString().trim()));
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+}
diff --git a/src/com/android/contacts/interactions/GroupRenamingDialogFragment.java b/src/com/android/contacts/interactions/GroupRenamingDialogFragment.java
new file mode 100644
index 0000000..0db435e
--- /dev/null
+++ b/src/com/android/contacts/interactions/GroupRenamingDialogFragment.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 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.interactions;
+
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.R;
+
+import android.app.FragmentManager;
+import android.os.Bundle;
+import android.widget.EditText;
+
+/**
+ * A dialog for renaming a group.
+ */
+public class GroupRenamingDialogFragment extends GroupNameDialogFragment {
+
+ private static final String ARG_GROUP_ID = "groupId";
+ private static final String ARG_LABEL = "label";
+
+ public static void show(FragmentManager fragmentManager, long groupId, String label) {
+ GroupRenamingDialogFragment dialog = new GroupRenamingDialogFragment();
+ Bundle args = new Bundle();
+ args.putLong(ARG_GROUP_ID, groupId);
+ args.putString(ARG_LABEL, label);
+ dialog.setArguments(args);
+ dialog.show(fragmentManager, "renameGroup");
+ }
+
+ @Override
+ protected void initializeGroupLabelEditText(EditText editText) {
+ String label = getArguments().getString(ARG_LABEL);
+ editText.setText(label);
+ if (label != null) {
+ editText.setSelection(label.length());
+ }
+ }
+
+ @Override
+ protected int getTitleResourceId() {
+ return R.string.rename_group_dialog_title;
+ }
+
+ @Override
+ protected void onCompleted(String groupLabel) {
+ Bundle arguments = getArguments();
+ long groupId = arguments.getLong(ARG_GROUP_ID);
+
+ getActivity().startService(ContactSaveService.createGroupRenameIntent(
+ getActivity(), groupId, groupLabel));
+ }
+}
diff --git a/src/com/android/contacts/interactions/ImportExportInteraction.java b/src/com/android/contacts/interactions/ImportExportInteraction.java
new file mode 100644
index 0000000..bd8ac16
--- /dev/null
+++ b/src/com/android/contacts/interactions/ImportExportInteraction.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2010 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.interactions;
+
+import com.android.contacts.R;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.util.AccountSelectionUtil;
+import com.android.contacts.vcard.ExportVCardActivity;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.List;
+
+/**
+ * An interaction invoked to import/export contacts.
+ */
+public class ImportExportInteraction {
+
+ private static final String TAG = "ImportExportInteraction";
+
+ private final String[] LOOKUP_PROJECTION = new String[] {
+ Contacts.LOOKUP_KEY
+ };
+
+ private final Context mContext;
+
+ public ImportExportInteraction(Context context) {
+ this.mContext = context;
+ }
+
+ /**
+ * Creates a {@link Dialog} that allows the user to choose between a bulk import
+ * and bulk export task across all contacts.
+ */
+ public void startInteraction() {
+ showDialog(R.id.dialog_import_export_options, null);
+ }
+
+ public Dialog onCreateDialog(int id, Bundle bundle) {
+ switch (id) {
+ case R.id.dialog_import_export_options: {
+ return createOptionsDialog();
+ }
+ case R.string.import_from_sim:
+ case R.string.import_from_sdcard: {
+ return AccountSelectionUtil.getSelectAccountDialog(mContext, id);
+ }
+ case R.id.dialog_sdcard_not_found: {
+ return new AlertDialog.Builder(mContext)
+ .setTitle(R.string.no_sdcard_title)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setMessage(R.string.no_sdcard_message)
+ .setPositiveButton(android.R.string.ok, null).create();
+ }
+ }
+
+ return null;
+ }
+
+ private Dialog createOptionsDialog() {
+ // Wrap our context to inflate list items using the correct theme
+ final Resources res = mContext.getResources();
+ final LayoutInflater dialogInflater = (LayoutInflater)mContext
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ // Adapter that shows a list of string resources
+ final ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(mContext,
+ android.R.layout.select_dialog_item) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = dialogInflater.inflate(android.R.layout.select_dialog_item,
+ parent, false);
+ }
+
+ final int resId = this.getItem(position);
+ ((TextView)convertView).setText(resId);
+ return convertView;
+ }
+ };
+
+ if (TelephonyManager.getDefault().hasIccCard()) {
+ adapter.add(R.string.import_from_sim);
+ }
+ if (res.getBoolean(R.bool.config_allow_import_from_sdcard)) {
+ adapter.add(R.string.import_from_sdcard);
+ }
+ if (res.getBoolean(R.bool.config_allow_export_to_sdcard)) {
+ adapter.add(R.string.export_to_sdcard);
+ }
+ if (res.getBoolean(R.bool.config_allow_share_visible_contacts)) {
+ adapter.add(R.string.share_visible_contacts);
+ }
+
+ final DialogInterface.OnClickListener clickListener =
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+
+ final int resId = adapter.getItem(which);
+ switch (resId) {
+ case R.string.import_from_sim:
+ case R.string.import_from_sdcard: {
+ handleImportRequest(resId);
+ break;
+ }
+ case R.string.export_to_sdcard: {
+ Intent exportIntent = new Intent(mContext, ExportVCardActivity.class);
+ mContext.startActivity(exportIntent);
+ break;
+ }
+ case R.string.share_visible_contacts: {
+ doShareVisibleContacts();
+ break;
+ }
+ default: {
+ Log.e(TAG, "Unexpected resource: " +
+ mContext.getResources().getResourceEntryName(resId));
+ }
+ }
+ }
+ };
+
+ return new AlertDialog.Builder(mContext)
+ .setTitle(R.string.dialog_import_export)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setSingleChoiceItems(adapter, -1, clickListener)
+ .create();
+ }
+
+ private void doShareVisibleContacts() {
+
+ // TODO move the query into a loader
+ final Cursor cursor = mContext.getContentResolver().query(Contacts.CONTENT_URI,
+ LOOKUP_PROJECTION, Contacts.IN_VISIBLE_GROUP + "!=0", null, null);
+ try {
+ if (!cursor.moveToFirst()) {
+ Toast.makeText(mContext, R.string.share_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ StringBuilder uriListBuilder = new StringBuilder();
+ int index = 0;
+ while (cursor.moveToNext()) {
+ if (index != 0)
+ uriListBuilder.append(':');
+ uriListBuilder.append(cursor.getString(0));
+ index++;
+ }
+ Uri uri = Uri.withAppendedPath(
+ Contacts.CONTENT_MULTI_VCARD_URI,
+ Uri.encode(uriListBuilder.toString()));
+
+ final Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType(Contacts.CONTENT_VCARD_TYPE);
+ intent.putExtra(Intent.EXTRA_STREAM, uri);
+ mContext.startActivity(intent);
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void handleImportRequest(int resId) {
+ // There are three possibilities:
+ // - more than one accounts -> ask the user
+ // - just one account -> use the account without asking the user
+ // - no account -> use phone-local storage without asking the user
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+ final List<Account> accountList = accountTypes.getAccounts(true);
+ final int size = accountList.size();
+ if (size > 1) {
+ showDialog(resId, null);
+ return;
+ }
+
+ AccountSelectionUtil.doImport(mContext, resId, (size == 1 ? accountList.get(0) : null));
+ }
+
+ /* Visible for testing */
+ void showDialog(int dialogId, Bundle bundle) {
+ ((Activity)mContext).showDialog(dialogId, bundle);
+ }
+}
diff --git a/src/com/android/contacts/interactions/PhoneNumberInteraction.java b/src/com/android/contacts/interactions/PhoneNumberInteraction.java
new file mode 100644
index 0000000..277fa56
--- /dev/null
+++ b/src/com/android/contacts/interactions/PhoneNumberInteraction.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2010 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.interactions;
+
+
+import com.android.contacts.Collapser;
+import com.android.contacts.Collapser.Collapsible;
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.AccountType.StringInflater;
+import com.android.contacts.model.AccountTypeManager;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.Loader.OnLoadCompleteListener;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.telephony.PhoneNumberUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * Initiates phone calls or a text message.
+ */
+public class PhoneNumberInteraction
+ implements OnLoadCompleteListener<Cursor>, OnClickListener {
+
+ public static final String EXTRA_KEY_ITEMS = "items";
+
+ /**
+ * A model object for capturing a phone number for a given contact.
+ */
+ static class PhoneItem implements Parcelable, Collapsible<PhoneItem> {
+ long id;
+ String phoneNumber;
+ String accountType;
+ long type;
+ String label;
+
+ public static Parcelable.Creator<PhoneItem> CREATOR = new Creator<PhoneItem>() {
+
+ public PhoneItem[] newArray(int size) {
+ return new PhoneItem[size];
+ }
+
+ public PhoneItem createFromParcel(Parcel source) {
+ PhoneItem item = new PhoneItem();
+ item.id = source.readLong();
+ item.phoneNumber = source.readString();
+ item.accountType = source.readString();
+ item.type = source.readLong();
+ item.label = source.readString();
+ return item;
+ }
+ };
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(id);
+ dest.writeString(phoneNumber);
+ dest.writeString(accountType);
+ dest.writeLong(type);
+ dest.writeString(label);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public boolean collapseWith(PhoneItem phoneItem) {
+ if (!shouldCollapseWith(phoneItem)) {
+ return false;
+ }
+ // Just keep the number and id we already have.
+ return true;
+ }
+
+ public boolean shouldCollapseWith(PhoneItem phoneItem) {
+ if (PhoneNumberUtils.compareStrictly(phoneNumber, phoneItem.phoneNumber)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return phoneNumber;
+ }
+ }
+
+ /**
+ * A list adapter that populates the list of contact's phone numbers.
+ */
+ private class PhoneItemAdapter extends ArrayAdapter<PhoneItem> {
+ private final AccountTypeManager mAccountTypes;
+
+ public PhoneItemAdapter(Context context) {
+ super(context, R.layout.phone_disambig_item, android.R.id.text2);
+ mAccountTypes = AccountTypeManager.getInstance(context);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View view = super.getView(position, convertView, parent);
+
+ PhoneItem item = getItem(position);
+ AccountType accountType = mAccountTypes.getAccountType(item.accountType);
+
+ // Obtain a string representation of the phone type specific to the
+ // account type associated with that phone number
+ TextView typeView = (TextView)view.findViewById(android.R.id.text1);
+ DataKind kind = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
+ if (kind != null) {
+ ContentValues values = new ContentValues();
+ values.put(Phone.TYPE, item.type);
+ values.put(Phone.LABEL, item.label);
+ StringInflater header = mSendTextMessage ? kind.actionAltHeader : kind.actionHeader;
+ typeView.setText(header.inflateUsing(getContext(), values));
+ } else {
+ typeView.setText(R.string.call_other);
+ }
+ return view;
+ }
+ }
+
+ private static final String[] PHONE_NUMBER_PROJECTION = new String[] {
+ Phone._ID,
+ Phone.NUMBER,
+ Phone.IS_SUPER_PRIMARY,
+ RawContacts.ACCOUNT_TYPE,
+ Phone.TYPE,
+ Phone.LABEL
+ };
+
+ private static final String PHONE_NUMBER_SELECTION = Data.MIMETYPE + "='"
+ + Phone.CONTENT_ITEM_TYPE + "' AND " + Phone.NUMBER + " NOT NULL";
+
+ private final Context mContext;
+ private final OnDismissListener mDismissListener;
+ private final boolean mSendTextMessage;
+
+ private CursorLoader mLoader;
+
+ public PhoneNumberInteraction(Context context, boolean sendTextMessage,
+ DialogInterface.OnDismissListener dismissListener) {
+ mContext = context;
+ mSendTextMessage = sendTextMessage;
+ mDismissListener = dismissListener;
+ }
+
+ private void performAction(String phoneNumber) {
+ Intent intent;
+ if (mSendTextMessage) {
+ intent = new Intent(
+ Intent.ACTION_SENDTO, Uri.fromParts("sms", phoneNumber, null));
+ } else {
+ intent = new Intent(
+ Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts("tel", phoneNumber, null));
+
+ }
+ startActivity(intent);
+ }
+
+ /**
+ * Initiates the interaction. This may result in a phone call or sms message started
+ * or a disambiguation dialog to determine which phone number should be used.
+ */
+ public void startInteraction(Uri contactUri) {
+ if (mLoader != null) {
+ mLoader.reset();
+ }
+
+ mLoader = new CursorLoader(mContext,
+ Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY),
+ PHONE_NUMBER_PROJECTION,
+ PHONE_NUMBER_SELECTION,
+ null,
+ null);
+ mLoader.registerListener(0, this);
+ mLoader.startLoading();
+ }
+
+ @Override
+ public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
+ if (cursor == null) {
+ onDismiss();
+ return;
+ }
+
+ ArrayList<PhoneItem> phoneList = new ArrayList<PhoneItem>();
+ String primaryPhone = null;
+ try {
+ while (cursor.moveToNext()) {
+ if (cursor.getInt(cursor.getColumnIndex(Phone.IS_SUPER_PRIMARY)) != 0) {
+ // Found super primary, call it.
+ primaryPhone = cursor.getString(cursor.getColumnIndex(Phone.NUMBER));
+ break;
+ }
+
+ PhoneItem item = new PhoneItem();
+ item.id = cursor.getLong(cursor.getColumnIndex(Data._ID));
+ item.phoneNumber = cursor.getString(cursor.getColumnIndex(Phone.NUMBER));
+ item.accountType =
+ cursor.getString(cursor.getColumnIndex(RawContacts.ACCOUNT_TYPE));
+ item.type = cursor.getInt(cursor.getColumnIndex(Phone.TYPE));
+ item.label = cursor.getString(cursor.getColumnIndex(Phone.LABEL));
+
+ phoneList.add(item);
+ }
+ } finally {
+ cursor.close();
+ }
+
+ if (primaryPhone != null) {
+ performAction(primaryPhone);
+ onDismiss();
+ return;
+ }
+
+ Collapser.collapseList(phoneList);
+
+ if (phoneList.size() == 0) {
+ onDismiss();
+ } else if (phoneList.size() == 1) {
+ onDismiss();
+ performAction(phoneList.get(0).phoneNumber);
+ } else {
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(EXTRA_KEY_ITEMS, phoneList);
+ showDialog(getDialogId(), bundle);
+ }
+ }
+
+ private void onDismiss() {
+ if (mDismissListener != null) {
+ mDismissListener.onDismiss(null);
+ }
+ }
+
+ private int getDialogId() {
+ return mSendTextMessage
+ ? R.id.dialog_phone_number_message_disambiguation
+ : R.id.dialog_phone_number_call_disambiguation;
+ }
+
+ public Dialog onCreateDialog(int id, Bundle bundle) {
+ if (id != getDialogId()) {
+ return null;
+ }
+
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View setPrimaryView = inflater.inflate(R.layout.set_primary_checkbox, null);
+ AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setAdapter(new PhoneItemAdapter(mContext), this)
+ .setView(setPrimaryView)
+ .setTitle(mSendTextMessage
+ ? R.string.sms_disambig_title
+ : R.string.call_disambig_title)
+ .create();
+ dialog.setOnDismissListener(mDismissListener);
+ return dialog;
+ }
+
+ public boolean onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
+ if (id != getDialogId()) {
+ return false;
+ }
+
+ ArrayList<PhoneItem> phoneList = bundle.getParcelableArrayList(EXTRA_KEY_ITEMS);
+
+ AlertDialog alertDialog = (AlertDialog)dialog;
+ PhoneItemAdapter adapter = (PhoneItemAdapter)alertDialog.getListView().getAdapter();
+ adapter.clear();
+ adapter.addAll(phoneList);
+
+ return true;
+ }
+
+ /**
+ * Handles the user selection in the disambiguation dialog.
+ */
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ AlertDialog alertDialog = (AlertDialog)dialog;
+ PhoneItemAdapter adapter = (PhoneItemAdapter)alertDialog.getListView().getAdapter();
+ PhoneItem phoneItem = adapter.getItem(which);
+ if (phoneItem != null) {
+ long id = phoneItem.id;
+ String phone = phoneItem.phoneNumber;
+
+ CheckBox checkBox = (CheckBox)alertDialog.findViewById(R.id.setPrimary);
+ if (checkBox.isChecked()) {
+ makePrimary(id);
+ }
+
+ performAction(phone);
+ }
+ }
+
+ /**
+ * Makes the selected phone number primary.
+ */
+ void makePrimary(long id) {
+ final Intent intent = ContactSaveService.createSetSuperPrimaryIntent(mContext, id);
+ mContext.startService(intent);
+ }
+
+ /* Visible for testing */
+ void showDialog(int dialogId, Bundle bundle) {
+ Activity activity = (Activity)mContext;
+ if (!activity.isFinishing()) {
+ activity.showDialog(dialogId, bundle);
+ }
+ }
+
+ /* Visible for testing */
+ void startActivity(Intent intent) {
+ mContext.startActivity(intent);
+ }
+
+ /* Visible for testing */
+ CursorLoader getLoader() {
+ return mLoader;
+ }
+}
diff --git a/src/com/android/contacts/list/ContactBrowseListContextMenuAdapter.java b/src/com/android/contacts/list/ContactBrowseListContextMenuAdapter.java
new file mode 100644
index 0000000..f2a53ca
--- /dev/null
+++ b/src/com/android/contacts/list/ContactBrowseListContextMenuAdapter.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+import com.android.contacts.util.PhoneCapabilityTester;
+import com.android.contacts.widget.ContextMenuAdapter;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView;
+
+/**
+ * A contextual menu adapter for the basic contact list.
+ */
+public class ContactBrowseListContextMenuAdapter implements ContextMenuAdapter {
+
+ private static final int MENU_ITEM_VIEW_CONTACT = 1;
+ private static final int MENU_ITEM_CALL = 2;
+ private static final int MENU_ITEM_SEND_SMS = 3;
+ private static final int MENU_ITEM_EDIT = 4;
+ private static final int MENU_ITEM_DELETE = 5;
+ private static final int MENU_ITEM_TOGGLE_STAR = 6;
+
+ private static final String TAG = "LightContactBrowserContextMenuAdapter";
+
+ private final ContactBrowseListFragment mContactListFragment;
+
+ public ContactBrowseListContextMenuAdapter(ContactBrowseListFragment fragment) {
+ this.mContactListFragment = fragment;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+ AdapterView.AdapterContextMenuInfo info;
+ try {
+ info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ } catch (ClassCastException e) {
+ Log.wtf(TAG, "Bad menuInfo", e);
+ return;
+ }
+
+ ContactListAdapter adapter = mContactListFragment.getAdapter();
+ int headerViewsCount = mContactListFragment.getListView().getHeaderViewsCount();
+ int position = info.position - headerViewsCount;
+
+ // Setup the menu header
+ menu.setHeaderTitle(adapter.getContactDisplayName(position));
+
+ // View contact details
+ menu.add(0, MENU_ITEM_VIEW_CONTACT, 0, R.string.menu_viewContact);
+
+ if (adapter.getHasPhoneNumber(position)) {
+ final Context context = mContactListFragment.getContext();
+ boolean hasPhoneApp = PhoneCapabilityTester.isPhone(context);
+ boolean hasSmsApp = PhoneCapabilityTester.isSmsIntentRegistered(context);
+ // Calling contact
+ if (hasPhoneApp) menu.add(0, MENU_ITEM_CALL, 0, R.string.menu_call);
+ // Send SMS item
+ if (hasSmsApp) menu.add(0, MENU_ITEM_SEND_SMS, 0, R.string.menu_sendSMS);
+ }
+
+ // Star toggling
+ if (!adapter.isContactStarred(position)) {
+ 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);
+ 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.wtf(TAG, "Bad menuInfo", e);
+ return false;
+ }
+
+ ContactListAdapter adapter = mContactListFragment.getAdapter();
+ int headerViewsCount = mContactListFragment.getListView().getHeaderViewsCount();
+ int position = info.position - headerViewsCount;
+
+ final Uri contactUri = adapter.getContactUri(position);
+ switch (item.getItemId()) {
+ case MENU_ITEM_VIEW_CONTACT: {
+ mContactListFragment.viewContact(contactUri);
+ return true;
+ }
+
+ case MENU_ITEM_TOGGLE_STAR: {
+ if (adapter.isContactStarred(position)) {
+ mContactListFragment.removeFromFavorites(contactUri);
+ } else {
+ mContactListFragment.addToFavorites(contactUri);
+ }
+ return true;
+ }
+
+ case MENU_ITEM_CALL: {
+ mContactListFragment.callContact(contactUri);
+ return true;
+ }
+
+ case MENU_ITEM_SEND_SMS: {
+ mContactListFragment.smsContact(contactUri);
+ return true;
+ }
+
+ case MENU_ITEM_EDIT: {
+ mContactListFragment.editContact(contactUri);
+ return true;
+ }
+
+ case MENU_ITEM_DELETE: {
+ mContactListFragment.deleteContact(contactUri);
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
new file mode 100644
index 0000000..9b99639
--- /dev/null
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -0,0 +1,671 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.common.widget.CompositeCursorAdapter.Partition;
+import com.android.contacts.R;
+import com.android.contacts.widget.AutoScrollListView;
+
+import android.app.Activity;
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Loader;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.PreferenceManager;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Directory;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Fragment containing a contact list used for browsing (as compared to
+ * picking a contact with one of the PICK intents).
+ */
+public abstract class ContactBrowseListFragment extends
+ ContactEntryListFragment<ContactListAdapter> {
+
+ private static final String TAG = "ContactList";
+
+ private static final String KEY_SELECTED_URI = "selectedUri";
+ private static final String KEY_SELECTION_VERIFIED = "selectionVerified";
+ private static final String KEY_FILTER = "filter";
+ private static final String KEY_LAST_SELECTED_POSITION = "lastSelected";
+
+ private static final String PERSISTENT_SELECTION_PREFIX = "defaultContactBrowserSelection";
+
+ /**
+ * The id for a delayed message that triggers automatic selection of the first
+ * found contact in search mode.
+ */
+ private static final int MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT = 1;
+
+ /**
+ * The delay that is used for automatically selecting the first found contact.
+ */
+ private static final int DELAY_AUTOSELECT_FIRST_FOUND_CONTACT_MILLIS = 500;
+
+ /**
+ * The minimum number of characters in the search query that is required
+ * before we automatically select the first found contact.
+ */
+ private static final int AUTOSELECT_FIRST_FOUND_CONTACT_MIN_QUERY_LENGTH = 2;
+
+ private SharedPreferences mPrefs;
+ private Handler mHandler;
+
+ private boolean mStartedLoading;
+ private boolean mSelectionRequired;
+ private boolean mSelectionToScreenRequested;
+ private boolean mSmoothScrollRequested;
+ private boolean mSelectionPersistenceRequested;
+ private Uri mSelectedContactUri;
+ private long mSelectedContactDirectoryId;
+ private String mSelectedContactLookupKey;
+ private long mSelectedContactId;
+ private boolean mSelectionVerified;
+ private int mLastSelectedPosition = -1;
+ private boolean mRefreshingContactUri;
+ private ContactListFilter mFilter;
+ private String mPersistentSelectionPrefix = PERSISTENT_SELECTION_PREFIX;
+
+ protected OnContactBrowserActionListener mListener;
+
+ /**
+ * Refreshes a contact URI: it may have changed as a result of aggregation
+ * activity.
+ */
+ private class ContactUriQueryHandler extends AsyncQueryHandler {
+
+ public ContactUriQueryHandler(ContentResolver cr) {
+ super(cr);
+ }
+
+ public void runQuery() {
+ startQuery(0, mSelectedContactUri, mSelectedContactUri,
+ new String[] { Contacts._ID, Contacts.LOOKUP_KEY }, null, null, null);
+ }
+
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor data) {
+ long contactId = 0;
+ String lookupKey = null;
+ if (data != null) {
+ if (data.moveToFirst()) {
+ contactId = data.getLong(0);
+ lookupKey = data.getString(1);
+ }
+ data.close();
+ }
+
+ if (!cookie.equals(mSelectedContactUri)) {
+ return;
+ }
+
+ Uri uri;
+ if (contactId != 0 && lookupKey != null) {
+ uri = Contacts.getLookupUri(contactId, lookupKey);
+ } else {
+ uri = null;
+ }
+
+ onContactUriQueryFinished(uri);
+ }
+ }
+
+ private ContactUriQueryHandler mQueryHandler;
+
+ private boolean mDelaySelection;
+
+ private Handler getHandler() {
+ if (mHandler == null) {
+ mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT:
+ selectDefaultContact();
+ break;
+ }
+ }
+ };
+ }
+ return mHandler;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ mQueryHandler = new ContactUriQueryHandler(activity.getContentResolver());
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(activity);
+ restoreFilter();
+ restoreSelectedUri(false);
+ }
+
+ @Override
+ public void setSearchMode(boolean flag) {
+ if (isSearchMode() != flag) {
+ if (!flag) {
+ restoreSelectedUri(true);
+ }
+ super.setSearchMode(flag);
+ }
+ }
+
+ public void setFilter(ContactListFilter filter) {
+ setFilter(filter, true);
+ }
+
+ public void setFilter(ContactListFilter filter, boolean restoreSelectedUri) {
+ if (mFilter == null && filter == null) {
+ return;
+ }
+
+ if (mFilter != null && mFilter.equals(filter)) {
+ return;
+ }
+
+ Log.v(TAG, "New filter: " + filter);
+
+ mFilter = filter;
+ mLastSelectedPosition = -1;
+ saveFilter();
+ if (restoreSelectedUri) {
+ mSelectedContactUri = null;
+ restoreSelectedUri(true);
+ }
+ reloadData();
+ }
+
+ public ContactListFilter getFilter() {
+ return mFilter;
+ }
+
+ @Override
+ public void restoreSavedState(Bundle savedState) {
+ super.restoreSavedState(savedState);
+
+ if (savedState == null) {
+ return;
+ }
+
+ mFilter = savedState.getParcelable(KEY_FILTER);
+ mSelectedContactUri = savedState.getParcelable(KEY_SELECTED_URI);
+ mSelectionVerified = savedState.getBoolean(KEY_SELECTION_VERIFIED);
+ mLastSelectedPosition = savedState.getInt(KEY_LAST_SELECTED_POSITION);
+ parseSelectedContactUri();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putParcelable(KEY_FILTER, mFilter);
+ outState.putParcelable(KEY_SELECTED_URI, mSelectedContactUri);
+ outState.putBoolean(KEY_SELECTION_VERIFIED, mSelectionVerified);
+ outState.putInt(KEY_LAST_SELECTED_POSITION, mLastSelectedPosition);
+ }
+
+ protected void refreshSelectedContactUri() {
+ if (mQueryHandler == null) {
+ return;
+ }
+
+ mQueryHandler.cancelOperation(0);
+
+ if (!isSelectionVisible()) {
+ return;
+ }
+
+ mRefreshingContactUri = true;
+
+ if (mSelectedContactUri == null) {
+ onContactUriQueryFinished(null);
+ return;
+ }
+
+ if (mSelectedContactDirectoryId != Directory.DEFAULT
+ && mSelectedContactDirectoryId != Directory.LOCAL_INVISIBLE) {
+ onContactUriQueryFinished(mSelectedContactUri);
+ } else {
+ mQueryHandler.runQuery();
+ }
+ }
+
+ protected void onContactUriQueryFinished(Uri uri) {
+ mRefreshingContactUri = false;
+ mSelectedContactUri = uri;
+ parseSelectedContactUri();
+ checkSelection();
+ }
+
+ @Override
+ protected void prepareEmptyView() {
+ if (isSearchMode()) {
+ return;
+ } else if (isSyncActive()) {
+ if (hasIccCard()) {
+ setEmptyText(R.string.noContactsHelpTextWithSync);
+ } else {
+ setEmptyText(R.string.noContactsNoSimHelpTextWithSync);
+ }
+ } else {
+ if (hasIccCard()) {
+ setEmptyText(R.string.noContactsHelpText);
+ } else {
+ setEmptyText(R.string.noContactsNoSimHelpText);
+ }
+ }
+ }
+
+ public Uri getSelectedContactUri() {
+ return mSelectedContactUri;
+ }
+
+ /**
+ * Sets the new selection for the list.
+ */
+ public void setSelectedContactUri(Uri uri) {
+ setSelectedContactUri(uri, true, true, true, false);
+ }
+
+ @Override
+ public void setQueryString(String queryString, boolean delaySelection) {
+ mDelaySelection = delaySelection;
+ super.setQueryString(queryString, delaySelection);
+ }
+
+ /**
+ * Sets the new contact selection.
+ *
+ * @param uri the new selection
+ * @param required if true, we need to check if the selection is present in
+ * the list and if not notify the listener so that it can load a
+ * different list
+ * @param smoothScroll if true, the UI will roll smoothly to the new
+ * selection
+ * @param persistent if true, the selection will be stored in shared
+ * preferences.
+ * @param willReloadData if true, the selection will be remembered but not
+ * actually shown, because we are expecting that the data will be
+ * reloaded momentarily
+ */
+ private void setSelectedContactUri(Uri uri, boolean required, boolean smoothScroll,
+ boolean persistent, boolean willReloadData) {
+ mSmoothScrollRequested = smoothScroll;
+ mSelectionToScreenRequested = true;
+
+ if ((mSelectedContactUri == null && uri != null)
+ || (mSelectedContactUri != null && !mSelectedContactUri.equals(uri))) {
+ mSelectionVerified = false;
+ mSelectionRequired = required;
+ mSelectionPersistenceRequested = persistent;
+ mSelectedContactUri = uri;
+ parseSelectedContactUri();
+
+ if (!willReloadData) {
+ // Configure the adapter to show the selection based on the
+ // lookup key extracted from the URI
+ ContactListAdapter adapter = getAdapter();
+ if (adapter != null) {
+ adapter.setSelectedContact(mSelectedContactDirectoryId,
+ mSelectedContactLookupKey, mSelectedContactId);
+ getListView().invalidateViews();
+ }
+ }
+
+ // Also, launch a loader to pick up a new lookup URI in case it has changed
+ refreshSelectedContactUri();
+ }
+ }
+
+ private void parseSelectedContactUri() {
+ if (mSelectedContactUri != null) {
+ String directoryParam =
+ mSelectedContactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
+ mSelectedContactDirectoryId = TextUtils.isEmpty(directoryParam) ? Directory.DEFAULT
+ : Long.parseLong(directoryParam);
+ if (mSelectedContactUri.toString().startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
+ List<String> pathSegments = mSelectedContactUri.getPathSegments();
+ mSelectedContactLookupKey = Uri.encode(pathSegments.get(2));
+ if (pathSegments.size() == 4) {
+ mSelectedContactId = ContentUris.parseId(mSelectedContactUri);
+ }
+ } else if (mSelectedContactUri.toString().startsWith(Contacts.CONTENT_URI.toString()) &&
+ mSelectedContactUri.getPathSegments().size() >= 2) {
+ mSelectedContactLookupKey = null;
+ mSelectedContactId = ContentUris.parseId(mSelectedContactUri);
+ } else {
+ Log.e(TAG, "Unsupported contact URI: " + mSelectedContactUri);
+ mSelectedContactLookupKey = null;
+ mSelectedContactId = 0;
+ }
+
+ } else {
+ mSelectedContactDirectoryId = Directory.DEFAULT;
+ mSelectedContactLookupKey = null;
+ mSelectedContactId = 0;
+ }
+ }
+
+ @Override
+ protected void configureAdapter() {
+ super.configureAdapter();
+
+ ContactListAdapter adapter = getAdapter();
+ if (adapter == null) {
+ return;
+ }
+
+ if (!isSearchMode() && mFilter != null) {
+ adapter.setFilter(mFilter);
+ if (mSelectionRequired
+ || mFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
+ adapter.setSelectedContact(
+ mSelectedContactDirectoryId, mSelectedContactLookupKey, mSelectedContactId);
+ }
+ }
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ super.onLoadFinished(loader, data);
+ mSelectionVerified = false;
+
+ // Refresh the currently selected lookup in case it changed while we were sleeping
+ refreshSelectedContactUri();
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ }
+
+ private void checkSelection() {
+ if (mSelectionVerified) {
+ return;
+ }
+
+ if (mRefreshingContactUri) {
+ return;
+ }
+
+ if (isLoadingDirectoryList()) {
+ return;
+ }
+
+ ContactListAdapter adapter = getAdapter();
+ if (adapter == null) {
+ return;
+ }
+
+ boolean directoryLoading = true;
+ int count = adapter.getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = adapter.getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ DirectoryPartition directory = (DirectoryPartition) partition;
+ if (directory.getDirectoryId() == mSelectedContactDirectoryId) {
+ directoryLoading = directory.isLoading();
+ break;
+ }
+ }
+ }
+
+ if (directoryLoading) {
+ return;
+ }
+
+ adapter.setSelectedContact(
+ mSelectedContactDirectoryId, mSelectedContactLookupKey, mSelectedContactId);
+
+ int selectedPosition = adapter.getSelectedContactPosition();
+ if (selectedPosition != -1) {
+ mLastSelectedPosition = selectedPosition;
+ } else {
+ if (isSearchMode()) {
+ if (mDelaySelection) {
+ selectFirstFoundContactAfterDelay();
+ if (mListener != null) {
+ mListener.onSelectionChange();
+ }
+ return;
+ }
+ } else if (mSelectionRequired) {
+ // A specific contact was requested, but it's not in the loaded list.
+
+ // Try reconfiguring and reloading the list that will hopefully contain
+ // the requested contact. Only take one attempt to avoid an infinite loop
+ // in case the contact cannot be found at all.
+ mSelectionRequired = false;
+
+ // If we were looking at a different specific contact, just reload
+ if (mFilter != null
+ && mFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
+ reloadData();
+ } else {
+ // Otherwise, call the listener, which will adjust the filter.
+ notifyInvalidSelection();
+ }
+ return;
+ } else if (mFilter != null
+ && mFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
+ // If we were trying to load a specific contact, but that contact no longer
+ // exists, call the listener, which will adjust the filter.
+ notifyInvalidSelection();
+ return;
+ }
+
+ saveSelectedUri(null);
+ selectDefaultContact();
+ }
+
+ mSelectionRequired = false;
+ mSelectionVerified = true;
+
+ if (mSelectionPersistenceRequested) {
+ saveSelectedUri(mSelectedContactUri);
+ mSelectionPersistenceRequested = false;
+ }
+
+ if (mSelectionToScreenRequested) {
+ requestSelectionToScreen();
+ }
+
+ getListView().invalidateViews();
+
+ if (mListener != null) {
+ mListener.onSelectionChange();
+ }
+ }
+
+ /**
+ * Automatically selects the first found contact in search mode. The selection
+ * is updated after a delay to allow the user to type without to much UI churn
+ * and to save bandwidth on directory queries.
+ */
+ public void selectFirstFoundContactAfterDelay() {
+ Handler handler = getHandler();
+ handler.removeMessages(MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT);
+
+ String queryString = getQueryString();
+ if (queryString != null
+ && queryString.length() >= AUTOSELECT_FIRST_FOUND_CONTACT_MIN_QUERY_LENGTH) {
+ handler.sendEmptyMessageDelayed(MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT,
+ DELAY_AUTOSELECT_FIRST_FOUND_CONTACT_MILLIS);
+ } else {
+ setSelectedContactUri(null, false, false, false, false);
+ }
+ }
+
+ protected void selectDefaultContact() {
+ Uri contactUri = null;
+ if (mLastSelectedPosition != -1) {
+ contactUri = getAdapter().getContactUri(mLastSelectedPosition);
+ }
+
+ if (contactUri == null) {
+ contactUri = getAdapter().getFirstContactUri();
+ }
+
+ setSelectedContactUri(contactUri, false, mSmoothScrollRequested, false, false);
+ }
+
+ protected void requestSelectionToScreen() {
+ int selectedPosition = getAdapter().getSelectedContactPosition();
+ if (selectedPosition != -1) {
+ AutoScrollListView listView = (AutoScrollListView)getListView();
+ listView.requestPositionToScreen(
+ selectedPosition + listView.getHeaderViewsCount(), mSmoothScrollRequested);
+ mSelectionToScreenRequested = false;
+ }
+ }
+
+ @Override
+ public boolean isLoading() {
+ return mRefreshingContactUri || super.isLoading();
+ }
+
+ @Override
+ protected void startLoading() {
+ mStartedLoading = true;
+ mSelectionVerified = false;
+ super.startLoading();
+ }
+
+ public void reloadDataAndSetSelectedUri(Uri uri) {
+ setSelectedContactUri(uri, true, true, true, true);
+ reloadData();
+ }
+
+ @Override
+ public void reloadData() {
+ if (mStartedLoading) {
+ mSelectionVerified = false;
+ mLastSelectedPosition = -1;
+ super.reloadData();
+ }
+ }
+
+ public void setOnContactListActionListener(OnContactBrowserActionListener listener) {
+ mListener = listener;
+ }
+
+ public void createNewContact() {
+ if (mListener != null) mListener.onCreateNewContactAction();
+ }
+
+ public void viewContact(Uri contactUri) {
+ setSelectedContactUri(contactUri, false, false, true, false);
+ if (mListener != null) { mListener.onViewContactAction(contactUri); }
+ }
+
+ public void editContact(Uri contactUri) {
+ if (mListener != null) mListener.onEditContactAction(contactUri);
+ }
+
+ public void deleteContact(Uri contactUri) {
+ if (mListener != null) mListener.onDeleteContactAction(contactUri);
+ }
+
+ public void addToFavorites(Uri contactUri) {
+ if (mListener != null) mListener.onAddToFavoritesAction(contactUri);
+ }
+
+ public void removeFromFavorites(Uri contactUri) {
+ if (mListener != null) mListener.onRemoveFromFavoritesAction(contactUri);
+ }
+
+ public void callContact(Uri contactUri) {
+ if (mListener != null) mListener.onCallContactAction(contactUri);
+ }
+
+ public void smsContact(Uri contactUri) {
+ if (mListener != null) mListener.onSmsContactAction(contactUri);
+ }
+
+ private void notifyInvalidSelection() {
+ if (mListener != null) mListener.onInvalidSelection();
+ }
+
+ @Override
+ protected void finish() {
+ super.finish();
+ if (mListener != null) mListener.onFinishAction();
+ }
+
+ private void saveSelectedUri(Uri contactUri) {
+ if (isSearchMode()) {
+ return;
+ }
+
+ ContactListFilter.storeToPreferences(mPrefs, mFilter);
+
+ Editor editor = mPrefs.edit();
+ if (contactUri == null) {
+ editor.remove(getPersistentSelectionKey());
+ } else {
+ editor.putString(getPersistentSelectionKey(), contactUri.toString());
+ }
+ editor.apply();
+ }
+
+ private void restoreSelectedUri(boolean willReloadData) {
+ // The meaning of mSelectionRequired is that we need to show some
+ // selection other than the previous selection saved in shared preferences
+ if (mSelectionRequired) {
+ return;
+ }
+
+ String selectedUri = mPrefs.getString(getPersistentSelectionKey(), null);
+ if (selectedUri == null) {
+ setSelectedContactUri(null, false, false, false, willReloadData);
+ } else {
+ setSelectedContactUri(Uri.parse(selectedUri), false, false, false, willReloadData);
+ }
+ }
+
+ private void saveFilter() {
+ ContactListFilter.storeToPreferences(mPrefs, mFilter);
+ }
+
+ private void restoreFilter() {
+ mFilter = ContactListFilter.restoreFromPreferences(mPrefs);
+ if (mFilter == null) {
+ mFilter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+ }
+ }
+
+ private String getPersistentSelectionKey() {
+ if (mFilter == null) {
+ return mPersistentSelectionPrefix;
+ } else {
+ return mPersistentSelectionPrefix + "-" + mFilter.getId();
+ }
+ }
+
+ public boolean isOptionsMenuChanged() {
+ // This fragment does not have an option menu of its own
+ return false;
+ }
+}
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
new file mode 100644
index 0000000..44e09b0
--- /dev/null
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -0,0 +1,546 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+import com.android.contacts.widget.IndexerListAdapter;
+import com.android.contacts.widget.TextWithHighlightingFactory;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.ContactCounts;
+import android.provider.ContactsContract.Directory;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.HashSet;
+
+/**
+ * Common base class for various contact-related lists, e.g. contact list, phone number list
+ * etc.
+ */
+public abstract class ContactEntryListAdapter extends IndexerListAdapter {
+
+ private static final String TAG = "ContactEntryListAdapter";
+
+ /**
+ * Indicates whether the {@link Directory#LOCAL_INVISIBLE} directory should
+ * be included in the search.
+ */
+ private static final boolean LOCAL_INVISIBLE_DIRECTORY_ENABLED = false;
+
+ /**
+ * The animation is used here to allocate animated name text views.
+ */
+ private TextWithHighlightingFactory mTextWithHighlightingFactory;
+ private int mDisplayOrder;
+ private int mSortOrder;
+ private boolean mNameHighlightingEnabled;
+ private boolean mDataRestrictedByCallingPackage;
+
+ private boolean mDisplayPhotos;
+ private boolean mQuickContactEnabled;
+ private ContactPhotoLoader mPhotoLoader;
+
+ private String mQueryString;
+ private char[] mUpperCaseQueryString;
+ private boolean mSearchMode;
+ private int mDirectorySearchMode;
+ private int mDirectoryResultLimit = Integer.MAX_VALUE;
+
+ private boolean mLoading = true;
+ private boolean mEmptyListEnabled = true;
+
+ private boolean mSelectionVisible;
+
+ public ContactEntryListAdapter(Context context) {
+ super(context);
+ addPartitions();
+ }
+
+ @Override
+ protected View createPinnedSectionHeaderView(Context context, ViewGroup parent) {
+ return new ContactListPinnedHeaderView(context, null);
+ }
+
+ @Override
+ protected void setPinnedSectionTitle(View pinnedHeaderView, String title) {
+ ((ContactListPinnedHeaderView)pinnedHeaderView).setSectionHeader(title);
+ }
+
+ protected void addPartitions() {
+ addPartition(createDefaultDirectoryPartition());
+ }
+
+ protected DirectoryPartition createDefaultDirectoryPartition() {
+ DirectoryPartition partition = new DirectoryPartition(true, true);
+ partition.setDirectoryId(Directory.DEFAULT);
+ partition.setDirectoryType(getContext().getString(R.string.contactsList));
+ partition.setPriorityDirectory(true);
+ partition.setPhotoSupported(true);
+ return partition;
+ }
+
+ private int getPartitionByDirectoryId(long id) {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ if (((DirectoryPartition)partition).getDirectoryId() == id) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ public abstract String getContactDisplayName(int position);
+ public abstract void configureLoader(CursorLoader loader, long directoryId);
+
+ /**
+ * Marks all partitions as "loading"
+ */
+ public void onDataReload() {
+ boolean notify = false;
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ DirectoryPartition directoryPartition = (DirectoryPartition)partition;
+ if (!directoryPartition.isLoading()) {
+ notify = true;
+ }
+ directoryPartition.setStatus(DirectoryPartition.STATUS_NOT_LOADED);
+ }
+ }
+ if (notify) {
+ notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void clearPartitions() {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ DirectoryPartition directoryPartition = (DirectoryPartition)partition;
+ directoryPartition.setStatus(DirectoryPartition.STATUS_NOT_LOADED);
+ }
+ }
+ super.clearPartitions();
+ }
+
+ public boolean isSearchMode() {
+ return mSearchMode;
+ }
+
+ public void setSearchMode(boolean flag) {
+ mSearchMode = flag;
+ }
+
+ public String getQueryString() {
+ return mQueryString;
+ }
+
+ public void setQueryString(String queryString) {
+ mQueryString = queryString;
+ if (TextUtils.isEmpty(queryString)) {
+ mUpperCaseQueryString = null;
+ } else {
+ mUpperCaseQueryString = queryString.toUpperCase().toCharArray();
+ }
+ }
+
+ public char[] getUpperCaseQueryString() {
+ return mUpperCaseQueryString;
+ }
+
+ public int getDirectorySearchMode() {
+ return mDirectorySearchMode;
+ }
+
+ public void setDirectorySearchMode(int mode) {
+ mDirectorySearchMode = mode;
+ }
+
+ public int getDirectoryResultLimit() {
+ return mDirectoryResultLimit;
+ }
+
+ public void setDirectoryResultLimit(int limit) {
+ this.mDirectoryResultLimit = limit;
+ }
+
+ public int getContactNameDisplayOrder() {
+ return mDisplayOrder;
+ }
+
+ public void setContactNameDisplayOrder(int displayOrder) {
+ mDisplayOrder = displayOrder;
+ }
+
+ public int getSortOrder() {
+ return mSortOrder;
+ }
+
+ public void setSortOrder(int sortOrder) {
+ mSortOrder = sortOrder;
+ }
+
+ public void setNameHighlightingEnabled(boolean flag) {
+ mNameHighlightingEnabled = flag;
+ }
+
+ public boolean isNameHighlightingEnabled() {
+ return mNameHighlightingEnabled;
+ }
+
+ public void setTextWithHighlightingFactory(TextWithHighlightingFactory factory) {
+ mTextWithHighlightingFactory = factory;
+ }
+
+ protected TextWithHighlightingFactory getTextWithHighlightingFactory() {
+ return mTextWithHighlightingFactory;
+ }
+
+ public void setPhotoLoader(ContactPhotoLoader photoLoader) {
+ mPhotoLoader = photoLoader;
+ }
+
+ protected ContactPhotoLoader getPhotoLoader() {
+ return mPhotoLoader;
+ }
+
+ public boolean getDisplayPhotos() {
+ return mDisplayPhotos;
+ }
+
+ public void setDisplayPhotos(boolean displayPhotos) {
+ mDisplayPhotos = displayPhotos;
+ }
+
+ public boolean isEmptyListEnabled() {
+ return mEmptyListEnabled;
+ }
+
+ public void setEmptyListEnabled(boolean flag) {
+ mEmptyListEnabled = flag;
+ }
+
+ public boolean isSelectionVisible() {
+ return mSelectionVisible;
+ }
+
+ public void setSelectionVisible(boolean flag) {
+ this.mSelectionVisible = flag;
+ }
+
+ public boolean isQuickContactEnabled() {
+ return mQuickContactEnabled;
+ }
+
+ public void setQuickContactEnabled(boolean quickContactEnabled) {
+ mQuickContactEnabled = quickContactEnabled;
+ }
+
+ public boolean isDataRestrictedByCallingPackage() {
+ return mDataRestrictedByCallingPackage;
+ }
+
+ public void setDataRestrictedByCallingPackage(boolean flag) {
+ mDataRestrictedByCallingPackage = flag;
+ }
+
+ /**
+ * Adds a parameter to the URI that ensures that only unrestricted data
+ * is included in the list, if {@link #isDataRestrictedByCallingPackage()} is true.
+ */
+ protected Uri applyDataRestriction(Uri uri) {
+ if (!mDataRestrictedByCallingPackage) {
+ return uri;
+ }
+
+ return applyDataRestriction(uri.buildUpon()).build();
+ }
+
+ /**
+ * See {@link #applyDataRestriction(Uri)}.
+ */
+ protected Uri.Builder applyDataRestriction(Uri.Builder builder) {
+ if (!mDataRestrictedByCallingPackage) {
+ return builder;
+ }
+
+ String callingPackage = ((Activity)getContext()).getCallingPackage();
+ if (!TextUtils.isEmpty(callingPackage)) {
+ return builder.appendQueryParameter(
+ ContactsContract.REQUESTING_PACKAGE_PARAM_KEY, callingPackage);
+ }
+
+ return builder;
+ }
+
+ public void configureDirectoryLoader(DirectoryListLoader loader) {
+ loader.setDirectorySearchMode(mDirectorySearchMode);
+ loader.setLocalInvisibleDirectoryEnabled(LOCAL_INVISIBLE_DIRECTORY_ENABLED);
+ }
+
+ /**
+ * Updates partitions according to the directory meta-data contained in the supplied
+ * cursor. Takes ownership of the cursor and will close it.
+ */
+ public void changeDirectories(Cursor cursor) {
+ HashSet<Long> directoryIds = new HashSet<Long>();
+
+ int idColumnIndex = cursor.getColumnIndex(Directory._ID);
+ int directoryTypeColumnIndex = cursor.getColumnIndex(DirectoryListLoader.DIRECTORY_TYPE);
+ int displayNameColumnIndex = cursor.getColumnIndex(Directory.DISPLAY_NAME);
+ int photoSupportColumnIndex = cursor.getColumnIndex(Directory.PHOTO_SUPPORT);
+
+ // TODO preserve the order of partition to match those of the cursor
+ // Phase I: add new directories
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ long id = cursor.getLong(idColumnIndex);
+ directoryIds.add(id);
+ if (getPartitionByDirectoryId(id) == -1) {
+ DirectoryPartition partition = new DirectoryPartition(false, true);
+ partition.setDirectoryId(id);
+ partition.setDirectoryType(cursor.getString(directoryTypeColumnIndex));
+ partition.setDisplayName(cursor.getString(displayNameColumnIndex));
+ int photoSupport = cursor.getInt(photoSupportColumnIndex);
+ partition.setPhotoSupported(photoSupport == Directory.PHOTO_SUPPORT_THUMBNAIL_ONLY
+ || photoSupport == Directory.PHOTO_SUPPORT_FULL);
+ addPartition(partition);
+ }
+ }
+
+ // Phase II: remove deleted directories
+ int count = getPartitionCount();
+ for (int i = count; --i >= 0; ) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ long id = ((DirectoryPartition)partition).getDirectoryId();
+ if (!directoryIds.contains(id)) {
+ removePartition(i);
+ }
+ }
+ }
+
+ invalidate();
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void changeCursor(int partitionIndex, Cursor cursor) {
+ if (partitionIndex >= getPartitionCount()) {
+ // There is no partition for this data
+ return;
+ }
+
+ Partition partition = getPartition(partitionIndex);
+ if (partition instanceof DirectoryPartition) {
+ ((DirectoryPartition)partition).setStatus(DirectoryPartition.STATUS_LOADED);
+ }
+
+ if (mDisplayPhotos && mPhotoLoader != null && isPhotoSupported(partitionIndex)) {
+ mPhotoLoader.refreshCache();
+ }
+
+ super.changeCursor(partitionIndex, cursor);
+
+ if (isSectionHeaderDisplayEnabled() && partitionIndex == getIndexedPartition()) {
+ updateIndexer(cursor);
+ }
+ }
+
+ public void changeCursor(Cursor cursor) {
+ changeCursor(0, cursor);
+ }
+
+ /**
+ * Updates the indexer, which is used to produce section headers.
+ */
+ private void updateIndexer(Cursor cursor) {
+ if (cursor == null) {
+ setIndexer(null);
+ return;
+ }
+
+ Bundle bundle = cursor.getExtras();
+ if (bundle.containsKey(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES)) {
+ String sections[] =
+ bundle.getStringArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
+ int counts[] = bundle.getIntArray(ContactCounts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
+ setIndexer(new ContactsSectionIndexer(sections, counts));
+ } else {
+ setIndexer(null);
+ }
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ // We need a separate view type for each item type, plus another one for
+ // each type with header, plus one for "other".
+ return getItemViewTypeCount() * 2 + 1;
+ }
+
+ @Override
+ public int getItemViewType(int partitionIndex, int position) {
+ int type = super.getItemViewType(partitionIndex, position);
+ if (isSectionHeaderDisplayEnabled() && partitionIndex == getIndexedPartition()) {
+ Placement placement = getItemPlacementInSection(position);
+ return placement.firstInSection ? type : getItemViewTypeCount() + type;
+ } else {
+ return type;
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ // TODO
+// if (contactsListActivity.mProviderStatus != ProviderStatus.STATUS_NORMAL) {
+// return true;
+// }
+
+ if (!mEmptyListEnabled) {
+ return false;
+ } else if (isSearchMode()) {
+ return TextUtils.isEmpty(getQueryString());
+ } else if (mLoading) {
+ // We don't want the empty state to show when loading.
+ return false;
+ } else {
+ return super.isEmpty();
+ }
+ }
+
+ public boolean isLoading() {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition
+ && ((DirectoryPartition) partition).isLoading()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean areAllPartitionsEmpty() {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ if (!isPartitionEmpty(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Changes visibility parameters for the default directory partition.
+ */
+ public void configureDefaultPartition(boolean showIfEmpty, boolean hasHeader) {
+ int defaultPartitionIndex = -1;
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition &&
+ ((DirectoryPartition)partition).getDirectoryId() == Directory.DEFAULT) {
+ defaultPartitionIndex = i;
+ break;
+ }
+ }
+ if (defaultPartitionIndex != -1) {
+ setShowIfEmpty(defaultPartitionIndex, showIfEmpty);
+ setHasHeader(defaultPartitionIndex, hasHeader);
+ }
+ }
+
+ @Override
+ protected View newHeaderView(Context context, int partition, Cursor cursor,
+ ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ return inflater.inflate(R.layout.directory_header, parent, false);
+ }
+
+ @Override
+ protected void bindHeaderView(View view, int partitionIndex, Cursor cursor) {
+ Partition partition = getPartition(partitionIndex);
+ if (!(partition instanceof DirectoryPartition)) {
+ return;
+ }
+
+ DirectoryPartition directoryPartition = (DirectoryPartition)partition;
+ long directoryId = directoryPartition.getDirectoryId();
+ TextView labelTextView = (TextView)view.findViewById(R.id.label);
+ TextView displayNameTextView = (TextView)view.findViewById(R.id.display_name);
+ if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
+ labelTextView.setText(R.string.local_search_label);
+ displayNameTextView.setText(null);
+ } else {
+ labelTextView.setText(R.string.directory_search_label);
+ String directoryName = directoryPartition.getDisplayName();
+ String displayName = !TextUtils.isEmpty(directoryName)
+ ? directoryName
+ : directoryPartition.getDirectoryType();
+ displayNameTextView.setText(displayName);
+ }
+
+ TextView countText = (TextView)view.findViewById(R.id.count);
+ if (directoryPartition.isLoading()) {
+ countText.setText(R.string.search_results_searching);
+ } else {
+ int count = cursor == null ? 0 : cursor.getCount();
+ if (directoryId != Directory.DEFAULT && directoryId != Directory.LOCAL_INVISIBLE
+ && count >= getDirectoryResultLimit()) {
+ countText.setText(mContext.getString(
+ R.string.foundTooManyContacts, getDirectoryResultLimit()));
+ } else {
+ countText.setText(getQuantityText(
+ count, R.string.listFoundAllContactsZero, R.plurals.searchFoundContacts));
+ }
+ }
+ }
+
+ // TODO: fix PluralRules to handle zero correctly and use Resources.getQuantityText directly
+ public String getQuantityText(int count, int zeroResourceId, int pluralResourceId) {
+ if (count == 0) {
+ return getContext().getString(zeroResourceId);
+ } else {
+ String format = getContext().getResources()
+ .getQuantityText(pluralResourceId, count).toString();
+ return String.format(format, count);
+ }
+ }
+
+ public boolean isPhotoSupported(int partitionIndex) {
+ Partition partition = getPartition(partitionIndex);
+ if (partition instanceof DirectoryPartition) {
+ return ((DirectoryPartition) partition).isPhotoSupported();
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
new file mode 100644
index 0000000..70eb0db
--- /dev/null
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -0,0 +1,951 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.common.widget.CompositeCursorAdapter.Partition;
+import com.android.contacts.ContactListEmptyView;
+import com.android.contacts.ContactsSearchManager;
+import com.android.contacts.R;
+import com.android.contacts.preference.ContactsPreferences;
+import com.android.contacts.widget.ContextMenuAdapter;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.IContentService;
+import android.content.Intent;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Directory;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * Common base class for various contact-related list fragments.
+ */
+public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter>
+ extends Fragment
+ implements OnItemClickListener, OnScrollListener, OnFocusChangeListener, OnTouchListener,
+ LoaderCallbacks<Cursor> {
+ private static final String TAG = "ContactEntryListFragment";
+
+ // TODO: Make this protected. This should not be used from the ContactBrowserActivity but
+ // instead use the new startActivityWithResultFromFragment API
+ public static final int ACTIVITY_REQUEST_CODE_PICKER = 1;
+
+ private static final String KEY_LIST_STATE = "liststate";
+ private static final String KEY_SECTION_HEADER_DISPLAY_ENABLED = "sectionHeaderDisplayEnabled";
+ private static final String KEY_PHOTO_LOADER_ENABLED = "photoLoaderEnabled";
+ private static final String KEY_QUICK_CONTACT_ENABLED = "quickContactEnabled";
+ private static final String KEY_SEARCH_MODE = "searchMode";
+ private static final String KEY_VISIBLE_SCROLLBAR_ENABLED = "visibleScrollbarEnabled";
+ private static final String KEY_SCROLLBAR_POSITION = "scrollbarPosition";
+ private static final String KEY_QUERY_STRING = "queryString";
+ private static final String KEY_DIRECTORY_SEARCH_MODE = "directorySearchMode";
+ private static final String KEY_SELECTION_VISIBLE = "selectionVisible";
+ private static final String KEY_REQUEST = "request";
+ private static final String KEY_LEGACY_COMPATIBILITY = "legacyCompatibility";
+ private static final String KEY_DIRECTORY_RESULT_LIMIT = "directoryResultLimit";
+
+ private static final String DIRECTORY_ID_ARG_KEY = "directoryId";
+
+ private static final int DIRECTORY_LOADER_ID = -1;
+
+ private static final int DIRECTORY_SEARCH_DELAY_MILLIS = 300;
+ private static final int DIRECTORY_SEARCH_MESSAGE = 1;
+
+ private static final int DEFAULT_DIRECTORY_RESULT_LIMIT = 20;
+
+ private boolean mSectionHeaderDisplayEnabled;
+ private boolean mPhotoLoaderEnabled;
+ private boolean mQuickContactEnabled = true;
+ private boolean mSearchMode;
+ private boolean mVisibleScrollbarEnabled;
+ private int mVerticalScrollbarPosition = View.SCROLLBAR_POSITION_RIGHT;
+ private String mQueryString;
+ private int mDirectorySearchMode = DirectoryListLoader.SEARCH_MODE_NONE;
+ private boolean mSelectionVisible;
+ private ContactsRequest mRequest;
+ private boolean mLegacyCompatibility;
+
+ private boolean mEnabled = true;
+
+ private T mAdapter;
+ private View mView;
+ private ListView mListView;
+
+ /**
+ * Used for keeping track of the scroll state of the list.
+ */
+ private Parcelable mListState;
+
+ private int mDisplayOrder;
+ private int mSortOrder;
+ private int mDirectoryResultLimit = DEFAULT_DIRECTORY_RESULT_LIMIT;
+
+ private ContextMenuAdapter mContextMenuAdapter;
+ private ContactPhotoLoader mPhotoLoader;
+ private ContactListEmptyView mEmptyView;
+ private ProviderStatusLoader mProviderStatusLoader;
+ private ContactsPreferences mContactsPrefs;
+
+ private boolean mForceLoad;
+
+ private static final int STATUS_NOT_LOADED = 0;
+ private static final int STATUS_LOADING = 1;
+ private static final int STATUS_LOADED = 2;
+
+ private int mDirectoryListStatus = STATUS_NOT_LOADED;
+
+ /**
+ * Indicates whether we are doing the initial complete load of data (false) or
+ * a refresh caused by a change notification (true)
+ */
+ private boolean mLoadPriorityDirectoriesOnly;
+
+ private Context mContext;
+
+ private LoaderManager mLoaderManager;
+
+ private Handler mDelayedDirectorySearchHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == DIRECTORY_SEARCH_MESSAGE) {
+ loadDirectoryPartition(msg.arg1, (DirectoryPartition) msg.obj);
+ }
+ }
+ };
+
+
+ protected abstract View inflateView(LayoutInflater inflater, ViewGroup container);
+ protected abstract T createListAdapter();
+
+ /**
+ * @param position Please note that the position is already adjusted for
+ * header views, so "0" means the first list item below header
+ * views.
+ */
+ protected abstract void onItemClick(int position, long id);
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ setContext(activity);
+ setLoaderManager(super.getLoaderManager());
+ }
+
+ /**
+ * Sets a context for the fragment in the unit test environment.
+ */
+ public void setContext(Context context) {
+ mContext = context;
+ configurePhotoLoader();
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public void setEnabled(boolean enabled) {
+ if (mEnabled != enabled) {
+ mEnabled = enabled;
+ if (mAdapter != null) {
+ if (mEnabled) {
+ reloadData();
+ } else {
+ mAdapter.clearPartitions();
+ }
+ }
+ }
+ }
+
+ /**
+ * Overrides a loader manager for use in unit tests.
+ */
+ public void setLoaderManager(LoaderManager loaderManager) {
+ mLoaderManager = loaderManager;
+ }
+
+ @Override
+ public LoaderManager getLoaderManager() {
+ return mLoaderManager;
+ }
+
+ public T getAdapter() {
+ return mAdapter;
+ }
+
+ @Override
+ public View getView() {
+ return mView;
+ }
+
+ public ListView getListView() {
+ return mListView;
+ }
+
+ public ContactListEmptyView getEmptyView() {
+ return mEmptyView;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_SECTION_HEADER_DISPLAY_ENABLED, mSectionHeaderDisplayEnabled);
+ outState.putBoolean(KEY_PHOTO_LOADER_ENABLED, mPhotoLoaderEnabled);
+ outState.putBoolean(KEY_QUICK_CONTACT_ENABLED, mQuickContactEnabled);
+ outState.putBoolean(KEY_SEARCH_MODE, mSearchMode);
+ outState.putBoolean(KEY_VISIBLE_SCROLLBAR_ENABLED, mVisibleScrollbarEnabled);
+ outState.putInt(KEY_SCROLLBAR_POSITION, mVerticalScrollbarPosition);
+ outState.putInt(KEY_DIRECTORY_SEARCH_MODE, mDirectorySearchMode);
+ outState.putBoolean(KEY_SELECTION_VISIBLE, mSelectionVisible);
+ outState.putBoolean(KEY_LEGACY_COMPATIBILITY, mLegacyCompatibility);
+ outState.putString(KEY_QUERY_STRING, mQueryString);
+ outState.putInt(KEY_DIRECTORY_RESULT_LIMIT, mDirectoryResultLimit);
+ outState.putParcelable(KEY_REQUEST, mRequest);
+
+ if (mListView != null) {
+ mListState = mListView.onSaveInstanceState();
+ outState.putParcelable(KEY_LIST_STATE, mListState);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+ mContactsPrefs = new ContactsPreferences(mContext);
+ restoreSavedState(savedState);
+ }
+
+ public void restoreSavedState(Bundle savedState) {
+ if (savedState == null) {
+ return;
+ }
+
+ mSectionHeaderDisplayEnabled = savedState.getBoolean(KEY_SECTION_HEADER_DISPLAY_ENABLED);
+ mPhotoLoaderEnabled = savedState.getBoolean(KEY_PHOTO_LOADER_ENABLED);
+ mQuickContactEnabled = savedState.getBoolean(KEY_QUICK_CONTACT_ENABLED);
+ mSearchMode = savedState.getBoolean(KEY_SEARCH_MODE);
+ mVisibleScrollbarEnabled = savedState.getBoolean(KEY_VISIBLE_SCROLLBAR_ENABLED);
+ mVerticalScrollbarPosition = savedState.getInt(KEY_SCROLLBAR_POSITION);
+ mDirectorySearchMode = savedState.getInt(KEY_DIRECTORY_SEARCH_MODE);
+ mSelectionVisible = savedState.getBoolean(KEY_SELECTION_VISIBLE);
+ mLegacyCompatibility = savedState.getBoolean(KEY_LEGACY_COMPATIBILITY);
+ mQueryString = savedState.getString(KEY_QUERY_STRING);
+ mDirectoryResultLimit = savedState.getInt(KEY_DIRECTORY_RESULT_LIMIT);
+ mRequest = savedState.getParcelable(KEY_REQUEST);
+
+ // Retrieve list state. This will be applied in onLoadFinished
+ mListState = savedState.getParcelable(KEY_LIST_STATE);
+ }
+
+ /**
+ * Returns the parsed intent that started the activity hosting this fragment.
+ */
+ public ContactsRequest getContactsRequest() {
+ return mRequest;
+ }
+
+ /**
+ * Sets a parsed intent that started the activity hosting this fragment.
+ */
+ public void setContactsRequest(ContactsRequest request) {
+ mRequest = request;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ mContactsPrefs.registerChangeListener(mPreferencesChangeListener);
+
+ if (mProviderStatusLoader == null) {
+ mProviderStatusLoader = new ProviderStatusLoader(mContext);
+ }
+
+ mForceLoad = loadPreferences();
+
+ mDirectoryListStatus = STATUS_NOT_LOADED;
+ mLoadPriorityDirectoriesOnly = true;
+
+ startLoading();
+ }
+
+ protected void startLoading() {
+ if (mAdapter == null) {
+ // The method was called before the fragment was started
+ return;
+ }
+
+ configureAdapter();
+ int partitionCount = mAdapter.getPartitionCount();
+ for (int i = 0; i < partitionCount; i++) {
+ Partition partition = mAdapter.getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ DirectoryPartition directoryPartition = (DirectoryPartition)partition;
+ if (directoryPartition.getStatus() == DirectoryPartition.STATUS_NOT_LOADED) {
+ if (directoryPartition.isPriorityDirectory() || !mLoadPriorityDirectoriesOnly) {
+ startLoadingDirectoryPartition(i);
+ }
+ }
+ } else {
+ getLoaderManager().initLoader(i, null, this);
+ }
+ }
+
+ // Next time this method is called, we should start loading non-priority directories
+ mLoadPriorityDirectoriesOnly = false;
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ if (id == DIRECTORY_LOADER_ID) {
+ DirectoryListLoader loader = new DirectoryListLoader(mContext);
+ mAdapter.configureDirectoryLoader(loader);
+ return loader;
+ } else {
+ CursorLoader loader = new CursorLoader(mContext, null, null, null, null, null);
+ long directoryId = args != null && args.containsKey(DIRECTORY_ID_ARG_KEY)
+ ? args.getLong(DIRECTORY_ID_ARG_KEY)
+ : Directory.DEFAULT;
+ mAdapter.configureLoader(loader, directoryId);
+ return loader;
+ }
+ }
+
+ private void startLoadingDirectoryPartition(int partitionIndex) {
+ DirectoryPartition partition = (DirectoryPartition)mAdapter.getPartition(partitionIndex);
+ partition.setStatus(DirectoryPartition.STATUS_LOADING);
+ long directoryId = partition.getDirectoryId();
+ if (mForceLoad) {
+ if (directoryId == Directory.DEFAULT) {
+ loadDirectoryPartition(partitionIndex, partition);
+ } else {
+ loadDirectoryPartitionDelayed(partitionIndex, partition);
+ }
+ } else {
+ Bundle args = new Bundle();
+ args.putLong(DIRECTORY_ID_ARG_KEY, directoryId);
+ getLoaderManager().initLoader(partitionIndex, args, this);
+ }
+ }
+
+ /**
+ * Queues up a delayed request to search the specified directory. Since
+ * directory search will likely introduce a lot of network traffic, we want
+ * to wait for a pause in the user's typing before sending a directory request.
+ */
+ private void loadDirectoryPartitionDelayed(int partitionIndex, DirectoryPartition partition) {
+ mDelayedDirectorySearchHandler.removeMessages(DIRECTORY_SEARCH_MESSAGE, partition);
+ Message msg = mDelayedDirectorySearchHandler.obtainMessage(
+ DIRECTORY_SEARCH_MESSAGE, partitionIndex, 0, partition);
+ mDelayedDirectorySearchHandler.sendMessageDelayed(msg, DIRECTORY_SEARCH_DELAY_MILLIS);
+ }
+
+ /**
+ * Loads the directory partition.
+ */
+ protected void loadDirectoryPartition(int partitionIndex, DirectoryPartition partition) {
+ Bundle args = new Bundle();
+ args.putLong(DIRECTORY_ID_ARG_KEY, partition.getDirectoryId());
+ getLoaderManager().restartLoader(partitionIndex, args, this);
+ }
+
+ /**
+ * Cancels all queued directory loading requests.
+ */
+ private void removePendingDirectorySearchRequests() {
+ mDelayedDirectorySearchHandler.removeMessages(DIRECTORY_SEARCH_MESSAGE);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ if (!mEnabled) {
+ return;
+ }
+
+ int loaderId = loader.getId();
+ if (loaderId == DIRECTORY_LOADER_ID) {
+ mDirectoryListStatus = STATUS_LOADED;
+ mAdapter.changeDirectories(data);
+ startLoading();
+ } else {
+ onPartitionLoaded(loaderId, data);
+ if (isSearchMode()) {
+ int directorySearchMode = getDirectorySearchMode();
+ if (directorySearchMode != DirectoryListLoader.SEARCH_MODE_NONE) {
+ if (mDirectoryListStatus == STATUS_NOT_LOADED) {
+ mDirectoryListStatus = STATUS_LOADING;
+ getLoaderManager().initLoader(DIRECTORY_LOADER_ID, null, this);
+ } else {
+ startLoading();
+ }
+ }
+ } else {
+ mDirectoryListStatus = STATUS_NOT_LOADED;
+ getLoaderManager().destroyLoader(DIRECTORY_LOADER_ID);
+ }
+ }
+ }
+
+ public void onLoaderReset(Loader<Cursor> loader) {
+ }
+
+ protected void onPartitionLoaded(int partitionIndex, Cursor data) {
+ if (partitionIndex >= mAdapter.getPartitionCount()) {
+ // When we get unsolicited data, ignore it. This could happen
+ // when we are switching from search mode to the default mode.
+ return;
+ }
+
+ mAdapter.changeCursor(partitionIndex, data);
+ showCount(partitionIndex, data);
+
+ if (!isLoading()) {
+ completeRestoreInstanceState();
+ }
+ }
+
+ public boolean isLoading() {
+ if (mAdapter != null && mAdapter.isLoading()) {
+ return true;
+ }
+
+ if (isLoadingDirectoryList()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean isLoadingDirectoryList() {
+ return isSearchMode() && getDirectorySearchMode() != DirectoryListLoader.SEARCH_MODE_NONE
+ && (mDirectoryListStatus == STATUS_NOT_LOADED
+ || mDirectoryListStatus == STATUS_LOADING);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ mContactsPrefs.unregisterChangeListener();
+ mAdapter.clearPartitions();
+ }
+
+ protected void reloadData() {
+ removePendingDirectorySearchRequests();
+ mAdapter.onDataReload();
+ mLoadPriorityDirectoriesOnly = true;
+ mForceLoad = true;
+ startLoading();
+ }
+
+ /**
+ * Configures the empty view. It is called when we are about to populate
+ * the list with an empty cursor.
+ */
+ protected void prepareEmptyView() {
+ }
+
+ /**
+ * Shows the count of entries included in the list. The default
+ * implementation does nothing.
+ */
+ protected void showCount(int partitionIndex, Cursor data) {
+ }
+
+ /**
+ * Provides logic that dismisses this fragment. The default implementation
+ * does nothing.
+ */
+ protected void finish() {
+ }
+
+ public void setSectionHeaderDisplayEnabled(boolean flag) {
+ if (mSectionHeaderDisplayEnabled != flag) {
+ mSectionHeaderDisplayEnabled = flag;
+ if (mAdapter != null) {
+ mAdapter.setSectionHeaderDisplayEnabled(flag);
+ }
+ configureVerticalScrollbar();
+ }
+ }
+
+ public boolean isSectionHeaderDisplayEnabled() {
+ return mSectionHeaderDisplayEnabled;
+ }
+
+ public void setVisibleScrollbarEnabled(boolean flag) {
+ if (mVisibleScrollbarEnabled != flag) {
+ mVisibleScrollbarEnabled = flag;
+ configureVerticalScrollbar();
+ }
+ }
+
+ public boolean isVisibleScrollbarEnabled() {
+ return mVisibleScrollbarEnabled;
+ }
+
+ public void setVerticalScrollbarPosition(int position) {
+ if (mVerticalScrollbarPosition != position) {
+ mVerticalScrollbarPosition = position;
+ configureVerticalScrollbar();
+ }
+ }
+
+ private void configureVerticalScrollbar() {
+ boolean hasScrollbar = isVisibleScrollbarEnabled() && isSectionHeaderDisplayEnabled();
+
+ if (mListView != null) {
+ mListView.setFastScrollEnabled(hasScrollbar);
+ mListView.setFastScrollAlwaysVisible(hasScrollbar);
+ mListView.setVerticalScrollbarPosition(mVerticalScrollbarPosition);
+ int leftPadding = 0;
+ int rightPadding = 0;
+ if (mVerticalScrollbarPosition == View.SCROLLBAR_POSITION_LEFT) {
+ leftPadding = mContext.getResources().getDimensionPixelOffset(
+ R.dimen.list_visible_scrollbar_padding);
+ } else if (hasScrollbar){
+ rightPadding = mContext.getResources().getDimensionPixelOffset(
+ R.dimen.list_visible_scrollbar_padding);
+ }
+ mListView.setPadding(leftPadding, mListView.getPaddingTop(),
+ rightPadding, mListView.getPaddingBottom());
+ }
+ }
+
+ public void setPhotoLoaderEnabled(boolean flag) {
+ mPhotoLoaderEnabled = flag;
+ configurePhotoLoader();
+ }
+
+ public boolean isPhotoLoaderEnabled() {
+ return mPhotoLoaderEnabled;
+ }
+
+ /**
+ * Returns true if the list is supposed to visually highlight the selected item.
+ */
+ public boolean isSelectionVisible() {
+ return mSelectionVisible;
+ }
+
+ public void setSelectionVisible(boolean flag) {
+ this.mSelectionVisible = flag;
+ }
+
+ public void setQuickContactEnabled(boolean flag) {
+ this.mQuickContactEnabled = flag;
+ }
+
+ public void setSearchMode(boolean flag) {
+ if (mSearchMode != flag) {
+ mSearchMode = flag;
+ setSectionHeaderDisplayEnabled(!mSearchMode);
+
+ if (!flag) {
+ mDirectoryListStatus = STATUS_NOT_LOADED;
+ getLoaderManager().destroyLoader(DIRECTORY_LOADER_ID);
+ }
+
+ if (mAdapter != null) {
+ mAdapter.setPinnedPartitionHeadersEnabled(flag);
+ mAdapter.setSearchMode(flag);
+
+ mAdapter.clearPartitions();
+ if (!flag) {
+ // If we are switching from search to regular display,
+ // remove all directory partitions (except the default one).
+ int count = mAdapter.getPartitionCount();
+ for (int i = count; --i >= 1;) {
+ mAdapter.removePartition(i);
+ }
+ }
+ mAdapter.configureDefaultPartition(false, flag);
+ reloadData();
+ }
+
+ if (mListView != null) {
+ mListView.setFastScrollEnabled(!flag);
+ }
+ }
+ }
+
+ public boolean isSearchMode() {
+ return mSearchMode;
+ }
+
+ public String getQueryString() {
+ return mQueryString;
+ }
+
+ public void setQueryString(String queryString, boolean delaySelection) {
+ if (!TextUtils.equals(mQueryString, queryString)) {
+ mQueryString = queryString;
+ if (mAdapter != null) {
+ mAdapter.setQueryString(queryString);
+ reloadData();
+ }
+ }
+ }
+
+ public int getDirectorySearchMode() {
+ return mDirectorySearchMode;
+ }
+
+ public void setDirectorySearchMode(int mode) {
+ mDirectorySearchMode = mode;
+ }
+
+ public boolean isLegacyCompatibilityMode() {
+ return mLegacyCompatibility;
+ }
+
+ public void setLegacyCompatibilityMode(boolean flag) {
+ mLegacyCompatibility = flag;
+ }
+
+ protected int getContactNameDisplayOrder() {
+ return mDisplayOrder;
+ }
+
+ protected void setContactNameDisplayOrder(int displayOrder) {
+ mDisplayOrder = displayOrder;
+ if (mAdapter != null) {
+ mAdapter.setContactNameDisplayOrder(displayOrder);
+ }
+ }
+
+ public int getSortOrder() {
+ return mSortOrder;
+ }
+
+ public void setSortOrder(int sortOrder) {
+ mSortOrder = sortOrder;
+ if (mAdapter != null) {
+ mAdapter.setSortOrder(sortOrder);
+ }
+ }
+
+ public void setDirectoryResultLimit(int limit) {
+ mDirectoryResultLimit = limit;
+ }
+
+ public void setContextMenuAdapter(ContextMenuAdapter adapter) {
+ mContextMenuAdapter = adapter;
+ if (mListView != null) {
+ mListView.setOnCreateContextMenuListener(adapter);
+ }
+ }
+
+ public ContextMenuAdapter getContextMenuAdapter() {
+ return mContextMenuAdapter;
+ }
+
+ protected boolean loadPreferences() {
+ boolean changed = false;
+ if (getContactNameDisplayOrder() != mContactsPrefs.getDisplayOrder()) {
+ setContactNameDisplayOrder(mContactsPrefs.getDisplayOrder());
+ changed = true;
+ }
+
+ if (getSortOrder() != mContactsPrefs.getSortOrder()) {
+ setSortOrder(mContactsPrefs.getSortOrder());
+ changed = true;
+ }
+
+ if (mListView instanceof ContactEntryListView) {
+ ContactEntryListView listView = (ContactEntryListView)mListView;
+ listView.setHighlightNamesWhenScrolling(isNameHighlighingEnabled());
+ }
+
+ return changed;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ onCreateView(inflater, container);
+
+ mAdapter = createListAdapter();
+
+ boolean searchMode = isSearchMode();
+ mAdapter.setSearchMode(searchMode);
+ mAdapter.configureDefaultPartition(false, searchMode);
+ mAdapter.setPhotoLoader(mPhotoLoader);
+ mListView.setAdapter(mAdapter);
+
+ if (!isSearchMode()) {
+ mListView.setFocusableInTouchMode(true);
+ mListView.requestFocus();
+ }
+
+ return mView;
+ }
+
+ protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
+ mView = inflateView(inflater, container);
+
+ mListView = (ListView)mView.findViewById(android.R.id.list);
+ if (mListView == null) {
+ throw new RuntimeException(
+ "Your content must have a ListView whose id attribute is " +
+ "'android.R.id.list'");
+ }
+
+ View emptyView = mView.findViewById(com.android.internal.R.id.empty);
+ if (emptyView != null) {
+ mListView.setEmptyView(emptyView);
+ if (emptyView instanceof ContactListEmptyView) {
+ mEmptyView = (ContactListEmptyView)emptyView;
+ }
+ }
+
+ mListView.setOnItemClickListener(this);
+ mListView.setOnFocusChangeListener(this);
+ mListView.setOnTouchListener(this);
+ mListView.setFastScrollEnabled(!isSearchMode());
+
+ // Tell list view to not show dividers. We'll do it ourself so that we can *not* show
+ // them when an A-Z headers is visible.
+ mListView.setDividerHeight(0);
+
+ // We manually save/restore the listview state
+ mListView.setSaveEnabled(false);
+
+ if (mContextMenuAdapter != null) {
+ mListView.setOnCreateContextMenuListener(mContextMenuAdapter);
+ }
+
+ configureVerticalScrollbar();
+ configurePhotoLoader();
+ }
+
+ protected void configurePhotoLoader() {
+ if (isPhotoLoaderEnabled() && mContext != null) {
+ if (mPhotoLoader == null) {
+ mPhotoLoader = new ContactPhotoLoader(mContext, R.drawable.ic_contact_picture);
+ }
+ if (mListView != null) {
+ mListView.setOnScrollListener(this);
+ }
+ if (mAdapter != null) {
+ mAdapter.setPhotoLoader(mPhotoLoader);
+ }
+ }
+ }
+
+ protected void configureAdapter() {
+ if (mAdapter == null) {
+ return;
+ }
+
+ mAdapter.setQuickContactEnabled(mQuickContactEnabled);
+ mAdapter.setQueryString(mQueryString);
+ mAdapter.setDirectorySearchMode(mDirectorySearchMode);
+ mAdapter.setPinnedPartitionHeadersEnabled(mSearchMode);
+ mAdapter.setContactNameDisplayOrder(mDisplayOrder);
+ mAdapter.setSortOrder(mSortOrder);
+ mAdapter.setNameHighlightingEnabled(isNameHighlighingEnabled());
+ mAdapter.setSectionHeaderDisplayEnabled(mSectionHeaderDisplayEnabled);
+ mAdapter.setSelectionVisible(mSelectionVisible);
+ mAdapter.setDirectoryResultLimit(mDirectoryResultLimit);
+ }
+
+ protected boolean isNameHighlighingEnabled() {
+ // When sort order and display order contradict each other, we want to
+ // highlight the part of the name used for sorting.
+ if (mSortOrder == ContactsContract.Preferences.SORT_ORDER_PRIMARY &&
+ mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_ALTERNATIVE) {
+ return true;
+ } else if (mSortOrder == ContactsContract.Preferences.SORT_ORDER_ALTERNATIVE &&
+ mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {
+ mPhotoLoader.pause();
+ } else if (isPhotoLoaderEnabled()) {
+ mPhotoLoader.resume();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ if (isPhotoLoaderEnabled()) {
+ mPhotoLoader.resume();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ if (isPhotoLoaderEnabled()) {
+ mPhotoLoader.stop();
+ }
+ super.onDestroy();
+ }
+
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ hideSoftKeyboard();
+
+ int adjPosition = position - mListView.getHeaderViewsCount();
+ if (adjPosition >= 0) {
+ onItemClick(adjPosition, id);
+ }
+ }
+
+ private void hideSoftKeyboard() {
+ // Hide soft keyboard, if visible
+ InputMethodManager inputMethodManager = (InputMethodManager)
+ mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.hideSoftInputFromWindow(mListView.getWindowToken(), 0);
+ }
+
+ /**
+ * Dismisses the soft keyboard when the list takes focus.
+ */
+ public void onFocusChange(View view, boolean hasFocus) {
+ if (view == mListView && hasFocus) {
+ hideSoftKeyboard();
+ }
+ }
+
+ /**
+ * Dismisses the soft keyboard when the list is touched.
+ */
+ public boolean onTouch(View view, MotionEvent event) {
+ if (view == mListView) {
+ hideSoftKeyboard();
+ }
+ return false;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ removePendingDirectorySearchRequests();
+ }
+
+ /**
+ * Dismisses the search UI along with the keyboard if the filter text is empty.
+ */
+ public void onClose() {
+ hideSoftKeyboard();
+ finish();
+ }
+
+ /**
+ * Restore the list state after the adapter is populated.
+ */
+ protected void completeRestoreInstanceState() {
+ if (mListState != null) {
+ mListView.onRestoreInstanceState(mListState);
+ mListState = null;
+ }
+ }
+
+ protected void setEmptyText(int resourceId) {
+ TextView empty = (TextView) getEmptyView().findViewById(R.id.emptyText);
+ empty.setText(mContext.getText(resourceId));
+ empty.setVisibility(View.VISIBLE);
+ }
+
+ // TODO redesign into an async task or loader
+ protected boolean isSyncActive() {
+ Account[] accounts = AccountManager.get(mContext).getAccounts();
+ if (accounts != null && accounts.length > 0) {
+ IContentService contentService = ContentResolver.getContentService();
+ for (Account account : accounts) {
+ try {
+ if (contentService.isSyncActive(account, ContactsContract.AUTHORITY)) {
+ return true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not get the sync status");
+ }
+ }
+ }
+ return false;
+ }
+
+ protected boolean hasIccCard() {
+ TelephonyManager telephonyManager =
+ (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ return telephonyManager.hasIccCard();
+ }
+
+ /**
+ * Processes a user request to start search. This may be triggered by the
+ * search key, a menu item or some other user action.
+ */
+ public void startSearch(String initialQuery) {
+ ContactsSearchManager.startSearch(getActivity(), initialQuery, mRequest);
+ }
+
+ /**
+ * Processes a result returned by the contact picker.
+ */
+ public void onPickerResult(Intent data) {
+ throw new UnsupportedOperationException("Picker result handler is not implemented.");
+ }
+
+ private ContactsPreferences.ChangeListener mPreferencesChangeListener =
+ new ContactsPreferences.ChangeListener() {
+ @Override
+ public void onChange() {
+ loadPreferences();
+ reloadData();
+ }
+ };
+}
diff --git a/src/com/android/contacts/list/ContactEntryListView.java b/src/com/android/contacts/list/ContactEntryListView.java
new file mode 100644
index 0000000..86e33fe
--- /dev/null
+++ b/src/com/android/contacts/list/ContactEntryListView.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+import com.android.contacts.widget.PinnedHeaderListView;
+import com.android.contacts.widget.TextHighlightingAnimation;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.AbsListView;
+import android.widget.ListAdapter;
+
+/**
+ * A custom list view for a list of contacts or contact-related entries. It handles
+ * animation of names on scroll.
+ */
+public class ContactEntryListView extends PinnedHeaderListView {
+
+ private static final int TEXT_HIGHLIGHTING_ANIMATION_DURATION = 350;
+
+ private final TextHighlightingAnimation mHighlightingAnimation =
+ new ContactNameHighlightingAnimation(this, TEXT_HIGHLIGHTING_ANIMATION_DURATION);
+
+ private boolean mHighlightNamesWhenScrolling;
+
+ public ContactEntryListView(Context context) {
+ this(context, null);
+ }
+
+ public ContactEntryListView(Context context, AttributeSet attrs) {
+ this(context, attrs, com.android.internal.R.attr.listViewStyle);
+ }
+
+ public ContactEntryListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setSelector(getContext().getResources().getDrawable(R.drawable.list_selector));
+ }
+
+ public TextHighlightingAnimation getTextHighlightingAnimation() {
+ return mHighlightingAnimation;
+ }
+
+ public boolean getHighlightNamesWhenScrolling() {
+ return mHighlightNamesWhenScrolling;
+ }
+
+ public void setHighlightNamesWhenScrolling(boolean flag) {
+ mHighlightNamesWhenScrolling = flag;
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ super.setAdapter(adapter);
+ if (adapter instanceof ContactEntryListAdapter) {
+ ((ContactEntryListAdapter)adapter)
+ .setTextWithHighlightingFactory(mHighlightingAnimation);
+ }
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ super.onScrollStateChanged(view, scrollState);
+ if (mHighlightNamesWhenScrolling) {
+ if (scrollState != OnScrollListener.SCROLL_STATE_IDLE) {
+ mHighlightingAnimation.startHighlighting();
+ } else {
+ mHighlightingAnimation.stopHighlighting();
+ }
+ }
+ }
+}
diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java
new file mode 100644
index 0000000..a6db641
--- /dev/null
+++ b/src/com/android/contacts/list/ContactListAdapter.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.ContactCounts;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.SearchSnippetColumns;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+import android.widget.QuickContactBadge;
+
+
+/**
+ * A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type.
+ */
+public abstract class ContactListAdapter extends ContactEntryListAdapter {
+
+ protected static final String[] PROJECTION_CONTACT = new String[] {
+ Contacts._ID, // 0
+ Contacts.DISPLAY_NAME_PRIMARY, // 1
+ Contacts.DISPLAY_NAME_ALTERNATIVE, // 2
+ Contacts.SORT_KEY_PRIMARY, // 3
+ Contacts.STARRED, // 4
+ Contacts.CONTACT_PRESENCE, // 5
+ Contacts.CONTACT_CHAT_CAPABILITY, // 6
+ Contacts.PHOTO_ID, // 7
+ Contacts.PHOTO_THUMBNAIL_URI, // 8
+ Contacts.LOOKUP_KEY, // 9
+ Contacts.PHONETIC_NAME, // 10
+ Contacts.HAS_PHONE_NUMBER, // 11
+ };
+
+ protected static final String[] PROJECTION_DATA = new String[] {
+ Data.CONTACT_ID, // 0
+ Data.DISPLAY_NAME_PRIMARY, // 1
+ Data.DISPLAY_NAME_ALTERNATIVE, // 2
+ Data.SORT_KEY_PRIMARY, // 3
+ Data.STARRED, // 4
+ Data.CONTACT_PRESENCE, // 5
+ Data.CONTACT_CHAT_CAPABILITY, // 6
+ Data.PHOTO_ID, // 7
+ Data.PHOTO_THUMBNAIL_URI, // 8
+ Data.LOOKUP_KEY, // 9
+ Data.PHONETIC_NAME, // 10
+ Data.HAS_PHONE_NUMBER, // 11
+ };
+
+ protected static final String[] FILTER_PROJECTION = new String[] {
+ Contacts._ID, // 0
+ Contacts.DISPLAY_NAME_PRIMARY, // 1
+ Contacts.DISPLAY_NAME_ALTERNATIVE, // 2
+ Contacts.SORT_KEY_PRIMARY, // 3
+ Contacts.STARRED, // 4
+ Contacts.CONTACT_PRESENCE, // 5
+ Contacts.CONTACT_CHAT_CAPABILITY, // 6
+ Contacts.PHOTO_ID, // 7
+ Contacts.PHOTO_THUMBNAIL_URI, // 8
+ Contacts.LOOKUP_KEY, // 9
+ Contacts.PHONETIC_NAME, // 10
+ Contacts.HAS_PHONE_NUMBER, // 11
+ SearchSnippetColumns.SNIPPET_MIMETYPE, // 12
+ SearchSnippetColumns.SNIPPET_DATA1, // 13
+ SearchSnippetColumns.SNIPPET_DATA4, // 14
+ };
+
+ protected static final int CONTACT_ID_COLUMN_INDEX = 0;
+ protected static final int CONTACT_DISPLAY_NAME_PRIMARY_COLUMN_INDEX = 1;
+ protected static final int CONTACT_DISPLAY_NAME_ALTERNATIVE_COLUMN_INDEX = 2;
+ protected static final int CONTACT_SORT_KEY_PRIMARY_COLUMN_INDEX = 3;
+ protected static final int CONTACT_STARRED_COLUMN_INDEX = 4;
+ protected static final int CONTACT_PRESENCE_STATUS_COLUMN_INDEX = 5;
+ protected static final int CONTACT_CHAT_CAPABILITY_COLUMN_INDEX = 6;
+ protected static final int CONTACT_PHOTO_ID_COLUMN_INDEX = 7;
+ protected static final int CONTACT_PHOTO_URI_COLUMN_INDEX = 8;
+ protected static final int CONTACT_LOOKUP_KEY_COLUMN_INDEX = 9;
+ protected static final int CONTACT_PHONETIC_NAME_COLUMN_INDEX = 10;
+ protected static final int CONTACT_HAS_PHONE_COLUMN_INDEX = 11;
+ protected static final int CONTACT_SNIPPET_MIMETYPE_COLUMN_INDEX = 12;
+ protected static final int CONTACT_SNIPPET_DATA1_COLUMN_INDEX = 13;
+ protected static final int CONTACT_SNIPPET_DATA4_COLUMN_INDEX = 14;
+
+ private CharSequence mUnknownNameText;
+ private int mDisplayNameColumnIndex;
+ private int mAlternativeDisplayNameColumnIndex;
+
+ private long mSelectedContactDirectoryId;
+ private String mSelectedContactLookupKey;
+ private long mSelectedContactId;
+
+ private ContactListFilter mFilter;
+
+ public ContactListAdapter(Context context) {
+ super(context);
+
+ mUnknownNameText = context.getText(android.R.string.unknownName);
+ }
+
+ public CharSequence getUnknownNameText() {
+ return mUnknownNameText;
+ }
+
+ /**
+ * Returns the currently selected filter.
+ */
+ public ContactListFilter getFilter() {
+ return mFilter;
+ }
+
+ public void setFilter(ContactListFilter filter) {
+ mFilter = filter;
+ }
+
+ public long getSelectedContactDirectoryId() {
+ return mSelectedContactDirectoryId;
+ }
+
+ public String getSelectedContactLookupKey() {
+ return mSelectedContactLookupKey;
+ }
+
+ public long getSelectedContactId() {
+ return mSelectedContactId;
+ }
+
+ public void setSelectedContact(long selectedDirectoryId, String lookupKey, long contactId) {
+ mSelectedContactDirectoryId = selectedDirectoryId;
+ mSelectedContactLookupKey = lookupKey;
+ mSelectedContactId = contactId;
+ }
+
+ protected static Uri buildSectionIndexerUri(Uri uri) {
+ return uri.buildUpon()
+ .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
+ }
+
+ public boolean getHasPhoneNumber(int position) {
+ return ((Cursor)getItem(position)).getInt(CONTACT_HAS_PHONE_COLUMN_INDEX) != 0;
+ }
+
+ public boolean isContactStarred(int position) {
+ return ((Cursor)getItem(position)).getInt(CONTACT_STARRED_COLUMN_INDEX) != 0;
+ }
+
+ @Override
+ public String getContactDisplayName(int position) {
+ return ((Cursor)getItem(position)).getString(mDisplayNameColumnIndex);
+ }
+
+ @Override
+ public void setContactNameDisplayOrder(int displayOrder) {
+ super.setContactNameDisplayOrder(displayOrder);
+ if (getContactNameDisplayOrder() == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
+ mDisplayNameColumnIndex = CONTACT_DISPLAY_NAME_PRIMARY_COLUMN_INDEX;
+ mAlternativeDisplayNameColumnIndex = CONTACT_DISPLAY_NAME_ALTERNATIVE_COLUMN_INDEX;
+ } else {
+ mDisplayNameColumnIndex = CONTACT_DISPLAY_NAME_ALTERNATIVE_COLUMN_INDEX;
+ mAlternativeDisplayNameColumnIndex = CONTACT_DISPLAY_NAME_PRIMARY_COLUMN_INDEX;
+ }
+ }
+
+ /**
+ * Builds the {@link Contacts#CONTENT_LOOKUP_URI} for the given
+ * {@link ListView} position.
+ */
+ public Uri getContactUri(int position) {
+ int partitionIndex = getPartitionForPosition(position);
+ Cursor item = (Cursor)getItem(position);
+ return item != null ? getContactUri(partitionIndex, item) : null;
+ }
+
+ public Uri getContactUri(int partitionIndex, Cursor cursor) {
+ long contactId = cursor.getLong(CONTACT_ID_COLUMN_INDEX);
+ String lookupKey = cursor.getString(CONTACT_LOOKUP_KEY_COLUMN_INDEX);
+ Uri uri = Contacts.getLookupUri(contactId, lookupKey);
+ long directoryId = ((DirectoryPartition)getPartition(partitionIndex)).getDirectoryId();
+ if (directoryId != Directory.DEFAULT) {
+ uri = uri.buildUpon().appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(directoryId)).build();
+ }
+ return uri;
+ }
+
+ /**
+ * Returns true if the specified contact is selected in the list. For a
+ * contact to be shown as selected, we need both the directory and and the
+ * lookup key to be the same. We are paying no attention to the contactId,
+ * because it is volatile, especially in the case of directories.
+ */
+ public boolean isSelectedContact(int partitionIndex, Cursor cursor) {
+ long directoryId = ((DirectoryPartition)getPartition(partitionIndex)).getDirectoryId();
+ if (getSelectedContactDirectoryId() != directoryId) {
+ return false;
+ }
+ String lookupKey = getSelectedContactLookupKey();
+ if (lookupKey != null && TextUtils.equals(lookupKey,
+ cursor.getString(CONTACT_LOOKUP_KEY_COLUMN_INDEX))) {
+ return true;
+ }
+
+ return directoryId != Directory.DEFAULT && directoryId != Directory.LOCAL_INVISIBLE
+ && getSelectedContactId() == cursor.getLong(CONTACT_ID_COLUMN_INDEX);
+ }
+
+ @Override
+ protected View newView(Context context, int partition, Cursor cursor, int position,
+ ViewGroup parent) {
+ final ContactListItemView view = new ContactListItemView(context, null);
+ view.setUnknownNameText(mUnknownNameText);
+ view.setTextWithHighlightingFactory(getTextWithHighlightingFactory());
+ view.setQuickContactEnabled(isQuickContactEnabled());
+ view.setActivatedStateSupported(isSelectionVisible());
+ return view;
+ }
+
+ protected void bindSectionHeaderAndDivider(ContactListItemView view, int position) {
+ if (isSectionHeaderDisplayEnabled()) {
+ Placement placement = getItemPlacementInSection(position);
+ view.setSectionHeader(placement.firstInSection ? placement.sectionHeader : null);
+ view.setDividerVisible(!placement.lastInSection);
+ } else {
+ view.setSectionHeader(null);
+ view.setDividerVisible(true);
+ }
+ }
+
+ protected void bindPhoto(final ContactListItemView view, int partitionIndex, Cursor cursor) {
+ if (!isPhotoSupported(partitionIndex)) {
+ view.removePhotoView();
+ return;
+ }
+
+ // Set the photo, if available
+ long photoId = 0;
+ if (!cursor.isNull(CONTACT_PHOTO_ID_COLUMN_INDEX)) {
+ photoId = cursor.getLong(CONTACT_PHOTO_ID_COLUMN_INDEX);
+ }
+
+ if (photoId != 0) {
+ getPhotoLoader().loadPhoto(view.getPhotoView(), photoId);
+ } else {
+ final String photoUriString = cursor.getString(CONTACT_PHOTO_URI_COLUMN_INDEX);
+ final Uri photoUri = photoUriString == null ? null : Uri.parse(photoUriString);
+ getPhotoLoader().loadPhoto(view.getPhotoView(), photoUri);
+ }
+ }
+
+ protected void bindQuickContact(
+ final ContactListItemView view, int partitionIndex, Cursor cursor) {
+ long photoId = 0;
+ if (!cursor.isNull(CONTACT_PHOTO_ID_COLUMN_INDEX)) {
+ photoId = cursor.getLong(CONTACT_PHOTO_ID_COLUMN_INDEX);
+ }
+
+ QuickContactBadge quickContact = view.getQuickContact();
+ quickContact.assignContactUri(getContactUri(partitionIndex, cursor));
+ getPhotoLoader().loadPhoto(quickContact, photoId);
+ }
+
+ protected void bindName(final ContactListItemView view, Cursor cursor) {
+ view.showDisplayName(cursor, mDisplayNameColumnIndex, isNameHighlightingEnabled(),
+ mAlternativeDisplayNameColumnIndex);
+ view.showPhoneticName(cursor, CONTACT_PHONETIC_NAME_COLUMN_INDEX);
+ }
+
+ protected void bindPresence(final ContactListItemView view, Cursor cursor) {
+ view.showPresence(cursor, CONTACT_PRESENCE_STATUS_COLUMN_INDEX,
+ CONTACT_CHAT_CAPABILITY_COLUMN_INDEX);
+ }
+
+ protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
+ view.showSnippet(cursor, CONTACT_SNIPPET_MIMETYPE_COLUMN_INDEX,
+ CONTACT_SNIPPET_DATA1_COLUMN_INDEX, CONTACT_SNIPPET_DATA4_COLUMN_INDEX);
+ }
+
+ public int getSelectedContactPosition() {
+ if (mSelectedContactLookupKey == null && mSelectedContactId == 0) {
+ return -1;
+ }
+
+ Cursor cursor = null;
+ int partitionIndex = -1;
+ int partitionCount = getPartitionCount();
+ for (int i = 0; i < partitionCount; i++) {
+ DirectoryPartition partition = (DirectoryPartition) getPartition(i);
+ if (partition.getDirectoryId() == mSelectedContactDirectoryId) {
+ partitionIndex = i;
+ break;
+ }
+ }
+ if (partitionIndex == -1) {
+ return -1;
+ }
+
+ cursor = getCursor(partitionIndex);
+ if (cursor == null) {
+ return -1;
+ }
+
+ cursor.moveToPosition(-1); // Reset cursor
+ int offset = -1;
+ while (cursor.moveToNext()) {
+ if (mSelectedContactLookupKey != null) {
+ String lookupKey = cursor.getString(CONTACT_LOOKUP_KEY_COLUMN_INDEX);
+ if (mSelectedContactLookupKey.equals(lookupKey)) {
+ offset = cursor.getPosition();
+ break;
+ }
+ }
+ if (mSelectedContactId != 0 && (mSelectedContactDirectoryId == Directory.DEFAULT
+ || mSelectedContactDirectoryId == Directory.LOCAL_INVISIBLE)) {
+ long contactId = cursor.getLong(CONTACT_ID_COLUMN_INDEX);
+ if (contactId == mSelectedContactId) {
+ offset = cursor.getPosition();
+ break;
+ }
+ }
+ }
+ if (offset == -1) {
+ return -1;
+ }
+
+ int position = getPositionForPartition(partitionIndex) + offset;
+ if (hasHeader(partitionIndex)) {
+ position++;
+ }
+ return position;
+ }
+
+ public boolean hasValidSelection() {
+ return getSelectedContactPosition() != -1;
+ }
+
+ public Uri getFirstContactUri() {
+ int partitionCount = getPartitionCount();
+ for (int i = 0; i < partitionCount; i++) {
+ DirectoryPartition partition = (DirectoryPartition) getPartition(i);
+ if (partition.isLoading()) {
+ continue;
+ }
+
+ Cursor cursor = getCursor(i);
+ if (cursor == null) {
+ continue;
+ }
+
+ if (!cursor.moveToFirst()) {
+ continue;
+ }
+
+ return getContactUri(i, cursor);
+ }
+
+ return null;
+ }
+}
diff --git a/src/com/android/contacts/list/ContactListFilter.java b/src/com/android/contacts/list/ContactListFilter.java
new file mode 100644
index 0000000..c53859b
--- /dev/null
+++ b/src/com/android/contacts/list/ContactListFilter.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.SharedPreferences;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Contact list filter parameters.
+ */
+public final class ContactListFilter implements Comparable<ContactListFilter>, Parcelable {
+
+ public static final int FILTER_TYPE_DEFAULT = -1;
+ public static final int FILTER_TYPE_ALL_ACCOUNTS = -2;
+ public static final int FILTER_TYPE_CUSTOM = -3;
+ public static final int FILTER_TYPE_STARRED = -4;
+ public static final int FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY = -5;
+ public static final int FILTER_TYPE_SINGLE_CONTACT = -6;
+
+ public static final int FILTER_TYPE_ACCOUNT = 0;
+ public static final int FILTER_TYPE_GROUP = 1;
+
+ private static final String KEY_FILTER_TYPE = "filter.type";
+ private static final String KEY_ACCOUNT_NAME = "filter.accountName";
+ private static final String KEY_ACCOUNT_TYPE = "filter.accountType";
+ private static final String KEY_GROUP_ID = "filter.groupId";
+ private static final String KEY_GROUP_SOURCE_ID = "filter.groupSourceId";
+ private static final String KEY_GROUP_READ_ONLY = "filter.groupReadOnly";
+ private static final String KEY_GROUP_TITLE = "filter.groupTitle";
+
+ public int filterType;
+ public String accountType;
+ public String accountName;
+ public Drawable icon;
+ public long groupId;
+ public String groupSourceId;
+ public boolean groupReadOnly;
+ public String title;
+ private String mId;
+
+ public ContactListFilter(int filterType) {
+ this.filterType = filterType;
+ }
+
+ public ContactListFilter(
+ String accountType, String accountName, Drawable icon, String title) {
+ this.filterType = ContactListFilter.FILTER_TYPE_ACCOUNT;
+ this.accountType = accountType;
+ this.accountName = accountName;
+ this.icon = icon;
+ this.title = title;
+ }
+
+ public ContactListFilter(String accountType, String accountName, long groupId,
+ String groupSourceId, boolean groupReadOnly, String title) {
+ this.filterType = ContactListFilter.FILTER_TYPE_GROUP;
+ this.accountType = accountType;
+ this.accountName = accountName;
+ this.groupId = groupId;
+ this.groupSourceId = groupSourceId;
+ this.groupReadOnly = groupReadOnly;
+ this.title = title;
+ }
+
+ /**
+ * Returns true if this filter is based on data and may become invalid over time.
+ */
+ public boolean isValidationRequired() {
+ return filterType == FILTER_TYPE_ACCOUNT || filterType == FILTER_TYPE_GROUP;
+ }
+
+ @Override
+ public String toString() {
+ switch (filterType) {
+ case FILTER_TYPE_DEFAULT:
+ return "default";
+ case FILTER_TYPE_ALL_ACCOUNTS:
+ return "all_accounts";
+ case FILTER_TYPE_CUSTOM:
+ return "custom";
+ case FILTER_TYPE_STARRED:
+ return "starred";
+ case FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
+ return "with_phones";
+ case FILTER_TYPE_SINGLE_CONTACT:
+ return "single";
+ case FILTER_TYPE_ACCOUNT:
+ return "account: " + accountType + " " + accountName;
+ case FILTER_TYPE_GROUP:
+ return "group: " + accountType + " " + accountName + " " + title + "(" + groupId
+ + ")";
+ }
+ return super.toString();
+ }
+
+ @Override
+ public int compareTo(ContactListFilter another) {
+ int res = accountName.compareTo(another.accountName);
+ if (res != 0) {
+ return res;
+ }
+
+ res = accountType.compareTo(another.accountType);
+ if (res != 0) {
+ return res;
+ }
+
+ if (filterType != another.filterType) {
+ return filterType - another.filterType;
+ }
+
+ String title1 = title != null ? title : "";
+ String title2 = another.title != null ? another.title : "";
+ return title1.compareTo(title2);
+ }
+
+ @Override
+ public int hashCode() {
+ int code = filterType;
+ if (accountType != null) {
+ code = code * 31 + accountType.hashCode();
+ code = code * 31 + accountName.hashCode();
+ }
+ if (groupSourceId != null) {
+ code = code * 31 + groupSourceId.hashCode();
+ } else if (groupId != 0) {
+ code = code * 31 + (int) groupId;
+ }
+ return code;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+
+ if (!(other instanceof ContactListFilter)) {
+ return false;
+ }
+
+ ContactListFilter otherFilter = (ContactListFilter) other;
+ if (filterType != otherFilter.filterType
+ || !TextUtils.equals(accountName, otherFilter.accountName)
+ || !TextUtils.equals(accountType, otherFilter.accountType)) {
+ return false;
+ }
+
+ if (groupSourceId != null && otherFilter.groupSourceId != null) {
+ return groupSourceId.equals(otherFilter.groupSourceId);
+ }
+
+ return groupId == otherFilter.groupId;
+ }
+
+ public static void storeToPreferences(SharedPreferences prefs, ContactListFilter filter) {
+ prefs.edit()
+ .putInt(KEY_FILTER_TYPE, filter == null ? FILTER_TYPE_DEFAULT : filter.filterType)
+ .putString(KEY_ACCOUNT_NAME, filter == null ? null : filter.accountName)
+ .putString(KEY_ACCOUNT_TYPE, filter == null ? null : filter.accountType)
+ .putLong(KEY_GROUP_ID, filter == null ? -1 : filter.groupId)
+ .putString(KEY_GROUP_SOURCE_ID, filter == null ? null : filter.groupSourceId)
+ .putBoolean(KEY_GROUP_READ_ONLY, filter == null ? false : filter.groupReadOnly)
+ .putString(KEY_GROUP_TITLE, filter == null ? null : filter.title)
+ .apply();
+ }
+
+ public static ContactListFilter restoreFromPreferences(SharedPreferences prefs) {
+ int filterType = prefs.getInt(KEY_FILTER_TYPE, FILTER_TYPE_DEFAULT);
+ if (filterType == FILTER_TYPE_DEFAULT) {
+ return null;
+ }
+
+ ContactListFilter filter = new ContactListFilter(filterType);
+ filter.accountName = prefs.getString(KEY_ACCOUNT_NAME, null);
+ filter.accountType = prefs.getString(KEY_ACCOUNT_TYPE, null);
+ filter.groupId = prefs.getLong(KEY_GROUP_ID, -1);
+ filter.groupSourceId = prefs.getString(KEY_GROUP_SOURCE_ID, null);
+ filter.groupReadOnly = prefs.getBoolean(KEY_GROUP_READ_ONLY, false);
+ filter.title = prefs.getString(KEY_GROUP_TITLE, "group");
+ return filter;
+ }
+
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(filterType);
+ dest.writeString(accountName);
+ dest.writeString(accountType);
+ dest.writeLong(groupId);
+ dest.writeString(groupSourceId);
+ dest.writeInt(groupReadOnly ? 1 : 0);
+ }
+
+ public static final Parcelable.Creator<ContactListFilter> CREATOR =
+ new Parcelable.Creator<ContactListFilter>()
+ {
+ @Override
+ public ContactListFilter createFromParcel(Parcel source) {
+ int filterType = source.readInt();
+ ContactListFilter filter = new ContactListFilter(filterType);
+ filter.accountName = source.readString();
+ filter.accountType = source.readString();
+ filter.groupId = source.readLong();
+ filter.groupSourceId = source.readString();
+ filter.groupReadOnly = source.readInt() != 0;
+ return filter;
+ }
+
+ @Override
+ public ContactListFilter[] newArray(int size) {
+ return new ContactListFilter[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns a string that can be used as a stable persistent identifier for this filter.
+ */
+ public String getId() {
+ if (mId == null) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(filterType);
+ if (accountType != null) {
+ sb.append('-').append(accountType);
+ }
+ if (accountName != null) {
+ sb.append('-').append(accountName.replace('-', '_'));
+ }
+ if (groupSourceId != null) {
+ sb.append('-').append(groupSourceId);
+ } else if (groupId != 0) {
+ sb.append('-').append(groupId);
+ }
+ mId = sb.toString();
+ }
+ return mId;
+ }
+}
diff --git a/src/com/android/contacts/list/ContactListFilterController.java b/src/com/android/contacts/list/ContactListFilterController.java
new file mode 100644
index 0000000..c9f5530
--- /dev/null
+++ b/src/com/android/contacts/list/ContactListFilterController.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+
+import android.app.Activity;
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Context;
+import android.content.Loader;
+import android.content.SharedPreferences;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.ListPopupWindow;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Controls a list of {@link ContactListFilter}'s.
+ */
+public class ContactListFilterController
+ implements LoaderCallbacks<List<ContactListFilter>>, OnClickListener, OnItemClickListener {
+
+ public interface ContactListFilterListener {
+ void onContactListFiltersLoaded();
+ void onContactListFilterChanged();
+ void onContactListFilterCustomizationRequest();
+ }
+
+ private Context mContext;
+ private LoaderManager mLoaderManager;
+ private boolean mEnabled = true;
+ private List<ContactListFilterListener> mListeners = new ArrayList<ContactListFilterListener>();
+ private ListPopupWindow mPopup;
+ private int mPopupWidth = -1;
+ private List<ContactListFilter> mCachedFilters;
+ private SparseArray<ContactListFilter> mFilters;
+ private int mNextFilterId = 1;
+ private View mAnchor;
+ private FilterListAdapter mFilterListAdapter;
+ private ContactListFilter mFilter;
+ private boolean mFiltersLoaded;
+ private int mAccountCount;
+
+ public ContactListFilterController(Activity activity) {
+ mContext = activity;
+ mLoaderManager = activity.getLoaderManager();
+ }
+
+ public void setEnabled(boolean flag) {
+ mEnabled = flag;
+ }
+
+ public void addListener(ContactListFilterListener listener) {
+ mListeners.add(listener);
+ }
+
+ public void removeListener(ContactListFilterListener listener) {
+ mListeners.remove(listener);
+ }
+
+ public void setAnchor(View anchor) {
+ mAnchor = anchor;
+ mAnchor.setOnClickListener(this);
+ }
+
+ public ContactListFilter getFilter() {
+ return mFilter;
+ }
+
+ public int getFilterCount() {
+ return mFilters != null ? mFilters.size() : 0;
+ }
+
+ public boolean isLoaded() {
+ return mFiltersLoaded;
+ }
+
+ public void onStart() {
+ if (mFilter == null) {
+ mFilter = ContactListFilter.restoreFromPreferences(getSharedPreferences());
+ }
+ mLoaderManager.initLoader(R.id.contact_list_filter_loader, null, this);
+ }
+
+ public void onStop() {
+ mLoaderManager.destroyLoader(R.id.contact_list_filter_loader);
+ }
+
+ private SharedPreferences getSharedPreferences() {
+ return PreferenceManager.getDefaultSharedPreferences(mContext);
+ }
+
+ @Override
+ public ContactListFilterLoader onCreateLoader(int id, Bundle args) {
+ return new ContactListFilterLoader(mContext);
+ }
+
+ @Override
+ public void onLoadFinished(
+ Loader<List<ContactListFilter>> loader, List<ContactListFilter> filters) {
+ int count = filters.size();
+ if (mCachedFilters != null && mCachedFilters.size() == count) {
+ boolean changed = false;
+ for (int i = 0; i < filters.size(); i++) {
+ ContactListFilter filter1 = mCachedFilters.get(i);
+ ContactListFilter filter2 = filters.get(i);
+ if (!filter1.equals(filter2)) {
+ changed = true;
+ break;
+ }
+
+ // Group title is intentionally not included in the "equals" algorithm for
+ // ContactListFilter, because we want stability of filter identity
+ // across label changes. However, here we do care about the label changes.
+ if (filter1.filterType == ContactListFilter.FILTER_TYPE_GROUP &&
+ !TextUtils.equals(filter1.title, filter2.title)) {
+ changed = true;
+ break;
+ }
+ }
+
+ if (!changed) {
+ return;
+ }
+ }
+
+ mCachedFilters = filters;
+
+ if (mFilters == null) {
+ mFilters = new SparseArray<ContactListFilter>(filters.size());
+ } else {
+ mFilters.clear();
+ }
+
+ boolean filterValid = mFilter != null && !mFilter.isValidationRequired();
+
+ mAccountCount = 0;
+ for (int index = 0; index < count; index++) {
+ if (filters.get(index).filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ mAccountCount++;
+ }
+ }
+
+ if (mAccountCount != 1) {
+ mFilters.append(mNextFilterId++,
+ new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
+ mFilters.append(mNextFilterId++,
+ new ContactListFilter(ContactListFilter.FILTER_TYPE_STARRED));
+ }
+
+ for (int index = 0; index < count; index++) {
+ ContactListFilter filter = filters.get(index);
+
+ boolean firstAndOnly = mAccountCount == 1
+ && filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT;
+
+ // If we only have one account, don't show it as "account", instead show it as "all"
+ if (firstAndOnly) {
+ filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+ }
+
+ mFilters.append(mNextFilterId++, filter);
+
+ if (filter.equals(mFilter)) {
+ // Refresh the filter in case the title got changed
+ mFilter = filter;
+ filterValid = true;
+ }
+
+ if (firstAndOnly) {
+ mFilters.append(mNextFilterId++,
+ new ContactListFilter(ContactListFilter.FILTER_TYPE_STARRED));
+ }
+ }
+
+ if (mAccountCount > 0) {
+ mFilters.append(
+ mNextFilterId++, new ContactListFilter(ContactListFilter.FILTER_TYPE_CUSTOM));
+ }
+
+ boolean filterChanged = false;
+ if (mFilter == null || !filterValid) {
+ filterChanged = mFilter != null;
+ mFilter = getDefaultFilter();
+ }
+
+ if (mFilterListAdapter == null) {
+ mFilterListAdapter = new FilterListAdapter();
+ } else {
+ mFilterListAdapter.notifyDataSetChanged();
+ }
+
+ mFiltersLoaded = true;
+ notifyContacListFiltersLoaded();
+
+ if (filterChanged) {
+ notifyContactListFilterChanged();
+ }
+ }
+
+ public void onLoaderReset(Loader<List<ContactListFilter>> loader) {
+ }
+
+ private void setContactListFilter(int filterId) {
+ ContactListFilter filter;
+ if (filterId == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
+ filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+ } else if (filterId == ContactListFilter.FILTER_TYPE_CUSTOM) {
+ filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_CUSTOM);
+ } else if (filterId == ContactListFilter.FILTER_TYPE_STARRED) {
+ filter = new ContactListFilter(ContactListFilter.FILTER_TYPE_STARRED);
+ } else {
+ filter = mFilters.get(filterId);
+ if (filter == null) {
+ filter = getDefaultFilter();
+ }
+ }
+
+ setContactListFilter(filter, true);
+ }
+
+ public void setContactListFilter(ContactListFilter filter, boolean persistent) {
+ if (!filter.equals(mFilter)) {
+ mFilter = filter;
+ if (persistent) {
+ ContactListFilter.storeToPreferences(getSharedPreferences(), mFilter);
+ }
+ if (mListeners != null) {
+ notifyContactListFilterChanged();
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (!mFiltersLoaded || !mEnabled) {
+ return;
+ }
+
+ if (mPopupWidth == -1) {
+ TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ContactBrowser);
+ mPopupWidth = a.getDimensionPixelSize(
+ R.styleable.ContactBrowser_contact_filter_popup_width, -1);
+ a.recycle();
+
+ if (mPopupWidth == -1) {
+ mPopupWidth = mAnchor.getWidth();
+ }
+ }
+
+ mPopup = new ListPopupWindow(mContext, null);
+ mPopup.setWidth(mPopupWidth);
+ mPopup.setAdapter(mFilterListAdapter);
+ mPopup.setAnchorView(mAnchor);
+ mPopup.setOnItemClickListener(this);
+ mPopup.setModal(true);
+ mPopup.show();
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ mPopup.dismiss();
+ if (mFilters.get((int) id).filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
+ notifyContactListFilterCustomizationRequest();
+ } else {
+ setContactListFilter((int) id);
+ }
+ }
+
+ public void selectCustomFilter() {
+ mFilter = new ContactListFilter(ContactListFilter.FILTER_TYPE_CUSTOM);
+ notifyContactListFilterChanged();
+ }
+
+ public int getAccountCount() {
+ return mAccountCount;
+ }
+
+ private ContactListFilter getDefaultFilter() {
+ return mFilters.size() > 0 ? mFilters.valueAt(0) : null;
+ }
+
+ private void notifyContacListFiltersLoaded() {
+ for (ContactListFilterListener listener : mListeners) {
+ listener.onContactListFiltersLoaded();
+ }
+ }
+
+ private void notifyContactListFilterChanged() {
+ for (ContactListFilterListener listener : mListeners) {
+ listener.onContactListFilterChanged();
+ }
+ }
+
+ private void notifyContactListFilterCustomizationRequest() {
+ for (ContactListFilterListener listener : mListeners) {
+ listener.onContactListFilterCustomizationRequest();
+ }
+ }
+
+ private class FilterListAdapter extends BaseAdapter {
+ private LayoutInflater mLayoutInflater;
+
+ public FilterListAdapter() {
+ mLayoutInflater = LayoutInflater.from(mContext);
+ }
+
+ @Override
+ public int getCount() {
+ return mFilters.size();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mFilters.keyAt(position);
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mFilters.valueAt(position);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ContactListFilterView view;
+ if (convertView != null) {
+ view = (ContactListFilterView) convertView;
+ } else {
+ view = (ContactListFilterView) mLayoutInflater.inflate(
+ R.layout.filter_spinner_item, parent, false);
+ }
+ view.setSingleAccount(mAccountCount == 1);
+ ContactListFilter filter = mFilters.valueAt(position);
+ view.setContactListFilter(filter);
+ view.setActivated(filter.equals(mFilter));
+ view.bindView(true);
+ return view;
+ }
+ }
+}
diff --git a/src/com/android/contacts/list/ContactListFilterLoader.java b/src/com/android/contacts/list/ContactListFilterLoader.java
new file mode 100644
index 0000000..82749ff
--- /dev/null
+++ b/src/com/android/contacts/list/ContactListFilterLoader.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+
+import android.accounts.Account;
+import android.content.AsyncTaskLoader;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Groups;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A loader for the data needed for the group selector.
+ */
+public class ContactListFilterLoader extends AsyncTaskLoader<List<ContactListFilter>> {
+
+ private static final class GroupQuery {
+ public static final String[] COLUMNS = {
+ Groups._ID,
+ Groups.ACCOUNT_TYPE,
+ Groups.ACCOUNT_NAME,
+ Groups.TITLE,
+ Groups.AUTO_ADD,
+ Groups.SOURCE_ID,
+ Groups.GROUP_IS_READ_ONLY,
+ };
+
+ public static final int ID = 0;
+ public static final int ACCOUNT_TYPE = 1;
+ public static final int ACCOUNT_NAME = 2;
+ public static final int TITLE = 3;
+ public static final int IS_DEFAULT_GROUP = 4; // Using the AUTO_ADD group as default
+ public static final int SOURCE_ID = 5;
+ public static final int GROUP_IS_READ_ONLY = 6;
+
+ private static final String SELECTION =
+ Groups.DELETED + "=0" +
+ " AND " + Groups.FAVORITES + "=0" +
+ " AND " + Groups.ACCOUNT_TYPE + " NOT NULL" +
+ " AND " + Groups.ACCOUNT_NAME + " NOT NULL";
+ }
+
+ private boolean mStopped;
+ private ForceLoadContentObserver mObserver;
+ private ArrayList<ContactListFilter> mResults;
+
+ public ContactListFilterLoader(Context context) {
+ super(context);
+ }
+
+ @Override
+ public List<ContactListFilter> loadInBackground() {
+
+ ArrayList<ContactListFilter> results = new ArrayList<ContactListFilter>();
+ Context context = getContext();
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
+ ArrayList<Account> accounts = accountTypes.getAccounts(false);
+ for (Account account : accounts) {
+ AccountType accountType = accountTypes.getAccountType(account.type);
+ Drawable icon = accountType != null ? accountType.getDisplayIcon(getContext()) : null;
+ results.add(new ContactListFilter(account.type, account.name, icon, account.name));
+ }
+
+ ContentResolver resolver = context.getContentResolver();
+
+ Cursor cursor = resolver.query(
+ Groups.CONTENT_URI, GroupQuery.COLUMNS, GroupQuery.SELECTION, null, null);
+ try {
+ while (cursor.moveToNext()) {
+ long groupId = cursor.getLong(GroupQuery.ID);
+ String groupSourceId = cursor.getString(GroupQuery.SOURCE_ID);
+ boolean groupReadOnly = cursor.getInt(GroupQuery.GROUP_IS_READ_ONLY) != 0;
+ String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
+ String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
+ boolean defaultGroup = false;
+ if (!cursor.isNull(GroupQuery.IS_DEFAULT_GROUP)) {
+ defaultGroup = cursor.getInt(GroupQuery.IS_DEFAULT_GROUP) != 0;
+ }
+ if (defaultGroup) {
+ // Find the filter for this account and set the default group ID
+ for (ContactListFilter filter : results) {
+ if (filter.accountName.equals(accountName)
+ && filter.accountType.equals(accountType)) {
+ filter.groupId = groupId;
+ filter.groupSourceId = groupSourceId;
+ break;
+ }
+ }
+ } else {
+ String title = cursor.getString(GroupQuery.TITLE);
+ results.add(new ContactListFilter(accountType, accountName, groupId,
+ groupSourceId, groupReadOnly, title));
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+
+ Collections.sort(results);
+
+ mResults = results;
+ return results;
+ }
+
+ /* Runs on the UI thread */
+ @Override
+ public void deliverResult(List<ContactListFilter> results) {
+ if (!mStopped) {
+ super.deliverResult(results);
+ }
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mObserver == null) {
+ mObserver = new ForceLoadContentObserver();
+ getContext().getContentResolver().registerContentObserver(
+ Contacts.CONTENT_URI, true, mObserver);
+ }
+
+ mStopped = false;
+
+ if (mResults != null) {
+ deliverResult(mResults);
+ } else {
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ if (mObserver != null) {
+ getContext().getContentResolver().unregisterContentObserver(mObserver);
+ mObserver = null;
+ }
+
+ mResults = null;
+
+ // Attempt to cancel the current load task if possible.
+ cancelLoad();
+
+ // Make sure that any outstanding loads clean themselves up properly
+ mStopped = true;
+ }
+
+ @Override
+ protected void onReset() {
+ stopLoading();
+ }
+}
diff --git a/src/com/android/contacts/list/ContactListFilterView.java b/src/com/android/contacts/list/ContactListFilterView.java
new file mode 100644
index 0000000..6dc9bc3
--- /dev/null
+++ b/src/com/android/contacts/list/ContactListFilterView.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+import com.android.contacts.util.ThemeUtils;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * Contact list filter parameters.
+ */
+public class ContactListFilterView extends LinearLayout {
+
+ private ImageView mIcon;
+ private TextView mLabel;
+ private View mIndent;
+ private ContactListFilter mFilter;
+ private boolean mSingleAccount;
+ private int mActivatedBackground;
+
+ public ContactListFilterView(Context context) {
+ super(context);
+ }
+
+ public ContactListFilterView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setContactListFilter(ContactListFilter filter) {
+ mFilter = filter;
+ }
+
+ public ContactListFilter getContactListFilter() {
+ return mFilter;
+ }
+
+ public void setSingleAccount(boolean flag) {
+ this.mSingleAccount = flag;
+ }
+
+ public void bindView(boolean dropdown) {
+ if (dropdown) {
+ if (mActivatedBackground == 0) {
+ mActivatedBackground = ThemeUtils.getActivatedBackground(getContext().getTheme());
+ }
+ setBackgroundResource(mActivatedBackground);
+ }
+
+ if (mLabel == null) {
+ mIcon = (ImageView) findViewById(R.id.icon);
+ mLabel = (TextView) findViewById(R.id.label);
+ mIndent = findViewById(R.id.indent);
+ }
+
+ if (mFilter == null) {
+ mLabel.setText(R.string.contactsList);
+ return;
+ }
+
+ switch (mFilter.filterType) {
+ case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS: {
+ bindView(R.drawable.ic_menu_contacts_holo_light, R.string.list_filter_all_accounts,
+ dropdown);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_STARRED: {
+ bindView(R.drawable.ic_menu_star_holo_light, R.string.list_filter_all_starred,
+ dropdown);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_CUSTOM: {
+ bindView(R.drawable.ic_menu_settings_holo_light,
+ dropdown ? R.string.list_filter_customize : R.string.list_filter_custom,
+ dropdown);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY: {
+ bindView(0, R.string.list_filter_phones, dropdown);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_SINGLE_CONTACT: {
+ bindView(0, R.string.list_filter_single, dropdown);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_ACCOUNT: {
+ mIcon.setVisibility(View.VISIBLE);
+ if (mFilter.icon != null) {
+ mIcon.setImageDrawable(mFilter.icon);
+ } else {
+ mIcon.setImageResource(R.drawable.unknown_source);
+ }
+ mLabel.setText(mFilter.accountName);
+ if (dropdown) {
+ mIndent.setVisibility(View.GONE);
+ }
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_GROUP: {
+ mIcon.setVisibility(View.VISIBLE);
+ mIcon.setImageResource(R.drawable.ic_menu_display_all_holo_light);
+ mLabel.setText(mFilter.title);
+ if (dropdown) {
+ mIndent.setVisibility(mSingleAccount ? View.GONE : View.VISIBLE);
+ }
+ break;
+ }
+ }
+ }
+
+ private void bindView(int iconResource, int textResource, boolean dropdown) {
+ if (iconResource != 0) {
+ mIcon.setVisibility(View.VISIBLE);
+ mIcon.setImageResource(iconResource);
+ } else {
+ mIcon.setVisibility(dropdown ? View.INVISIBLE : View.GONE);
+ }
+
+ mLabel.setText(textResource);
+
+ if (mIndent != null) {
+ mIndent.setVisibility(View.GONE);
+ }
+ }
+}
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
new file mode 100644
index 0000000..c943534
--- /dev/null
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -0,0 +1,995 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.ContactPresenceIconUtil;
+import com.android.contacts.R;
+import com.android.contacts.widget.TextWithHighlighting;
+import com.android.contacts.widget.TextWithHighlightingFactory;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.CharArrayBuffer;
+import android.database.Cursor;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.text.SpannableString;
+import android.text.TextUtils;
+import android.text.TextUtils.TruncateAt;
+import android.text.style.ForegroundColorSpan;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView.SelectionBoundsAdjuster;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+
+/**
+ * A custom view for an item in the contact list.
+ */
+public class ContactListItemView extends ViewGroup
+ implements SelectionBoundsAdjuster
+{
+
+ private static final int QUICK_CONTACT_BADGE_STYLE =
+ com.android.internal.R.attr.quickContactBadgeStyleWindowMedium;
+
+ protected final Context mContext;
+
+ private final int mPreferredHeight;
+ private final int mVerticalDividerMargin;
+ private final int mPaddingTop;
+ private final int mPaddingRight;
+ private final int mPaddingBottom;
+ private final int mPaddingLeft;
+ private final int mGapBetweenImageAndText;
+ private final int mGapBetweenLabelAndData;
+ private final int mCallButtonPadding;
+ private final int mPresenceIconMargin;
+ private final int mPrefixHightlightColor;
+ private final int mHeaderTextColor;
+ private final int mHeaderTextIndent;
+ private final int mHeaderTextSize;
+
+ private Drawable mActivatedBackgroundDrawable;
+
+ private boolean mHorizontalDividerVisible = true;
+ private Drawable mHorizontalDividerDrawable;
+ private int mHorizontalDividerHeight;
+
+ private boolean mVerticalDividerVisible;
+ private Drawable mVerticalDividerDrawable;
+ private int mVerticalDividerWidth;
+
+ private boolean mHeaderVisible;
+ private Drawable mHeaderBackgroundDrawable;
+ private int mHeaderBackgroundHeight;
+ private TextView mHeaderTextView;
+
+ private boolean mQuickContactEnabled = true;
+ private QuickContactBadge mQuickContact;
+ private ImageView mPhotoView;
+ private TextView mNameTextView;
+ private TextView mPhoneticNameTextView;
+ private DontPressWithParentImageView mCallButton;
+ private TextView mLabelView;
+ private TextView mDataView;
+ private TextView mSnippetView;
+ private ImageView mPresenceIcon;
+
+ private char[] mHighlightedPrefix;
+
+ private int mDefaultPhotoViewSize;
+ private int mPhotoViewWidth;
+ private int mPhotoViewHeight;
+ private int mLine1Height;
+ private int mLine2Height;
+ private int mLine3Height;
+ private int mLine4Height;
+
+ private OnClickListener mCallButtonClickListener;
+ private TextWithHighlightingFactory mTextWithHighlightingFactory;
+ private CharArrayBuffer mNameBuffer = new CharArrayBuffer(128);
+ private CharArrayBuffer mDataBuffer = new CharArrayBuffer(128);
+ private CharArrayBuffer mHighlightedTextBuffer = new CharArrayBuffer(128);
+ private TextWithHighlighting mTextWithHighlighting;
+ private CharArrayBuffer mPhoneticNameBuffer = new CharArrayBuffer(128);
+
+ private CharSequence mUnknownNameText;
+
+ private boolean mActivatedStateSupported;
+
+ private ForegroundColorSpan mPrefixColorSpan;
+
+ private Rect mBoundsWithoutHeader = new Rect();
+
+ /**
+ * Special class to allow the parent to be pressed without being pressed itself.
+ * This way the line of a tab can be pressed, but the image itself is not.
+ */
+ // TODO: understand this
+ private static class DontPressWithParentImageView extends ImageView {
+
+ public DontPressWithParentImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void setPressed(boolean pressed) {
+ // If the parent is pressed, do not set to pressed.
+ if (pressed && ((View) getParent()).isPressed()) {
+ return;
+ }
+ super.setPressed(pressed);
+ }
+ }
+
+ public ContactListItemView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+
+ TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ContactListItemView);
+ mPreferredHeight = a.getDimensionPixelSize(
+ R.styleable.ContactListItemView_list_item_height, 0);
+ mActivatedBackgroundDrawable = a.getDrawable(
+ R.styleable.ContactListItemView_activated_background);
+ mHeaderBackgroundDrawable = a.getDrawable(
+ R.styleable.ContactListItemView_section_header_background);
+ mHorizontalDividerDrawable = a.getDrawable(
+ R.styleable.ContactListItemView_list_item_divider);
+ mVerticalDividerMargin = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_vertical_divider_margin, 0);
+ mPaddingTop = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_padding_top, 0);
+ mPaddingBottom = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_padding_bottom, 0);
+ mPaddingLeft = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_padding_left, 0);
+ mPaddingRight = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_padding_right, 0);
+ mGapBetweenImageAndText = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_gap_between_image_and_text, 0);
+ mGapBetweenLabelAndData = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_gap_between_label_and_data, 0);
+ mCallButtonPadding = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_call_button_padding, 0);
+ mPresenceIconMargin = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_presence_icon_margin, 0);
+ mDefaultPhotoViewSize = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_photo_size, 0);
+ mPrefixHightlightColor = a.getColor(
+ R.styleable.ContactListItemView_list_item_prefix_highlight_color, Color.GREEN);
+
+ mHeaderTextIndent = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_header_text_indent, 0);
+ mHeaderTextColor = a.getColor(
+ R.styleable.ContactListItemView_list_item_header_text_color, Color.BLACK);
+ mHeaderTextSize = a.getDimensionPixelSize(
+ R.styleable.ContactListItemView_list_item_header_text_size, 12);
+
+ a.recycle();
+
+ mHeaderBackgroundHeight = mHeaderBackgroundDrawable.getIntrinsicHeight();
+ mHorizontalDividerHeight = mHorizontalDividerDrawable.getIntrinsicHeight();
+
+ if (mActivatedBackgroundDrawable != null) {
+ mActivatedBackgroundDrawable.setCallback(this);
+ }
+ }
+
+ /**
+ * Installs a call button listener.
+ */
+ public void setOnCallButtonClickListener(OnClickListener callButtonClickListener) {
+ mCallButtonClickListener = callButtonClickListener;
+ }
+
+ public void setTextWithHighlightingFactory(TextWithHighlightingFactory factory) {
+ mTextWithHighlightingFactory = factory;
+ }
+
+ public void setUnknownNameText(CharSequence unknownNameText) {
+ mUnknownNameText = unknownNameText;
+ }
+
+ public void setQuickContactEnabled(boolean flag) {
+ mQuickContactEnabled = flag;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // We will match parent's width and wrap content vertically, but make sure
+ // height is no less than listPreferredItemHeight.
+ int width = resolveSize(0, widthMeasureSpec);
+ int height = 0;
+ int preferredHeight = mPreferredHeight;
+
+ mLine1Height = 0;
+ mLine2Height = 0;
+ mLine3Height = 0;
+ mLine4Height = 0;
+
+ // Obtain the natural dimensions of the name text (we only care about height)
+ if (isVisible(mNameTextView)) {
+ mNameTextView.measure(0, 0);
+ mLine1Height = mNameTextView.getMeasuredHeight();
+ }
+
+ if (isVisible(mPhoneticNameTextView)) {
+ mPhoneticNameTextView.measure(0, 0);
+ mLine2Height = mPhoneticNameTextView.getMeasuredHeight();
+ }
+
+ if (isVisible(mLabelView)) {
+ mLabelView.measure(0, 0);
+ mLine3Height = mLabelView.getMeasuredHeight();
+ }
+
+ if (isVisible(mDataView)) {
+ mDataView.measure(0, 0);
+ mLine3Height = Math.max(mLine3Height, mDataView.getMeasuredHeight());
+ }
+
+ if (isVisible(mSnippetView)) {
+ mSnippetView.measure(0, 0);
+ mLine4Height = mSnippetView.getMeasuredHeight();
+ }
+
+ height += mLine1Height + mLine2Height + mLine3Height + mLine4Height
+ + mPaddingTop + mPaddingBottom;
+
+ if (isVisible(mCallButton)) {
+ mCallButton.measure(0, 0);
+ }
+ if (isVisible(mPresenceIcon)) {
+ mPresenceIcon.measure(0, 0);
+ }
+
+ ensurePhotoViewSize();
+
+ height = Math.max(height, mPhotoViewHeight + mPaddingBottom + mPaddingTop);
+
+ if (mHorizontalDividerVisible) {
+ height += mHorizontalDividerHeight;
+ preferredHeight += mHorizontalDividerHeight;
+ }
+
+ height = Math.max(height, preferredHeight);
+
+ if (mHeaderVisible) {
+ mHeaderTextView.measure(
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(mHeaderBackgroundHeight, MeasureSpec.EXACTLY));
+ height += mHeaderBackgroundHeight;
+ }
+
+ setMeasuredDimension(width, height);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ int height = bottom - top;
+ int width = right - left;
+
+ // Determine the vertical bounds by laying out the header first.
+ int topBound = 0;
+ int bottomBound = height;
+
+ if (mHeaderVisible) {
+ mHeaderBackgroundDrawable.setBounds(
+ 0,
+ 0,
+ width,
+ mHeaderBackgroundHeight);
+ mHeaderTextView.layout(mHeaderTextIndent, 0, width, mHeaderBackgroundHeight);
+ topBound += mHeaderBackgroundHeight;
+ }
+
+ if (mHorizontalDividerVisible) {
+ mHorizontalDividerDrawable.setBounds(
+ 0,
+ height - mHorizontalDividerHeight,
+ width,
+ height);
+ bottomBound -= mHorizontalDividerHeight;
+ }
+
+ mBoundsWithoutHeader.set(0, topBound, width, bottomBound);
+
+ if (mActivatedStateSupported) {
+ mActivatedBackgroundDrawable.setBounds(mBoundsWithoutHeader);
+ }
+
+ topBound += mPaddingTop;
+ bottomBound -= mPaddingBottom;
+
+ // Positions of views on the left are fixed and so are those on the right side.
+ // The stretchable part of the layout is in the middle. So, we will start off
+ // by laying out the left and right sides. Then we will allocate the remainder
+ // to the text fields in the middle.
+
+ int leftBound = layoutLeftSide(height, topBound, bottomBound, mPaddingLeft);
+ int rightBound = layoutRightSide(height, topBound, width);
+
+ // Text lines, centered vertically
+ rightBound -= mPaddingRight;
+
+ // Center text vertically
+ int totalTextHeight = mLine1Height + mLine2Height + mLine3Height + mLine4Height;
+ int textTopBound = (bottomBound + topBound - totalTextHeight) / 2;
+
+ if (isVisible(mNameTextView)) {
+ mNameTextView.layout(leftBound,
+ textTopBound,
+ rightBound,
+ textTopBound + mLine1Height);
+ }
+
+ int dataLeftBound = leftBound;
+ if (isVisible(mPhoneticNameTextView)) {
+ mPhoneticNameTextView.layout(leftBound,
+ textTopBound + mLine1Height,
+ rightBound,
+ textTopBound + mLine1Height + mLine2Height);
+ }
+
+ if (isVisible(mLabelView)) {
+ dataLeftBound = leftBound + mLabelView.getMeasuredWidth();
+ mLabelView.layout(leftBound,
+ textTopBound + mLine1Height + mLine2Height,
+ dataLeftBound,
+ textTopBound + mLine1Height + mLine2Height + mLine3Height);
+ dataLeftBound += mGapBetweenLabelAndData;
+ }
+
+ if (isVisible(mDataView)) {
+ mDataView.layout(dataLeftBound,
+ textTopBound + mLine1Height + mLine2Height,
+ rightBound,
+ textTopBound + mLine1Height + mLine2Height + mLine3Height);
+ }
+
+ if (isVisible(mSnippetView)) {
+ mSnippetView.layout(leftBound,
+ textTopBound + mLine1Height + mLine2Height + mLine3Height,
+ rightBound,
+ textTopBound + mLine1Height + mLine2Height + mLine3Height + mLine4Height);
+ }
+ }
+
+ /**
+ * Performs layout of the left side of the view
+ *
+ * @return new left boundary
+ */
+ protected int layoutLeftSide(int height, int topBound, int bottomBound, int leftBound) {
+ View photoView = mQuickContact != null ? mQuickContact : mPhotoView;
+ if (photoView != null) {
+ // Center the photo vertically
+ int photoTop = topBound + (bottomBound - topBound - mPhotoViewHeight) / 2;
+ photoView.layout(
+ leftBound,
+ photoTop,
+ leftBound + mPhotoViewWidth,
+ photoTop + mPhotoViewHeight);
+ leftBound += mPhotoViewWidth + mGapBetweenImageAndText;
+ }
+ return leftBound;
+ }
+
+ /**
+ * Performs layout of the right side of the view
+ *
+ * @return new right boundary
+ */
+ protected int layoutRightSide(int height, int topBound, int rightBound) {
+ if (isVisible(mCallButton)) {
+ int buttonWidth = mCallButton.getMeasuredWidth();
+ rightBound -= buttonWidth;
+ mCallButton.layout(
+ rightBound,
+ topBound,
+ rightBound + buttonWidth,
+ height - mHorizontalDividerHeight);
+ mVerticalDividerVisible = true;
+ ensureVerticalDivider();
+ rightBound -= mVerticalDividerWidth;
+ mVerticalDividerDrawable.setBounds(
+ rightBound,
+ topBound + mVerticalDividerMargin,
+ rightBound + mVerticalDividerWidth,
+ height - mVerticalDividerMargin);
+ } else {
+ mVerticalDividerVisible = false;
+ }
+
+ if (isVisible(mPresenceIcon)) {
+ int iconWidth = mPresenceIcon.getMeasuredWidth();
+ rightBound -= mPresenceIconMargin + iconWidth;
+ mPresenceIcon.layout(
+ rightBound,
+ topBound,
+ rightBound + iconWidth,
+ height);
+ }
+ return rightBound;
+ }
+
+ @Override
+ public void adjustListItemSelectionBounds(Rect bounds) {
+ bounds.top += mBoundsWithoutHeader.top;
+ bounds.bottom = bounds.top + mBoundsWithoutHeader.height();
+ }
+
+ protected boolean isVisible(View view) {
+ return view != null && view.getVisibility() == View.VISIBLE;
+ }
+
+ /**
+ * Loads the drawable for the vertical divider if it has not yet been loaded.
+ */
+ private void ensureVerticalDivider() {
+ if (mVerticalDividerDrawable == null) {
+ mVerticalDividerDrawable = mContext.getResources().getDrawable(
+ R.drawable.divider_vertical_dark);
+ mVerticalDividerWidth = mVerticalDividerDrawable.getIntrinsicWidth();
+ }
+ }
+
+ /**
+ * Extracts width and height from the style
+ */
+ private void ensurePhotoViewSize() {
+ if (mPhotoViewWidth == 0 && mPhotoViewHeight == 0) {
+ if (mQuickContactEnabled) {
+ TypedArray a = mContext.obtainStyledAttributes(null,
+ com.android.internal.R.styleable.ViewGroup_Layout,
+ QUICK_CONTACT_BADGE_STYLE, 0);
+ mPhotoViewWidth = a.getLayoutDimension(
+ android.R.styleable.ViewGroup_Layout_layout_width,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ mPhotoViewHeight = a.getLayoutDimension(
+ android.R.styleable.ViewGroup_Layout_layout_height,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ a.recycle();
+ } else {
+ mPhotoViewWidth = mPhotoViewHeight = mDefaultPhotoViewSize;
+ }
+ }
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+ if (mActivatedStateSupported) {
+ mActivatedBackgroundDrawable.setState(getDrawableState());
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return who == mActivatedBackgroundDrawable || super.verifyDrawable(who);
+ }
+
+ @Override
+ public void jumpDrawablesToCurrentState() {
+ super.jumpDrawablesToCurrentState();
+ if (mActivatedStateSupported) {
+ mActivatedBackgroundDrawable.jumpToCurrentState();
+ }
+ }
+
+ @Override
+ public void dispatchDraw(Canvas canvas) {
+ if (mActivatedStateSupported) {
+ mActivatedBackgroundDrawable.draw(canvas);
+ }
+ if (mHeaderVisible) {
+ mHeaderBackgroundDrawable.draw(canvas);
+ }
+ if (mHorizontalDividerVisible) {
+ mHorizontalDividerDrawable.draw(canvas);
+ }
+ if (mVerticalDividerVisible) {
+ mVerticalDividerDrawable.draw(canvas);
+ }
+
+ super.dispatchDraw(canvas);
+ }
+
+ /**
+ * Sets the flag that determines whether a divider should drawn at the bottom
+ * of the view.
+ */
+ public void setDividerVisible(boolean visible) {
+ mHorizontalDividerVisible = visible;
+ }
+
+ /**
+ * Sets section header or makes it invisible if the title is null.
+ */
+ public void setSectionHeader(String title) {
+ if (!TextUtils.isEmpty(title)) {
+ if (mHeaderTextView == null) {
+ mHeaderTextView = new TextView(mContext);
+ mHeaderTextView.setTextColor(mHeaderTextColor);
+ mHeaderTextView.setTextSize(mHeaderTextSize);
+ mHeaderTextView.setTypeface(mHeaderTextView.getTypeface(), Typeface.BOLD);
+ mHeaderTextView.setGravity(Gravity.CENTER_VERTICAL);
+ addView(mHeaderTextView);
+ }
+ mHeaderTextView.setText(title);
+ mHeaderTextView.setVisibility(View.VISIBLE);
+ mHeaderVisible = true;
+ } else {
+ if (mHeaderTextView != null) {
+ mHeaderTextView.setVisibility(View.GONE);
+ }
+ mHeaderVisible = false;
+ }
+ }
+
+ /**
+ * Returns the quick contact badge, creating it if necessary.
+ */
+ public QuickContactBadge getQuickContact() {
+ if (!mQuickContactEnabled) {
+ throw new IllegalStateException("QuickContact is disabled for this view");
+ }
+ if (mQuickContact == null) {
+ mQuickContact = new QuickContactBadge(mContext, null, QUICK_CONTACT_BADGE_STYLE);
+ mQuickContact.setExcludeMimes(new String[] { Contacts.CONTENT_ITEM_TYPE });
+ addView(mQuickContact);
+ }
+ return mQuickContact;
+ }
+
+ /**
+ * Returns the photo view, creating it if necessary.
+ */
+ public ImageView getPhotoView() {
+ if (mPhotoView == null) {
+ if (mQuickContactEnabled) {
+ mPhotoView = new ImageView(mContext, null, QUICK_CONTACT_BADGE_STYLE);
+ } else {
+ mPhotoView = new ImageView(mContext);
+ }
+ // Quick contact style used above will set a background - remove it
+ mPhotoView.setBackgroundDrawable(null);
+ addView(mPhotoView);
+ }
+ return mPhotoView;
+ }
+
+ /**
+ * Removes the photo view. Should not be needed once we start handling different
+ * types of views as different types of views from the List's perspective.
+ */
+ public void removePhotoView() {
+ if (mPhotoView != null) {
+ removeView(mPhotoView);
+ mPhotoView = null;
+ }
+ if (mQuickContact != null) {
+ removeView(mQuickContact);
+ mQuickContact = null;
+ }
+ }
+
+ /**
+ * Sets a word prefix that will be highlighted if encountered in fields like
+ * name and search snippet.
+ * <p>
+ * NOTE: must be all upper-case
+ */
+ public void setHighlightedPrefix(char[] upperCasePrefix) {
+ mHighlightedPrefix = upperCasePrefix;
+ }
+
+ /**
+ * Returns the text view for the contact name, creating it if necessary.
+ */
+ public TextView getNameTextView() {
+ if (mNameTextView == null) {
+ mNameTextView = new TextView(mContext);
+ mNameTextView.setSingleLine(true);
+ mNameTextView.setEllipsize(getTextEllipsis());
+ mNameTextView.setTextAppearance(mContext, android.R.style.TextAppearance_Medium);
+ mNameTextView.setGravity(Gravity.CENTER_VERTICAL);
+ addView(mNameTextView);
+ }
+ return mNameTextView;
+ }
+
+ /**
+ * Adds a call button using the supplied arguments as an id and tag.
+ */
+ public void showCallButton(int id, int tag) {
+ if (mCallButton == null) {
+ mCallButton = new DontPressWithParentImageView(mContext, null);
+ mCallButton.setId(id);
+ mCallButton.setOnClickListener(mCallButtonClickListener);
+ mCallButton.setBackgroundResource(R.drawable.call_background);
+ mCallButton.setImageResource(android.R.drawable.sym_action_call);
+ mCallButton.setPadding(mCallButtonPadding, 0, mCallButtonPadding, 0);
+ mCallButton.setScaleType(ScaleType.CENTER);
+ addView(mCallButton);
+ }
+
+ mCallButton.setTag(tag);
+ mCallButton.setVisibility(View.VISIBLE);
+ }
+
+ public void hideCallButton() {
+ if (mCallButton != null) {
+ mCallButton.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Adds or updates a text view for the phonetic name.
+ */
+ public void setPhoneticName(char[] text, int size) {
+ if (text == null || size == 0) {
+ if (mPhoneticNameTextView != null) {
+ mPhoneticNameTextView.setVisibility(View.GONE);
+ }
+ } else {
+ getPhoneticNameTextView();
+ mPhoneticNameTextView.setText(text, 0, size);
+ mPhoneticNameTextView.setVisibility(VISIBLE);
+ }
+ }
+
+ /**
+ * Returns the text view for the phonetic name, creating it if necessary.
+ */
+ public TextView getPhoneticNameTextView() {
+ if (mPhoneticNameTextView == null) {
+ mPhoneticNameTextView = new TextView(mContext);
+ mPhoneticNameTextView.setSingleLine(true);
+ mPhoneticNameTextView.setEllipsize(getTextEllipsis());
+ mPhoneticNameTextView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
+ mPhoneticNameTextView.setTypeface(mPhoneticNameTextView.getTypeface(), Typeface.BOLD);
+ addView(mPhoneticNameTextView);
+ }
+ return mPhoneticNameTextView;
+ }
+
+ /**
+ * Adds or updates a text view for the data label.
+ */
+ public void setLabel(CharSequence text) {
+ if (TextUtils.isEmpty(text)) {
+ if (mLabelView != null) {
+ mLabelView.setVisibility(View.GONE);
+ }
+ } else {
+ getLabelView();
+ mLabelView.setText(text);
+ mLabelView.setVisibility(VISIBLE);
+ }
+ }
+
+ /**
+ * Adds or updates a text view for the data label.
+ */
+ public void setLabel(char[] text, int size) {
+ if (text == null || size == 0) {
+ if (mLabelView != null) {
+ mLabelView.setVisibility(View.GONE);
+ }
+ } else {
+ getLabelView();
+ mLabelView.setText(text, 0, size);
+ mLabelView.setVisibility(VISIBLE);
+ }
+ }
+
+ /**
+ * Returns the text view for the data label, creating it if necessary.
+ */
+ public TextView getLabelView() {
+ if (mLabelView == null) {
+ mLabelView = new TextView(mContext);
+ mLabelView.setSingleLine(true);
+ mLabelView.setEllipsize(getTextEllipsis());
+ mLabelView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
+ mLabelView.setTypeface(mLabelView.getTypeface(), Typeface.BOLD);
+ addView(mLabelView);
+ }
+ return mLabelView;
+ }
+
+ /**
+ * Adds or updates a text view for the data element.
+ */
+ public void setData(char[] text, int size) {
+ if (text == null || size == 0) {
+ if (mDataView != null) {
+ mDataView.setVisibility(View.GONE);
+ }
+ return;
+ } else {
+ getDataView();
+ mDataView.setText(text, 0, size);
+ mDataView.setVisibility(VISIBLE);
+ }
+ }
+
+ /**
+ * Returns the text view for the data text, creating it if necessary.
+ */
+ public TextView getDataView() {
+ if (mDataView == null) {
+ mDataView = new TextView(mContext);
+ mDataView.setSingleLine(true);
+ mDataView.setEllipsize(getTextEllipsis());
+ mDataView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
+ addView(mDataView);
+ }
+ return mDataView;
+ }
+
+ /**
+ * Adds or updates a text view for the search snippet.
+ */
+ public void setSnippet(String text) {
+ if (TextUtils.isEmpty(text)) {
+ if (mSnippetView != null) {
+ mSnippetView.setVisibility(View.GONE);
+ }
+ } else {
+ getSnippetView();
+ setTextWithPrefixHighlighting(mSnippetView, text);
+ mSnippetView.setVisibility(VISIBLE);
+ }
+ }
+
+ /**
+ * Returns the text view for the search snippet, creating it if necessary.
+ */
+ public TextView getSnippetView() {
+ if (mSnippetView == null) {
+ mSnippetView = new TextView(mContext);
+ mSnippetView.setSingleLine(true);
+ mSnippetView.setEllipsize(getTextEllipsis());
+ mSnippetView.setTextAppearance(mContext, android.R.style.TextAppearance_Small);
+ mSnippetView.setTypeface(mSnippetView.getTypeface(), Typeface.BOLD);
+ addView(mSnippetView);
+ }
+ return mSnippetView;
+ }
+
+ /**
+ * Adds or updates the presence icon view.
+ */
+ public void setPresence(Drawable icon) {
+ if (icon != null) {
+ if (mPresenceIcon == null) {
+ mPresenceIcon = new ImageView(mContext);
+ addView(mPresenceIcon);
+ }
+ mPresenceIcon.setImageDrawable(icon);
+ mPresenceIcon.setScaleType(ScaleType.CENTER);
+ mPresenceIcon.setVisibility(View.VISIBLE);
+ } else {
+ if (mPresenceIcon != null) {
+ mPresenceIcon.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private TruncateAt getTextEllipsis() {
+ return mActivatedStateSupported ? TruncateAt.START : TruncateAt.MARQUEE;
+ }
+
+ public void showDisplayName(Cursor cursor, int nameColumnIndex, boolean highlightingEnabled,
+ int alternativeNameColumnIndex) {
+ cursor.copyStringToBuffer(nameColumnIndex, mNameBuffer);
+ TextView nameView = getNameTextView();
+ int size = mNameBuffer.sizeCopied;
+ if (size != 0) {
+ if (mHighlightedPrefix != null) {
+ setTextWithPrefixHighlighting(nameView, mNameBuffer);
+ } else if (highlightingEnabled) {
+ if (mTextWithHighlighting == null) {
+ mTextWithHighlighting =
+ mTextWithHighlightingFactory.createTextWithHighlighting();
+ }
+ cursor.copyStringToBuffer(alternativeNameColumnIndex, mHighlightedTextBuffer);
+ mTextWithHighlighting.setText(mNameBuffer, mHighlightedTextBuffer);
+ nameView.setText(mTextWithHighlighting);
+ } else {
+ nameView.setText(mNameBuffer.data, 0, size);
+ }
+ } else {
+ nameView.setText(mUnknownNameText);
+ }
+ }
+
+ public void showPhoneticName(Cursor cursor, int phoneticNameColumnIndex) {
+ cursor.copyStringToBuffer(phoneticNameColumnIndex, mPhoneticNameBuffer);
+ int phoneticNameSize = mPhoneticNameBuffer.sizeCopied;
+ if (phoneticNameSize != 0) {
+ setPhoneticName(mPhoneticNameBuffer.data, phoneticNameSize);
+ } else {
+ setPhoneticName(null, 0);
+ }
+ }
+
+ /**
+ * Sets the proper icon (star or presence or nothing)
+ */
+ public void showPresence(Cursor cursor, int presenceColumnIndex, int capabilityColumnIndex) {
+ Drawable icon = null;
+ if (!cursor.isNull(presenceColumnIndex)) {
+ int status = cursor.getInt(presenceColumnIndex);
+ int chatCapability = 0;
+ if (capabilityColumnIndex != 0 && !cursor.isNull(presenceColumnIndex)) {
+ chatCapability = cursor.getInt(capabilityColumnIndex);
+ }
+ icon = ContactPresenceIconUtil.getChatCapabilityIcon(
+ getContext(), status, chatCapability);
+ }
+ setPresence(icon);
+ }
+
+ /**
+ * Shows search snippet.
+ */
+ public void showSnippet(Cursor cursor, int summarySnippetMimetypeColumnIndex,
+ int summarySnippetData1ColumnIndex, int summarySnippetData4ColumnIndex) {
+ if (cursor.getColumnCount() <= summarySnippetMimetypeColumnIndex) {
+ setSnippet(null);
+ return;
+ }
+
+ String snippet = null;
+ String snippetMimeType = cursor.getString(summarySnippetMimetypeColumnIndex);
+ if (Email.CONTENT_ITEM_TYPE.equals(snippetMimeType)
+ || Nickname.CONTENT_ITEM_TYPE.equals(snippetMimeType)
+ || Phone.CONTENT_ITEM_TYPE.equals(snippetMimeType)) {
+ String value = cursor.getString(summarySnippetData1ColumnIndex);
+ if (!TextUtils.isEmpty(value)) {
+ snippet = value;
+ }
+ } else if (Organization.CONTENT_ITEM_TYPE.equals(snippetMimeType)) {
+ String company = cursor.getString(summarySnippetData1ColumnIndex);
+ String title = cursor.getString(summarySnippetData4ColumnIndex);
+ if (!TextUtils.isEmpty(company)) {
+ if (!TextUtils.isEmpty(title)) {
+ snippet = company + " / " + title;
+ } else {
+ snippet = company;
+ }
+ } else if (!TextUtils.isEmpty(title)) {
+ snippet = title;
+ }
+ }
+
+ setSnippet(snippet);
+ }
+
+ /**
+ * Shows data element (e.g. phone number).
+ */
+ public void showData(Cursor cursor, int dataColumnIndex) {
+ cursor.copyStringToBuffer(dataColumnIndex, mDataBuffer);
+ setData(mDataBuffer.data, mDataBuffer.sizeCopied);
+ }
+
+ public void setActivatedStateSupported(boolean flag) {
+ this.mActivatedStateSupported = flag;
+ }
+
+ /**
+ * Sets text on the given text view, highlighting the word that matches
+ * the given prefix (see {@link #setHighlightedPrefix}).
+ */
+ private void setTextWithPrefixHighlighting(TextView textView, String text) {
+ mHighlightedTextBuffer.sizeCopied =
+ Math.min(text.length(), mHighlightedTextBuffer.data.length);
+ text.getChars(0, mHighlightedTextBuffer.sizeCopied, mHighlightedTextBuffer.data, 0);
+ setTextWithPrefixHighlighting(textView, mHighlightedTextBuffer);
+ }
+
+ /**
+ * Sets text on the given text view, highlighting the word that matches
+ * the given prefix (see {@link #setHighlightedPrefix}).
+ */
+ private void setTextWithPrefixHighlighting(TextView textView, CharArrayBuffer text) {
+ int index = indexOfWordPrefix(text, mHighlightedPrefix);
+ if (index != -1) {
+ if (mPrefixColorSpan == null) {
+ mPrefixColorSpan = new ForegroundColorSpan(mPrefixHightlightColor);
+ }
+
+ String string = new String(text.data, 0, text.sizeCopied);
+ SpannableString name = new SpannableString(string);
+ name.setSpan(mPrefixColorSpan, index, index + mHighlightedPrefix.length, 0 /* flags */);
+ textView.setText(name);
+ } else {
+ textView.setText(text.data, 0, text.sizeCopied);
+ }
+ }
+
+ /**
+ * Finds the index of the word that starts with the given prefix. If not found,
+ * returns -1.
+ */
+ private int indexOfWordPrefix(CharArrayBuffer buffer, char[] prefix) {
+ if (prefix == null || prefix.length == 0) {
+ return -1;
+ }
+
+ char[] string1 = buffer.data;
+ int count1 = buffer.sizeCopied;
+ int count2 = prefix.length;
+
+ int size = count2;
+ int i = 0;
+ while (i < count1) {
+
+ // Skip non-word characters
+ while (i < count1 && !Character.isLetterOrDigit(string1[i])) {
+ i++;
+ }
+
+ if (i + size > count1) {
+ return -1;
+ }
+
+ // Compare the prefixes
+ int j;
+ for (j = 0; j < size; j++) {
+ if (Character.toUpperCase(string1[i+j]) != prefix[j]) {
+ break;
+ }
+ }
+ if (j == size) {
+ return i;
+ }
+
+ // Skip this word
+ while (i < count1 && Character.isLetterOrDigit(string1[i])) {
+ i++;
+ }
+ }
+
+ return -1;
+ }
+
+ @Override
+ public void requestLayout() {
+ // We will assume that once measured this will not need to resize
+ // itself, so there is no need to pass the layout request to the parent
+ // view (ListView).
+ forceLayout();
+ }
+}
diff --git a/src/com/android/contacts/list/ContactListPinnedHeaderView.java b/src/com/android/contacts/list/ContactListPinnedHeaderView.java
new file mode 100644
index 0000000..e850511
--- /dev/null
+++ b/src/com/android/contacts/list/ContactListPinnedHeaderView.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * A custom view for the pinned section header shown at the top of the contact list.
+ */
+public class ContactListPinnedHeaderView extends ViewGroup {
+
+ protected final Context mContext;
+
+ private final int mHeaderTextColor;
+ private final int mHeaderTextIndent;
+ private final int mHeaderTextSize;
+
+ private Drawable mHeaderBackgroundDrawable;
+ private int mHeaderBackgroundHeight;
+ private TextView mHeaderTextView;
+
+ public ContactListPinnedHeaderView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mContext = context;
+
+ TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ContactListItemView);
+
+ mHeaderBackgroundDrawable = a.getDrawable(
+ R.styleable.ContactListItemView_section_header_background);
+ mHeaderTextIndent = a.getDimensionPixelOffset(
+ R.styleable.ContactListItemView_list_item_header_text_indent, 0);
+ mHeaderTextColor = a.getColor(
+ R.styleable.ContactListItemView_list_item_header_text_color, Color.BLACK);
+ mHeaderTextSize = a.getDimensionPixelSize(
+ R.styleable.ContactListItemView_list_item_header_text_size, 12);
+
+ a.recycle();
+
+ mHeaderBackgroundHeight = mHeaderBackgroundDrawable.getIntrinsicHeight();
+
+ mHeaderTextView = new TextView(mContext);
+ mHeaderTextView.setTextColor(mHeaderTextColor);
+ mHeaderTextView.setTextSize(mHeaderTextSize);
+ mHeaderTextView.setTypeface(mHeaderTextView.getTypeface(), Typeface.BOLD);
+ mHeaderTextView.setGravity(Gravity.CENTER_VERTICAL);
+ addView(mHeaderTextView);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ // We will match parent's width and wrap content vertically.
+ int width = resolveSize(0, widthMeasureSpec);
+
+ mHeaderTextView.measure(
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(mHeaderBackgroundHeight, MeasureSpec.EXACTLY));
+
+ setMeasuredDimension(width, mHeaderBackgroundHeight);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ int width = right - left;
+ mHeaderBackgroundDrawable.setBounds(0, 0, width, mHeaderBackgroundHeight);
+ mHeaderTextView.layout(mHeaderTextIndent, 0, width, mHeaderBackgroundHeight);
+ }
+
+ @Override
+ public void dispatchDraw(Canvas canvas) {
+ mHeaderBackgroundDrawable.draw(canvas);
+ super.dispatchDraw(canvas);
+ }
+
+ /**
+ * Sets section header or makes it invisible if the title is null.
+ */
+ public void setSectionHeader(String title) {
+ if (!TextUtils.isEmpty(title)) {
+ mHeaderTextView.setText(title);
+ mHeaderTextView.setVisibility(View.VISIBLE);
+ } else {
+ mHeaderTextView.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void requestLayout() {
+ // We will assume that once measured this will not need to resize
+ // itself, so there is no need to pass the layout request to the parent
+ // view (ListView).
+ forceLayout();
+ }
+}
diff --git a/src/com/android/contacts/list/ContactNameHighlightingAnimation.java b/src/com/android/contacts/list/ContactNameHighlightingAnimation.java
new file mode 100644
index 0000000..05881ce
--- /dev/null
+++ b/src/com/android/contacts/list/ContactNameHighlightingAnimation.java
@@ -0,0 +1,61 @@
+/*
+ * 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.list;
+
+import com.android.contacts.widget.TextHighlightingAnimation;
+
+import android.view.View;
+import android.widget.ListView;
+
+/**
+ * A {@link TextHighlightingAnimation} that redraws just the contact display name in a
+ * list item.
+ */
+public class ContactNameHighlightingAnimation extends TextHighlightingAnimation {
+ private final ListView mListView;
+ private boolean mSavedScrollingCacheEnabledFlag;
+
+ public ContactNameHighlightingAnimation(ListView listView, int duration) {
+ super(duration);
+ this.mListView = listView;
+ }
+
+ /**
+ * Redraws all visible items of the list corresponding to contacts
+ */
+ @Override
+ protected void invalidate() {
+ int childCount = mListView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View itemView = mListView.getChildAt(i);
+ if (itemView instanceof ContactListItemView) {
+ final ContactListItemView view = (ContactListItemView)itemView;
+ view.getNameTextView().invalidate();
+ }
+ }
+ }
+
+ @Override
+ protected void onAnimationStarted() {
+ mSavedScrollingCacheEnabledFlag = mListView.isScrollingCacheEnabled();
+ mListView.setScrollingCacheEnabled(false);
+ }
+
+ @Override
+ protected void onAnimationEnded() {
+ mListView.setScrollingCacheEnabled(mSavedScrollingCacheEnabledFlag);
+ }
+}
diff --git a/src/com/android/contacts/list/ContactPhotoLoader.java b/src/com/android/contacts/list/ContactPhotoLoader.java
new file mode 100644
index 0000000..a6f40b6
--- /dev/null
+++ b/src/com/android/contacts/list/ContactPhotoLoader.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.google.android.collect.Lists;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.provider.ContactsContract.Contacts.Photo;
+import android.provider.ContactsContract.Data;
+import android.util.Log;
+import android.widget.ImageView;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Asynchronously loads contact photos and maintains cache of photos. The class is
+ * mostly single-threaded. The only two methods accessed by the loader thread are
+ * {@link #cacheBitmap} and {@link #obtainPhotoIdsAndUrisToLoad}. Those methods access concurrent
+ * hash maps shared with the main thread.
+ */
+public class ContactPhotoLoader implements Callback {
+ private static final String TAG = "ContactPhotoLoader";
+ private static final String LOADER_THREAD_NAME = "ContactPhotoLoader";
+
+ /**
+ * Type of message sent by the UI thread to itself to indicate that some photos
+ * need to be loaded.
+ */
+ private static final int MESSAGE_REQUEST_LOADING = 1;
+
+ /**
+ * Type of message sent by the loader thread to indicate that some photos have
+ * been loaded.
+ */
+ private static final int MESSAGE_PHOTOS_LOADED = 2;
+
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ private final String[] COLUMNS = new String[] { Photo._ID, Photo.PHOTO };
+
+ /**
+ * The resource ID of the image to be used when the photo is unavailable or being
+ * loaded.
+ */
+ private final int mDefaultResourceId;
+
+ /**
+ * Maintains the state of a particular photo.
+ */
+ private static class BitmapHolder {
+ private static final int NEEDED = 0;
+ private static final int LOADING = 1;
+ private static final int LOADED = 2;
+ private static final int LOADED_NEEDS_RELOAD = 3;
+
+ int state;
+ Bitmap bitmap;
+ SoftReference<Bitmap> bitmapRef;
+ }
+
+ /**
+ * A soft cache for photos.
+ */
+ private final ConcurrentHashMap<Object, BitmapHolder> mBitmapCache =
+ new ConcurrentHashMap<Object, BitmapHolder>();
+
+ /**
+ * A map from ImageView to the corresponding photo ID. Please note that this
+ * photo ID may change before the photo loading request is started.
+ */
+ private final ConcurrentHashMap<ImageView, Object> mPendingRequests =
+ new ConcurrentHashMap<ImageView, Object>();
+
+ /**
+ * Handler for messages sent to the UI thread.
+ */
+ private final Handler mMainThreadHandler = new Handler(this);
+
+ /**
+ * Thread responsible for loading photos from the database. Created upon
+ * the first request.
+ */
+ private LoaderThread mLoaderThread;
+
+ /**
+ * A gate to make sure we only send one instance of MESSAGE_PHOTOS_NEEDED at a time.
+ */
+ private boolean mLoadingRequested;
+
+ /**
+ * Flag indicating if the image loading is paused.
+ */
+ private boolean mPaused;
+
+ private final Context mContext;
+
+ /**
+ * Constructor.
+ *
+ * @param context content context
+ * @param defaultResourceId the image resource ID to be used when there is
+ * no photo for a contact
+ */
+ public ContactPhotoLoader(Context context, int defaultResourceId) {
+ mDefaultResourceId = defaultResourceId;
+ mContext = context;
+ }
+
+ /**
+ * Load photo into the supplied image view. If the photo is already cached,
+ * it is displayed immediately. Otherwise a request is sent to load the photo
+ * from the database.
+ */
+ public void loadPhoto(ImageView view, long photoId) {
+ if (photoId == 0) {
+ // No photo is needed
+ view.setImageResource(mDefaultResourceId);
+ mPendingRequests.remove(view);
+ } else {
+ loadPhotoByIdOrUri(view, photoId);
+ }
+ }
+
+ /**
+ * Load photo into the supplied image view. If the photo is already cached,
+ * it is displayed immediately. Otherwise a request is sent to load the photo
+ * from the location specified by the URI.
+ */
+ public void loadPhoto(ImageView view, Uri photoUri) {
+ if (photoUri == null) {
+ // No photo is needed
+ view.setImageResource(mDefaultResourceId);
+ mPendingRequests.remove(view);
+ } else {
+ loadPhotoByIdOrUri(view, photoUri);
+ }
+ }
+
+ private void loadPhotoByIdOrUri(ImageView view, Object key) {
+ boolean loaded = loadCachedPhoto(view, key);
+ if (loaded) {
+ mPendingRequests.remove(view);
+ } else {
+ mPendingRequests.put(view, key);
+ if (!mPaused) {
+ // Send a request to start loading photos
+ requestLoading();
+ }
+ }
+ }
+
+ /**
+ * Mark all cached photos for reloading. We can continue using cache but should
+ * also make sure the photos haven't changed in the background and notify the views
+ * if so.
+ */
+ public void refreshCache() {
+ for (BitmapHolder holder : mBitmapCache.values()) {
+ if (holder.state == BitmapHolder.LOADED) {
+ holder.state = BitmapHolder.LOADED_NEEDS_RELOAD;
+ }
+ }
+ }
+
+ /**
+ * Checks if the photo is present in cache. If so, sets the photo on the view,
+ * otherwise sets the state of the photo to {@link BitmapHolder#NEEDED} and
+ * temporarily set the image to the default resource ID.
+ */
+ private boolean loadCachedPhoto(ImageView view, Object key) {
+ BitmapHolder holder = mBitmapCache.get(key);
+ if (holder == null) {
+ holder = new BitmapHolder();
+ mBitmapCache.put(key, holder);
+ } else {
+ boolean loaded = (holder.state == BitmapHolder.LOADED);
+ boolean loadedNeedsReload = (holder.state == BitmapHolder.LOADED_NEEDS_RELOAD);
+ if (loadedNeedsReload) {
+ holder.state = BitmapHolder.NEEDED;
+ }
+
+ // Null bitmap reference means that database contains no bytes for the photo
+ if ((loaded || loadedNeedsReload) && holder.bitmapRef == null) {
+ view.setImageResource(mDefaultResourceId);
+ return loaded;
+ }
+
+ if (holder.bitmapRef != null) {
+ Bitmap bitmap = holder.bitmapRef.get();
+ if (bitmap != null) {
+ view.setImageBitmap(bitmap);
+ return loaded;
+ }
+
+ // Null bitmap means that the soft reference was released by the GC
+ // and we need to reload the photo.
+ holder.bitmapRef = null;
+ }
+ }
+
+ // The bitmap has not been loaded - should display the placeholder image.
+ view.setImageResource(mDefaultResourceId);
+ holder.state = BitmapHolder.NEEDED;
+ return false;
+ }
+
+ /**
+ * Stops loading images, kills the image loader thread and clears all caches.
+ */
+ public void stop() {
+ pause();
+
+ if (mLoaderThread != null) {
+ mLoaderThread.quit();
+ mLoaderThread = null;
+ }
+
+ mPendingRequests.clear();
+ mBitmapCache.clear();
+ }
+
+ public void clear() {
+ mPendingRequests.clear();
+ mBitmapCache.clear();
+ }
+
+ /**
+ * Temporarily stops loading photos from the database.
+ */
+ public void pause() {
+ mPaused = true;
+ }
+
+ /**
+ * Resumes loading photos from the database.
+ */
+ public void resume() {
+ mPaused = false;
+ if (!mPendingRequests.isEmpty()) {
+ requestLoading();
+ }
+ }
+
+ /**
+ * Sends a message to this thread itself to start loading images. If the current
+ * view contains multiple image views, all of those image views will get a chance
+ * to request their respective photos before any of those requests are executed.
+ * This allows us to load images in bulk.
+ */
+ private void requestLoading() {
+ if (!mLoadingRequested) {
+ mLoadingRequested = true;
+ mMainThreadHandler.sendEmptyMessage(MESSAGE_REQUEST_LOADING);
+ }
+ }
+
+ /**
+ * Processes requests on the main thread.
+ */
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_REQUEST_LOADING: {
+ mLoadingRequested = false;
+ if (!mPaused) {
+ if (mLoaderThread == null) {
+ mLoaderThread = new LoaderThread(mContext.getContentResolver());
+ mLoaderThread.start();
+ }
+
+ mLoaderThread.requestLoading();
+ }
+ return true;
+ }
+
+ case MESSAGE_PHOTOS_LOADED: {
+ if (!mPaused) {
+ processLoadedImages();
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Goes over pending loading requests and displays loaded photos. If some of the
+ * photos still haven't been loaded, sends another request for image loading.
+ */
+ private void processLoadedImages() {
+ Iterator<ImageView> iterator = mPendingRequests.keySet().iterator();
+ while (iterator.hasNext()) {
+ ImageView view = iterator.next();
+ Object key = mPendingRequests.get(view);
+ boolean loaded = loadCachedPhoto(view, key);
+ if (loaded) {
+ iterator.remove();
+ }
+ }
+
+ softenCache();
+
+ if (!mPendingRequests.isEmpty()) {
+ requestLoading();
+ }
+ }
+
+ /**
+ * Removes strong references to loaded bitmaps to allow them to be garbage collected
+ * if needed.
+ */
+ private void softenCache() {
+ for (BitmapHolder holder : mBitmapCache.values()) {
+ holder.bitmap = null;
+ }
+ }
+
+ /**
+ * Stores the supplied bitmap in cache.
+ */
+ private void cacheBitmap(Object key, byte[] bytes) {
+ if (mPaused) {
+ return;
+ }
+
+ BitmapHolder holder = new BitmapHolder();
+ holder.state = BitmapHolder.LOADED;
+ if (bytes != null) {
+ try {
+ Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null);
+ holder.bitmap = bitmap;
+ holder.bitmapRef = new SoftReference<Bitmap>(bitmap);
+ } catch (OutOfMemoryError e) {
+ // Do nothing - the photo will appear to be missing
+ }
+ }
+ mBitmapCache.put(key, holder);
+ }
+
+ /**
+ * Populates an array of photo IDs that need to be loaded.
+ */
+ private void obtainPhotoIdsAndUrisToLoad(ArrayList<Long> photoIds,
+ ArrayList<String> photoIdsAsStrings, ArrayList<Uri> uris) {
+ photoIds.clear();
+ photoIdsAsStrings.clear();
+ uris.clear();
+
+ /*
+ * Since the call is made from the loader thread, the map could be
+ * changing during the iteration. That's not really a problem:
+ * ConcurrentHashMap will allow those changes to happen without throwing
+ * exceptions. Since we may miss some requests in the situation of
+ * concurrent change, we will need to check the map again once loading
+ * is complete.
+ */
+ Iterator<Object> iterator = mPendingRequests.values().iterator();
+ while (iterator.hasNext()) {
+ Object key = iterator.next();
+ BitmapHolder holder = mBitmapCache.get(key);
+ if (holder != null && holder.state == BitmapHolder.NEEDED) {
+ // Assuming atomic behavior
+ holder.state = BitmapHolder.LOADING;
+ if (key instanceof Long) {
+ photoIds.add((Long)key);
+ photoIdsAsStrings.add(key.toString());
+ } else {
+ uris.add((Uri)key);
+ }
+ }
+ }
+ }
+
+ /**
+ * The thread that performs loading of photos from the database.
+ */
+ private class LoaderThread extends HandlerThread implements Callback {
+ private static final int BUFFER_SIZE = 1024*16;
+
+ private final ContentResolver mResolver;
+ private final StringBuilder mStringBuilder = new StringBuilder();
+ private final ArrayList<Long> mPhotoIds = Lists.newArrayList();
+ private final ArrayList<String> mPhotoIdsAsStrings = Lists.newArrayList();
+ private final ArrayList<Uri> mPhotoUris = Lists.newArrayList();
+ private Handler mLoaderThreadHandler;
+ private byte mBuffer[];
+
+ public LoaderThread(ContentResolver resolver) {
+ super(LOADER_THREAD_NAME);
+ mResolver = resolver;
+ }
+
+ /**
+ * Sends a message to this thread to load requested photos.
+ */
+ public void requestLoading() {
+ if (mLoaderThreadHandler == null) {
+ mLoaderThreadHandler = new Handler(getLooper(), this);
+ }
+ mLoaderThreadHandler.sendEmptyMessage(0);
+ }
+
+ /**
+ * Receives the above message, loads photos and then sends a message
+ * to the main thread to process them.
+ */
+ public boolean handleMessage(Message msg) {
+ loadPhotosFromDatabase();
+ return true;
+ }
+
+ private void loadPhotosFromDatabase() {
+ obtainPhotoIdsAndUrisToLoad(mPhotoIds, mPhotoIdsAsStrings, mPhotoUris);
+
+ int count = mPhotoIds.size();
+ if (count != 0) {
+ mStringBuilder.setLength(0);
+ mStringBuilder.append(Photo._ID + " IN(");
+ for (int i = 0; i < count; i++) {
+ if (i != 0) {
+ mStringBuilder.append(',');
+ }
+ mStringBuilder.append('?');
+ }
+ mStringBuilder.append(')');
+
+ Cursor cursor = null;
+ try {
+ cursor = mResolver.query(Data.CONTENT_URI,
+ COLUMNS,
+ mStringBuilder.toString(),
+ mPhotoIdsAsStrings.toArray(EMPTY_STRING_ARRAY),
+ null);
+
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ Long id = cursor.getLong(0);
+ byte[] bytes = cursor.getBlob(1);
+ cacheBitmap(id, bytes);
+ mPhotoIds.remove(id);
+ }
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ // Remaining photos were not found in the database - mark the cache accordingly.
+ count = mPhotoIds.size();
+ for (int i = 0; i < count; i++) {
+ cacheBitmap(mPhotoIds.get(i), null);
+ }
+ mMainThreadHandler.sendEmptyMessage(MESSAGE_PHOTOS_LOADED);
+ }
+
+ count = mPhotoUris.size();
+ for (int i = 0; i < count; i++) {
+ Uri uri = mPhotoUris.get(i);
+ if (mBuffer == null) {
+ mBuffer = new byte[BUFFER_SIZE];
+ }
+ try {
+ InputStream is = mResolver.openInputStream(uri);
+ if (is != null) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ int size;
+ while ((size = is.read(mBuffer)) != -1) {
+ baos.write(mBuffer, 0, size);
+ }
+ } finally {
+ is.close();
+ }
+ cacheBitmap(uri, baos.toByteArray());
+ mMainThreadHandler.sendEmptyMessage(MESSAGE_PHOTOS_LOADED);
+ } else {
+ Log.v(TAG, "Cannot load photo " + uri);
+ cacheBitmap(uri, null);
+ }
+ } catch (Exception ex) {
+ Log.v(TAG, "Cannot load photo " + uri, ex);
+ cacheBitmap(uri, null);
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/contacts/list/ContactPickerFragment.java b/src/com/android/contacts/list/ContactPickerFragment.java
new file mode 100644
index 0000000..17e3fb9
--- /dev/null
+++ b/src/com/android/contacts/list/ContactPickerFragment.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.ContactsSearchManager;
+import com.android.contacts.R;
+import com.android.contacts.list.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+
+/**
+ * Fragment for the contact list used for browsing contacts (as compared to
+ * picking a contact with one of the PICK or SHORTCUT intents).
+ */
+public class ContactPickerFragment extends ContactEntryListFragment<ContactEntryListAdapter>
+ implements OnShortcutIntentCreatedListener {
+
+ private static final String KEY_EDIT_MODE = "editMode";
+ private static final String KEY_CREATE_CONTACT_ENABLED = "createContactEnabled";
+ private static final String KEY_SHORTCUT_REQUESTED = "shortcutRequested";
+
+ private OnContactPickerActionListener mListener;
+ private boolean mCreateContactEnabled;
+ private boolean mEditMode;
+ private boolean mShortcutRequested;
+
+ public ContactPickerFragment() {
+ setPhotoLoaderEnabled(true);
+ setSectionHeaderDisplayEnabled(true);
+ setVisibleScrollbarEnabled(true);
+ setQuickContactEnabled(false);
+ setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_CONTACT_SHORTCUT);
+ }
+
+ public void setOnContactPickerActionListener(OnContactPickerActionListener listener) {
+ mListener = listener;
+ }
+
+ public boolean isCreateContactEnabled() {
+ return mCreateContactEnabled;
+ }
+
+ public void setCreateContactEnabled(boolean flag) {
+ this.mCreateContactEnabled = flag;
+ }
+
+ public boolean isEditMode() {
+ return mEditMode;
+ }
+
+ public void setEditMode(boolean flag) {
+ mEditMode = flag;
+ }
+
+ public boolean isShortcutRequested() {
+ return mShortcutRequested;
+ }
+
+ public void setShortcutRequested(boolean flag) {
+ mShortcutRequested = flag;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_EDIT_MODE, mEditMode);
+ outState.putBoolean(KEY_CREATE_CONTACT_ENABLED, mCreateContactEnabled);
+ outState.putBoolean(KEY_SHORTCUT_REQUESTED, mShortcutRequested);
+ }
+
+ @Override
+ public void restoreSavedState(Bundle savedState) {
+ super.restoreSavedState(savedState);
+
+ if (savedState == null) {
+ return;
+ }
+
+ mEditMode = savedState.getBoolean(KEY_EDIT_MODE);
+ mCreateContactEnabled = savedState.getBoolean(KEY_CREATE_CONTACT_ENABLED);
+ mShortcutRequested = savedState.getBoolean(KEY_SHORTCUT_REQUESTED);
+ }
+
+ @Override
+ protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
+ super.onCreateView(inflater, container);
+ if (mCreateContactEnabled) {
+ getListView().addHeaderView(inflater.inflate(R.layout.create_new_contact, null, false));
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if (position == 0 && mCreateContactEnabled) {
+ mListener.onCreateNewContactAction();
+ } else {
+ super.onItemClick(parent, view, position, id);
+ }
+ }
+
+ @Override
+ protected void onItemClick(int position, long id) {
+ Uri uri;
+ if (isLegacyCompatibilityMode()) {
+ uri = ((LegacyContactListAdapter)getAdapter()).getPersonUri(position);
+ } else {
+ uri = ((ContactListAdapter)getAdapter()).getContactUri(position);
+ }
+ if (mEditMode) {
+ editContact(uri);
+ } else if (mShortcutRequested) {
+ ShortcutIntentBuilder builder = new ShortcutIntentBuilder(getActivity(), this);
+ builder.createContactShortcutIntent(uri);
+ } else {
+ pickContact(uri);
+ }
+ }
+
+ public void createNewContact() {
+ mListener.onCreateNewContactAction();
+ }
+
+ public void editContact(Uri contactUri) {
+ mListener.onEditContactAction(contactUri);
+ }
+
+ public void pickContact(Uri uri) {
+ mListener.onPickContactAction(uri);
+ }
+
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ if (!isLegacyCompatibilityMode()) {
+ DefaultContactListAdapter adapter = new DefaultContactListAdapter(getActivity());
+ adapter.setFilter(
+ new ContactListFilter(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
+ adapter.setSectionHeaderDisplayEnabled(true);
+ adapter.setDisplayPhotos(true);
+ adapter.setQuickContactEnabled(false);
+ return adapter;
+ } else {
+ LegacyContactListAdapter adapter = new LegacyContactListAdapter(getActivity());
+ adapter.setSectionHeaderDisplayEnabled(false);
+ adapter.setDisplayPhotos(false);
+ return adapter;
+ }
+ }
+
+ @Override
+ protected void configureAdapter() {
+ super.configureAdapter();
+
+ ContactEntryListAdapter adapter = getAdapter();
+ adapter.setDataRestrictedByCallingPackage(true);
+
+ // If "Create new contact" is shown, don't display the empty list UI
+ adapter.setEmptyListEnabled(!isCreateContactEnabled());
+ }
+
+ @Override
+ protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+ return inflater.inflate(R.layout.contact_picker_content, null);
+ }
+
+ @Override
+ protected void prepareEmptyView() {
+ if (isSearchMode()) {
+ return;
+ } else if (isSyncActive()) {
+ if (mShortcutRequested) {
+ // Help text is the same no matter whether there is SIM or not.
+ setEmptyText(R.string.noContactsHelpTextWithSyncForCreateShortcut);
+ } else if (hasIccCard()) {
+ setEmptyText(R.string.noContactsHelpTextWithSync);
+ } else {
+ setEmptyText(R.string.noContactsNoSimHelpTextWithSync);
+ }
+ } else {
+ if (mShortcutRequested) {
+ // Help text is the same no matter whether there is SIM or not.
+ setEmptyText(R.string.noContactsHelpTextWithSyncForCreateShortcut);
+ } else if (hasIccCard()) {
+ setEmptyText(R.string.noContactsHelpText);
+ } else {
+ setEmptyText(R.string.noContactsNoSimHelpText);
+ }
+ }
+ }
+
+ public void onShortcutIntentCreated(Uri uri, Intent shortcutIntent) {
+ mListener.onShortcutIntentCreated(shortcutIntent);
+ }
+
+ @Override
+ public void startSearch(String initialQuery) {
+ ContactsSearchManager.startSearchForResult(getActivity(), initialQuery,
+ ACTIVITY_REQUEST_CODE_PICKER, getContactsRequest());
+ }
+
+ @Override
+ public void onPickerResult(Intent data) {
+ mListener.onPickContactAction(data.getData());
+ }
+}
diff --git a/src/com/android/contacts/list/ContactsIntentResolver.java b/src/com/android/contacts/list/ContactsIntentResolver.java
new file mode 100644
index 0000000..afe29df
--- /dev/null
+++ b/src/com/android/contacts/list/ContactsIntentResolver.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.CallContactActivity;
+import com.android.contacts.ContactsSearchManager;
+
+import android.app.Activity;
+import android.app.SearchManager;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Contacts.ContactMethods;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Intents;
+import android.provider.ContactsContract.Intents.UI;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Parses a Contacts intent, extracting all relevant parts and packaging them
+ * as a {@link ContactsRequest} object.
+ */
+@SuppressWarnings("deprecation")
+public class ContactsIntentResolver {
+
+ private static final String TAG = "ContactsIntentResolver";
+
+ private final Activity mContext;
+
+ public ContactsIntentResolver(Activity context) {
+ this.mContext = context;
+ }
+
+ public ContactsRequest resolveIntent(Intent intent) {
+ ContactsRequest request = new ContactsRequest();
+
+ String action = intent.getAction();
+
+ Log.i(TAG, "Called with action: " + action);
+
+ if (UI.LIST_DEFAULT.equals(action) ) {
+ request.setActionCode(ContactsRequest.ACTION_DEFAULT);
+ } else if (UI.LIST_ALL_CONTACTS_ACTION.equals(action)) {
+ request.setActionCode(ContactsRequest.ACTION_ALL_CONTACTS);
+ } else if (UI.LIST_CONTACTS_WITH_PHONES_ACTION.equals(action)) {
+ request.setActionCode(ContactsRequest.ACTION_CONTACTS_WITH_PHONES);
+ } else if (UI.LIST_STARRED_ACTION.equals(action)) {
+ request.setActionCode(ContactsRequest.ACTION_STARRED);
+ } else if (UI.LIST_FREQUENT_ACTION.equals(action)) {
+ request.setActionCode(ContactsRequest.ACTION_FREQUENT);
+ } else if (UI.LIST_STREQUENT_ACTION.equals(action)) {
+ request.setActionCode(ContactsRequest.ACTION_STREQUENT);
+ } else if (UI.LIST_GROUP_ACTION.equals(action)) {
+ request.setActionCode(ContactsRequest.ACTION_GROUP);
+ String groupName = intent.getStringExtra(UI.GROUP_NAME_EXTRA_KEY);
+ if (!TextUtils.isEmpty(groupName)) {
+ request.setGroupName(groupName);
+ } else {
+ Log.e(TAG, "Intent missing a required extra: " + UI.GROUP_NAME_EXTRA_KEY);
+ request.setValid(false);
+ }
+ } else if (Intent.ACTION_PICK.equals(action)) {
+ final String resolvedType = intent.resolveType(mContext);
+ if (Contacts.CONTENT_TYPE.equals(resolvedType)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_CONTACT);
+ } else if (People.CONTENT_TYPE.equals(resolvedType)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_CONTACT);
+ request.setLegacyCompatibilityMode(true);
+ } else if (Phone.CONTENT_TYPE.equals(resolvedType)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_PHONE);
+ } else if (Phones.CONTENT_TYPE.equals(resolvedType)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_PHONE);
+ request.setLegacyCompatibilityMode(true);
+ } else if (StructuredPostal.CONTENT_TYPE.equals(resolvedType)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_POSTAL);
+ } else if (ContactMethods.CONTENT_POSTAL_TYPE.equals(resolvedType)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_POSTAL);
+ request.setLegacyCompatibilityMode(true);
+ }
+ } else if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
+ String component = intent.getComponent().getClassName();
+ if (component.equals("alias.DialShortcut")) {
+ request.setActionCode(ContactsRequest.ACTION_CREATE_SHORTCUT_CALL);
+ } else if (component.equals("alias.MessageShortcut")) {
+ request.setActionCode(ContactsRequest.ACTION_CREATE_SHORTCUT_SMS);
+ } else {
+ request.setActionCode(ContactsRequest.ACTION_CREATE_SHORTCUT_CONTACT);
+ }
+ } else if (Intent.ACTION_GET_CONTENT.equals(action)) {
+ String type = intent.getType();
+ if (Contacts.CONTENT_ITEM_TYPE.equals(type)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT);
+ } else if (Phone.CONTENT_ITEM_TYPE.equals(type)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_PHONE);
+ } else if (Phones.CONTENT_ITEM_TYPE.equals(type)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_PHONE);
+ request.setLegacyCompatibilityMode(true);
+ } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(type)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_POSTAL);
+ } else if (ContactMethods.CONTENT_POSTAL_ITEM_TYPE.equals(type)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_POSTAL);
+ request.setLegacyCompatibilityMode(true);
+ } else if (People.CONTENT_ITEM_TYPE.equals(type)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_OR_CREATE_CONTACT);
+ request.setLegacyCompatibilityMode(true);
+ }
+ } else if (Intent.ACTION_INSERT_OR_EDIT.equals(action)) {
+ request.setActionCode(ContactsRequest.ACTION_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));
+ request.setRedirectIntent(newIntent);
+ }
+ } else {
+ request.setQueryString(intent.getStringExtra(SearchManager.QUERY));
+ request.setSearchMode(true);
+ }
+ } else if (Intent.ACTION_VIEW.equals(action)) {
+ request.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
+ request.setContactUri(intent.getData());
+ intent.setAction(Intent.ACTION_DEFAULT);
+ intent.setData(null);
+ } else if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
+ // When we get a FILTER_CONTACTS_ACTION, it represents search in the context
+ // of some other action. Let's retrieve the original action to provide proper
+ // context for the search queries.
+ request.setActionCode(ContactsRequest.ACTION_DEFAULT);
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ request.setQueryString(extras.getString(UI.FILTER_TEXT_EXTRA_KEY));
+
+ ContactsRequest originalRequest =
+ (ContactsRequest)extras.get(ContactsSearchManager.ORIGINAL_REQUEST_KEY);
+ if (originalRequest != null) {
+ request.copyFrom(originalRequest);
+ }
+ }
+
+ request.setSearchMode(true);
+
+ // 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)) {
+ Uri data = intent.getData();
+ // See if the suggestion was clicked with a search action key (call button)
+ if ("call".equals(intent.getStringExtra(SearchManager.ACTION_MSG))) {
+ Intent newIntent = new Intent(mContext, CallContactActivity.class);
+ newIntent.setData(data);
+ request.setRedirectIntent(newIntent);
+ } else {
+ request.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
+ request.setContactUri(data);
+ intent.setAction(Intent.ACTION_DEFAULT);
+ intent.setData(null);
+ }
+ } else if (Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED.equals(action)) {
+ request.setRedirectIntent(new Intent(Intent.ACTION_CALL_PRIVILEGED, intent.getData()));
+ } else if (Intents.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED.equals(action)) {
+ // TODO actually support this in EditContactActivity.
+ String number = intent.getData().getSchemeSpecificPart();
+ Intent newIntent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+ newIntent.putExtra(Intents.Insert.PHONE, number);
+ request.setRedirectIntent(newIntent);
+
+ }
+ // Allow the title to be set to a custom String using an extra on the intent
+ String title = intent.getStringExtra(UI.TITLE_EXTRA_KEY);
+ if (title != null) {
+ request.setActivityTitle(title);
+ }
+
+ return request;
+ }
+}
diff --git a/src/com/android/contacts/list/ContactsRequest.java b/src/com/android/contacts/list/ContactsRequest.java
new file mode 100644
index 0000000..aefa451
--- /dev/null
+++ b/src/com/android/contacts/list/ContactsRequest.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Parsed form of the intent sent to the Contacts application.
+ */
+public class ContactsRequest implements Parcelable {
+
+ /** Default mode: browse contacts */
+ public static final int ACTION_DEFAULT = 10;
+
+ /** Show all contacts */
+ public static final int ACTION_ALL_CONTACTS = 15;
+
+ /** Show all contacts with phone numbers */
+ public static final int ACTION_CONTACTS_WITH_PHONES = 17;
+
+ /** Show contents of a specific group */
+ public static final int ACTION_GROUP = 20;
+
+ /** Show all starred contacts */
+ public static final int ACTION_STARRED = 30;
+
+ /** Show frequently contacted contacts */
+ public static final int ACTION_FREQUENT = 40;
+
+ /** Show starred and the frequent */
+ public static final int ACTION_STREQUENT = 50;
+
+ /** Show all contacts and pick them when clicking */
+ public static final int ACTION_PICK_CONTACT = 60;
+
+ /** Show all contacts as well as the option to create a new one */
+ public static final int ACTION_PICK_OR_CREATE_CONTACT = 70;
+
+ /** Show all contacts and pick them for edit when clicking, and allow creating a new contact */
+ public static final int ACTION_INSERT_OR_EDIT_CONTACT = 80;
+
+ /** Show all phone numbers and pick them when clicking */
+ public static final int ACTION_PICK_PHONE = 90;
+
+ /** Show all postal addresses and pick them when clicking */
+ public static final int ACTION_PICK_POSTAL = 100;
+
+ /** Show all contacts and create a shortcut for the picked contact */
+ public static final int ACTION_CREATE_SHORTCUT_CONTACT = 110;
+
+ /** Show all phone numbers and create a call shortcut for the picked number */
+ public static final int ACTION_CREATE_SHORTCUT_CALL = 120;
+
+ /** Show all phone numbers and create an SMS shortcut for the picked number */
+ public static final int ACTION_CREATE_SHORTCUT_SMS = 130;
+
+ /** Show all contacts and activate the specified one */
+ public static final int ACTION_VIEW_CONTACT = 140;
+
+ private boolean mValid = true;
+ private int mActionCode = ACTION_DEFAULT;
+ private Intent mRedirectIntent;
+ private CharSequence mTitle;
+ private boolean mSearchMode;
+ private String mQueryString;
+ private String mGroupName;
+ private boolean mLegacyCompatibilityMode;
+ private boolean mDirectorySearchEnabled = true;
+ private Uri mContactUri;
+
+ /**
+ * Copies all fields.
+ */
+ public void copyFrom(ContactsRequest request) {
+ mValid = request.mValid;
+ mActionCode = request.mActionCode;
+ mRedirectIntent = request.mRedirectIntent;
+ mTitle = request.mTitle;
+ mSearchMode = request.mSearchMode;
+ mQueryString = request.mQueryString;
+ mGroupName = request.mGroupName;
+ mLegacyCompatibilityMode = request.mLegacyCompatibilityMode;
+ mDirectorySearchEnabled = request.mDirectorySearchEnabled;
+ mContactUri = request.mContactUri;
+ }
+
+ public static Parcelable.Creator<ContactsRequest> CREATOR = new Creator<ContactsRequest>() {
+
+ public ContactsRequest[] newArray(int size) {
+ return new ContactsRequest[size];
+ }
+
+ public ContactsRequest createFromParcel(Parcel source) {
+ ClassLoader classLoader = this.getClass().getClassLoader();
+ ContactsRequest request = new ContactsRequest();
+ request.mValid = source.readInt() != 0;
+ request.mActionCode = source.readInt();
+ request.mRedirectIntent = source.readParcelable(classLoader);
+ request.mTitle = source.readCharSequence();
+ request.mSearchMode = source.readInt() != 0;
+ request.mQueryString = source.readString();
+ request.mGroupName = source.readString();
+ request.mLegacyCompatibilityMode = source.readInt() != 0;
+ request.mDirectorySearchEnabled = source.readInt() != 0;
+ request.mContactUri = source.readParcelable(classLoader);
+ return request;
+ }
+ };
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mValid ? 1 : 0);
+ dest.writeInt(mActionCode);
+ dest.writeParcelable(mRedirectIntent, 0);
+ dest.writeCharSequence(mTitle);
+ dest.writeInt(mSearchMode ? 1 : 0);
+ dest.writeString(mQueryString);
+ dest.writeString(mGroupName);
+ dest.writeInt(mLegacyCompatibilityMode ? 1 : 0);
+ dest.writeInt(mDirectorySearchEnabled ? 1 : 0);
+ dest.writeParcelable(mContactUri, 0);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public boolean isValid() {
+ return mValid;
+ }
+
+ public void setValid(boolean flag) {
+ mValid = flag;
+ }
+
+ public Intent getRedirectIntent() {
+ return mRedirectIntent;
+ }
+
+ public void setRedirectIntent(Intent intent) {
+ mRedirectIntent = intent;
+ }
+
+ public void setActivityTitle(CharSequence title) {
+ mTitle = title;
+ }
+
+ public CharSequence getActivityTitle() {
+ return mTitle;
+ }
+
+ public int getActionCode() {
+ return mActionCode;
+ }
+
+ public void setActionCode(int actionCode) {
+ mActionCode = actionCode;
+ }
+
+ public boolean isSearchMode() {
+ return mSearchMode;
+ }
+
+ public void setSearchMode(boolean flag) {
+ mSearchMode = flag;
+ }
+
+ public String getQueryString() {
+ return mQueryString;
+ }
+
+ public void setQueryString(String string) {
+ mQueryString = string;
+ }
+
+ public String getGroupName() {
+ return mGroupName;
+ }
+
+ public void setGroupName(String groupName) {
+ mGroupName = groupName;
+ }
+
+ public boolean isLegacyCompatibilityMode() {
+ return mLegacyCompatibilityMode;
+ }
+
+ public void setLegacyCompatibilityMode(boolean flag) {
+ mLegacyCompatibilityMode = flag;
+ }
+
+ /**
+ * Determines whether this search request should include directories or
+ * is limited to local contacts only.
+ */
+ public boolean isDirectorySearchEnabled() {
+ return mDirectorySearchEnabled;
+ }
+
+ public void setDirectorySearchEnabled(boolean flag) {
+ mDirectorySearchEnabled = flag;
+ }
+
+ public Uri getContactUri() {
+ return mContactUri;
+ }
+
+ public void setContactUri(Uri contactUri) {
+ this.mContactUri = contactUri;
+ }
+}
diff --git a/src/com/android/contacts/list/ContactsSectionIndexer.java b/src/com/android/contacts/list/ContactsSectionIndexer.java
new file mode 100644
index 0000000..a80d1de
--- /dev/null
+++ b/src/com/android/contacts/list/ContactsSectionIndexer.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.widget.SectionIndexer;
+
+import java.util.Arrays;
+
+/**
+ * A section indexer that is configured with precomputed section titles and
+ * their respective counts.
+ */
+public class ContactsSectionIndexer implements SectionIndexer {
+
+ private final String[] mSections;
+ private final int[] mPositions;
+ private final int mCount;
+
+ /**
+ * Constructor.
+ *
+ * @param sections a non-null array
+ * @param counts a non-null array of the same size as <code>sections</code>
+ */
+ public ContactsSectionIndexer(String[] sections, int[] counts) {
+ if (sections == null || counts == null) {
+ throw new NullPointerException();
+ }
+
+ if (sections.length != counts.length) {
+ throw new IllegalArgumentException(
+ "The sections and counts arrays must have the same length");
+ }
+
+ // TODO process sections/counts based on current locale and/or specific section titles
+
+ this.mSections = sections;
+ mPositions = new int[counts.length];
+ int position = 0;
+ for (int i = 0; i < counts.length; i++) {
+ if (mSections[i] == null) {
+ mSections[i] = " ";
+ } else {
+ mSections[i] = mSections[i].trim();
+ }
+
+ mPositions[i] = position;
+ position += counts[i];
+ }
+ mCount = position;
+ }
+
+ public Object[] getSections() {
+ return mSections;
+ }
+
+ public int getPositionForSection(int section) {
+ if (section < 0 || section >= mSections.length) {
+ return -1;
+ }
+
+ return mPositions[section];
+ }
+
+ public int getSectionForPosition(int position) {
+ if (position < 0 || position >= mCount) {
+ return -1;
+ }
+
+ int index = Arrays.binarySearch(mPositions, position);
+
+ /*
+ * Consider this example: section positions are 0, 3, 5; the supplied
+ * position is 4. The section corresponding to position 4 starts at
+ * position 3, so the expected return value is 1. Binary search will not
+ * find 4 in the array and thus will return -insertPosition-1, i.e. -3.
+ * To get from that number to the expected value of 1 we need to negate
+ * and subtract 2.
+ */
+ return index >= 0 ? index : -index - 2;
+ }
+}
diff --git a/src/com/android/contacts/list/ContactsUnavailableFragment.java b/src/com/android/contacts/list/ContactsUnavailableFragment.java
new file mode 100644
index 0000000..e93687f
--- /dev/null
+++ b/src/com/android/contacts/list/ContactsUnavailableFragment.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.provider.ContactsContract.ProviderStatus;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+/**
+ * Fragment shown when contacts are unavailable. It contains provider status
+ * messaging as well as instructions for the user.
+ */
+public class ContactsUnavailableFragment extends Fragment implements OnClickListener {
+
+ private ProviderStatusLoader mProviderStatusLoader;
+
+ private View mView;
+ private TextView mMessageView;
+ private Button mCreateContactButton;
+ private Button mAddAccountButton;
+ private Button mImportContactsButton;
+ private Button mUninstallAppsButton;
+ private Button mRetryUpgradeButton;
+ private ProgressBar mProgress;
+
+ private OnContactsUnavailableActionListener mListener;
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ mView = inflater.inflate(R.layout.contacts_unavailable_fragment, null);
+ mMessageView = (TextView) mView.findViewById(R.id.message);
+ mCreateContactButton = (Button) mView.findViewById(R.id.create_contact_button);
+ mCreateContactButton.setOnClickListener(this);
+ mAddAccountButton = (Button) mView.findViewById(R.id.add_account_button);
+ mAddAccountButton.setOnClickListener(this);
+ mImportContactsButton = (Button) mView.findViewById(R.id.import_contacts_button);
+ mImportContactsButton.setOnClickListener(this);
+ mUninstallAppsButton = (Button) mView.findViewById(R.id.import_failure_uninstall_button);
+ mUninstallAppsButton.setOnClickListener(this);
+ mRetryUpgradeButton = (Button) mView.findViewById(R.id.import_failure_retry_button);
+ mRetryUpgradeButton.setOnClickListener(this);
+ mProgress = (ProgressBar) mView.findViewById(R.id.progress);
+ update();
+ return mView;
+ }
+
+ public void setOnContactsUnavailableActionListener(
+ OnContactsUnavailableActionListener listener) {
+ mListener = listener;
+ }
+
+ public void setProviderStatusLoader(ProviderStatusLoader loader) {
+ mProviderStatusLoader = loader;
+ }
+
+ public void update() {
+ int providerStatus = mProviderStatusLoader.getProviderStatus();
+ switch (providerStatus) {
+ case ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS:
+ mMessageView.setGravity(Gravity.LEFT);
+ mMessageView.setVisibility(View.GONE);
+ mCreateContactButton.setVisibility(View.VISIBLE);
+ mAddAccountButton.setVisibility(View.VISIBLE);
+ mImportContactsButton.setVisibility(View.VISIBLE);
+ mUninstallAppsButton.setVisibility(View.GONE);
+ mRetryUpgradeButton.setVisibility(View.GONE);
+ mProgress.setVisibility(View.GONE);
+ break;
+
+ case ProviderStatus.STATUS_CHANGING_LOCALE:
+ mMessageView.setText(R.string.locale_change_in_progress);
+ mMessageView.setGravity(Gravity.CENTER_HORIZONTAL);
+ mMessageView.setVisibility(View.VISIBLE);
+ mCreateContactButton.setVisibility(View.GONE);
+ mAddAccountButton.setVisibility(View.GONE);
+ mImportContactsButton.setVisibility(View.GONE);
+ mUninstallAppsButton.setVisibility(View.GONE);
+ mRetryUpgradeButton.setVisibility(View.GONE);
+ mProgress.setVisibility(View.VISIBLE);
+ break;
+
+ case ProviderStatus.STATUS_UPGRADING:
+ mMessageView.setText(R.string.upgrade_in_progress);
+ mMessageView.setGravity(Gravity.CENTER_HORIZONTAL);
+ mMessageView.setVisibility(View.VISIBLE);
+ mCreateContactButton.setVisibility(View.GONE);
+ mAddAccountButton.setVisibility(View.GONE);
+ mImportContactsButton.setVisibility(View.GONE);
+ mUninstallAppsButton.setVisibility(View.GONE);
+ mRetryUpgradeButton.setVisibility(View.GONE);
+ mProgress.setVisibility(View.VISIBLE);
+ break;
+
+ case ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY:
+ String message = getResources().getString(R.string.upgrade_out_of_memory,
+ new Object[] { mProviderStatusLoader.getProviderStatusData() });
+ mMessageView.setText(message);
+ mMessageView.setGravity(Gravity.LEFT);
+ mMessageView.setVisibility(View.VISIBLE);
+ mCreateContactButton.setVisibility(View.GONE);
+ mAddAccountButton.setVisibility(View.GONE);
+ mImportContactsButton.setVisibility(View.GONE);
+ mUninstallAppsButton.setVisibility(View.VISIBLE);
+ mRetryUpgradeButton.setVisibility(View.VISIBLE);
+ mProgress.setVisibility(View.GONE);
+ break;
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mListener == null) {
+ return;
+ }
+ switch (v.getId()) {
+ case R.id.create_contact_button:
+ mListener.onCreateNewContactAction();
+ break;
+ case R.id.add_account_button:
+ mListener.onAddAccountAction();
+ break;
+ case R.id.import_contacts_button:
+ mListener.onImportContactsFromFileAction();
+ break;
+ case R.id.import_failure_uninstall_button:
+ mListener.onFreeInternalStorageAction();
+ break;
+ case R.id.import_failure_retry_button:
+ mProviderStatusLoader.retryUpgrade();
+ break;
+ }
+ }
+}
diff --git a/src/com/android/contacts/list/CustomContactListFilterActivity.java b/src/com/android/contacts/list/CustomContactListFilterActivity.java
new file mode 100644
index 0000000..0f3fafd
--- /dev/null
+++ b/src/com/android/contacts/list/CustomContactListFilterActivity.java
@@ -0,0 +1,958 @@
+/*
+ * 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.list;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.ContactsSearchManager;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.model.GoogleAccountType;
+import com.android.contacts.preference.ContactsPreferences;
+import com.android.contacts.util.EmptyService;
+import com.android.contacts.util.LocalizedNameResolver;
+import com.android.contacts.util.WeakAsyncTask;
+import com.google.android.collect.Lists;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.app.ProgressDialog;
+import android.content.AsyncTaskLoader;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.EntityIterator;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.OperationApplicationException;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.preference.PreferenceManager;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Groups;
+import android.provider.ContactsContract.Settings;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.CheckBox;
+import android.widget.ExpandableListAdapter;
+import android.widget.ExpandableListView;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+
+/**
+ * Shows a list of all available {@link Groups} available, letting the user
+ * select which ones they want to be visible.
+ */
+public class CustomContactListFilterActivity extends ContactsActivity
+ implements AdapterView.OnItemClickListener, View.OnClickListener,
+ ExpandableListView.OnChildClickListener,
+ LoaderCallbacks<CustomContactListFilterActivity.AccountSet>
+{
+ private static final String TAG = "CustomContactListFilterActivity";
+
+ private static final int ACCOUNT_SET_LOADER_ID = 1;
+
+ private ExpandableListView mList;
+ private DisplayAdapter mAdapter;
+
+ private SharedPreferences mPrefs;
+
+ private CheckBox mDisplayPhones;
+
+ private View mHeaderPhones;
+ private View mHeaderSeparator;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.contact_list_filter_custom);
+
+ mList = (ExpandableListView) findViewById(com.android.internal.R.id.list);
+ mList.setOnChildClickListener(this);
+ mList.setHeaderDividersEnabled(true);
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ mAdapter = new DisplayAdapter(this);
+
+ final LayoutInflater inflater = getLayoutInflater();
+
+ createWithPhonesOnlyPreferenceView(inflater);
+ createDisplayGroupHeader(inflater);
+
+ mList.addHeaderView(mHeaderPhones, null, true);
+ mList.addHeaderView(mHeaderSeparator, null, false);
+
+ findViewById(R.id.btn_done).setOnClickListener(this);
+ findViewById(R.id.btn_discard).setOnClickListener(this);
+
+ // Catch clicks on the header views
+ mList.setOnItemClickListener(this);
+ mList.setOnCreateContextMenuListener(this);
+
+ mList.setAdapter(mAdapter);
+ }
+
+ private void createWithPhonesOnlyPreferenceView(LayoutInflater inflater) {
+ // Add the "Only contacts with phones" header modifier.
+ mHeaderPhones = inflater.inflate(R.layout.contact_list_filter_phones_only, mList, false);
+ mHeaderPhones.setId(R.id.header_phones);
+ mDisplayPhones = (CheckBox) mHeaderPhones.findViewById(android.R.id.checkbox);
+ mDisplayPhones.setChecked(mPrefs.getBoolean(ContactsPreferences.PREF_DISPLAY_ONLY_PHONES,
+ ContactsPreferences.PREF_DISPLAY_ONLY_PHONES_DEFAULT));
+ final TextView text1 = (TextView) mHeaderPhones.findViewById(android.R.id.text1);
+ text1.setText(R.string.showFilterPhones);
+ final TextView text2 = (TextView) mHeaderPhones.findViewById(android.R.id.text2);
+ text2.setText(R.string.showFilterPhonesDescrip);
+ }
+
+ private void createDisplayGroupHeader(LayoutInflater inflater) {
+ // Add the separator before showing the detailed group list.
+ mHeaderSeparator = inflater.inflate(R.layout.list_separator, mList, false);
+ final TextView text1 = (TextView) mHeaderSeparator;
+ text1.setText(R.string.headerContactGroups);
+ }
+
+ public static class CustomFilterConfigurationLoader extends AsyncTaskLoader<AccountSet> {
+
+ private AccountSet mAccountSet;
+
+ public CustomFilterConfigurationLoader(Context context) {
+ super(context);
+ }
+
+ @Override
+ public AccountSet loadInBackground() {
+ Context context = getContext();
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
+ final ContentResolver resolver = context.getContentResolver();
+
+ final AccountSet accounts = new AccountSet();
+ for (Account account : accountTypes.getAccounts(false)) {
+ AccountDisplay accountDisplay =
+ new AccountDisplay(resolver, account.name, account.type);
+
+ final Uri groupsUri = Groups.CONTENT_URI.buildUpon()
+ .appendQueryParameter(Groups.ACCOUNT_NAME, account.name)
+ .appendQueryParameter(Groups.ACCOUNT_TYPE, account.type).build();
+ EntityIterator iterator = ContactsContract.Groups.newEntityIterator(resolver.query(
+ groupsUri, null, null, null, null));
+ try {
+ boolean hasGroups = false;
+
+ // Create entries for each known group
+ while (iterator.hasNext()) {
+ final ContentValues values = iterator.next().getEntityValues();
+ final GroupDelta group = GroupDelta.fromBefore(values);
+ accountDisplay.addGroup(group);
+ hasGroups = true;
+ }
+ // Create single entry handling ungrouped status
+ accountDisplay.mUngrouped =
+ GroupDelta.fromSettings(resolver, account.name, account.type, hasGroups);
+ accountDisplay.addGroup(accountDisplay.mUngrouped);
+ } finally {
+ iterator.close();
+ }
+
+ accounts.add(accountDisplay);
+ }
+
+ return accounts;
+ }
+
+ @Override
+ public void deliverResult(AccountSet cursor) {
+ if (isReset()) {
+ return;
+ }
+
+ mAccountSet = cursor;
+
+ if (isStarted()) {
+ super.deliverResult(cursor);
+ }
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mAccountSet != null) {
+ deliverResult(mAccountSet);
+ }
+ if (takeContentChanged() || mAccountSet == null) {
+ forceLoad();
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ onStopLoading();
+ mAccountSet = null;
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ getLoaderManager().initLoader(ACCOUNT_SET_LOADER_ID, null, this);
+ super.onStart();
+ }
+
+ @Override
+ public Loader<AccountSet> onCreateLoader(int id, Bundle args) {
+ return new CustomFilterConfigurationLoader(this);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<AccountSet> loader, AccountSet data) {
+ mAdapter.setAccounts(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<AccountSet> loader) {
+ mAdapter.setAccounts(null);
+ }
+
+ private static final int DEFAULT_SHOULD_SYNC = 1;
+ private static final int DEFAULT_VISIBLE = 0;
+
+ /**
+ * Entry holding any changes to {@link Groups} or {@link Settings} rows,
+ * such as {@link Groups#SHOULD_SYNC} or {@link Groups#GROUP_VISIBLE}.
+ */
+ protected static class GroupDelta extends ValuesDelta {
+ private boolean mUngrouped = false;
+ private boolean mAccountHasGroups;
+
+ private GroupDelta() {
+ super();
+ }
+
+ /**
+ * Build {@link GroupDelta} from the {@link Settings} row for the given
+ * {@link Settings#ACCOUNT_NAME} and {@link Settings#ACCOUNT_TYPE}.
+ */
+ public static GroupDelta fromSettings(ContentResolver resolver, String accountName,
+ String accountType, boolean accountHasGroups) {
+ final Uri settingsUri = Settings.CONTENT_URI.buildUpon()
+ .appendQueryParameter(Settings.ACCOUNT_NAME, accountName)
+ .appendQueryParameter(Settings.ACCOUNT_TYPE, accountType).build();
+ final Cursor cursor = resolver.query(settingsUri, new String[] {
+ Settings.SHOULD_SYNC, Settings.UNGROUPED_VISIBLE
+ }, null, null, null);
+
+ try {
+ final ContentValues values = new ContentValues();
+ values.put(Settings.ACCOUNT_NAME, accountName);
+ values.put(Settings.ACCOUNT_TYPE, accountType);
+
+ if (cursor != null && cursor.moveToFirst()) {
+ // Read existing values when present
+ values.put(Settings.SHOULD_SYNC, cursor.getInt(0));
+ values.put(Settings.UNGROUPED_VISIBLE, cursor.getInt(1));
+ return fromBefore(values).setUngrouped(accountHasGroups);
+ } else {
+ // Nothing found, so treat as create
+ values.put(Settings.SHOULD_SYNC, DEFAULT_SHOULD_SYNC);
+ values.put(Settings.UNGROUPED_VISIBLE, DEFAULT_VISIBLE);
+ return fromAfter(values).setUngrouped(accountHasGroups);
+ }
+ } finally {
+ if (cursor != null) cursor.close();
+ }
+ }
+
+ public static GroupDelta fromBefore(ContentValues before) {
+ final GroupDelta entry = new GroupDelta();
+ entry.mBefore = before;
+ entry.mAfter = new ContentValues();
+ return entry;
+ }
+
+ public static GroupDelta fromAfter(ContentValues after) {
+ final GroupDelta entry = new GroupDelta();
+ entry.mBefore = null;
+ entry.mAfter = after;
+ return entry;
+ }
+
+ protected GroupDelta setUngrouped(boolean accountHasGroups) {
+ mUngrouped = true;
+ mAccountHasGroups = accountHasGroups;
+ return this;
+ }
+
+ @Override
+ public boolean beforeExists() {
+ return mBefore != null;
+ }
+
+ public boolean getShouldSync() {
+ return getAsInteger(mUngrouped ? Settings.SHOULD_SYNC : Groups.SHOULD_SYNC,
+ DEFAULT_SHOULD_SYNC) != 0;
+ }
+
+ public boolean getVisible() {
+ return getAsInteger(mUngrouped ? Settings.UNGROUPED_VISIBLE : Groups.GROUP_VISIBLE,
+ DEFAULT_VISIBLE) != 0;
+ }
+
+ public void putShouldSync(boolean shouldSync) {
+ put(mUngrouped ? Settings.SHOULD_SYNC : Groups.SHOULD_SYNC, shouldSync ? 1 : 0);
+ }
+
+ public void putVisible(boolean visible) {
+ put(mUngrouped ? Settings.UNGROUPED_VISIBLE : Groups.GROUP_VISIBLE, visible ? 1 : 0);
+ }
+
+ private String getAccountType() {
+ return (mBefore == null ? mAfter : mBefore).getAsString(Settings.ACCOUNT_TYPE);
+ }
+
+ public CharSequence getTitle(Context context) {
+ if (mUngrouped) {
+ final String customAllContactsName =
+ LocalizedNameResolver.getAllContactsName(context, getAccountType());
+ if (customAllContactsName != null) {
+ return customAllContactsName;
+ }
+ if (mAccountHasGroups) {
+ return context.getText(R.string.display_ungrouped);
+ } else {
+ return context.getText(R.string.display_all_contacts);
+ }
+ } else {
+ final Integer titleRes = getAsInteger(Groups.TITLE_RES);
+ if (titleRes != null) {
+ final String packageName = getAsString(Groups.RES_PACKAGE);
+ return context.getPackageManager().getText(packageName, titleRes, null);
+ } else {
+ return getAsString(Groups.TITLE);
+ }
+ }
+ }
+
+ /**
+ * Build a possible {@link ContentProviderOperation} to persist any
+ * changes to the {@link Groups} or {@link Settings} row described by
+ * this {@link GroupDelta}.
+ */
+ public ContentProviderOperation buildDiff() {
+ if (isInsert()) {
+ // Only allow inserts for Settings
+ if (mUngrouped) {
+ mAfter.remove(mIdColumn);
+ return ContentProviderOperation.newInsert(Settings.CONTENT_URI)
+ .withValues(mAfter)
+ .build();
+ }
+ else {
+ throw new IllegalStateException("Unexpected diff");
+ }
+ } else if (isUpdate()) {
+ if (mUngrouped) {
+ return ContentProviderOperation.newUpdate(Settings.CONTENT_URI)
+ .withSelection(Settings.ACCOUNT_NAME + "=? AND "
+ + Settings.ACCOUNT_TYPE + "=?",
+ new String[] {
+ this.getAsString(Settings.ACCOUNT_NAME),
+ this.getAsString(Settings.ACCOUNT_TYPE)
+ })
+ .withValues(mAfter)
+ .build();
+ } else {
+ return ContentProviderOperation.newUpdate(
+ addCallerIsSyncAdapterParameter(Groups.CONTENT_URI))
+ .withSelection(Groups._ID + "=" + this.getId(), null)
+ .withValues(mAfter)
+ .build();
+ }
+ } else {
+ return null;
+ }
+ }
+ }
+
+ private static Uri addCallerIsSyncAdapterParameter(Uri uri) {
+ return uri.buildUpon()
+ .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
+ .build();
+ }
+
+ /**
+ * {@link Comparator} to sort by {@link Groups#_ID}.
+ */
+ private static Comparator<GroupDelta> sIdComparator = new Comparator<GroupDelta>() {
+ public int compare(GroupDelta object1, GroupDelta object2) {
+ final Long id1 = object1.getId();
+ final Long id2 = object2.getId();
+ if (id1 == null && id2 == null) {
+ return 0;
+ } else if (id1 == null) {
+ return -1;
+ } else if (id2 == null) {
+ return 1;
+ } else if (id1 < id2) {
+ return -1;
+ } else if (id1 > id2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ };
+
+ /**
+ * Set of all {@link AccountDisplay} entries, one for each source.
+ */
+ protected static class AccountSet extends ArrayList<AccountDisplay> {
+ public ArrayList<ContentProviderOperation> buildDiff() {
+ final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
+ for (AccountDisplay account : this) {
+ account.buildDiff(diff);
+ }
+ return diff;
+ }
+ }
+
+ /**
+ * {@link GroupDelta} details for a single {@link Account}, usually shown as
+ * children under a single expandable group.
+ */
+ protected static class AccountDisplay {
+ public String mName;
+ public String mType;
+
+ public GroupDelta mUngrouped;
+ public ArrayList<GroupDelta> mSyncedGroups = Lists.newArrayList();
+ public ArrayList<GroupDelta> mUnsyncedGroups = Lists.newArrayList();
+
+ /**
+ * Build an {@link AccountDisplay} covering all {@link Groups} under the
+ * given {@link Account}.
+ */
+ public AccountDisplay(ContentResolver resolver, String accountName, String accountType) {
+ mName = accountName;
+ mType = accountType;
+ }
+
+ /**
+ * Add the given {@link GroupDelta} internally, filing based on its
+ * {@link GroupDelta#getShouldSync()} status.
+ */
+ private void addGroup(GroupDelta group) {
+ if (group.getShouldSync()) {
+ mSyncedGroups.add(group);
+ } else {
+ mUnsyncedGroups.add(group);
+ }
+ }
+
+ /**
+ * Set the {@link GroupDelta#putShouldSync(boolean)} value for all
+ * children {@link GroupDelta} rows.
+ */
+ public void setShouldSync(boolean shouldSync) {
+ final Iterator<GroupDelta> oppositeChildren = shouldSync ?
+ mUnsyncedGroups.iterator() : mSyncedGroups.iterator();
+ while (oppositeChildren.hasNext()) {
+ final GroupDelta child = oppositeChildren.next();
+ setShouldSync(child, shouldSync, false);
+ oppositeChildren.remove();
+ }
+ }
+
+ public void setShouldSync(GroupDelta child, boolean shouldSync) {
+ setShouldSync(child, shouldSync, true);
+ }
+
+ /**
+ * Set {@link GroupDelta#putShouldSync(boolean)}, and file internally
+ * based on updated state.
+ */
+ public void setShouldSync(GroupDelta child, boolean shouldSync, boolean attemptRemove) {
+ child.putShouldSync(shouldSync);
+ if (shouldSync) {
+ if (attemptRemove) {
+ mUnsyncedGroups.remove(child);
+ }
+ mSyncedGroups.add(child);
+ Collections.sort(mSyncedGroups, sIdComparator);
+ } else {
+ if (attemptRemove) {
+ mSyncedGroups.remove(child);
+ }
+ mUnsyncedGroups.add(child);
+ }
+ }
+
+ /**
+ * Build set of {@link ContentProviderOperation} to persist any user
+ * changes to {@link GroupDelta} rows under this {@link Account}.
+ */
+ public void buildDiff(ArrayList<ContentProviderOperation> diff) {
+ for (GroupDelta group : mSyncedGroups) {
+ final ContentProviderOperation oper = group.buildDiff();
+ if (oper != null) diff.add(oper);
+ }
+ for (GroupDelta group : mUnsyncedGroups) {
+ final ContentProviderOperation oper = group.buildDiff();
+ if (oper != null) diff.add(oper);
+ }
+ }
+ }
+
+ /**
+ * {@link ExpandableListAdapter} that shows {@link GroupDelta} settings,
+ * grouped by {@link Account} type. Shows footer row when any groups are
+ * unsynced, as determined through {@link AccountDisplay#mUnsyncedGroups}.
+ */
+ protected static class DisplayAdapter extends BaseExpandableListAdapter {
+ private Context mContext;
+ private LayoutInflater mInflater;
+ private AccountTypeManager mAccountTypes;
+ private AccountSet mAccounts;
+
+ private boolean mChildWithPhones = false;
+
+ public DisplayAdapter(Context context) {
+ mContext = context;
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mAccountTypes = AccountTypeManager.getInstance(context);
+ }
+
+ public void setAccounts(AccountSet accounts) {
+ mAccounts = accounts;
+ notifyDataSetChanged();
+ }
+
+ /**
+ * In group descriptions, show the number of contacts with phone
+ * numbers, in addition to the total contacts.
+ */
+ public void setChildDescripWithPhones(boolean withPhones) {
+ mChildWithPhones = withPhones;
+ }
+
+ @Override
+ public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
+ ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(
+ R.layout.custom_contact_list_filter_account, parent, false);
+ }
+
+ final TextView text1 = (TextView)convertView.findViewById(android.R.id.text1);
+ final TextView text2 = (TextView)convertView.findViewById(android.R.id.text2);
+
+ final AccountDisplay account = (AccountDisplay)this.getGroup(groupPosition);
+
+ final AccountType accountType = mAccountTypes.getAccountType(account.mType);
+
+ text1.setText(account.mName);
+ text1.setVisibility(account.mName == null ? View.GONE : View.VISIBLE);
+ text2.setText(accountType.getDisplayLabel(mContext));
+
+ return convertView;
+ }
+
+ @Override
+ public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+ View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(
+ R.layout.custom_contact_list_filter_group, parent, false);
+ }
+
+ final TextView text1 = (TextView)convertView.findViewById(android.R.id.text1);
+ final TextView text2 = (TextView)convertView.findViewById(android.R.id.text2);
+ final CheckBox checkbox = (CheckBox)convertView.findViewById(android.R.id.checkbox);
+
+ final AccountDisplay account = mAccounts.get(groupPosition);
+ final GroupDelta child = (GroupDelta)this.getChild(groupPosition, childPosition);
+ if (child != null) {
+ // Handle normal group, with title and checkbox
+ final boolean groupVisible = child.getVisible();
+ checkbox.setVisibility(View.VISIBLE);
+ checkbox.setChecked(groupVisible);
+
+ final CharSequence groupTitle = child.getTitle(mContext);
+ text1.setText(groupTitle);
+ text2.setVisibility(View.GONE);
+ } else {
+ // When unknown child, this is "more" footer view
+ checkbox.setVisibility(View.GONE);
+ text1.setText(R.string.display_more_groups);
+ text2.setVisibility(View.GONE);
+ }
+
+ return convertView;
+ }
+
+ @Override
+ public Object getChild(int groupPosition, int childPosition) {
+ final AccountDisplay account = mAccounts.get(groupPosition);
+ final boolean validChild = childPosition >= 0
+ && childPosition < account.mSyncedGroups.size();
+ if (validChild) {
+ return account.mSyncedGroups.get(childPosition);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public long getChildId(int groupPosition, int childPosition) {
+ final GroupDelta child = (GroupDelta)getChild(groupPosition, childPosition);
+ if (child != null) {
+ final Long childId = child.getId();
+ return childId != null ? childId : Long.MIN_VALUE;
+ } else {
+ return Long.MIN_VALUE;
+ }
+ }
+
+ @Override
+ public int getChildrenCount(int groupPosition) {
+ // Count is any synced groups, plus possible footer
+ final AccountDisplay account = mAccounts.get(groupPosition);
+ final boolean anyHidden = account.mUnsyncedGroups.size() > 0;
+ return account.mSyncedGroups.size() + (anyHidden ? 1 : 0);
+ }
+
+ @Override
+ public Object getGroup(int groupPosition) {
+ return mAccounts.get(groupPosition);
+ }
+
+ @Override
+ public int getGroupCount() {
+ if (mAccounts == null) {
+ return 0;
+ }
+ return mAccounts.size();
+ }
+
+ @Override
+ public long getGroupId(int groupPosition) {
+ return groupPosition;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return true;
+ }
+ }
+
+ /**
+ * Handle any clicks on header views added to our {@link #mAdapter}, which
+ * are usually the global modifier checkboxes.
+ */
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ Log.d(TAG, "OnItemClick, position=" + position + ", id=" + id);
+ if (view == mHeaderPhones) {
+ mDisplayPhones.toggle();
+ return;
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.btn_done: {
+ this.doSaveAction();
+ break;
+ }
+ case R.id.btn_discard: {
+ this.finish();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Assign a specific value to {@link ContactsPreferences#PREF_DISPLAY_ONLY_PHONES}, refreshing
+ * the visible list as needed.
+ */
+ protected void setDisplayOnlyPhones(boolean displayOnlyPhones) {
+ mDisplayPhones.setChecked(displayOnlyPhones);
+
+ Editor editor = mPrefs.edit();
+ editor.putBoolean(ContactsPreferences.PREF_DISPLAY_ONLY_PHONES, displayOnlyPhones);
+ editor.apply();
+
+ mAdapter.setChildDescripWithPhones(displayOnlyPhones);
+ mAdapter.notifyDataSetChanged();
+ }
+
+ /**
+ * Handle any clicks on {@link ExpandableListAdapter} children, which
+ * usually mean toggling its visible state.
+ */
+ @Override
+ public boolean onChildClick(ExpandableListView parent, View view, int groupPosition,
+ int childPosition, long id) {
+ final CheckBox checkbox = (CheckBox)view.findViewById(android.R.id.checkbox);
+
+ final AccountDisplay account = (AccountDisplay)mAdapter.getGroup(groupPosition);
+ final GroupDelta child = (GroupDelta)mAdapter.getChild(groupPosition, childPosition);
+ if (child != null) {
+ checkbox.toggle();
+ child.putVisible(checkbox.isChecked());
+ } else {
+ // Open context menu for bringing back unsynced
+ this.openContextMenu(view);
+ }
+ return true;
+ }
+
+ // TODO: move these definitions to framework constants when we begin
+ // defining this mode through <sync-adapter> tags
+ private static final int SYNC_MODE_UNSUPPORTED = 0;
+ private static final int SYNC_MODE_UNGROUPED = 1;
+ private static final int SYNC_MODE_EVERYTHING = 2;
+
+ protected int getSyncMode(AccountDisplay account) {
+ // TODO: read sync mode through <sync-adapter> definition
+ if (GoogleAccountType.ACCOUNT_TYPE.equals(account.mType)) {
+ return SYNC_MODE_EVERYTHING;
+ } else {
+ return SYNC_MODE_UNSUPPORTED;
+ }
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view,
+ ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
+
+ // Bail if not working with expandable long-press, or if not child
+ if (!(menuInfo instanceof ExpandableListContextMenuInfo)) return;
+
+ final ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuInfo;
+ final int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
+ final int childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition);
+
+ // Skip long-press on expandable parents
+ if (childPosition == -1) return;
+
+ final AccountDisplay account = (AccountDisplay)mAdapter.getGroup(groupPosition);
+ final GroupDelta child = (GroupDelta)mAdapter.getChild(groupPosition, childPosition);
+
+ // Ignore when selective syncing unsupported
+ final int syncMode = getSyncMode(account);
+ if (syncMode == SYNC_MODE_UNSUPPORTED) return;
+
+ if (child != null) {
+ showRemoveSync(menu, account, child, syncMode);
+ } else {
+ showAddSync(menu, account, syncMode);
+ }
+ }
+
+ protected void showRemoveSync(ContextMenu menu, final AccountDisplay account,
+ final GroupDelta child, final int syncMode) {
+ final CharSequence title = child.getTitle(this);
+
+ menu.setHeaderTitle(title);
+ menu.add(R.string.menu_sync_remove).setOnMenuItemClickListener(
+ new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ handleRemoveSync(account, child, syncMode, title);
+ return true;
+ }
+ });
+ }
+
+ protected void handleRemoveSync(final AccountDisplay account, final GroupDelta child,
+ final int syncMode, CharSequence title) {
+ final boolean shouldSyncUngrouped = account.mUngrouped.getShouldSync();
+ if (syncMode == SYNC_MODE_EVERYTHING && shouldSyncUngrouped
+ && !child.equals(account.mUngrouped)) {
+ // Warn before removing this group when it would cause ungrouped to stop syncing
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ final CharSequence removeMessage = this.getString(
+ R.string.display_warn_remove_ungrouped, title);
+ builder.setTitle(R.string.menu_sync_remove);
+ builder.setMessage(removeMessage);
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // Mark both this group and ungrouped to stop syncing
+ account.setShouldSync(account.mUngrouped, false);
+ account.setShouldSync(child, false);
+ mAdapter.notifyDataSetChanged();
+ }
+ });
+ builder.show();
+ } else {
+ // Mark this group to not sync
+ account.setShouldSync(child, false);
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+ protected void showAddSync(ContextMenu menu, final AccountDisplay account, final int syncMode) {
+ menu.setHeaderTitle(R.string.dialog_sync_add);
+
+ // Create item for each available, unsynced group
+ for (final GroupDelta child : account.mUnsyncedGroups) {
+ if (!child.getShouldSync()) {
+ final CharSequence title = child.getTitle(this);
+ menu.add(title).setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ // Adding specific group for syncing
+ if (child.mUngrouped && syncMode == SYNC_MODE_EVERYTHING) {
+ account.setShouldSync(true);
+ } else {
+ account.setShouldSync(child, true);
+ }
+ mAdapter.notifyDataSetChanged();
+ return true;
+ }
+ });
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onBackPressed() {
+ doSaveAction();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void doSaveAction() {
+ if (mAdapter == null || mAdapter.mAccounts == null) {
+ finish();
+ return;
+ }
+
+ setDisplayOnlyPhones(mDisplayPhones.isChecked());
+ setResult(RESULT_OK);
+
+ final ArrayList<ContentProviderOperation> diff = mAdapter.mAccounts.buildDiff();
+ if (diff.isEmpty()) {
+ finish();
+ return;
+ }
+
+ new UpdateTask(this).execute(diff);
+ }
+
+ /**
+ * Background task that persists changes to {@link Groups#GROUP_VISIBLE},
+ * showing spinner dialog to user while updating.
+ */
+ public static class UpdateTask extends
+ WeakAsyncTask<ArrayList<ContentProviderOperation>, Void, Void, Activity> {
+ private ProgressDialog mProgress;
+
+ public UpdateTask(Activity target) {
+ super(target);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onPreExecute(Activity target) {
+ final Context context = target;
+
+ mProgress = ProgressDialog.show(
+ context, null, context.getText(R.string.savingDisplayGroups));
+
+ // Before starting this task, start an empty service to protect our
+ // process from being reclaimed by the system.
+ context.startService(new Intent(context, EmptyService.class));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected Void doInBackground(
+ Activity target, ArrayList<ContentProviderOperation>... params) {
+ final Context context = target;
+ final ContentValues values = new ContentValues();
+ final ContentResolver resolver = context.getContentResolver();
+
+ try {
+ final ArrayList<ContentProviderOperation> diff = params[0];
+ resolver.applyBatch(ContactsContract.AUTHORITY, diff);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Problem saving display groups", e);
+ } catch (OperationApplicationException e) {
+ Log.e(TAG, "Problem saving display groups", e);
+ }
+
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onPostExecute(Activity target, Void result) {
+ final Context context = target;
+
+ try {
+ mProgress.dismiss();
+ } catch (Exception e) {
+ Log.e(TAG, "Error dismissing progress dialog", e);
+ }
+
+ target.finish();
+
+ // Stop the service that was protecting us
+ context.stopService(new Intent(context, EmptyService.class));
+ }
+ }
+
+ @Override
+ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+ boolean globalSearch) {
+ if (globalSearch) {
+ super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+ } else {
+ ContactsSearchManager.startSearch(this, initialQuery);
+ }
+ }
+}
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
new file mode 100644
index 0000000..f95c4af
--- /dev/null
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+
+import android.database.Cursor;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+/**
+ * Fragment containing a contact list used for browsing (as compared to
+ * picking a contact with one of the PICK intents).
+ */
+public class DefaultContactBrowseListFragment extends ContactBrowseListFragment {
+
+ private View mCounterHeaderView;
+ private View mSearchHeaderView;
+
+ public DefaultContactBrowseListFragment() {
+ setPhotoLoaderEnabled(true);
+ setSectionHeaderDisplayEnabled(true);
+ setVisibleScrollbarEnabled(true);
+ }
+
+ @Override
+ protected void onItemClick(int position, long id) {
+ viewContact(getAdapter().getContactUri(position));
+ }
+
+ @Override
+ protected ContactListAdapter createListAdapter() {
+ DefaultContactListAdapter adapter = new DefaultContactListAdapter(getContext());
+ adapter.setSectionHeaderDisplayEnabled(isSectionHeaderDisplayEnabled());
+ adapter.setDisplayPhotos(true);
+ return adapter;
+ }
+
+ @Override
+ protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+ return inflater.inflate(R.layout.contacts_list_content, null);
+ }
+
+ @Override
+ protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
+ super.onCreateView(inflater, container);
+
+ // Putting the header view inside a container will allow us to make
+ // it invisible later. See checkHeaderViewVisibility()
+ FrameLayout headerContainer = new FrameLayout(inflater.getContext());
+ mCounterHeaderView = inflater.inflate(R.layout.total_contacts, null, false);
+ headerContainer.addView(mCounterHeaderView);
+ mSearchHeaderView = inflater.inflate(R.layout.search_header, null, false);
+ headerContainer.addView(mSearchHeaderView);
+ getListView().addHeaderView(headerContainer, null, false);
+ checkHeaderViewVisibility();
+ }
+
+ @Override
+ public void setSearchMode(boolean flag) {
+ super.setSearchMode(flag);
+ checkHeaderViewVisibility();
+ }
+
+ private void checkHeaderViewVisibility() {
+ if (mCounterHeaderView != null) {
+ mCounterHeaderView.setVisibility(isSearchMode() ? View.GONE : View.VISIBLE);
+ }
+
+ // Hide the search header by default. See showCount().
+ if (mSearchHeaderView != null) {
+ mSearchHeaderView.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ protected void showCount(int partitionIndex, Cursor data) {
+ if (!isSearchMode() && data != null) {
+ int count = data.getCount();
+ TextView textView = (TextView) mCounterHeaderView.findViewById(R.id.totalContactsText);
+ if (count != 0) {
+ String format = getResources().getQuantityText(
+ R.plurals.listTotalAllContacts, count).toString();
+ textView.setText(String.format(format, count));
+ } else {
+ ContactListFilter filter = getFilter();
+ int filterType = filter != null ? filter.filterType
+ : ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS;
+ switch (filterType) {
+ case ContactListFilter.FILTER_TYPE_ACCOUNT:
+ textView.setText(getString(
+ R.string.listTotalAllContactsZeroGroup, filter.accountName));
+ break;
+ case ContactListFilter.FILTER_TYPE_GROUP:
+ textView.setText(
+ getString(R.string.listTotalAllContactsZeroGroup, filter.title));
+ break;
+ case ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY:
+ textView.setText(R.string.listTotalPhoneContactsZero);
+ break;
+ case ContactListFilter.FILTER_TYPE_STARRED:
+ textView.setText(R.string.listTotalAllContactsZeroStarred);
+ break;
+ case ContactListFilter.FILTER_TYPE_CUSTOM:
+ textView.setText(R.string.listTotalAllContactsZeroCustom);
+ break;
+ default:
+ textView.setText(R.string.listTotalAllContactsZero);
+ break;
+ }
+ }
+ } else {
+ ContactListAdapter adapter = getAdapter();
+ if (adapter == null) {
+ return;
+ }
+
+ // In search mode we only display the header if there is nothing found
+ if (TextUtils.isEmpty(getQueryString()) || !adapter.areAllPartitionsEmpty()) {
+ mSearchHeaderView.setVisibility(View.GONE);
+ } else {
+ TextView textView = (TextView) mSearchHeaderView.findViewById(
+ R.id.totalContactsText);
+ ProgressBar progress = (ProgressBar) mSearchHeaderView.findViewById(
+ R.id.progress);
+ if (adapter.isLoading()) {
+ textView.setText(R.string.search_results_searching);
+ progress.setVisibility(View.VISIBLE);
+ } else {
+ textView.setText(R.string.listFoundAllContactsZero);
+ progress.setVisibility(View.GONE);
+ }
+ mSearchHeaderView.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+}
diff --git a/src/com/android/contacts/list/DefaultContactListAdapter.java b/src/com/android/contacts/list/DefaultContactListAdapter.java
new file mode 100644
index 0000000..164a2cd
--- /dev/null
+++ b/src/com/android/contacts/list/DefaultContactListAdapter.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.preference.ContactsPreferences;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.preference.PreferenceManager;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type.
+ */
+public class DefaultContactListAdapter extends ContactListAdapter {
+
+ public DefaultContactListAdapter(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void configureLoader(CursorLoader loader, long directoryId) {
+
+ ContactListFilter filter = getFilter();
+ if (isSearchMode()) {
+ String query = getQueryString();
+ if (query == null) {
+ query = "";
+ }
+ query = query.trim();
+ if (TextUtils.isEmpty(query)) {
+ // Regardless of the directory, we don't want anything returned,
+ // so let's just send a "nothing" query to the local directory.
+ loader.setUri(Contacts.CONTENT_URI);
+ loader.setProjection(PROJECTION_CONTACT);
+ loader.setSelection("0");
+ } else {
+ Builder builder = Contacts.CONTENT_FILTER_URI.buildUpon();
+ builder.appendPath(query); // Builder will encode the query
+ builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(directoryId));
+ if (directoryId != Directory.DEFAULT && directoryId != Directory.LOCAL_INVISIBLE) {
+ builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
+ String.valueOf(getDirectoryResultLimit()));
+ }
+ applyDataRestriction(builder);
+ loader.setUri(builder.build());
+ loader.setProjection(FILTER_PROJECTION);
+ }
+ } else {
+ configureUri(loader, directoryId, filter);
+ configureProjection(loader, directoryId, filter);
+ configureSelection(loader, directoryId, filter);
+ }
+
+ String sortOrder;
+ if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
+ sortOrder = Contacts.SORT_KEY_PRIMARY;
+ } else {
+ sortOrder = Contacts.SORT_KEY_ALTERNATIVE;
+ }
+
+ loader.setSortOrder(sortOrder);
+ }
+
+ protected void configureUri(CursorLoader loader, long directoryId, ContactListFilter filter) {
+ Uri uri = Contacts.CONTENT_URI;
+ if (filter != null) {
+ if (filter.filterType == ContactListFilter.FILTER_TYPE_GROUP) {
+ uri = Data.CONTENT_URI;
+ } else if (filter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
+ String lookupKey = getSelectedContactLookupKey();
+ if (lookupKey != null) {
+ uri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
+ } else {
+ uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, getSelectedContactId());
+ }
+ }
+ }
+
+ if (directoryId == Directory.DEFAULT && isSectionHeaderDisplayEnabled()) {
+ uri = buildSectionIndexerUri(uri);
+ }
+
+ // The "All accounts" filter is the same as the entire contents of Directory.DEFAULT
+ if (filter != null
+ && filter.filterType != ContactListFilter.FILTER_TYPE_CUSTOM
+ && filter.filterType != ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
+ uri = uri.buildUpon().appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
+ .build();
+ }
+ uri = applyDataRestriction(uri);
+
+ loader.setUri(uri);
+ }
+
+ protected void configureProjection(
+ CursorLoader loader, long directoryId, ContactListFilter filter) {
+ if (filter != null && filter.filterType == ContactListFilter.FILTER_TYPE_GROUP) {
+ loader.setProjection(PROJECTION_DATA);
+ } else {
+ loader.setProjection(PROJECTION_CONTACT);
+ }
+ }
+
+ private void configureSelection(
+ CursorLoader loader, long directoryId, ContactListFilter filter) {
+ if (filter == null) {
+ return;
+ }
+
+ if (directoryId != Directory.DEFAULT) {
+ return;
+ }
+
+ StringBuilder selection = new StringBuilder();
+ List<String> selectionArgs = new ArrayList<String>();
+
+ switch (filter.filterType) {
+ case ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS: {
+ // We have already added directory=0 to the URI, which takes care of this
+ // filter
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_SINGLE_CONTACT: {
+ // We have already added the lookup key to the URI, which takes care of this
+ // filter
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_STARRED: {
+ selection.append(Contacts.STARRED + "!=0");
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_WITH_PHONE_NUMBERS_ONLY: {
+ selection.append(Contacts.HAS_PHONE_NUMBER + "=1");
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_CUSTOM: {
+ selection.append(Contacts.IN_VISIBLE_GROUP + "=1");
+ if (isCustomFilterForPhoneNumbersOnly()) {
+ selection.append(" AND " + Contacts.HAS_PHONE_NUMBER + "=1");
+ }
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_ACCOUNT: {
+ // TODO: avoid the use of private API
+ selection.append(
+ Contacts._ID + " IN ("
+ + "SELECT DISTINCT " + RawContacts.CONTACT_ID
+ + " FROM raw_contacts"
+ + " WHERE " + RawContacts.ACCOUNT_TYPE + "=?"
+ + " AND " + RawContacts.ACCOUNT_NAME + "=?)");
+ selectionArgs.add(filter.accountType);
+ selectionArgs.add(filter.accountName);
+ break;
+ }
+ case ContactListFilter.FILTER_TYPE_GROUP: {
+ selection.append(Data.MIMETYPE + "=?"
+ + " AND " + GroupMembership.GROUP_ROW_ID + "=?");
+ selectionArgs.add(GroupMembership.CONTENT_ITEM_TYPE);
+ selectionArgs.add(String.valueOf(filter.groupId));
+ break;
+ }
+ }
+ loader.setSelection(selection.toString());
+ loader.setSelectionArgs(selectionArgs.toArray(new String[0]));
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ final ContactListItemView view = (ContactListItemView)itemView;
+
+ view.setHighlightedPrefix(isSearchMode() ? getUpperCaseQueryString() : null);
+
+ if (isSelectionVisible()) {
+ view.setActivated(isSelectedContact(partition, cursor));
+ }
+
+ bindSectionHeaderAndDivider(view, position);
+
+ if (isQuickContactEnabled()) {
+ bindQuickContact(view, partition, cursor);
+ } else {
+ bindPhoto(view, partition, cursor);
+ }
+
+ bindName(view, cursor);
+ bindPresence(view, cursor);
+
+ if (isSearchMode()) {
+ bindSearchSnippet(view, cursor);
+ } else {
+ view.setSnippet(null);
+ }
+ }
+
+ private boolean isCustomFilterForPhoneNumbersOnly() {
+ // TODO: this flag should not be stored in shared prefs. It needs to be in the db.
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ return prefs.getBoolean(ContactsPreferences.PREF_DISPLAY_ONLY_PHONES,
+ ContactsPreferences.PREF_DISPLAY_ONLY_PHONES_DEFAULT);
+ }
+}
diff --git a/src/com/android/contacts/list/DirectoryListLoader.java b/src/com/android/contacts/list/DirectoryListLoader.java
new file mode 100644
index 0000000..0bb250a
--- /dev/null
+++ b/src/com/android/contacts/list/DirectoryListLoader.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.ContactsContract.Directory;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * A specialized loader for the list of directories, see {@link Directory}.
+ */
+public class DirectoryListLoader extends AsyncTaskLoader<Cursor> {
+
+ private static final String TAG = "ContactEntryListAdapter";
+
+ public static final int SEARCH_MODE_NONE = 0;
+ public static final int SEARCH_MODE_DEFAULT = 1;
+ public static final int SEARCH_MODE_CONTACT_SHORTCUT = 2;
+ public static final int SEARCH_MODE_DATA_SHORTCUT = 3;
+
+ private static final class DirectoryQuery {
+ public static final Uri URI = Directory.CONTENT_URI;
+ public static final String ORDER_BY = Directory._ID;
+
+ public static final String[] PROJECTION = {
+ Directory._ID,
+ Directory.PACKAGE_NAME,
+ Directory.TYPE_RESOURCE_ID,
+ Directory.DISPLAY_NAME,
+ Directory.PHOTO_SUPPORT,
+ };
+
+ public static final int ID = 0;
+ public static final int PACKAGE_NAME = 1;
+ public static final int TYPE_RESOURCE_ID = 2;
+ public static final int DISPLAY_NAME = 3;
+ public static final int PHOTO_SUPPORT = 4;
+ }
+
+ public static final String DIRECTORY_TYPE = "directoryType";
+
+ private static final String[] RESULT_PROJECTION = {
+ Directory._ID,
+ DIRECTORY_TYPE,
+ Directory.DISPLAY_NAME,
+ Directory.PHOTO_SUPPORT,
+ };
+
+ private final ContentObserver mObserver = new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ forceLoad();
+ }
+ };
+
+ private int mDirectorySearchMode;
+ private boolean mLocalInvisibleDirectoryEnabled;
+
+ private MatrixCursor mDefaultDirectoryList;
+
+ public DirectoryListLoader(Context context) {
+ super(context);
+ }
+
+ public void setDirectorySearchMode(int mode) {
+ mDirectorySearchMode = mode;
+ }
+
+ /**
+ * A flag that indicates whether the {@link Directory#LOCAL_INVISIBLE} directory should
+ * be included in the results.
+ */
+ public void setLocalInvisibleDirectoryEnabled(boolean flag) {
+ this.mLocalInvisibleDirectoryEnabled = flag;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ getContext().getContentResolver().
+ registerContentObserver(Directory.CONTENT_URI, false, mObserver);
+ forceLoad();
+ }
+
+ @Override
+ protected void onStopLoading() {
+ getContext().getContentResolver().unregisterContentObserver(mObserver);
+ }
+
+ @Override
+ public Cursor loadInBackground() {
+ if (mDirectorySearchMode == SEARCH_MODE_NONE) {
+ return getDefaultDirectories();
+ }
+
+ MatrixCursor result = new MatrixCursor(RESULT_PROJECTION);
+ Context context = getContext();
+ PackageManager pm = context.getPackageManager();
+ String selection;
+ switch (mDirectorySearchMode) {
+ case SEARCH_MODE_DEFAULT:
+ selection = mLocalInvisibleDirectoryEnabled ? null
+ : (Directory._ID + "!=" + Directory.LOCAL_INVISIBLE);
+ break;
+
+ case SEARCH_MODE_CONTACT_SHORTCUT:
+ selection = Directory.SHORTCUT_SUPPORT + "=" + Directory.SHORTCUT_SUPPORT_FULL
+ + (mLocalInvisibleDirectoryEnabled ? ""
+ : (" AND " + Directory._ID + "!=" + Directory.LOCAL_INVISIBLE));
+ break;
+
+ case SEARCH_MODE_DATA_SHORTCUT:
+ selection = Directory.SHORTCUT_SUPPORT + " IN ("
+ + Directory.SHORTCUT_SUPPORT_FULL + ", "
+ + Directory.SHORTCUT_SUPPORT_DATA_ITEMS_ONLY + ")"
+ + (mLocalInvisibleDirectoryEnabled ? ""
+ : (" AND " + Directory._ID + "!=" + Directory.LOCAL_INVISIBLE));
+ break;
+
+ default:
+ throw new RuntimeException(
+ "Unsupported directory search mode: " + mDirectorySearchMode);
+ }
+
+ Cursor cursor = context.getContentResolver().query(DirectoryQuery.URI,
+ DirectoryQuery.PROJECTION, selection, null, DirectoryQuery.ORDER_BY);
+ try {
+ while(cursor.moveToNext()) {
+ long directoryId = cursor.getLong(DirectoryQuery.ID);
+ String directoryType = null;
+
+ String packageName = cursor.getString(DirectoryQuery.PACKAGE_NAME);
+ int typeResourceId = cursor.getInt(DirectoryQuery.TYPE_RESOURCE_ID);
+ if (!TextUtils.isEmpty(packageName) && typeResourceId != 0) {
+ try {
+ directoryType = pm.getResourcesForApplication(packageName)
+ .getString(typeResourceId);
+ } catch (Exception e) {
+ Log.e(TAG, "Cannot obtain directory type from package: " + packageName);
+ }
+ }
+ String displayName = cursor.getString(DirectoryQuery.DISPLAY_NAME);
+ int photoSupport = cursor.getInt(DirectoryQuery.PHOTO_SUPPORT);
+ result.addRow(new Object[]{directoryId, directoryType, displayName, photoSupport});
+ }
+ } finally {
+ cursor.close();
+ }
+
+ return result;
+ }
+
+ private Cursor getDefaultDirectories() {
+ if (mDefaultDirectoryList == null) {
+ mDefaultDirectoryList = new MatrixCursor(RESULT_PROJECTION);
+ mDefaultDirectoryList.addRow(new Object[] {
+ Directory.DEFAULT,
+ getContext().getString(R.string.contactsList),
+ null
+ });
+ mDefaultDirectoryList.addRow(new Object[] {
+ Directory.LOCAL_INVISIBLE,
+ getContext().getString(R.string.local_invisible_directory),
+ null
+ });
+ }
+ return mDefaultDirectoryList;
+ }
+
+ @Override
+ protected void onReset() {
+ stopLoading();
+ }
+}
diff --git a/src/com/android/contacts/list/DirectoryPartition.java b/src/com/android/contacts/list/DirectoryPartition.java
new file mode 100644
index 0000000..47a1037
--- /dev/null
+++ b/src/com/android/contacts/list/DirectoryPartition.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.common.widget.CompositeCursorAdapter;
+
+import android.provider.ContactsContract.Directory;
+
+/**
+ * Model object for a {@link Directory} row.
+ */
+public final class DirectoryPartition extends CompositeCursorAdapter.Partition {
+
+ public static final int STATUS_NOT_LOADED = 0;
+ public static final int STATUS_LOADING = 1;
+ public static final int STATUS_LOADED = 2;
+
+ private long mDirectoryId;
+ private String mDirectoryType;
+ private String mDisplayName;
+ private int mStatus;
+ private boolean mPriorityDirectory;
+ private boolean mPhotoSupported;
+
+ public DirectoryPartition(boolean showIfEmpty, boolean hasHeader) {
+ super(showIfEmpty, hasHeader);
+ }
+
+ /**
+ * Directory ID, see {@link Directory}.
+ */
+ public long getDirectoryId() {
+ return mDirectoryId;
+ }
+
+ public void setDirectoryId(long directoryId) {
+ this.mDirectoryId = directoryId;
+ }
+
+ /**
+ * Directory type resolved from {@link Directory#PACKAGE_NAME} and
+ * {@link Directory#TYPE_RESOURCE_ID};
+ */
+ public String getDirectoryType() {
+ return mDirectoryType;
+ }
+
+ public void setDirectoryType(String directoryType) {
+ this.mDirectoryType = directoryType;
+ }
+
+ /**
+ * See {@link Directory#DISPLAY_NAME}.
+ */
+ public String getDisplayName() {
+ return mDisplayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.mDisplayName = displayName;
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public void setStatus(int status) {
+ mStatus = status;
+ }
+
+ public boolean isLoading() {
+ return mStatus == STATUS_NOT_LOADED || mStatus == STATUS_LOADING;
+ }
+
+ /**
+ * Returns true if this directory should be loaded before non-priority directories.
+ */
+ public boolean isPriorityDirectory() {
+ return mPriorityDirectory;
+ }
+
+ public void setPriorityDirectory(boolean priorityDirectory) {
+ mPriorityDirectory = priorityDirectory;
+ }
+
+ /**
+ * Returns true if this directory supports photos.
+ */
+ public boolean isPhotoSupported() {
+ return mPhotoSupported;
+ }
+
+ public void setPhotoSupported(boolean flag) {
+ this.mPhotoSupported = flag;
+ }
+}
diff --git a/src/com/android/contacts/list/JoinContactListAdapter.java b/src/com/android/contacts/list/JoinContactListAdapter.java
new file mode 100644
index 0000000..e7a9eb9
--- /dev/null
+++ b/src/com/android/contacts/list/JoinContactListAdapter.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Contacts.AggregationSuggestions;
+import android.provider.ContactsContract.Directory;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class JoinContactListAdapter extends ContactListAdapter {
+
+ /** Maximum number of suggestions shown for joining aggregates */
+ private static final int MAX_SUGGESTIONS = 4;
+
+ public static final int PARTITION_SUGGESTIONS = 0;
+ public static final int PARTITION_SHOW_ALL_CONTACTS = 1;
+ public static final int PARTITION_ALL_CONTACTS = 2;
+
+ private long mTargetContactId;
+
+ private int mShowAllContactsViewType;
+
+ /**
+ * Determines whether we display a list item with the label
+ * "Show all contacts" or actually show all contacts
+ */
+ private boolean mAllContactsListShown;
+
+
+ public JoinContactListAdapter(Context context) {
+ super(context);
+ setPinnedPartitionHeadersEnabled(true);
+ setSectionHeaderDisplayEnabled(true);
+ setIndexedPartition(PARTITION_ALL_CONTACTS);
+ setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
+ mShowAllContactsViewType = getViewTypeCount() - 1;
+ }
+
+ @Override
+ protected void addPartitions() {
+
+ // Partition 0: suggestions
+ addPartition(false, true);
+
+ // Partition 1: "Show all contacts"
+ addPartition(false, false);
+
+ // Partition 2: All contacts
+ addPartition(createDefaultDirectoryPartition());
+ }
+
+ public void setTargetContactId(long targetContactId) {
+ this.mTargetContactId = targetContactId;
+ }
+
+ @Override
+ public void configureLoader(CursorLoader cursorLoader, long directoryId) {
+ JoinContactLoader loader = (JoinContactLoader)cursorLoader;
+ loader.setLoadSuggestionsAndAllContacts(mAllContactsListShown);
+
+ Builder builder = Contacts.CONTENT_URI.buildUpon();
+ builder.appendEncodedPath(String.valueOf(mTargetContactId));
+ builder.appendEncodedPath(AggregationSuggestions.CONTENT_DIRECTORY);
+
+ String filter = getQueryString();
+ if (!TextUtils.isEmpty(filter)) {
+ builder.appendEncodedPath(Uri.encode(filter));
+ }
+
+ builder.appendQueryParameter("limit", String.valueOf(MAX_SUGGESTIONS));
+
+ loader.setSuggestionUri(builder.build());
+
+ // TODO simplify projection
+ loader.setProjection(PROJECTION_CONTACT);
+ Uri allContactsUri = buildSectionIndexerUri(Contacts.CONTENT_URI).buildUpon()
+ .appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
+ .build();
+ loader.setUri(allContactsUri);
+ loader.setSelection(Contacts._ID + "!=?");
+ loader.setSelectionArgs(new String[]{String.valueOf(mTargetContactId)});
+ if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
+ loader.setSortOrder(Contacts.SORT_KEY_PRIMARY);
+ } else {
+ loader.setSortOrder(Contacts.SORT_KEY_ALTERNATIVE);
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ public boolean isAllContactsListShown() {
+ return mAllContactsListShown;
+ }
+
+ public void setAllContactsListShown(boolean flag) {
+ mAllContactsListShown = flag;
+ }
+
+ public void setSuggestionsCursor(Cursor cursor) {
+ changeCursor(PARTITION_SUGGESTIONS, cursor);
+ if (cursor != null && cursor.getCount() != 0 && !mAllContactsListShown) {
+ changeCursor(PARTITION_SHOW_ALL_CONTACTS, getShowAllContactsLabelCursor());
+ } else {
+ changeCursor(PARTITION_SHOW_ALL_CONTACTS, null);
+ }
+ }
+
+ @Override
+ public void changeCursor(Cursor cursor) {
+ changeCursor(PARTITION_ALL_CONTACTS, cursor);
+ }
+
+ @Override
+ public void configureDefaultPartition(boolean showIfEmpty, boolean hasHeader) {
+ // Don't change default partition parameters from these defaults
+ super.configureDefaultPartition(false, true);
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return super.getViewTypeCount() + 1;
+ }
+
+ @Override
+ public int getItemViewType(int partition, int position) {
+ if (partition == PARTITION_SHOW_ALL_CONTACTS) {
+ return mShowAllContactsViewType;
+ }
+ return super.getItemViewType(partition, position);
+ }
+
+ @Override
+ protected View newHeaderView(Context context, int partition, Cursor cursor,
+ ViewGroup parent) {
+ switch (partition) {
+ case PARTITION_SUGGESTIONS: {
+ View view = inflate(R.layout.join_contact_picker_section, parent);
+ ((TextView) view.findViewById(R.id.text)).setText(
+ R.string.separatorJoinAggregateSuggestions);
+ return view;
+ }
+ case PARTITION_ALL_CONTACTS: {
+ View view = inflate(R.layout.join_contact_picker_section, parent);
+ ((TextView) view.findViewById(R.id.text)).setText(
+ R.string.separatorJoinAggregateAll);
+ return view;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void bindHeaderView(View view, int partitionIndex, Cursor cursor) {
+ // Header views are static - nothing needs to be bound
+ }
+
+ @Override
+ protected View newView(Context context, int partition, Cursor cursor, int position,
+ ViewGroup parent) {
+ switch (partition) {
+ case PARTITION_SUGGESTIONS:
+ case PARTITION_ALL_CONTACTS:
+ return super.newView(context, partition, cursor, position, parent);
+ case PARTITION_SHOW_ALL_CONTACTS:
+ return inflate(R.layout.join_contact_picker_show_all, parent);
+ }
+ return null;
+ }
+
+ private View inflate(int layoutId, ViewGroup parent) {
+ return LayoutInflater.from(getContext()).inflate(layoutId, parent, false);
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ switch (partition) {
+ case PARTITION_SUGGESTIONS: {
+ final ContactListItemView view = (ContactListItemView)itemView;
+ view.setSectionHeader(null);
+ bindPhoto(view, partition, cursor);
+ bindName(view, cursor);
+ break;
+ }
+ case PARTITION_SHOW_ALL_CONTACTS: {
+ break;
+ }
+ case PARTITION_ALL_CONTACTS: {
+ final ContactListItemView view = (ContactListItemView)itemView;
+ bindSectionHeaderAndDivider(view, position);
+ bindPhoto(view, partition, cursor);
+ bindName(view, cursor);
+ break;
+ }
+ }
+ }
+
+ public Cursor getShowAllContactsLabelCursor() {
+ MatrixCursor matrixCursor = new MatrixCursor(PROJECTION_CONTACT);
+ Object[] row = new Object[PROJECTION_CONTACT.length];
+ matrixCursor.addRow(row);
+ return matrixCursor;
+ }
+
+ @Override
+ public Uri getContactUri(int partitionIndex, Cursor cursor) {
+ long contactId = cursor.getLong(CONTACT_ID_COLUMN_INDEX);
+ String lookupKey = cursor.getString(CONTACT_LOOKUP_KEY_COLUMN_INDEX);
+ return Contacts.getLookupUri(contactId, lookupKey);
+ }
+}
diff --git a/src/com/android/contacts/list/JoinContactListFragment.java b/src/com/android/contacts/list/JoinContactListFragment.java
new file mode 100644
index 0000000..59b29ed
--- /dev/null
+++ b/src/com/android/contacts/list/JoinContactListFragment.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.ContactsSearchManager;
+import com.android.contacts.R;
+
+import android.app.Activity;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ContentUris;
+import android.content.CursorLoader;
+import android.content.Intent;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * Fragment for the Join Contact list.
+ */
+public class JoinContactListFragment extends ContactEntryListFragment<JoinContactListAdapter> {
+
+ private static final int DISPLAY_NAME_LOADER = -2;
+
+ private static final String KEY_ALL_CONTACTS_LIST_SHOWN = "allContactsShown";
+
+ private OnContactPickerActionListener mListener;
+ private long mTargetContactId;
+ private boolean mAllContactsListShown = false;
+
+ private LoaderCallbacks<Cursor> mLoaderCallbacks = new LoaderCallbacks<Cursor>() {
+
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case DISPLAY_NAME_LOADER: {
+ // Loader for the display name of the target contact
+ return new CursorLoader(getActivity(),
+ ContentUris.withAppendedId(Contacts.CONTENT_URI, mTargetContactId),
+ new String[] { Contacts.DISPLAY_NAME }, null, null, null);
+ }
+ case JoinContactListAdapter.PARTITION_ALL_CONTACTS: {
+ JoinContactLoader loader = new JoinContactLoader(getActivity());
+ JoinContactListAdapter adapter = getAdapter();
+ if (adapter != null) {
+ adapter.configureLoader(loader, 0);
+ }
+ return loader;
+ }
+ }
+ throw new IllegalArgumentException("No loader for ID=" + id);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ switch (loader.getId()) {
+ case DISPLAY_NAME_LOADER: {
+ if (data != null && data.moveToFirst()) {
+ showTargetContactName(data.getString(0));
+ }
+ break;
+ }
+ case JoinContactListAdapter.PARTITION_ALL_CONTACTS: {
+ Cursor suggestionsCursor = ((JoinContactLoader)loader).getSuggestionsCursor();
+ onContactListLoaded(suggestionsCursor, data);
+ break;
+ }
+ }
+ }
+
+ public void onLoaderReset(Loader<Cursor> loader) {
+ }
+ };
+
+ public JoinContactListFragment() {
+ setPhotoLoaderEnabled(true);
+ setSectionHeaderDisplayEnabled(true);
+ setVisibleScrollbarEnabled(false);
+ setQuickContactEnabled(false);
+ }
+
+ public void setOnContactPickerActionListener(OnContactPickerActionListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ protected void startLoading() {
+ configureAdapter();
+
+ getLoaderManager().initLoader(DISPLAY_NAME_LOADER, null, mLoaderCallbacks);
+ getLoaderManager().initLoader(JoinContactListAdapter.PARTITION_ALL_CONTACTS,
+ null, mLoaderCallbacks);
+ }
+
+ void onContactListLoaded(Cursor suggestionsCursor, Cursor allContacts) {
+ JoinContactListAdapter adapter = getAdapter();
+ adapter.setSuggestionsCursor(suggestionsCursor);
+ if (suggestionsCursor == null || suggestionsCursor.getCount() == 0) {
+ mAllContactsListShown = true;
+ }
+ setVisibleScrollbarEnabled(mAllContactsListShown);
+ onPartitionLoaded(JoinContactListAdapter.PARTITION_ALL_CONTACTS, allContacts);
+ }
+
+ private void showTargetContactName(String displayName) {
+ Activity activity = getActivity();
+ TextView blurbView = (TextView)activity.findViewById(R.id.join_contact_blurb);
+ String blurb = activity.getString(R.string.blurbJoinContactDataWith, displayName);
+ blurbView.setText(blurb);
+ }
+
+ public void setTargetContactId(long targetContactId) {
+ mTargetContactId = targetContactId;
+ }
+
+ @Override
+ public JoinContactListAdapter createListAdapter() {
+ return new JoinContactListAdapter(getActivity());
+ }
+
+ @Override
+ protected void configureAdapter() {
+ super.configureAdapter();
+ JoinContactListAdapter adapter = getAdapter();
+ adapter.setAllContactsListShown(mAllContactsListShown);
+ adapter.setTargetContactId(mTargetContactId);
+ }
+
+ @Override
+ protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+ return inflater.inflate(R.layout.join_contact_picker_list_content, null);
+ }
+
+ @Override
+ protected void onItemClick(int position, long id) {
+ JoinContactListAdapter adapter = getAdapter();
+ int partition = adapter.getPartitionForPosition(position);
+ if (partition == JoinContactListAdapter.PARTITION_SHOW_ALL_CONTACTS) {
+ mAllContactsListShown = true;
+ configureAdapter();
+ getLoaderManager().restartLoader(JoinContactListAdapter.PARTITION_ALL_CONTACTS,
+ null, mLoaderCallbacks);
+ } else {
+ mListener.onPickContactAction(adapter.getContactUri(position));
+ }
+ }
+
+ @Override
+ public void startSearch(String initialQuery) {
+ ContactsRequest request = new ContactsRequest();
+ request.setActionCode(ContactsRequest.ACTION_PICK_CONTACT);
+ request.setDirectorySearchEnabled(false);
+ ContactsSearchManager.startSearchForResult(getActivity(), initialQuery,
+ ACTIVITY_REQUEST_CODE_PICKER, request);
+ }
+
+ @Override
+ public void onPickerResult(Intent data) {
+ mListener.onPickContactAction(data.getData());
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_ALL_CONTACTS_LIST_SHOWN, mAllContactsListShown);
+ }
+
+ @Override
+ public void restoreSavedState(Bundle savedState) {
+ super.restoreSavedState(savedState);
+ if (savedState != null) {
+ mAllContactsListShown = savedState.getBoolean(KEY_ALL_CONTACTS_LIST_SHOWN);
+ }
+ }
+}
diff --git a/src/com/android/contacts/list/JoinContactLoader.java b/src/com/android/contacts/list/JoinContactLoader.java
new file mode 100644
index 0000000..a137961
--- /dev/null
+++ b/src/com/android/contacts/list/JoinContactLoader.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+
+/**
+ * A specialized loader for the Join Contacts UI. It executes two queries:
+ * join suggestions and (optionally) the full contact list.
+ */
+public class JoinContactLoader extends CursorLoader {
+
+ private boolean mLoadSuggestionsAndAllContacts;
+ private String[] mProjection;
+ private Uri mSuggestionUri;
+ private MatrixCursor mSuggestionsCursor;
+
+ public JoinContactLoader(Context context) {
+ super(context, null, null, null, null, null);
+ }
+
+ public void setLoadSuggestionsAndAllContacts(boolean flag) {
+ mLoadSuggestionsAndAllContacts = flag;
+ }
+
+ public void setSuggestionUri(Uri uri) {
+ this.mSuggestionUri = uri;
+ }
+
+ @Override
+ public void setProjection(String[] projection) {
+ super.setProjection(projection);
+ this.mProjection = projection;
+ }
+
+ public Cursor getSuggestionsCursor() {
+ return mSuggestionsCursor;
+ }
+
+ @Override
+ public Cursor loadInBackground() {
+ // First execute the suggestions query, then call super.loadInBackground
+ // to load the entire list
+ mSuggestionsCursor = loadSuggestions();
+ if (!mLoadSuggestionsAndAllContacts && mSuggestionsCursor.getCount() != 0) {
+ // In case we only need suggestions, send "0" as the search query, which
+ // will always return an empty cursor (but we can still register to
+ // listen for changes on it).
+ setSelection("0");
+ setSelectionArgs(null);
+ }
+ return super.loadInBackground();
+ }
+
+ /**
+ * Loads join suggestions into a MatrixCursor.
+ */
+ private MatrixCursor loadSuggestions() {
+ Cursor cursor = getContext().getContentResolver().query(mSuggestionUri, mProjection,
+ null, null, null);
+ try {
+ MatrixCursor matrix = new MatrixCursor(mProjection);
+ Object[] row = new Object[mProjection.length];
+ while (cursor.moveToNext()) {
+ for (int i = 0; i < row.length; i++) {
+ row[i] = cursor.getString(i);
+ }
+ matrix.addRow(row);
+ }
+ return matrix;
+ } finally {
+ cursor.close();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/list/LegacyContactListAdapter.java b/src/com/android/contacts/list/LegacyContactListAdapter.java
new file mode 100644
index 0000000..6747d1f
--- /dev/null
+++ b/src/com/android/contacts/list/LegacyContactListAdapter.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Contacts.People;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A cursor adapter for the People.CONTENT_TYPE content type.
+ */
+@SuppressWarnings("deprecation")
+public class LegacyContactListAdapter extends ContactEntryListAdapter {
+
+ static final String[] PEOPLE_PROJECTION = new String[] {
+ People._ID, // 0
+ People.DISPLAY_NAME, // 1
+ People.PHONETIC_NAME, // 2
+ People.STARRED, // 3
+ People.PRESENCE_STATUS, // 4
+ };
+
+ protected static final int PERSON_ID_COLUMN_INDEX = 0;
+ protected static final int PERSON_DISPLAY_NAME_COLUMN_INDEX = 1;
+ protected static final int PERSON_PHONETIC_NAME_COLUMN_INDEX = 2;
+ protected static final int PERSON_STARRED_COLUMN_INDEX = 3;
+ protected static final int PERSON_PRESENCE_STATUS_COLUMN_INDEX = 4;
+
+ private CharSequence mUnknownNameText;
+
+ public LegacyContactListAdapter(Context context) {
+ super(context);
+ mUnknownNameText = context.getText(android.R.string.unknownName);
+ }
+
+ @Override
+ public void configureLoader(CursorLoader loader, long directoryId) {
+ loader.setUri(People.CONTENT_URI);
+ loader.setProjection(PEOPLE_PROJECTION);
+ loader.setSortOrder(People.DISPLAY_NAME);
+ }
+
+ @Override
+ public String getContactDisplayName(int position) {
+ return ((Cursor)getItem(position)).getString(PERSON_DISPLAY_NAME_COLUMN_INDEX);
+ }
+
+ public Uri getPersonUri(int position) {
+ Cursor cursor = ((Cursor)getItem(position));
+ long personId = cursor.getLong(PERSON_ID_COLUMN_INDEX);
+ return ContentUris.withAppendedId(People.CONTENT_URI, personId);
+ }
+
+ @Override
+ protected View newView(Context context, int partition, Cursor cursor, int position,
+ ViewGroup parent) {
+ final ContactListItemView view = new ContactListItemView(context, null);
+ view.setUnknownNameText(mUnknownNameText);
+ return view;
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ ContactListItemView view = (ContactListItemView)itemView;
+ bindName(view, cursor);
+ bindPresence(view, cursor);
+ }
+
+ protected void bindName(final ContactListItemView view, Cursor cursor) {
+ view.showDisplayName(cursor, PERSON_DISPLAY_NAME_COLUMN_INDEX, false, 0);
+ view.showPhoneticName(cursor, PERSON_PHONETIC_NAME_COLUMN_INDEX);
+ }
+
+ protected void bindPresence(final ContactListItemView view, Cursor cursor) {
+ view.showPresence(cursor, PERSON_PRESENCE_STATUS_COLUMN_INDEX, 0);
+ }
+}
diff --git a/src/com/android/contacts/list/LegacyPhoneNumberListAdapter.java b/src/com/android/contacts/list/LegacyPhoneNumberListAdapter.java
new file mode 100644
index 0000000..47747fb
--- /dev/null
+++ b/src/com/android/contacts/list/LegacyPhoneNumberListAdapter.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A cursor adapter for the Phones.CONTENT_TYPE content type.
+ */
+@SuppressWarnings("deprecation")
+public class LegacyPhoneNumberListAdapter extends ContactEntryListAdapter {
+
+ private static final String[] PHONES_PROJECTION = new String[] {
+ Phones._ID, // 0
+ Phones.TYPE, // 1
+ Phones.LABEL, // 2
+ Phones.NUMBER, // 3
+ People.DISPLAY_NAME, // 4
+ People.PHONETIC_NAME, // 5
+ };
+
+ private static final int PHONE_ID_COLUMN_INDEX = 0;
+ private static final int PHONE_TYPE_COLUMN_INDEX = 1;
+ private static final int PHONE_LABEL_COLUMN_INDEX = 2;
+ private static final int PHONE_NUMBER_COLUMN_INDEX = 3;
+ private static final int PHONE_DISPLAY_NAME_COLUMN_INDEX = 4;
+ private static final int PHONE_PHONETIC_NAME_COLUMN_INDEX = 5;
+
+ private CharSequence mUnknownNameText;
+
+ public LegacyPhoneNumberListAdapter(Context context) {
+ super(context);
+ mUnknownNameText = context.getText(android.R.string.unknownName);
+ }
+
+ @Override
+ public void configureLoader(CursorLoader loader, long directoryId) {
+ loader.setUri(Phones.CONTENT_URI);
+ loader.setProjection(PHONES_PROJECTION);
+ loader.setSortOrder(Phones.DISPLAY_NAME);
+ }
+
+ @Override
+ public String getContactDisplayName(int position) {
+ return ((Cursor)getItem(position)).getString(PHONE_DISPLAY_NAME_COLUMN_INDEX);
+ }
+
+ public Uri getPhoneUri(int position) {
+ Cursor cursor = ((Cursor)getItem(position));
+ long id = cursor.getLong(PHONE_ID_COLUMN_INDEX);
+ return ContentUris.withAppendedId(Phones.CONTENT_URI, id);
+ }
+
+ @Override
+ protected View newView(Context context, int partition, Cursor cursor, int position,
+ ViewGroup parent) {
+ final ContactListItemView view = new ContactListItemView(context, null);
+ view.setUnknownNameText(mUnknownNameText);
+ return view;
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ ContactListItemView view = (ContactListItemView)itemView;
+ bindName(view, cursor);
+ bindPhoneNumber(view, cursor);
+ }
+
+ protected void bindName(final ContactListItemView view, Cursor cursor) {
+ view.showDisplayName(cursor, PHONE_DISPLAY_NAME_COLUMN_INDEX, false, 0);
+ view.showPhoneticName(cursor, PHONE_PHONETIC_NAME_COLUMN_INDEX);
+ }
+
+ protected void bindPhoneNumber(ContactListItemView view, Cursor cursor) {
+ CharSequence label = null;
+ if (!cursor.isNull(PHONE_TYPE_COLUMN_INDEX)) {
+ final int type = cursor.getInt(PHONE_TYPE_COLUMN_INDEX);
+ final String customLabel = cursor.getString(PHONE_LABEL_COLUMN_INDEX);
+
+ // TODO cache
+ label = Phone.getTypeLabel(getContext().getResources(), type, customLabel);
+ }
+ view.setLabel(label);
+ view.showData(cursor, PHONE_NUMBER_COLUMN_INDEX);
+ }
+}
diff --git a/src/com/android/contacts/list/LegacyPostalAddressListAdapter.java b/src/com/android/contacts/list/LegacyPostalAddressListAdapter.java
new file mode 100644
index 0000000..3796c62
--- /dev/null
+++ b/src/com/android/contacts/list/LegacyPostalAddressListAdapter.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Contacts.ContactMethods;
+import android.provider.Contacts.People;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A cursor adapter for the ContactMethods.CONTENT_TYPE content type.
+ */
+@SuppressWarnings("deprecation")
+public class LegacyPostalAddressListAdapter extends ContactEntryListAdapter {
+
+ static final String[] POSTALS_PROJECTION = new String[] {
+ ContactMethods._ID, // 0
+ ContactMethods.TYPE, // 1
+ ContactMethods.LABEL, // 2
+ ContactMethods.DATA, // 3
+ People.DISPLAY_NAME, // 4
+ People.PHONETIC_NAME, // 5
+ };
+
+ public static final int POSTAL_ID_COLUMN_INDEX = 0;
+ public static final int POSTAL_TYPE_COLUMN_INDEX = 1;
+ public static final int POSTAL_LABEL_COLUMN_INDEX = 2;
+ public static final int POSTAL_NUMBER_COLUMN_INDEX = 3;
+ public static final int POSTAL_DISPLAY_NAME_COLUMN_INDEX = 4;
+ public static final int POSTAL_PHONETIC_NAME_COLUMN_INDEX = 5;
+
+ private CharSequence mUnknownNameText;
+
+ public LegacyPostalAddressListAdapter(Context context) {
+ super(context);
+ mUnknownNameText = context.getText(android.R.string.unknownName);
+ }
+
+ @Override
+ public void configureLoader(CursorLoader loader, long directoryId) {
+ loader.setUri(ContactMethods.CONTENT_URI);
+ loader.setProjection(POSTALS_PROJECTION);
+ loader.setSortOrder(People.DISPLAY_NAME);
+ loader.setSelection(ContactMethods.KIND + "=" + android.provider.Contacts.KIND_POSTAL);
+ }
+
+ @Override
+ public String getContactDisplayName(int position) {
+ return ((Cursor)getItem(position)).getString(POSTAL_DISPLAY_NAME_COLUMN_INDEX);
+ }
+
+ public Uri getContactMethodUri(int position) {
+ Cursor cursor = ((Cursor)getItem(position));
+ long id = cursor.getLong(POSTAL_ID_COLUMN_INDEX);
+ return ContentUris.withAppendedId(ContactMethods.CONTENT_URI, id);
+ }
+
+ @Override
+ protected View newView(Context context, int partition, Cursor cursor, int position,
+ ViewGroup parent) {
+ final ContactListItemView view = new ContactListItemView(context, null);
+ view.setUnknownNameText(mUnknownNameText);
+ return view;
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ ContactListItemView view = (ContactListItemView)itemView;
+ bindName(view, cursor);
+ bindPostalAddress(view, cursor);
+ }
+
+ protected void bindName(final ContactListItemView view, Cursor cursor) {
+ view.showDisplayName(cursor, POSTAL_DISPLAY_NAME_COLUMN_INDEX, false, 0);
+ view.showPhoneticName(cursor, POSTAL_PHONETIC_NAME_COLUMN_INDEX);
+ }
+
+ protected void bindPostalAddress(ContactListItemView view, Cursor cursor) {
+ CharSequence label = null;
+ if (!cursor.isNull(POSTAL_TYPE_COLUMN_INDEX)) {
+ final int type = cursor.getInt(POSTAL_TYPE_COLUMN_INDEX);
+ final String customLabel = cursor.getString(POSTAL_LABEL_COLUMN_INDEX);
+
+ // TODO cache
+ label = StructuredPostal.getTypeLabel(getContext().getResources(), type, customLabel);
+ }
+ view.setLabel(label);
+ view.showData(cursor, POSTAL_NUMBER_COLUMN_INDEX);
+ }
+}
diff --git a/src/com/android/contacts/list/OnContactBrowserActionListener.java b/src/com/android/contacts/list/OnContactBrowserActionListener.java
new file mode 100644
index 0000000..771f251
--- /dev/null
+++ b/src/com/android/contacts/list/OnContactBrowserActionListener.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.net.Uri;
+
+/**
+ * Action callbacks that can be sent by a contact list.
+ */
+public interface OnContactBrowserActionListener {
+
+ /**
+ * Notification of selection change, invoked when the selection of activated
+ * item(s) is change by either a user action or some other event, e.g. sync.
+ */
+ void onSelectionChange();
+
+ /**
+ * Opens the specified contact for viewing.
+ *
+ * @param contactLookupUri The lookup-uri of the Contact that should be opened
+ */
+ void onViewContactAction(Uri contactLookupUri);
+
+ /**
+ * Creates a new contact.
+ */
+ void onCreateNewContactAction();
+
+ /**
+ * Opens the specified contact for editing.
+ */
+ void onEditContactAction(Uri contactLookupUri);
+
+ /**
+ * Initiates the contact deletion process.
+ */
+ void onDeleteContactAction(Uri contactUri);
+
+ /**
+ * Adds the specified contact to favorites
+ */
+ void onAddToFavoritesAction(Uri contactUri);
+
+ /**
+ * Removes the specified contact from favorites.
+ */
+ void onRemoveFromFavoritesAction(Uri contactUri);
+
+ /**
+ * Places a call to the specified contact.
+ */
+ void onCallContactAction(Uri contactUri);
+
+ /**
+ * Initiates a text message to the specified contact.
+ */
+ void onSmsContactAction(Uri contactUri);
+
+ /**
+ * Closes the contact browser.
+ */
+ void onFinishAction();
+
+ /**
+ * Invoked if the requested selected contact is not found in the list.
+ */
+ void onInvalidSelection();
+}
diff --git a/src/com/android/contacts/list/OnContactPickerActionListener.java b/src/com/android/contacts/list/OnContactPickerActionListener.java
new file mode 100644
index 0000000..0c26e9e
--- /dev/null
+++ b/src/com/android/contacts/list/OnContactPickerActionListener.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Intent;
+import android.net.Uri;
+
+/**
+ * Action callbacks that can be sent by a contact picker.
+ */
+public interface OnContactPickerActionListener {
+
+ /**
+ * Returns the selected contact to the requester.
+ */
+ void onPickContactAction(Uri contactUri);
+
+ /**
+ * Returns the selected contact as a shortcut intent.
+ */
+ void onShortcutIntentCreated(Intent intent);
+
+ /**
+ * Creates a new contact and then returns it to the caller.
+ */
+ void onCreateNewContactAction();
+
+ /**
+ * Opens the specified contact for editing.
+ */
+ void onEditContactAction(Uri contactLookupUri);
+}
diff --git a/src/com/android/contacts/list/OnContactsUnavailableActionListener.java b/src/com/android/contacts/list/OnContactsUnavailableActionListener.java
new file mode 100644
index 0000000..e8edea8
--- /dev/null
+++ b/src/com/android/contacts/list/OnContactsUnavailableActionListener.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+/**
+ * Action callbacks that can be sent by the "contacts unavailable" fragment.
+ */
+public interface OnContactsUnavailableActionListener {
+
+ /**
+ * Creates a new contact.
+ */
+ void onCreateNewContactAction();
+
+ /**
+ * Initiates addition of a contacts account.
+ */
+ void onAddAccountAction();
+
+ /**
+ * Initiates contact import from a file.
+ */
+ void onImportContactsFromFileAction();
+
+ /**
+ * Initiates an interaction that frees up some internal storage for the purposes
+ * of a database upgrade.
+ */
+ void onFreeInternalStorageAction();
+}
diff --git a/src/com/android/contacts/list/OnMultiplePhoneNumberPickerActionListener.java b/src/com/android/contacts/list/OnMultiplePhoneNumberPickerActionListener.java
new file mode 100644
index 0000000..ac010ba
--- /dev/null
+++ b/src/com/android/contacts/list/OnMultiplePhoneNumberPickerActionListener.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.net.Uri;
+
+/**
+ * Action callbacks that can be sent by a multiple phone number picker.
+ */
+public interface OnMultiplePhoneNumberPickerActionListener {
+
+ /**
+ * Returns the selected phone numbers to the requester.
+ */
+ void onPhoneNumbersSelectedAction(Uri[] dataUris);
+
+ /**
+ * Closes the picker without changing the selection.
+ */
+ void onFinishAction();
+}
diff --git a/src/com/android/contacts/list/OnPhoneNumberPickerActionListener.java b/src/com/android/contacts/list/OnPhoneNumberPickerActionListener.java
new file mode 100644
index 0000000..1a90122
--- /dev/null
+++ b/src/com/android/contacts/list/OnPhoneNumberPickerActionListener.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.Intent;
+import android.net.Uri;
+
+/**
+ * Action callbacks that can be sent by a phone number picker.
+ */
+public interface OnPhoneNumberPickerActionListener {
+
+ /**
+ * Returns the selected phone number to the requester.
+ */
+ void onPickPhoneNumberAction(Uri dataUri);
+
+ /**
+ * Returns the selected number as a shortcut intent.
+ */
+ void onShortcutIntentCreated(Intent intent);
+}
diff --git a/src/com/android/contacts/list/OnPostalAddressPickerActionListener.java b/src/com/android/contacts/list/OnPostalAddressPickerActionListener.java
new file mode 100644
index 0000000..6ecde61
--- /dev/null
+++ b/src/com/android/contacts/list/OnPostalAddressPickerActionListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.net.Uri;
+
+/**
+ * Action callbacks that can be sent by a postal address picker.
+ */
+public interface OnPostalAddressPickerActionListener {
+
+ /**
+ * Returns the selected phone number to the requester.
+ */
+ void onPickPostalAddressAction(Uri dataUri);
+}
diff --git a/src/com/android/contacts/list/PhoneNumberListAdapter.java b/src/com/android/contacts/list/PhoneNumberListAdapter.java
new file mode 100644
index 0000000..48a6042
--- /dev/null
+++ b/src/com/android/contacts/list/PhoneNumberListAdapter.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.ContactCounts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Directory;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A cursor adapter for the {@link Phone#CONTENT_TYPE} content type.
+ */
+public class PhoneNumberListAdapter extends ContactEntryListAdapter {
+
+ protected static final String[] PHONES_PROJECTION = new String[] {
+ Phone._ID, // 0
+ Phone.TYPE, // 1
+ Phone.LABEL, // 2
+ Phone.NUMBER, // 3
+ Phone.DISPLAY_NAME_PRIMARY, // 4
+ Phone.DISPLAY_NAME_ALTERNATIVE, // 5
+ Phone.CONTACT_ID, // 6
+ Phone.PHOTO_ID, // 7
+ Phone.PHONETIC_NAME, // 8
+ };
+
+ protected static final int PHONE_ID_COLUMN_INDEX = 0;
+ protected static final int PHONE_TYPE_COLUMN_INDEX = 1;
+ protected static final int PHONE_LABEL_COLUMN_INDEX = 2;
+ protected static final int PHONE_NUMBER_COLUMN_INDEX = 3;
+ protected static final int PHONE_PRIMARY_DISPLAY_NAME_COLUMN_INDEX = 4;
+ protected static final int PHONE_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX = 5;
+ protected static final int PHONE_CONTACT_ID_COLUMN_INDEX = 6;
+ protected static final int PHONE_PHOTO_ID_COLUMN_INDEX = 7;
+ protected static final int PHONE_PHONETIC_NAME_COLUMN_INDEX = 8;
+
+ private CharSequence mUnknownNameText;
+ private int mDisplayNameColumnIndex;
+ private int mAlternativeDisplayNameColumnIndex;
+
+ public PhoneNumberListAdapter(Context context) {
+ super(context);
+
+ mUnknownNameText = context.getText(android.R.string.unknownName);
+ }
+
+ protected CharSequence getUnknownNameText() {
+ return mUnknownNameText;
+ }
+
+ @Override
+ public void configureLoader(CursorLoader loader, long directoryId) {
+ Uri uri;
+
+ if (isSearchMode()) {
+ String query = getQueryString();
+ Builder builder = Phone.CONTENT_FILTER_URI.buildUpon();
+ if (TextUtils.isEmpty(query)) {
+ builder.appendPath("");
+ } else {
+ builder.appendPath(query); // Builder will encode the query
+ }
+
+ builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(directoryId));
+ applyDataRestriction(builder);
+ uri = builder.build();
+ // TODO a projection that includes the search snippet
+ loader.setProjection(PHONES_PROJECTION);
+ } else {
+ uri = Phone.CONTENT_URI.buildUpon().appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
+ .build();
+ if (isSectionHeaderDisplayEnabled()) {
+ uri = buildSectionIndexerUri(uri);
+ }
+ uri = applyDataRestriction(uri);
+
+ loader.setProjection(PHONES_PROJECTION);
+ }
+
+ loader.setUri(uri);
+ if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
+ loader.setSortOrder(Phone.SORT_KEY_PRIMARY);
+ } else {
+ loader.setSortOrder(Phone.SORT_KEY_ALTERNATIVE);
+ }
+ }
+
+ protected static Uri buildSectionIndexerUri(Uri uri) {
+ return uri.buildUpon()
+ .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
+ }
+
+ @Override
+ public String getContactDisplayName(int position) {
+ return ((Cursor)getItem(position)).getString(mDisplayNameColumnIndex);
+ }
+
+ @Override
+ public void setContactNameDisplayOrder(int displayOrder) {
+ super.setContactNameDisplayOrder(displayOrder);
+ if (getContactNameDisplayOrder() == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
+ mDisplayNameColumnIndex = PHONE_PRIMARY_DISPLAY_NAME_COLUMN_INDEX;
+ mAlternativeDisplayNameColumnIndex = PHONE_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX;
+ } else {
+ mDisplayNameColumnIndex = PHONE_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX;
+ mAlternativeDisplayNameColumnIndex = PHONE_PRIMARY_DISPLAY_NAME_COLUMN_INDEX;
+ }
+ }
+
+ /**
+ * Builds a {@link Data#CONTENT_URI} for the given cursor position.
+ */
+ public Uri getDataUri(int position) {
+ Cursor cursor = ((Cursor)getItem(position));
+ long id = cursor.getLong(PHONE_ID_COLUMN_INDEX);
+ return ContentUris.withAppendedId(Data.CONTENT_URI, id);
+ }
+
+ @Override
+ protected View newView(Context context, int partition, Cursor cursor, int position,
+ ViewGroup parent) {
+ final ContactListItemView view = new ContactListItemView(context, null);
+ view.setUnknownNameText(mUnknownNameText);
+ view.setTextWithHighlightingFactory(getTextWithHighlightingFactory());
+ view.setQuickContactEnabled(isQuickContactEnabled());
+ return view;
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ ContactListItemView view = (ContactListItemView)itemView;
+ bindSectionHeaderAndDivider(view, position);
+ bindName(view, cursor);
+ bindPhoto(view, cursor);
+ bindPhoneNumber(view, cursor);
+ }
+
+ protected void bindPhoneNumber(ContactListItemView view, Cursor cursor) {
+ CharSequence label = null;
+ if (!cursor.isNull(PHONE_TYPE_COLUMN_INDEX)) {
+ final int type = cursor.getInt(PHONE_TYPE_COLUMN_INDEX);
+ final String customLabel = cursor.getString(PHONE_LABEL_COLUMN_INDEX);
+
+ // TODO cache
+ label = Phone.getTypeLabel(getContext().getResources(), type, customLabel);
+ }
+ view.setLabel(label);
+ view.showData(cursor, PHONE_NUMBER_COLUMN_INDEX);
+ }
+
+ protected void bindSectionHeaderAndDivider(final ContactListItemView view, int position) {
+ if (isSectionHeaderDisplayEnabled()) {
+ Placement placement = getItemPlacementInSection(position);
+ view.setSectionHeader(placement.firstInSection ? placement.sectionHeader : null);
+ view.setDividerVisible(!placement.lastInSection);
+ } else {
+ view.setSectionHeader(null);
+ view.setDividerVisible(true);
+ }
+ }
+
+ protected void bindName(final ContactListItemView view, Cursor cursor) {
+ view.showDisplayName(cursor, mDisplayNameColumnIndex, isNameHighlightingEnabled(),
+ mAlternativeDisplayNameColumnIndex);
+ view.showPhoneticName(cursor, PHONE_PHONETIC_NAME_COLUMN_INDEX);
+ }
+
+ protected void bindPhoto(final ContactListItemView view, Cursor cursor) {
+ long photoId = 0;
+ if (!cursor.isNull(PHONE_PHOTO_ID_COLUMN_INDEX)) {
+ photoId = cursor.getLong(PHONE_PHOTO_ID_COLUMN_INDEX);
+ }
+
+ getPhotoLoader().loadPhoto(view.getPhotoView(), photoId);
+ }
+}
diff --git a/src/com/android/contacts/list/PhoneNumberPickerFragment.java b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
new file mode 100644
index 0000000..071a370
--- /dev/null
+++ b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.ContactsSearchManager;
+import com.android.contacts.R;
+import com.android.contacts.list.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Fragment containing a phone number list for picking.
+ */
+public class PhoneNumberPickerFragment extends ContactEntryListFragment<ContactEntryListAdapter>
+ implements OnShortcutIntentCreatedListener {
+ private OnPhoneNumberPickerActionListener mListener;
+ private String mShortcutAction;
+
+ public PhoneNumberPickerFragment() {
+ setQuickContactEnabled(false);
+ setPhotoLoaderEnabled(true);
+ setVisibleScrollbarEnabled(true);
+ setSectionHeaderDisplayEnabled(true);
+ setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DATA_SHORTCUT);
+ }
+
+ public void setOnPhoneNumberPickerActionListener(OnPhoneNumberPickerActionListener listener) {
+ this.mListener = listener;
+ }
+
+ /**
+ * @param shortcutAction either {@link Intent#ACTION_CALL} or
+ * {@link Intent#ACTION_SENDTO} or null.
+ */
+ public void setShortcutAction(String shortcutAction) {
+ this.mShortcutAction = shortcutAction;
+ }
+
+ @Override
+ protected void onItemClick(int position, long id) {
+ if (!isLegacyCompatibilityMode()) {
+ PhoneNumberListAdapter adapter = (PhoneNumberListAdapter)getAdapter();
+ pickPhoneNumber(adapter.getDataUri(position));
+ } else {
+ LegacyPhoneNumberListAdapter adapter = (LegacyPhoneNumberListAdapter)getAdapter();
+ pickPhoneNumber(adapter.getPhoneUri(position));
+ }
+ }
+
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ if (!isLegacyCompatibilityMode()) {
+ PhoneNumberListAdapter adapter = new PhoneNumberListAdapter(getActivity());
+ adapter.setDisplayPhotos(true);
+ return adapter;
+ } else {
+ LegacyPhoneNumberListAdapter adapter = new LegacyPhoneNumberListAdapter(getActivity());
+ adapter.setDisplayPhotos(true);
+ return adapter;
+ }
+ }
+
+ @Override
+ protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+ return inflater.inflate(R.layout.contacts_list_content, null);
+ }
+
+ public void pickPhoneNumber(Uri uri) {
+ if (mShortcutAction == null) {
+ mListener.onPickPhoneNumberAction(uri);
+ } else {
+ if (isLegacyCompatibilityMode()) {
+ throw new UnsupportedOperationException();
+ }
+ ShortcutIntentBuilder builder = new ShortcutIntentBuilder(getActivity(), this);
+ builder.createPhoneNumberShortcutIntent(uri, mShortcutAction);
+ }
+ }
+
+ public void onShortcutIntentCreated(Uri uri, Intent shortcutIntent) {
+ mListener.onShortcutIntentCreated(shortcutIntent);
+ }
+
+ @Override
+ public void startSearch(String initialQuery) {
+ ContactsSearchManager.startSearchForResult(getActivity(), initialQuery,
+ ACTIVITY_REQUEST_CODE_PICKER, getContactsRequest());
+ }
+
+ @Override
+ public void onPickerResult(Intent data) {
+ mListener.onPickPhoneNumberAction(data.getData());
+ }
+}
diff --git a/src/com/android/contacts/list/PostalAddressListAdapter.java b/src/com/android/contacts/list/PostalAddressListAdapter.java
new file mode 100644
index 0000000..e622ec6
--- /dev/null
+++ b/src/com/android/contacts/list/PostalAddressListAdapter.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.ContactCounts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A cursor adapter for the {@link StructuredPostal#CONTENT_TYPE} content type.
+ */
+public class PostalAddressListAdapter extends ContactEntryListAdapter {
+
+ static final String[] POSTALS_PROJECTION = new String[] {
+ StructuredPostal._ID, // 0
+ StructuredPostal.TYPE, // 1
+ StructuredPostal.LABEL, // 2
+ StructuredPostal.DATA, // 3
+ StructuredPostal.DISPLAY_NAME_PRIMARY, // 4
+ StructuredPostal.DISPLAY_NAME_ALTERNATIVE, // 5
+ StructuredPostal.PHOTO_ID, // 6
+ };
+
+ protected static final int POSTAL_ID_COLUMN_INDEX = 0;
+ protected static final int POSTAL_TYPE_COLUMN_INDEX = 1;
+ protected static final int POSTAL_LABEL_COLUMN_INDEX = 2;
+ protected static final int POSTAL_ADDRESS_COLUMN_INDEX = 3;
+ protected static final int POSTAL_PRIMARY_DISPLAY_NAME_COLUMN_INDEX = 4;
+ protected static final int POSTAL_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX = 5;
+ protected static final int POSTAL_PHOTO_ID_COLUMN_INDEX = 6;
+
+ private CharSequence mUnknownNameText;
+ private int mDisplayNameColumnIndex;
+ private int mAlternativeDisplayNameColumnIndex;
+
+ public PostalAddressListAdapter(Context context) {
+ super(context);
+
+ mUnknownNameText = context.getText(android.R.string.unknownName);
+ }
+
+ @Override
+ public void configureLoader(CursorLoader loader, long directoryId) {
+ Uri uri = buildSectionIndexerUri(StructuredPostal.CONTENT_URI);
+ uri = applyDataRestriction(uri);
+ loader.setUri(uri);
+ loader.setProjection(POSTALS_PROJECTION);
+
+ if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
+ loader.setSortOrder(StructuredPostal.SORT_KEY_PRIMARY);
+ } else {
+ loader.setSortOrder(StructuredPostal.SORT_KEY_ALTERNATIVE);
+ }
+ }
+
+ protected static Uri buildSectionIndexerUri(Uri uri) {
+ return uri.buildUpon()
+ .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
+ }
+
+ @Override
+ public String getContactDisplayName(int position) {
+ return ((Cursor)getItem(position)).getString(mDisplayNameColumnIndex);
+ }
+
+ @Override
+ public void setContactNameDisplayOrder(int displayOrder) {
+ super.setContactNameDisplayOrder(displayOrder);
+ if (getContactNameDisplayOrder() == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
+ mDisplayNameColumnIndex = POSTAL_PRIMARY_DISPLAY_NAME_COLUMN_INDEX;
+ mAlternativeDisplayNameColumnIndex = POSTAL_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX;
+ } else {
+ mDisplayNameColumnIndex = POSTAL_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX;
+ mAlternativeDisplayNameColumnIndex = POSTAL_PRIMARY_DISPLAY_NAME_COLUMN_INDEX;
+ }
+ }
+
+ /**
+ * Builds a {@link Data#CONTENT_URI} for the current cursor
+ * position.
+ */
+ public Uri getDataUri(int position) {
+ long id = ((Cursor)getItem(position)).getLong(POSTAL_ID_COLUMN_INDEX);
+ return ContentUris.withAppendedId(Data.CONTENT_URI, id);
+ }
+
+ @Override
+ protected View newView(Context context, int partition, Cursor cursor, int position,
+ ViewGroup parent) {
+ final ContactListItemView view = new ContactListItemView(context, null);
+ view.setUnknownNameText(mUnknownNameText);
+ view.setTextWithHighlightingFactory(getTextWithHighlightingFactory());
+ view.setQuickContactEnabled(isQuickContactEnabled());
+ return view;
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ ContactListItemView view = (ContactListItemView)itemView;
+ bindSectionHeaderAndDivider(view, position);
+ bindName(view, cursor);
+ bindPhoto(view, cursor);
+ bindPostalAddress(view, cursor);
+ }
+
+ protected void bindPostalAddress(ContactListItemView view, Cursor cursor) {
+ CharSequence label = null;
+ if (!cursor.isNull(POSTAL_TYPE_COLUMN_INDEX)) {
+ final int type = cursor.getInt(POSTAL_TYPE_COLUMN_INDEX);
+ final String customLabel = cursor.getString(POSTAL_LABEL_COLUMN_INDEX);
+
+ // TODO cache
+ label = StructuredPostal.getTypeLabel(getContext().getResources(), type, label);
+ }
+ view.setLabel(label);
+ view.showData(cursor, POSTAL_ADDRESS_COLUMN_INDEX);
+ }
+
+ protected void bindSectionHeaderAndDivider(final ContactListItemView view, int position) {
+ final int section = getSectionForPosition(position);
+ if (getPositionForSection(section) == position) {
+ String title = (String)getSections()[section];
+ view.setSectionHeader(title);
+ } else {
+ view.setDividerVisible(false);
+ view.setSectionHeader(null);
+ }
+
+ // move the divider for the last item in a section
+ if (getPositionForSection(section + 1) - 1 == position) {
+ view.setDividerVisible(false);
+ } else {
+ view.setDividerVisible(true);
+ }
+ }
+
+ protected void bindName(final ContactListItemView view, Cursor cursor) {
+ view.showDisplayName(cursor, mDisplayNameColumnIndex, isNameHighlightingEnabled(),
+ mAlternativeDisplayNameColumnIndex);
+// view.showPhoneticName(cursor, PHONE_PHONETIC_NAME_COLUMN_INDEX);
+ }
+
+ protected void bindPhoto(final ContactListItemView view, Cursor cursor) {
+ long photoId = 0;
+ if (!cursor.isNull(POSTAL_PHOTO_ID_COLUMN_INDEX)) {
+ photoId = cursor.getLong(POSTAL_PHOTO_ID_COLUMN_INDEX);
+ }
+
+ getPhotoLoader().loadPhoto(view.getPhotoView(), photoId);
+ }
+//
+// protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
+// view.showSnippet(cursor, SUMMARY_SNIPPET_MIMETYPE_COLUMN_INDEX,
+// SUMMARY_SNIPPET_DATA1_COLUMN_INDEX, SUMMARY_SNIPPET_DATA4_COLUMN_INDEX);
+// }
+
+}
diff --git a/src/com/android/contacts/list/PostalAddressPickerFragment.java b/src/com/android/contacts/list/PostalAddressPickerFragment.java
new file mode 100644
index 0000000..f14b718
--- /dev/null
+++ b/src/com/android/contacts/list/PostalAddressPickerFragment.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Fragment containing a postal address list for picking.
+ */
+public class PostalAddressPickerFragment
+ extends ContactEntryListFragment<ContactEntryListAdapter> {
+ private OnPostalAddressPickerActionListener mListener;
+
+ public PostalAddressPickerFragment() {
+ setQuickContactEnabled(false);
+ setPhotoLoaderEnabled(true);
+ setSectionHeaderDisplayEnabled(true);
+ setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DATA_SHORTCUT);
+ }
+
+ public void setOnPostalAddressPickerActionListener(
+ OnPostalAddressPickerActionListener listener) {
+ this.mListener = listener;
+ }
+
+ @Override
+ protected void onItemClick(int position, long id) {
+ if (!isLegacyCompatibilityMode()) {
+ PostalAddressListAdapter adapter = (PostalAddressListAdapter)getAdapter();
+ pickPostalAddress(adapter.getDataUri(position));
+ } else {
+ LegacyPostalAddressListAdapter adapter = (LegacyPostalAddressListAdapter)getAdapter();
+ pickPostalAddress(adapter.getContactMethodUri(position));
+ }
+ }
+
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ if (!isLegacyCompatibilityMode()) {
+ PostalAddressListAdapter adapter = new PostalAddressListAdapter(getActivity());
+ adapter.setSectionHeaderDisplayEnabled(true);
+ adapter.setDisplayPhotos(true);
+ return adapter;
+ } else {
+ LegacyPostalAddressListAdapter adapter =
+ new LegacyPostalAddressListAdapter(getActivity());
+ adapter.setSectionHeaderDisplayEnabled(false);
+ adapter.setDisplayPhotos(false);
+ return adapter;
+ }
+ }
+
+ @Override
+ protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+ return inflater.inflate(R.layout.contacts_list_content, null);
+ }
+
+ public void pickPostalAddress(Uri uri) {
+ mListener.onPickPostalAddressAction(uri);
+ }
+}
diff --git a/src/com/android/contacts/list/ProviderStatusLoader.java b/src/com/android/contacts/list/ProviderStatusLoader.java
new file mode 100644
index 0000000..a6afa9f
--- /dev/null
+++ b/src/com/android/contacts/list/ProviderStatusLoader.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.os.Handler;
+import android.provider.ContactsContract.ProviderStatus;
+
+/**
+ * Checks provider status and configures a list adapter accordingly.
+ */
+public class ProviderStatusLoader extends ContentObserver {
+
+ /**
+ * Callback interface invoked when the provider status changes.
+ */
+ public interface ProviderStatusListener {
+ public void onProviderStatusChange();
+ }
+
+ private static final String[] PROJECTION = new String[] {
+ ProviderStatus.STATUS,
+ ProviderStatus.DATA1
+ };
+
+ private static final int UNKNOWN = -1;
+
+ private final Context mContext;
+ private int mProviderStatus = UNKNOWN;
+ private String mProviderData;
+ private ProviderStatusListener mListener;
+ private Handler mHandler = new Handler();
+
+ public ProviderStatusLoader(Context context) {
+ super(null);
+ this.mContext = context;
+ }
+
+ public int getProviderStatus() {
+ if (mProviderStatus == UNKNOWN) {
+ loadProviderStatus();
+ }
+
+ return mProviderStatus;
+ }
+
+ public String getProviderStatusData() {
+ if (mProviderStatus == UNKNOWN) {
+ loadProviderStatus();
+ }
+
+ return mProviderData;
+ }
+
+ protected void loadProviderStatus() {
+
+ // Default to normal status
+ mProviderStatus = ProviderStatus.STATUS_NORMAL;
+
+ // This query can be performed on the UI thread because
+ // the API explicitly allows such use.
+ Cursor cursor = mContext.getContentResolver().query(ProviderStatus.CONTENT_URI,
+ PROJECTION, null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ mProviderStatus = cursor.getInt(0);
+ mProviderData = cursor.getString(1);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+
+ public void setProviderStatusListener(ProviderStatusListener listener) {
+ mListener = listener;
+
+ ContentResolver resolver = mContext.getContentResolver();
+ if (listener != null) {
+ mProviderStatus = UNKNOWN;
+ resolver.registerContentObserver(ProviderStatus.CONTENT_URI, false, this);
+ } else {
+ resolver.unregisterContentObserver(this);
+ }
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ // Deliver a notification on the UI thread
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mListener != null) {
+ mProviderStatus = UNKNOWN;
+ mListener.onProviderStatusChange();
+ }
+ }
+ });
+ }
+
+ /**
+ * Sends a provider status update, which will trigger a retry of database upgrade
+ */
+ public void retryUpgrade() {
+ ContentValues values = new ContentValues();
+ values.put(ProviderStatus.STATUS, ProviderStatus.STATUS_UPGRADING);
+ mContext.getContentResolver().update(ProviderStatus.CONTENT_URI, values, null, null);
+ }
+}
diff --git a/src/com/android/contacts/list/ShortcutIntentBuilder.java b/src/com/android/contacts/list/ShortcutIntentBuilder.java
new file mode 100644
index 0000000..101b6d5
--- /dev/null
+++ b/src/com/android/contacts/list/ShortcutIntentBuilder.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+import com.android.contacts.util.Constants;
+
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+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.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+
+/**
+ * Constructs shortcut intents.
+ */
+public class ShortcutIntentBuilder {
+
+ private static final String[] CONTACT_COLUMNS = {
+ Contacts.DISPLAY_NAME,
+ Contacts.PHOTO_ID,
+ };
+
+ private static final int CONTACT_DISPLAY_NAME_COLUMN_INDEX = 0;
+ private static final int CONTACT_PHOTO_ID_COLUMN_INDEX = 1;
+
+ private static final String[] PHONE_COLUMNS = {
+ Phone.DISPLAY_NAME,
+ Phone.PHOTO_ID,
+ Phone.NUMBER,
+ Phone.TYPE,
+ };
+
+ private static final int PHONE_DISPLAY_NAME_COLUMN_INDEX = 0;
+ private static final int PHONE_PHOTO_ID_COLUMN_INDEX = 1;
+ private static final int PHONE_NUMBER_COLUMN_INDEX = 2;
+ private static final int PHONE_TYPE_COLUMN_INDEX = 3;
+
+ private static final String[] PHOTO_COLUMNS = {
+ Photo.PHOTO,
+ };
+
+ private static final int PHOTO_PHOTO_COLUMN_INDEX = 0;
+
+ private static final String PHOTO_SELECTION = Photo._ID + "=?";
+
+ private final OnShortcutIntentCreatedListener mListener;
+ private final Context mContext;
+ private final int mIconSize;
+ private final int mIconDensity;
+
+ /**
+ * Listener interface.
+ */
+ public interface OnShortcutIntentCreatedListener {
+
+ /**
+ * Callback for shortcut intent creation.
+ *
+ * @param uri the original URI for which the shortcut intent has been
+ * created.
+ * @param shortcutIntent resulting shortcut intent.
+ */
+ void onShortcutIntentCreated(Uri uri, Intent shortcutIntent);
+ }
+
+ public ShortcutIntentBuilder(Context context, OnShortcutIntentCreatedListener listener) {
+ mContext = context;
+ mListener = listener;
+
+ final ActivityManager am = (ActivityManager) context
+ .getSystemService(Context.ACTIVITY_SERVICE);
+ mIconSize = am.getLauncherLargeIconSize();
+ mIconDensity = am.getLauncherLargeIconDensity();
+ }
+
+ public void createContactShortcutIntent(Uri contactUri) {
+ new ContactLoadingAsyncTask(contactUri).execute();
+ }
+
+ public void createPhoneNumberShortcutIntent(Uri dataUri, String shortcutAction) {
+ new PhoneNumberLoadingAsyncTask(dataUri, shortcutAction).execute();
+ }
+
+ /**
+ * An asynchronous task that loads name, photo and other data from the database.
+ */
+ private abstract class LoadingAsyncTask extends AsyncTask<Void, Void, Void> {
+ protected Uri mUri;
+ protected String mDisplayName;
+ protected byte[] mBitmapData;
+ protected long mPhotoId;
+
+ public LoadingAsyncTask(Uri uri) {
+ mUri = uri;
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ loadData();
+ loadPhoto();
+ return null;
+ }
+
+ protected abstract void loadData();
+
+ private void loadPhoto() {
+ if (mPhotoId == 0) {
+ return;
+ }
+
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor cursor = resolver.query(Data.CONTENT_URI, PHOTO_COLUMNS, PHOTO_SELECTION,
+ new String[] { String.valueOf(mPhotoId) }, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ mBitmapData = cursor.getBlob(PHOTO_PHOTO_COLUMN_INDEX);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+ }
+
+ private final class ContactLoadingAsyncTask extends LoadingAsyncTask {
+ public ContactLoadingAsyncTask(Uri uri) {
+ super(uri);
+ }
+
+ @Override
+ protected void loadData() {
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor cursor = resolver.query(mUri, CONTACT_COLUMNS, null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ mDisplayName = cursor.getString(CONTACT_DISPLAY_NAME_COLUMN_INDEX);
+ mPhotoId = cursor.getLong(CONTACT_PHOTO_ID_COLUMN_INDEX);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+ @Override
+ protected void onPostExecute(Void result) {
+ createContactShortcutIntent(mUri, mDisplayName, mBitmapData);
+ }
+ }
+
+ private final class PhoneNumberLoadingAsyncTask extends LoadingAsyncTask {
+ private final String mShortcutAction;
+ private String mPhoneNumber;
+ private int mPhoneType;
+
+ public PhoneNumberLoadingAsyncTask(Uri uri, String shortcutAction) {
+ super(uri);
+ mShortcutAction = shortcutAction;
+ }
+
+ @Override
+ protected void loadData() {
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor cursor = resolver.query(mUri, PHONE_COLUMNS, null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ mDisplayName = cursor.getString(PHONE_DISPLAY_NAME_COLUMN_INDEX);
+ mPhotoId = cursor.getLong(PHONE_PHOTO_ID_COLUMN_INDEX);
+ mPhoneNumber = cursor.getString(PHONE_NUMBER_COLUMN_INDEX);
+ mPhoneType = cursor.getInt(PHONE_TYPE_COLUMN_INDEX);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ createPhoneNumberShortcutIntent(mUri, mDisplayName, mBitmapData, mPhoneNumber,
+ mPhoneType, mShortcutAction);
+ }
+ }
+
+ private void createContactShortcutIntent(Uri contactUri, String displayName,
+ byte[] bitmapData) {
+ Bitmap bitmap;
+ if (bitmapData != null) {
+ bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length, null);
+ } else {
+ bitmap = ((BitmapDrawable) mContext.getResources().getDrawableForDensity(
+ R.drawable.ic_contact_picture, mIconDensity)).getBitmap();
+ }
+
+ Intent shortcutIntent;
+ // This is a simple shortcut to view a contact.
+ shortcutIntent = new Intent(ContactsContract.QuickContact.ACTION_QUICK_CONTACT);
+ shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+ shortcutIntent.setData(contactUri);
+ shortcutIntent.putExtra(ContactsContract.QuickContact.EXTRA_MODE,
+ ContactsContract.QuickContact.MODE_LARGE);
+ shortcutIntent.putExtra(ContactsContract.QuickContact.EXTRA_EXCLUDE_MIMES,
+ (String[]) null);
+ shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ final Bitmap icon = generateQuickContactIcon(bitmap);
+
+ Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
+ intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
+ intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, displayName);
+
+ mListener.onShortcutIntentCreated(contactUri, intent);
+ }
+
+ private void createPhoneNumberShortcutIntent(Uri uri, String displayName, byte[] bitmapData,
+ String phoneNumber, int phoneType, String shortcutAction) {
+ Bitmap bitmap = null;
+ if (bitmapData != null) {
+ bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length, null);
+ }
+
+ Uri phoneUri;
+ if (Intent.ACTION_CALL.equals(shortcutAction)) {
+ // Make the URI a direct tel: URI so that it will always continue to work
+ phoneUri = Uri.fromParts(Constants.SCHEME_TEL, phoneNumber, null);
+ bitmap = generatePhoneNumberIcon(bitmap, phoneType, R.drawable.badge_action_call);
+ } else {
+ phoneUri = Uri.fromParts(Constants.SCHEME_SMSTO, phoneNumber, null);
+ bitmap = generatePhoneNumberIcon(bitmap, phoneType, R.drawable.badge_action_sms);
+ }
+
+ Intent shortcutIntent = new Intent(shortcutAction, phoneUri);
+ shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+ Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
+ intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
+ intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, displayName);
+
+ mListener.onShortcutIntentCreated(uri, intent);
+ }
+
+ private Bitmap generateQuickContactIcon(Bitmap photo) {
+
+ // Setup the drawing classes
+ Bitmap icon = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(icon);
+
+ // Copy in the photo
+ Paint photoPaint = new Paint();
+ photoPaint.setDither(true);
+ photoPaint.setFilterBitmap(true);
+ Rect src = new Rect(0,0, photo.getWidth(),photo.getHeight());
+ Rect dst = new Rect(0,0, mIconSize, mIconSize);
+ canvas.drawBitmap(photo, src, dst, photoPaint);
+
+ Drawable overlay = mContext.getResources().getDrawableForDensity(
+ com.android.internal.R.drawable.quickcontact_badge_overlay_dark, mIconDensity);
+
+ overlay.setBounds(dst);
+ overlay.draw(canvas);
+
+ return icon;
+ }
+
+ /**
+ * Generates a phone number shortcut icon. Adds an overlay describing the type of the phone
+ * number, and if there is a photo also adds the call action icon.
+ */
+ private Bitmap generatePhoneNumberIcon(Bitmap photo, int phoneType, int actionResId) {
+ final Resources r = mContext.getResources();
+ boolean drawPhoneOverlay = true;
+ final float scaleDensity = r.getDisplayMetrics().scaledDensity;
+
+ Bitmap phoneIcon = ((BitmapDrawable) r.getDrawableForDensity(actionResId, mIconDensity))
+ .getBitmap();
+
+ // If there isn't a photo use the generic phone action icon instead
+ if (photo == null) {
+ photo = phoneIcon;
+ drawPhoneOverlay = false;
+ }
+
+ // Setup the drawing classes
+ Bitmap icon = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(icon);
+
+ // Copy in the photo
+ Paint photoPaint = new Paint();
+ photoPaint.setDither(true);
+ photoPaint.setFilterBitmap(true);
+ Rect src = new Rect(0, 0, photo.getWidth(), photo.getHeight());
+ Rect dst = new Rect(0, 0, mIconSize, mIconSize);
+ canvas.drawBitmap(photo, src, dst, photoPaint);
+
+ // Create an overlay for the phone number type
+ String overlay = null;
+ switch (phoneType) {
+ case Phone.TYPE_HOME:
+ overlay = mContext.getString(R.string.type_short_home);
+ break;
+
+ case Phone.TYPE_MOBILE:
+ overlay = mContext.getString(R.string.type_short_mobile);
+ break;
+
+ case Phone.TYPE_WORK:
+ overlay = mContext.getString(R.string.type_short_work);
+ break;
+
+ case Phone.TYPE_PAGER:
+ overlay = mContext.getString(R.string.type_short_pager);
+ break;
+
+ case Phone.TYPE_OTHER:
+ overlay = mContext.getString(R.string.type_short_other);
+ break;
+ }
+
+ if (overlay != null) {
+ Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
+ textPaint.setTextSize(20.0f * scaleDensity);
+ textPaint.setTypeface(Typeface.DEFAULT_BOLD);
+ textPaint.setColor(r.getColor(R.color.textColorIconOverlay));
+ textPaint.setShadowLayer(3f, 1, 1, r.getColor(R.color.textColorIconOverlayShadow));
+ canvas.drawText(overlay, 2 * scaleDensity, 16 * scaleDensity, textPaint);
+ }
+
+ // Draw the phone action icon as an overlay
+ if (drawPhoneOverlay) {
+ src.set(0, 0, phoneIcon.getWidth(), phoneIcon.getHeight());
+ int iconWidth = icon.getWidth();
+ dst.set(iconWidth - ((int) (20 * scaleDensity)), -1,
+ iconWidth, ((int) (19 * scaleDensity)));
+ canvas.drawBitmap(phoneIcon, src, dst, photoPaint);
+ }
+
+ return icon;
+ }
+}
diff --git a/src/com/android/contacts/list/StrequentContactListAdapter.java b/src/com/android/contacts/list/StrequentContactListAdapter.java
new file mode 100644
index 0000000..12c56f3
--- /dev/null
+++ b/src/com/android/contacts/list/StrequentContactListAdapter.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+import com.android.contacts.util.PhoneCapabilityTester;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.Cursor;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+import android.widget.ListView;
+import android.widget.TextView;
+
+/**
+ * A cursor adapter for the {@link ContactsContract.Contacts#CONTENT_TYPE} content type loading
+ * a combination of starred and frequently contacted.
+ */
+public class StrequentContactListAdapter extends ContactListAdapter {
+
+ private int mFrequentSeparatorPos = ListView.INVALID_POSITION;
+ private TextView mSeparatorView;
+ private OnClickListener mCallButtonListener;
+ private int mCallButtonId;
+ private boolean mStarredContactsIncluded;
+ private boolean mFrequentlyContactedContactsIncluded;
+ private boolean mIsPhone;
+
+ public StrequentContactListAdapter(Context context, int callButtonId) {
+ super(context);
+ mCallButtonId = callButtonId;
+ }
+
+ public void setCallButtonListener(OnClickListener callButtonListener) {
+ mCallButtonListener = callButtonListener;
+ }
+
+ public void setStarredContactsIncluded(boolean flag) {
+ mStarredContactsIncluded = flag;
+ }
+
+ public void setFrequentlyContactedContactsIncluded(boolean flag) {
+ mFrequentlyContactedContactsIncluded = flag;
+ }
+
+ @Override
+ public void configureLoader(CursorLoader loader, long directoryId) {
+ String sortOrder = getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY
+ ? Contacts.SORT_KEY_PRIMARY
+ : Contacts.SORT_KEY_ALTERNATIVE;
+ if (mStarredContactsIncluded && mFrequentlyContactedContactsIncluded) {
+ loader.setUri(Contacts.CONTENT_STREQUENT_URI);
+ } else if (mStarredContactsIncluded) {
+ loader.setUri(Contacts.CONTENT_URI);
+ loader.setSelection(Contacts.STARRED + "!=0");
+ } else if (mFrequentlyContactedContactsIncluded) {
+ loader.setUri(Contacts.CONTENT_URI);
+ loader.setSelection(Contacts.TIMES_CONTACTED + " > 0");
+ sortOrder = Contacts.TIMES_CONTACTED + " DESC";
+ } else {
+ throw new UnsupportedOperationException("Neither StarredContactsIncluded nor "
+ + "FrequentlyContactedContactsIncluded is set");
+ }
+
+ loader.setProjection(PROJECTION_CONTACT);
+ loader.setSortOrder(sortOrder);
+ }
+
+ @Override
+ protected void invalidate() {
+ super.invalidate();
+
+ // Sometimes the adapter is invalidated without calling changeCursor,
+ // need to reset the separator position then.
+ mFrequentSeparatorPos = ListView.INVALID_POSITION;
+ mIsPhone = PhoneCapabilityTester.isPhone(getContext());
+ }
+
+ @Override
+ public void changeCursor(int partition, Cursor cursor) {
+ super.changeCursor(partition, cursor);
+
+ // Get the split between starred and frequent items, if the mode is strequent
+ mFrequentSeparatorPos = ListView.INVALID_POSITION;
+
+ if (mStarredContactsIncluded && mFrequentlyContactedContactsIncluded) {
+ int count = 0;
+ if (cursor != null && (count = cursor.getCount()) > 0) {
+ cursor.moveToPosition(-1);
+ for (int i = 0; cursor.moveToNext(); i++) {
+ int starred = cursor.getInt(CONTACT_STARRED_COLUMN_INDEX);
+ if (starred == 0) {
+ if (i > 0) {
+ // Only add the separator when there are starred items present
+ mFrequentSeparatorPos = i;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getCount() {
+ if (mFrequentSeparatorPos == ListView.INVALID_POSITION) {
+ return super.getCount();
+ } else {
+ // Add a row for the separator
+ return super.getCount() + 1;
+ }
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return mFrequentSeparatorPos == ListView.INVALID_POSITION;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return position != mFrequentSeparatorPos;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (mFrequentSeparatorPos == ListView.INVALID_POSITION
+ || position < mFrequentSeparatorPos) {
+ return super.getItem(position);
+ } else {
+ return super.getItem(position - 1);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (mFrequentSeparatorPos == ListView.INVALID_POSITION
+ || position < mFrequentSeparatorPos) {
+ return super.getItemId(position);
+ } else {
+ return super.getItemId(position - 1);
+ }
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (mFrequentSeparatorPos == ListView.INVALID_POSITION
+ || position < mFrequentSeparatorPos) {
+ return super.getItemViewType(position);
+ } else if (position == mFrequentSeparatorPos) {
+ return IGNORE_ITEM_VIEW_TYPE;
+ } else {
+ return super.getItemViewType(position - 1);
+ }
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (mFrequentSeparatorPos == ListView.INVALID_POSITION
+ || position < mFrequentSeparatorPos) {
+ return super.getView(position, convertView, parent);
+ } else if (position == mFrequentSeparatorPos) {
+ if (mSeparatorView == null) {
+ mSeparatorView = (TextView)LayoutInflater.from(getContext()).
+ inflate(R.layout.list_separator, parent, false);
+ mSeparatorView.setText(R.string.favoritesFrquentSeparator);
+ }
+ return mSeparatorView;
+ } else {
+ return super.getView(position - 1, convertView, parent);
+ }
+ }
+
+ @Override
+ protected View newView(Context context, int partition, Cursor cursor, int position,
+ ViewGroup parent) {
+ ContactListItemView view = (ContactListItemView)super.newView(context, partition, cursor,
+ position, parent);
+ view.setOnCallButtonClickListener(mCallButtonListener);
+ return view;
+ }
+
+ @Override
+ protected void bindView(View itemView, int partition, Cursor cursor, int position) {
+ final ContactListItemView view = (ContactListItemView)itemView;
+
+ if (isSelectionVisible()) {
+ view.setActivated(isSelectedContact(partition, cursor));
+ }
+
+ bindName(view, cursor);
+ if (isQuickContactEnabled()) {
+ bindQuickContact(view, partition, cursor);
+ } else {
+ bindPhoto(view, partition, cursor);
+ }
+ bindPresence(view, cursor);
+
+ // Make the call button visible if requested.
+ if (mIsPhone && getHasPhoneNumber(position)) {
+ view.showCallButton(mCallButtonId, position);
+ } else {
+ view.hideCallButton();
+ }
+ }
+}
diff --git a/src/com/android/contacts/list/StrequentContactListFragment.java b/src/com/android/contacts/list/StrequentContactListFragment.java
new file mode 100644
index 0000000..b88a60d
--- /dev/null
+++ b/src/com/android/contacts/list/StrequentContactListFragment.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 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.list;
+
+import com.android.contacts.R;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.View.OnClickListener;
+
+/**
+ * Fragment containing a list of starred contacts followed by a list of frequently contacted.
+ */
+
+// TODO: This class is currently unused. Bring it back as a mode of ContactBrowserListFragment
+public class StrequentContactListFragment extends ContactBrowseListFragment
+ implements OnClickListener {
+
+ private static final int CALL_BUTTON_ID = android.R.id.button1;
+
+ private boolean mStarredContactsIncluded = true;
+ private boolean mFrequentlyContactedContactsIncluded = true;
+
+ public StrequentContactListFragment() {
+ setSectionHeaderDisplayEnabled(false);
+ setPhotoLoaderEnabled(true);
+ }
+
+ @Override
+ protected boolean isNameHighlighingEnabled() {
+ // Since the list is not ordered alphabetically, we don't need to highlight the part
+ // that is used for sorting.
+ return false;
+ }
+
+ public void setStarredContactsIncluded(boolean flag) {
+ mStarredContactsIncluded = flag;
+ configureAdapter();
+ }
+
+ public void setFrequentlyContactedContactsIncluded(boolean flag) {
+ mFrequentlyContactedContactsIncluded = flag;
+ configureAdapter();
+ }
+
+ @Override
+ protected void onItemClick(int position, long id) {
+ ContactListAdapter adapter = getAdapter();
+ viewContact(adapter.getContactUri(position));
+ }
+
+ @Override
+ protected ContactListAdapter createListAdapter() {
+ StrequentContactListAdapter adapter =
+ new StrequentContactListAdapter(getActivity(), CALL_BUTTON_ID);
+ adapter.setSectionHeaderDisplayEnabled(false);
+ adapter.setDisplayPhotos(true);
+ adapter.setQuickContactEnabled(true);
+ adapter.setCallButtonListener(this);
+
+ return adapter;
+ }
+
+ @Override
+ protected void configureAdapter() {
+ super.configureAdapter();
+
+ StrequentContactListAdapter adapter = (StrequentContactListAdapter)getAdapter();
+ if (adapter != null) {
+ adapter.setStarredContactsIncluded(mStarredContactsIncluded);
+ adapter.setFrequentlyContactedContactsIncluded(mFrequentlyContactedContactsIncluded);
+ }
+ }
+
+ @Override
+ protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+ return inflater.inflate(R.layout.contacts_list_content, null);
+ }
+
+ @Override
+ protected void prepareEmptyView() {
+ setEmptyText(R.string.noFavoritesHelpText);
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ switch (id) {
+ case CALL_BUTTON_ID: {
+ final int position = (Integer)v.getTag();
+ ContactListAdapter adapter = getAdapter();
+ callContact(adapter.getContactUri(position));
+ break;
+ }
+ }
+ }
+}
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
new file mode 100644
index 0000000..0f67fba
--- /dev/null
+++ b/src/com/android/contacts/model/AccountType.java
@@ -0,0 +1,369 @@
+/*
+ * 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.model;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import android.accounts.Account;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.view.View;
+import android.widget.EditText;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Internal structure that represents constraints and styles for a specific data
+ * source, such as the various data types they support, including details on how
+ * those types should be rendered and edited.
+ * <p>
+ * In the future this may be inflated from XML defined by a data source.
+ */
+public abstract class AccountType {
+ /**
+ * The {@link RawContacts#ACCOUNT_TYPE} these constraints apply to.
+ */
+ public String accountType = null;
+
+ /**
+ * Package that resources should be loaded from, either defined through an
+ * {@link Account} or for matching against {@link Data#RES_PACKAGE}.
+ */
+ public String resPackageName;
+ public String summaryResPackageName;
+
+ public int titleRes;
+ public int iconRes;
+
+ public boolean readOnly;
+
+ /**
+ * Set of {@link DataKind} supported by this source.
+ */
+ private ArrayList<DataKind> mKinds = Lists.newArrayList();
+
+ /**
+ * Lookup map of {@link #mKinds} on {@link DataKind#mimeType}.
+ */
+ private HashMap<String, DataKind> mMimeKinds = Maps.newHashMap();
+
+ public boolean isExternal() {
+ return false;
+ }
+
+ /**
+ * Returns an optional custom edit activity. The activity class should reside
+ * in the sync adapter package as determined by {@link #resPackageName}.
+ */
+ public String getEditContactActivityClassName() {
+ return null;
+ }
+
+ /**
+ * Returns an optional custom new contact activity. The activity class should reside
+ * in the sync adapter package as determined by {@link #resPackageName}.
+ */
+ public String getCreateContactActivityClassName() {
+ return null;
+ }
+
+ public CharSequence getDisplayLabel(Context context) {
+ if (this.titleRes != -1 && this.summaryResPackageName != null) {
+ final PackageManager pm = context.getPackageManager();
+ return pm.getText(this.summaryResPackageName, this.titleRes, null);
+ } else if (this.titleRes != -1) {
+ return context.getText(this.titleRes);
+ } else {
+ return this.accountType;
+ }
+ }
+
+ public Drawable getDisplayIcon(Context context) {
+ if (this.titleRes != -1 && this.summaryResPackageName != null) {
+ final PackageManager pm = context.getPackageManager();
+ return pm.getDrawable(this.summaryResPackageName, this.iconRes, null);
+ } else if (this.titleRes != -1) {
+ return context.getResources().getDrawable(this.iconRes);
+ } else {
+ return null;
+ }
+ }
+
+ abstract public int getHeaderColor(Context context);
+
+ abstract public int getSideBarColor(Context context);
+
+ /**
+ * {@link Comparator} to sort by {@link DataKind#weight}.
+ */
+ private static Comparator<DataKind> sWeightComparator = new Comparator<DataKind>() {
+ public int compare(DataKind object1, DataKind object2) {
+ return object1.weight - object2.weight;
+ }
+ };
+
+ /**
+ * Return list of {@link DataKind} supported, sorted by
+ * {@link DataKind#weight}.
+ */
+ public ArrayList<DataKind> getSortedDataKinds() {
+ // TODO: optimize by marking if already sorted
+ Collections.sort(mKinds, sWeightComparator);
+ return mKinds;
+ }
+
+ /**
+ * Find the {@link DataKind} for a specific MIME-type, if it's handled by
+ * this data source. If you may need a fallback {@link DataKind}, use
+ * {@link AccountTypeManager#getKindOrFallback(String, String)}.
+ */
+ public DataKind getKindForMimetype(String mimeType) {
+ return this.mMimeKinds.get(mimeType);
+ }
+
+ /**
+ * Add given {@link DataKind} to list of those provided by this source.
+ */
+ public DataKind addKind(DataKind kind) {
+ kind.resPackageName = this.resPackageName;
+ this.mKinds.add(kind);
+ this.mMimeKinds.put(kind.mimeType, kind);
+ return kind;
+ }
+
+ /**
+ * Description of a specific data type, usually marked by a unique
+ * {@link Data#MIMETYPE}. Includes details about how to view and edit
+ * {@link Data} rows of this kind, including the possible {@link EditType}
+ * labels and editable {@link EditField}.
+ */
+ public static class DataKind {
+ public String resPackageName;
+ public String mimeType;
+ public int titleRes;
+ public int iconRes;
+ public int iconAltRes;
+ public int weight;
+ public boolean editable;
+
+ /**
+ * If this is true (default), the user can add and remove values.
+ * If false, the editor will always show a single field (which might be empty).
+ */
+ public boolean isList;
+
+ public StringInflater actionHeader;
+ public StringInflater actionAltHeader;
+ public StringInflater actionBody;
+
+ public boolean actionBodySocial = false;
+
+ public String typeColumn;
+
+ /**
+ * Maximum number of values allowed in the list. -1 represents infinity.
+ * If {@link DataKind#isList} is false, this value is ignored.
+ */
+ public int typeOverallMax;
+
+ public List<EditType> typeList;
+ public List<EditField> fieldList;
+
+ public ContentValues defaultValues;
+
+ public Class<? extends View> editorClass;
+
+ /**
+ * If this is a date field, this specifies the format of the date when saving. The
+ * date includes year, month and day. If this is not a date field or the date field is not
+ * editable, this value should be ignored.
+ */
+ public SimpleDateFormat dateFormatWithoutYear;
+
+ /**
+ * If this is a date field, this specifies the format of the date when saving. The
+ * date includes month and day. If this is not a date field, the field is not editable or
+ * dates without year are not supported, this value should be ignored.
+ */
+ public SimpleDateFormat dateFormatWithYear;
+
+ public DataKind() {
+ }
+
+ public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable) {
+ this(mimeType, titleRes, iconRes, weight, editable, null);
+ }
+
+ public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable,
+ Class<? extends View> editorClass) {
+ this.mimeType = mimeType;
+ this.titleRes = titleRes;
+ this.iconRes = iconRes;
+ this.weight = weight;
+ this.editable = editable;
+ this.isList = true;
+ this.typeOverallMax = -1;
+ this.editorClass = editorClass;
+ }
+ }
+
+ /**
+ * Description of a specific "type" or "label" of a {@link DataKind} row,
+ * such as {@link Phone#TYPE_WORK}. Includes constraints on total number of
+ * rows a {@link Contacts} may have of this type, and details on how
+ * user-defined labels are stored.
+ */
+ public static class EditType {
+ public int rawValue;
+ public int labelRes;
+ public boolean secondary;
+ public int specificMax;
+ public String customColumn;
+
+ /**
+ * True if this type may be shown as blank.
+ */
+ public boolean unspecifiedType;
+
+ public EditType(int rawValue, int labelRes) {
+ this.rawValue = rawValue;
+ this.labelRes = labelRes;
+ this.specificMax = -1;
+ }
+
+ public EditType setSecondary(boolean secondary) {
+ this.secondary = secondary;
+ return this;
+ }
+
+ public EditType setSpecificMax(int specificMax) {
+ this.specificMax = specificMax;
+ return this;
+ }
+
+ public EditType setCustomColumn(String customColumn) {
+ this.customColumn = customColumn;
+ return this;
+ }
+
+ public EditType setUnspecifiedType(boolean unspecifiedType) {
+ this.unspecifiedType = unspecifiedType;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object instanceof EditType) {
+ final EditType other = (EditType)object;
+ return other.rawValue == rawValue;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return rawValue;
+ }
+ }
+
+ public static class EventEditType extends EditType {
+ private boolean mYearOptional;
+
+ public EventEditType(int rawValue, int labelRes) {
+ super(rawValue, labelRes);
+ }
+
+ public boolean isYearOptional() {
+ return mYearOptional;
+ }
+
+ public EventEditType setYearOptional(boolean yearOptional) {
+ mYearOptional = yearOptional;
+ return this;
+ }
+ }
+
+ /**
+ * Description of a user-editable field on a {@link DataKind} row, such as
+ * {@link Phone#NUMBER}. Includes flags to apply to an {@link EditText}, and
+ * the column where this field is stored.
+ */
+ public static class EditField {
+ public String column;
+ public int titleRes;
+ public int inputType;
+ public int minLines;
+ public boolean optional;
+ public boolean shortForm;
+ public boolean longForm;
+
+ public EditField(String column, int titleRes) {
+ this.column = column;
+ this.titleRes = titleRes;
+ }
+
+ public EditField(String column, int titleRes, int inputType) {
+ this(column, titleRes);
+ this.inputType = inputType;
+ }
+
+ public EditField setOptional(boolean optional) {
+ this.optional = optional;
+ return this;
+ }
+
+ public EditField setShortForm(boolean shortForm) {
+ this.shortForm = shortForm;
+ return this;
+ }
+
+ public EditField setLongForm(boolean longForm) {
+ this.longForm = longForm;
+ return this;
+ }
+
+ public EditField setMinLines(int minLines) {
+ this.minLines = minLines;
+ return this;
+ }
+ }
+
+ /**
+ * Generic method of inflating a given {@link Cursor} into a user-readable
+ * {@link CharSequence}. For example, an inflater could combine the multiple
+ * columns of {@link StructuredPostal} together using a string resource
+ * before presenting to the user.
+ */
+ public interface StringInflater {
+ public CharSequence inflateUsing(Context context, Cursor cursor);
+ public CharSequence inflateUsing(Context context, ContentValues values);
+ }
+}
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
new file mode 100644
index 0000000..06bb9bd
--- /dev/null
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -0,0 +1,385 @@
+/*
+ * 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.model;
+
+import com.android.contacts.model.AccountType.DataKind;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorDescription;
+import android.accounts.OnAccountsUpdateListener;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IContentService;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SyncAdapterType;
+import android.content.SyncStatusObserver;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.provider.ContactsContract;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Singleton holder for all parsed {@link AccountType} available on the
+ * system, typically filled through {@link PackageManager} queries.
+ */
+public abstract class AccountTypeManager {
+ static final String TAG = "AccountTypeManager";
+
+ public static final String ACCOUNT_TYPE_SERVICE = "contactAccountTypes";
+
+ /**
+ * Requests the singleton instance of {@link AccountTypeManager} with data bound from
+ * the available authenticators. This method can safely be called from the UI thread.
+ */
+ public static AccountTypeManager getInstance(Context context) {
+ AccountTypeManager service =
+ (AccountTypeManager) context.getSystemService(ACCOUNT_TYPE_SERVICE);
+ if (service == null) {
+ service = createAccountTypeManager(context);
+ Log.e(TAG, "No account type service in context: " + context);
+ }
+ return service;
+ }
+
+ public static synchronized AccountTypeManager createAccountTypeManager(Context context) {
+ return new AccountTypeManagerImpl(context);
+ }
+
+ public abstract ArrayList<Account> getAccounts(boolean writableOnly);
+
+ public abstract AccountType getAccountType(String accountType);
+
+ /**
+ * Find the best {@link DataKind} matching the requested
+ * {@link AccountType#accountType} and {@link DataKind#mimeType}. If no
+ * direct match found, we try searching {@link FallbackAccountType}.
+ */
+ public DataKind getKindOrFallback(String accountType, String mimeType) {
+ final AccountType type = getAccountType(accountType);
+ return type == null ? null : type.getKindForMimetype(mimeType);
+ }
+}
+
+class AccountTypeManagerImpl extends AccountTypeManager
+ implements OnAccountsUpdateListener, SyncStatusObserver {
+
+ private Context mContext;
+ private AccountManager mAccountManager;
+
+ private AccountType mFallbackAccountType;
+
+ private ArrayList<Account> mAccounts = Lists.newArrayList();
+ private ArrayList<Account> mWritableAccounts = Lists.newArrayList();
+ private HashMap<String, AccountType> mAccountTypes = Maps.newHashMap();
+
+ private static final int MESSAGE_LOAD_DATA = 0;
+ private static final int MESSAGE_PROCESS_BROADCAST_INTENT = 1;
+
+ private HandlerThread mListenerThread;
+ private Handler mListenerHandler;
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Message msg = mListenerHandler.obtainMessage(MESSAGE_PROCESS_BROADCAST_INTENT, intent);
+ mListenerHandler.sendMessage(msg);
+ }
+
+ };
+
+ /* A latch that ensures that asynchronous initialization completes before data is used */
+ private volatile CountDownLatch mInitializationLatch = new CountDownLatch(1);
+
+ private static final Comparator<Account> ACCOUNT_COMPARATOR = new Comparator<Account>() {
+
+ @Override
+ public int compare(Account account1, Account account2) {
+ int diff = account1.name.compareTo(account2.name);
+ if (diff != 0) {
+ return diff;
+ }
+ return account1.type.compareTo(account2.type);
+ }
+ };
+
+ /**
+ * Internal constructor that only performs initial parsing.
+ */
+ public AccountTypeManagerImpl(Context context) {
+ mContext = context;
+ mFallbackAccountType = new FallbackAccountType(context);
+
+ mAccountManager = AccountManager.get(mContext);
+
+ mListenerThread = new HandlerThread("AccountChangeListener");
+ mListenerThread.start();
+ mListenerHandler = new Handler(mListenerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_LOAD_DATA:
+ loadAccountsInBackground();
+ break;
+ case MESSAGE_PROCESS_BROADCAST_INTENT:
+ processBroadcastIntent((Intent) msg.obj);
+ break;
+ }
+ }
+ };
+
+ // Request updates when packages or accounts change
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ IntentFilter sdFilter = new IntentFilter();
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+
+ // Request updates when locale is changed so that the order of each field will
+ // be able to be changed on the locale change.
+ filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+
+ mAccountManager.addOnAccountsUpdatedListener(this, mListenerHandler, false);
+
+ ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, this);
+
+ mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
+ }
+
+ @Override
+ public void onStatusChanged(int which) {
+ mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
+ }
+
+ public void processBroadcastIntent(Intent intent) {
+ mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
+ }
+
+ /* This notification will arrive on the background thread */
+ public void onAccountsUpdated(Account[] accounts) {
+ // Refresh to catch any changed accounts
+ loadAccountsInBackground();
+ }
+
+ /**
+ * Returns instantly if accounts and account types have already been loaded.
+ * Otherwise waits for the background thread to complete the loading.
+ */
+ void ensureAccountsLoaded() {
+ CountDownLatch latch = mInitializationLatch;
+ if (latch == null) {
+ return;
+ }
+ while (true) {
+ try {
+ latch.await();
+ return;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ /**
+ * Loads account list and corresponding account types. Always called on a
+ * background thread.
+ */
+ protected void loadAccountsInBackground() {
+ long startTime = SystemClock.currentThreadTimeMillis();
+
+ HashMap<String, AccountType> accountTypes = Maps.newHashMap();
+ ArrayList<Account> allAccounts = Lists.newArrayList();
+ ArrayList<Account> writableAccounts = Lists.newArrayList();
+
+ final AccountManager am = mAccountManager;
+ final IContentService cs = ContentResolver.getContentService();
+
+ try {
+ final SyncAdapterType[] syncs = cs.getSyncAdapterTypes();
+ final AuthenticatorDescription[] auths = am.getAuthenticatorTypes();
+
+ for (SyncAdapterType sync : syncs) {
+ if (!ContactsContract.AUTHORITY.equals(sync.authority)) {
+ // Skip sync adapters that don't provide contact data.
+ continue;
+ }
+
+ // Look for the formatting details provided by each sync
+ // adapter, using the authenticator to find general resources.
+ final String type = sync.accountType;
+ final AuthenticatorDescription auth = findAuthenticator(auths, type);
+ if (auth == null) {
+ Log.w(TAG, "No authenticator found for type=" + type + ", ignoring it.");
+ continue;
+ }
+
+ AccountType accountType;
+ if (GoogleAccountType.ACCOUNT_TYPE.equals(type)) {
+ accountType = new GoogleAccountType(mContext, auth.packageName);
+ } else if (ExchangeAccountType.ACCOUNT_TYPE.equals(type)) {
+ accountType = new ExchangeAccountType(mContext, auth.packageName);
+ } else {
+ // TODO: use syncadapter package instead, since it provides resources
+ Log.d(TAG, "Registering external account type=" + type
+ + ", packageName=" + auth.packageName);
+ accountType = new ExternalAccountType(mContext, auth.packageName);
+ accountType.readOnly = !sync.supportsUploading();
+ }
+
+ accountType.accountType = auth.type;
+ accountType.titleRes = auth.labelId;
+ accountType.iconRes = auth.iconId;
+
+ accountTypes.put(accountType.accountType, accountType);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Problem loading accounts: " + e.toString());
+ }
+
+ Account[] accounts = mAccountManager.getAccounts();
+ for (Account account : accounts) {
+ boolean syncable = false;
+ try {
+ int isSyncable = cs.getIsSyncable(account, ContactsContract.AUTHORITY);
+ if (isSyncable > 0) {
+ syncable = true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
+ }
+
+ if (syncable) {
+ // Ensure we have details loaded for each account
+ final AccountType accountType = accountTypes.get(account.type);
+ if (accountType != null) {
+ allAccounts.add(account);
+ if (!accountType.readOnly) {
+ writableAccounts.add(account);
+ }
+ }
+ }
+ }
+
+ Collections.sort(allAccounts, ACCOUNT_COMPARATOR);
+ Collections.sort(writableAccounts, ACCOUNT_COMPARATOR);
+
+ // The UI will need a phone number formatter. We can preload meta data for the
+ // current locale to prevent a delay later on.
+ PhoneNumberUtil.getInstance().getAsYouTypeFormatter(Locale.getDefault().getCountry());
+
+ long endTime = SystemClock.currentThreadTimeMillis();
+
+ synchronized (this) {
+ mAccountTypes = accountTypes;
+ mAccounts = allAccounts;
+ mWritableAccounts = writableAccounts;
+ }
+
+ Log.i(TAG, "Loaded meta-data for " + mAccountTypes.size() + " account types, "
+ + mAccounts.size() + " accounts in " + (endTime - startTime) + "ms");
+
+ if (mInitializationLatch != null) {
+ mInitializationLatch.countDown();
+ mInitializationLatch = null;
+ }
+ }
+
+ /**
+ * Find a specific {@link AuthenticatorDescription} in the provided list
+ * that matches the given account type.
+ */
+ protected static AuthenticatorDescription findAuthenticator(AuthenticatorDescription[] auths,
+ String accountType) {
+ for (AuthenticatorDescription auth : auths) {
+ if (accountType.equals(auth.type)) {
+ return auth;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return list of all known, writable {@link Account}'s.
+ */
+ @Override
+ public ArrayList<Account> getAccounts(boolean writableOnly) {
+ ensureAccountsLoaded();
+ return writableOnly ? mWritableAccounts : mAccounts;
+ }
+
+ /**
+ * Find the best {@link DataKind} matching the requested
+ * {@link AccountType#accountType} and {@link DataKind#mimeType}. If no
+ * direct match found, we try searching {@link FallbackAccountType}.
+ */
+ @Override
+ public DataKind getKindOrFallback(String accountType, String mimeType) {
+ ensureAccountsLoaded();
+ DataKind kind = null;
+
+ // Try finding account type and kind matching request
+ final AccountType type = mAccountTypes.get(accountType);
+ if (type != null) {
+ kind = type.getKindForMimetype(mimeType);
+ }
+
+ if (kind == null) {
+ // Nothing found, so try fallback as last resort
+ kind = mFallbackAccountType.getKindForMimetype(mimeType);
+ }
+
+ if (kind == null) {
+ Log.w(TAG, "Unknown type=" + accountType + ", mime=" + mimeType);
+ }
+
+ return kind;
+ }
+
+ /**
+ * Return {@link AccountType} for the given account type.
+ */
+ @Override
+ public AccountType getAccountType(String accountType) {
+ ensureAccountsLoaded();
+ synchronized (this) {
+ AccountType type = mAccountTypes.get(accountType);
+ return type != null ? type : mFallbackAccountType;
+ }
+ }
+}
diff --git a/src/com/android/contacts/model/BaseAccountType.java b/src/com/android/contacts/model/BaseAccountType.java
new file mode 100644
index 0000000..dbfeae4
--- /dev/null
+++ b/src/com/android/contacts/model/BaseAccountType.java
@@ -0,0 +1,612 @@
+/*
+ * 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.model;
+
+import com.android.contacts.R;
+import com.google.android.collect.Lists;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.view.inputmethod.EditorInfo;
+
+public class BaseAccountType extends AccountType {
+ protected static final int FLAGS_PHONE = EditorInfo.TYPE_CLASS_PHONE;
+ protected static final int FLAGS_EMAIL = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
+ protected static final int FLAGS_PERSON_NAME = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS | EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME;
+ protected static final int FLAGS_PHONETIC = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_PHONETIC;
+ protected static final int FLAGS_GENERIC_NAME = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
+ protected static final int FLAGS_NOTE = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+ protected static final int FLAGS_EVENT = EditorInfo.TYPE_CLASS_TEXT;
+ protected static final int FLAGS_WEBSITE = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_URI;
+ protected static final int FLAGS_POSTAL = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
+ | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+ protected static final int FLAGS_SIP_ADDRESS = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; // since SIP addresses have the same
+ // basic format as email addresses
+
+ public BaseAccountType() {
+ this.accountType = null;
+ this.titleRes = R.string.account_phone;
+ this.iconRes = R.mipmap.ic_launcher_contacts;
+ }
+
+ protected EditType buildPhoneType(int type) {
+ return new EditType(type, Phone.getTypeLabelResource(type))
+ .setUnspecifiedType(type == Phone.TYPE_OTHER);
+ }
+
+ protected EditType buildEmailType(int type) {
+ return new EditType(type, Email.getTypeLabelResource(type))
+ .setUnspecifiedType(type == Email.TYPE_OTHER);
+ }
+
+ protected EditType buildPostalType(int type) {
+ return new EditType(type, StructuredPostal.getTypeLabelResource(type))
+ .setUnspecifiedType(type == StructuredPostal.TYPE_OTHER);
+ }
+
+ protected EditType buildImType(int type) {
+ return new EditType(type, Im.getProtocolLabelResource(type))
+ .setUnspecifiedType(type == Im.TYPE_OTHER);
+ }
+
+ protected EditType buildEventType(int type, boolean yearOptional) {
+ return new EventEditType(type, Event.getTypeResource(type)).setYearOptional(yearOptional)
+ .setUnspecifiedType(type == Event.TYPE_OTHER);
+ }
+
+ protected EditType buildRelationType(int type) {
+ return new EditType(type, Relation.getTypeLabelResource(type));
+ }
+
+ protected DataKind addDataKindStructuredName(Context context) {
+ DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
+ R.string.nameLabelsGroup, -1, -1, true));
+ kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
+ kind.actionBody = new SimpleInflater(Nickname.NAME);
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(StructuredName.DISPLAY_NAME,
+ R.string.full_name, FLAGS_PERSON_NAME).setShortForm(true));
+
+ boolean displayOrderPrimary =
+ context.getResources().getBoolean(R.bool.config_editor_field_order_primary);
+
+ if (!displayOrderPrimary) {
+ kind.fieldList.add(new EditField(StructuredName.PREFIX, R.string.name_prefix,
+ FLAGS_PERSON_NAME).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family,
+ FLAGS_PERSON_NAME).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle,
+ FLAGS_PERSON_NAME).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given,
+ FLAGS_PERSON_NAME).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.SUFFIX, R.string.name_suffix,
+ FLAGS_PERSON_NAME).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
+ R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
+ R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
+ R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true));
+ } else {
+ kind.fieldList.add(new EditField(StructuredName.PREFIX, R.string.name_prefix,
+ FLAGS_PERSON_NAME).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given,
+ FLAGS_PERSON_NAME).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle,
+ FLAGS_PERSON_NAME).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family,
+ FLAGS_PERSON_NAME).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.SUFFIX, R.string.name_suffix,
+ FLAGS_PERSON_NAME).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
+ R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
+ R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true));
+ kind.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
+ R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true));
+ }
+
+ return kind;
+ }
+
+ protected DataKind addDataKindNickname(Context context) {
+ DataKind kind = addKind(new DataKind(Nickname.CONTENT_ITEM_TYPE,
+ R.string.nicknameLabelsGroup, -1, 115, true));
+
+ kind.isList = false;
+ kind.actionHeader = new SimpleInflater(R.string.nicknameLabelsGroup);
+ kind.actionBody = new SimpleInflater(Nickname.NAME);
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Nickname.TYPE, Nickname.TYPE_DEFAULT);
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Nickname.NAME, R.string.nicknameLabelsGroup,
+ FLAGS_PERSON_NAME));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindPhone(Context context) {
+ DataKind kind = addKind(new DataKind(Phone.CONTENT_ITEM_TYPE, R.string.phoneLabelsGroup,
+ android.R.drawable.sym_action_call, 10, true));
+ kind.iconAltRes = R.drawable.sym_action_sms;
+ kind.actionHeader = new PhoneActionInflater();
+ kind.actionAltHeader = new PhoneActionAltInflater();
+ kind.actionBody = new SimpleInflater(Phone.NUMBER);
+ kind.typeColumn = Phone.TYPE;
+ kind.typeList = Lists.newArrayList();
+ kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_WORK));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER));
+ kind.typeList.add(
+ buildPhoneType(Phone.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Phone.LABEL));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_CALLBACK).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_CAR).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_COMPANY_MAIN).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_ISDN).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MAIN).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER_FAX).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_RADIO).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_TELEX).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_TTY_TDD).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_MOBILE).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_PAGER).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true).setCustomColumn(
+ Phone.LABEL));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MMS).setSecondary(true));
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindEmail(Context context) {
+ DataKind kind = addKind(new DataKind(Email.CONTENT_ITEM_TYPE, R.string.emailLabelsGroup,
+ R.drawable.sym_action_email_holo_light, 15, true));
+ kind.actionHeader = new EmailActionInflater();
+ kind.actionBody = new SimpleInflater(Email.DATA);
+ kind.typeColumn = Email.TYPE;
+ kind.typeList = Lists.newArrayList();
+ kind.typeList.add(buildEmailType(Email.TYPE_HOME));
+ kind.typeList.add(buildEmailType(Email.TYPE_WORK));
+ kind.typeList.add(buildEmailType(Email.TYPE_OTHER));
+ kind.typeList.add(buildEmailType(Email.TYPE_MOBILE));
+ kind.typeList.add(
+ buildEmailType(Email.TYPE_CUSTOM).setSecondary(true).setCustomColumn(Email.LABEL));
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindStructuredPostal(Context context) {
+ DataKind kind = addKind(new DataKind(StructuredPostal.CONTENT_ITEM_TYPE,
+ R.string.postalLabelsGroup, R.drawable.sym_action_show_map_holo_light, 25,
+ true));
+ kind.actionHeader = new PostalActionInflater();
+ kind.actionBody = new SimpleInflater(StructuredPostal.FORMATTED_ADDRESS);
+ kind.typeColumn = StructuredPostal.TYPE;
+ kind.typeList = Lists.newArrayList();
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_HOME));
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_WORK));
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_OTHER));
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_CUSTOM).setSecondary(true)
+ .setCustomColumn(StructuredPostal.LABEL));
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(
+ new EditField(StructuredPostal.FORMATTED_ADDRESS, R.string.postal_address,
+ FLAGS_POSTAL).setMinLines(3));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindIm(Context context) {
+ DataKind kind = addKind(new DataKind(Im.CONTENT_ITEM_TYPE, R.string.imLabelsGroup,
+ R.drawable.sym_action_talk_holo_light, 20, true));
+ kind.actionHeader = new ImActionInflater();
+ kind.actionBody = new SimpleInflater(Im.DATA);
+
+ // NOTE: even though a traditional "type" exists, for editing
+ // purposes we're using the protocol to pick labels
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Im.TYPE, Im.TYPE_OTHER);
+
+ kind.typeColumn = Im.PROTOCOL;
+ kind.typeList = Lists.newArrayList();
+ kind.typeList.add(buildImType(Im.PROTOCOL_AIM));
+ kind.typeList.add(buildImType(Im.PROTOCOL_MSN));
+ kind.typeList.add(buildImType(Im.PROTOCOL_YAHOO));
+ kind.typeList.add(buildImType(Im.PROTOCOL_SKYPE));
+ kind.typeList.add(buildImType(Im.PROTOCOL_QQ));
+ kind.typeList.add(buildImType(Im.PROTOCOL_GOOGLE_TALK));
+ kind.typeList.add(buildImType(Im.PROTOCOL_ICQ));
+ kind.typeList.add(buildImType(Im.PROTOCOL_JABBER));
+ kind.typeList.add(buildImType(Im.PROTOCOL_CUSTOM).setSecondary(true).setCustomColumn(
+ Im.CUSTOM_PROTOCOL));
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindOrganization(Context context) {
+ DataKind kind = addKind(new DataKind(Organization.CONTENT_ITEM_TYPE,
+ R.string.organizationLabelsGroup, -1, 5, true));
+ kind.actionHeader = new SimpleInflater(Organization.COMPANY);
+ kind.actionBody = new SimpleInflater(Organization.TITLE);
+ kind.isList = false;
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Organization.COMPANY, R.string.ghostData_company,
+ FLAGS_GENERIC_NAME));
+ kind.fieldList.add(new EditField(Organization.TITLE, R.string.ghostData_title,
+ FLAGS_GENERIC_NAME));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindPhoto(Context context) {
+ DataKind kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, -1, -1, true));
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
+ return kind;
+ }
+
+ protected DataKind addDataKindNote(Context context) {
+ DataKind kind = addKind(new DataKind(Note.CONTENT_ITEM_TYPE,
+ R.string.label_notes, -1, 110, true));
+ kind.isList = false;
+ kind.actionHeader = new SimpleInflater(R.string.label_notes);
+ kind.actionBody = new SimpleInflater(Note.NOTE);
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Note.NOTE, R.string.label_notes, FLAGS_NOTE));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindWebsite(Context context) {
+ DataKind kind = addKind(new DataKind(Website.CONTENT_ITEM_TYPE,
+ R.string.websiteLabelsGroup, R.drawable.sym_action_goto_website_holo_light, 120,
+ true));
+ kind.actionHeader = new SimpleInflater(R.string.websiteLabelsGroup);
+ kind.actionBody = new SimpleInflater(Website.URL);
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Website.TYPE, Website.TYPE_OTHER);
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Website.URL, R.string.websiteLabelsGroup, FLAGS_WEBSITE));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindSipAddress(Context context) {
+ // The icon specified here is the one that gets displayed for
+ // "Internet call" items, in the "view contact" UI within the
+ // Contacts app.
+ //
+ // This is independent of the "SIP call" icon that gets
+ // displayed in the Quick Contacts widget, which comes from
+ // the android:icon attribute of the SIP-related
+ // intent-filters in the Phone app's manifest.
+ DataKind kind = addKind(new DataKind(SipAddress.CONTENT_ITEM_TYPE,
+ R.string.label_sip_address, android.R.drawable.sym_action_call, 130, true));
+
+ kind.isList = false;
+ kind.actionHeader = new SimpleInflater(R.string.label_sip_address);
+ kind.actionBody = new SimpleInflater(SipAddress.SIP_ADDRESS);
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(SipAddress.SIP_ADDRESS,
+ R.string.label_sip_address, FLAGS_SIP_ADDRESS));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindGroupMembership(Context context) {
+ DataKind kind = getKindForMimetype(GroupMembership.CONTENT_ITEM_TYPE);
+ kind = addKind(new DataKind(GroupMembership.CONTENT_ITEM_TYPE,
+ R.string.groupsLabel, android.R.drawable.sym_contact_card, 999, true));
+
+ kind.isList = false;
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(GroupMembership.GROUP_ROW_ID, -1, -1));
+
+ return kind;
+ }
+
+ /**
+ * Simple inflater that assumes a string resource has a "%s" that will be
+ * filled from the given column.
+ */
+ public static class SimpleInflater implements StringInflater {
+ private final int mStringRes;
+ private final String mColumnName;
+
+ public SimpleInflater(int stringRes) {
+ this(stringRes, null);
+ }
+
+ public SimpleInflater(String columnName) {
+ this(-1, columnName);
+ }
+
+ public SimpleInflater(int stringRes, String columnName) {
+ mStringRes = stringRes;
+ mColumnName = columnName;
+ }
+
+ public CharSequence inflateUsing(Context context, Cursor cursor) {
+ final int index = mColumnName != null ? cursor.getColumnIndex(mColumnName) : -1;
+ final boolean validString = mStringRes > 0;
+ final boolean validColumn = index != -1;
+
+ final CharSequence stringValue = validString ? context.getText(mStringRes) : null;
+ final CharSequence columnValue = validColumn ? cursor.getString(index) : null;
+
+ if (validString && validColumn) {
+ return String.format(stringValue.toString(), columnValue);
+ } else if (validString) {
+ return stringValue;
+ } else if (validColumn) {
+ return columnValue;
+ } else {
+ return null;
+ }
+ }
+
+ public CharSequence inflateUsing(Context context, ContentValues values) {
+ final boolean validColumn = values.containsKey(mColumnName);
+ final boolean validString = mStringRes > 0;
+
+ final CharSequence stringValue = validString ? context.getText(mStringRes) : null;
+ final CharSequence columnValue = validColumn ? values.getAsString(mColumnName) : null;
+
+ if (validString && validColumn) {
+ return String.format(stringValue.toString(), columnValue);
+ } else if (validString) {
+ return stringValue;
+ } else if (validColumn) {
+ return columnValue;
+ } else {
+ return null;
+ }
+ }
+ }
+
+ public static abstract class CommonInflater implements StringInflater {
+ protected abstract int getTypeLabelResource(Integer type);
+
+ protected boolean isCustom(Integer type) {
+ return type == BaseTypes.TYPE_CUSTOM;
+ }
+
+ protected String getTypeColumn() {
+ return Phone.TYPE;
+ }
+
+ protected String getLabelColumn() {
+ return Phone.LABEL;
+ }
+
+ protected CharSequence getTypeLabel(Resources res, Integer type, CharSequence label) {
+ final int labelRes = getTypeLabelResource(type);
+ if (type == null) {
+ return res.getText(labelRes);
+ } else if (isCustom(type)) {
+ return res.getString(labelRes, label == null ? "" : label);
+ } else {
+ return res.getText(labelRes);
+ }
+ }
+
+ public CharSequence inflateUsing(Context context, Cursor cursor) {
+ final Integer type = cursor.getInt(cursor.getColumnIndex(getTypeColumn()));
+ final String label = cursor.getString(cursor.getColumnIndex(getLabelColumn()));
+ return getTypeLabel(context.getResources(), type, label);
+ }
+
+ public CharSequence inflateUsing(Context context, ContentValues values) {
+ final Integer type = values.getAsInteger(getTypeColumn());
+ final String label = values.getAsString(getLabelColumn());
+ return getTypeLabel(context.getResources(), type, label);
+ }
+ }
+
+ public static class PhoneActionInflater extends CommonInflater {
+ @Override
+ protected boolean isCustom(Integer type) {
+ return type == Phone.TYPE_CUSTOM || type == Phone.TYPE_ASSISTANT;
+ }
+
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ if (type == null) return R.string.call_other;
+ switch (type) {
+ case Phone.TYPE_HOME: return R.string.call_home;
+ case Phone.TYPE_MOBILE: return R.string.call_mobile;
+ case Phone.TYPE_WORK: return R.string.call_work;
+ case Phone.TYPE_FAX_WORK: return R.string.call_fax_work;
+ case Phone.TYPE_FAX_HOME: return R.string.call_fax_home;
+ case Phone.TYPE_PAGER: return R.string.call_pager;
+ case Phone.TYPE_OTHER: return R.string.call_other;
+ case Phone.TYPE_CALLBACK: return R.string.call_callback;
+ case Phone.TYPE_CAR: return R.string.call_car;
+ case Phone.TYPE_COMPANY_MAIN: return R.string.call_company_main;
+ case Phone.TYPE_ISDN: return R.string.call_isdn;
+ case Phone.TYPE_MAIN: return R.string.call_main;
+ case Phone.TYPE_OTHER_FAX: return R.string.call_other_fax;
+ case Phone.TYPE_RADIO: return R.string.call_radio;
+ case Phone.TYPE_TELEX: return R.string.call_telex;
+ case Phone.TYPE_TTY_TDD: return R.string.call_tty_tdd;
+ case Phone.TYPE_WORK_MOBILE: return R.string.call_work_mobile;
+ case Phone.TYPE_WORK_PAGER: return R.string.call_work_pager;
+ case Phone.TYPE_ASSISTANT: return R.string.call_assistant;
+ case Phone.TYPE_MMS: return R.string.call_mms;
+ default: return R.string.call_custom;
+ }
+ }
+ }
+
+ public static class PhoneActionAltInflater extends CommonInflater {
+ @Override
+ protected boolean isCustom(Integer type) {
+ return (type == Phone.TYPE_CUSTOM || type == Phone.TYPE_ASSISTANT);
+ }
+
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ if (type == null) return R.string.sms_other;
+ switch (type) {
+ case Phone.TYPE_HOME: return R.string.sms_home;
+ case Phone.TYPE_MOBILE: return R.string.sms_mobile;
+ case Phone.TYPE_WORK: return R.string.sms_work;
+ case Phone.TYPE_FAX_WORK: return R.string.sms_fax_work;
+ case Phone.TYPE_FAX_HOME: return R.string.sms_fax_home;
+ case Phone.TYPE_PAGER: return R.string.sms_pager;
+ case Phone.TYPE_OTHER: return R.string.sms_other;
+ case Phone.TYPE_CALLBACK: return R.string.sms_callback;
+ case Phone.TYPE_CAR: return R.string.sms_car;
+ case Phone.TYPE_COMPANY_MAIN: return R.string.sms_company_main;
+ case Phone.TYPE_ISDN: return R.string.sms_isdn;
+ case Phone.TYPE_MAIN: return R.string.sms_main;
+ case Phone.TYPE_OTHER_FAX: return R.string.sms_other_fax;
+ case Phone.TYPE_RADIO: return R.string.sms_radio;
+ case Phone.TYPE_TELEX: return R.string.sms_telex;
+ case Phone.TYPE_TTY_TDD: return R.string.sms_tty_tdd;
+ case Phone.TYPE_WORK_MOBILE: return R.string.sms_work_mobile;
+ case Phone.TYPE_WORK_PAGER: return R.string.sms_work_pager;
+ case Phone.TYPE_ASSISTANT: return R.string.sms_assistant;
+ case Phone.TYPE_MMS: return R.string.sms_mms;
+ default: return R.string.sms_custom;
+ }
+ }
+ }
+
+ public static class EmailActionInflater extends CommonInflater {
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ if (type == null) return R.string.email;
+ switch (type) {
+ case Email.TYPE_HOME: return R.string.email_home;
+ case Email.TYPE_WORK: return R.string.email_work;
+ case Email.TYPE_OTHER: return R.string.email_other;
+ case Email.TYPE_MOBILE: return R.string.email_mobile;
+ default: return R.string.email_custom;
+ }
+ }
+ }
+
+ public static class EventActionInflater extends CommonInflater {
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ return Event.getTypeResource(type);
+ }
+ }
+
+ public static class RelationActionInflater extends CommonInflater {
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ return Relation.getTypeLabelResource(type == null ? Relation.TYPE_CUSTOM : type);
+ }
+ }
+
+ public static class PostalActionInflater extends CommonInflater {
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ if (type == null) return R.string.map_other;
+ switch (type) {
+ case StructuredPostal.TYPE_HOME: return R.string.map_home;
+ case StructuredPostal.TYPE_WORK: return R.string.map_work;
+ case StructuredPostal.TYPE_OTHER: return R.string.map_other;
+ default: return R.string.map_custom;
+ }
+ }
+ }
+
+ public static class ImActionInflater extends CommonInflater {
+ @Override
+ protected String getTypeColumn() {
+ return Im.PROTOCOL;
+ }
+
+ @Override
+ protected String getLabelColumn() {
+ return Im.CUSTOM_PROTOCOL;
+ }
+
+ @Override
+ protected int getTypeLabelResource(Integer type) {
+ if (type == null) return R.string.chat;
+ switch (type) {
+ case Im.PROTOCOL_AIM: return R.string.chat_aim;
+ case Im.PROTOCOL_MSN: return R.string.chat_msn;
+ case Im.PROTOCOL_YAHOO: return R.string.chat_yahoo;
+ case Im.PROTOCOL_SKYPE: return R.string.chat_skype;
+ case Im.PROTOCOL_QQ: return R.string.chat_qq;
+ case Im.PROTOCOL_GOOGLE_TALK: return R.string.chat_gtalk;
+ case Im.PROTOCOL_ICQ: return R.string.chat_icq;
+ case Im.PROTOCOL_JABBER: return R.string.chat_jabber;
+ case Im.PROTOCOL_NETMEETING: return R.string.chat;
+ default: return R.string.chat;
+ }
+ }
+ }
+
+ @Override
+ public int getHeaderColor(Context context) {
+ return 0xff7f93bc;
+ }
+
+ @Override
+ public int getSideBarColor(Context context) {
+ return 0xffbdc7b8;
+ }
+}
diff --git a/src/com/android/contacts/model/ContactsSource.java b/src/com/android/contacts/model/ContactsSource.java
deleted file mode 100644
index d008482..0000000
--- a/src/com/android/contacts/model/ContactsSource.java
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * 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.model;
-
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
-import android.accounts.Account;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.graphics.drawable.Drawable;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.widget.EditText;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Internal structure that represents constraints and styles for a specific data
- * source, such as the various data types they support, including details on how
- * those types should be rendered and edited.
- * <p>
- * In the future this may be inflated from XML defined by a data source.
- */
-public abstract class ContactsSource {
- /**
- * The {@link RawContacts#ACCOUNT_TYPE} these constraints apply to.
- */
- public String accountType = null;
-
- /**
- * Package that resources should be loaded from, either defined through an
- * {@link Account} or for matching against {@link Data#RES_PACKAGE}.
- */
- public String resPackageName;
- public String summaryResPackageName;
-
- public int titleRes;
- public int iconRes;
-
- public boolean readOnly;
-
- /**
- * Set of {@link DataKind} supported by this source.
- */
- private ArrayList<DataKind> mKinds = Lists.newArrayList();
-
- /**
- * Lookup map of {@link #mKinds} on {@link DataKind#mimeType}.
- */
- private HashMap<String, DataKind> mMimeKinds = Maps.newHashMap();
-
- public static final int LEVEL_NONE = 0;
- public static final int LEVEL_SUMMARY = 1;
- public static final int LEVEL_MIMETYPES = 2;
- public static final int LEVEL_CONSTRAINTS = 3;
-
- private int mInflatedLevel = LEVEL_NONE;
-
- public synchronized boolean isInflated(int inflateLevel) {
- return mInflatedLevel >= inflateLevel;
- }
-
- /** @hide exposed for unit tests */
- public void setInflatedLevel(int inflateLevel) {
- mInflatedLevel = inflateLevel;
- }
-
- /**
- * Ensure that this {@link ContactsSource} has been inflated to the
- * requested level.
- */
- public synchronized void ensureInflated(Context context, int inflateLevel) {
- if (!isInflated(inflateLevel)) {
- inflate(context, inflateLevel);
- }
- }
-
- /**
- * Perform the actual inflation to the requested level. Called by
- * {@link #ensureInflated(Context, int)} when inflation is needed.
- */
- protected abstract void inflate(Context context, int inflateLevel);
-
- /**
- * Invalidate any cache for this {@link ContactsSource}, removing all
- * inflated data. Calling {@link #ensureInflated(Context, int)} will
- * populate again from scratch.
- */
- public synchronized void invalidateCache() {
- this.mKinds.clear();
- this.mMimeKinds.clear();
- setInflatedLevel(LEVEL_NONE);
- }
-
- public CharSequence getDisplayLabel(Context context) {
- if (this.titleRes != -1 && this.summaryResPackageName != null) {
- final PackageManager pm = context.getPackageManager();
- return pm.getText(this.summaryResPackageName, this.titleRes, null);
- } else if (this.titleRes != -1) {
- return context.getText(this.titleRes);
- } else {
- return this.accountType;
- }
- }
-
- public Drawable getDisplayIcon(Context context) {
- if (this.titleRes != -1 && this.summaryResPackageName != null) {
- final PackageManager pm = context.getPackageManager();
- return pm.getDrawable(this.summaryResPackageName, this.iconRes, null);
- } else if (this.titleRes != -1) {
- return context.getResources().getDrawable(this.iconRes);
- } else {
- return null;
- }
- }
-
- abstract public int getHeaderColor(Context context);
-
- abstract public int getSideBarColor(Context context);
-
- /**
- * {@link Comparator} to sort by {@link DataKind#weight}.
- */
- private static Comparator<DataKind> sWeightComparator = new Comparator<DataKind>() {
- public int compare(DataKind object1, DataKind object2) {
- return object1.weight - object2.weight;
- }
- };
-
- /**
- * Return list of {@link DataKind} supported, sorted by
- * {@link DataKind#weight}.
- */
- public ArrayList<DataKind> getSortedDataKinds() {
- // TODO: optimize by marking if already sorted
- Collections.sort(mKinds, sWeightComparator);
- return mKinds;
- }
-
- /**
- * Find the {@link DataKind} for a specific MIME-type, if it's handled by
- * this data source. If you may need a fallback {@link DataKind}, use
- * {@link Sources#getKindOrFallback(String, String, Context, int)}.
- */
- public DataKind getKindForMimetype(String mimeType) {
- return this.mMimeKinds.get(mimeType);
- }
-
- /**
- * Add given {@link DataKind} to list of those provided by this source.
- */
- public DataKind addKind(DataKind kind) {
- kind.resPackageName = this.resPackageName;
- this.mKinds.add(kind);
- this.mMimeKinds.put(kind.mimeType, kind);
- return kind;
- }
-
- /**
- * Description of a specific data type, usually marked by a unique
- * {@link Data#MIMETYPE}. Includes details about how to view and edit
- * {@link Data} rows of this kind, including the possible {@link EditType}
- * labels and editable {@link EditField}.
- */
- public static class DataKind {
- public String resPackageName;
- public String mimeType;
- public int titleRes;
- public int iconRes;
- public int iconAltRes;
- public int weight;
- public boolean secondary;
- public boolean editable;
-
- /**
- * If this is true (default), the user can add and remove values.
- * If false, the editor will always show a single field (which might be empty).
- */
- public boolean isList;
-
- public StringInflater actionHeader;
- public StringInflater actionAltHeader;
- public StringInflater actionBody;
-
- public boolean actionBodySocial = false;
-
- public String typeColumn;
-
- /**
- * Maximum number of values allowed in the list. -1 represents infinity.
- * If {@link DataKind#isList} is false, this value is ignored.
- */
- public int typeOverallMax;
-
- public List<EditType> typeList;
- public List<EditField> fieldList;
-
- public ContentValues defaultValues;
-
- public DataKind() {
- }
-
- public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable) {
- this.mimeType = mimeType;
- this.titleRes = titleRes;
- this.iconRes = iconRes;
- this.weight = weight;
- this.editable = editable;
- this.isList = true;
- this.typeOverallMax = -1;
- }
- }
-
- /**
- * Description of a specific "type" or "label" of a {@link DataKind} row,
- * such as {@link Phone#TYPE_WORK}. Includes constraints on total number of
- * rows a {@link Contacts} may have of this type, and details on how
- * user-defined labels are stored.
- */
- public static class EditType {
- public int rawValue;
- public int labelRes;
-// public int actionRes;
-// public int actionAltRes;
- public boolean secondary;
- public int specificMax;
- public String customColumn;
-
- public EditType(int rawValue, int labelRes) {
- this.rawValue = rawValue;
- this.labelRes = labelRes;
- this.specificMax = -1;
- }
-
- public EditType setSecondary(boolean secondary) {
- this.secondary = secondary;
- return this;
- }
-
- public EditType setSpecificMax(int specificMax) {
- this.specificMax = specificMax;
- return this;
- }
-
- public EditType setCustomColumn(String customColumn) {
- this.customColumn = customColumn;
- return this;
- }
-
- @Override
- public boolean equals(Object object) {
- if (object instanceof EditType) {
- final EditType other = (EditType)object;
- return other.rawValue == rawValue;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return rawValue;
- }
- }
-
- /**
- * Description of a user-editable field on a {@link DataKind} row, such as
- * {@link Phone#NUMBER}. Includes flags to apply to an {@link EditText}, and
- * the column where this field is stored.
- */
- public static class EditField {
- public String column;
- public int titleRes;
- public int inputType;
- public int minLines;
- public boolean optional;
-
- public EditField(String column, int titleRes) {
- this.column = column;
- this.titleRes = titleRes;
- }
-
- public EditField(String column, int titleRes, int inputType) {
- this(column, titleRes);
- this.inputType = inputType;
- }
-
- public EditField setOptional(boolean optional) {
- this.optional = optional;
- return this;
- }
- }
-
- /**
- * Generic method of inflating a given {@link Cursor} into a user-readable
- * {@link CharSequence}. For example, an inflater could combine the multiple
- * columns of {@link StructuredPostal} together using a string resource
- * before presenting to the user.
- */
- public interface StringInflater {
- public CharSequence inflateUsing(Context context, Cursor cursor);
- public CharSequence inflateUsing(Context context, ContentValues values);
- }
-
-}
diff --git a/src/com/android/contacts/model/Editor.java b/src/com/android/contacts/model/Editor.java
deleted file mode 100644
index 473f0d3..0000000
--- a/src/com/android/contacts/model/Editor.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.model;
-
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.ui.ViewIdGenerator;
-
-import android.provider.ContactsContract.Data;
-
-/**
- * Generic definition of something that edits a {@link Data} row through an
- * {@link ValuesDelta} object.
- */
-public interface Editor {
- /**
- * Listener for an {@link Editor}, usually to handle deleted items.
- */
- public interface EditorListener {
- /**
- * Called when the given {@link Editor} has been deleted.
- */
- public void onDeleted(Editor editor);
-
- /**
- * Called when the given {@link Editor} has a request, for example it
- * wants to select a photo.
- */
- public void onRequest(int request);
-
- public static final int REQUEST_PICK_PHOTO = 1;
- public static final int FIELD_CHANGED = 2;
- }
-
- /**
- * Prepare this editor for the given {@link ValuesDelta}, which
- * builds any needed views. Any changes performed by the user will be
- * written back to that same object.
- */
- public void setValues(DataKind kind, ValuesDelta values, EntityDelta state, boolean readOnly,
- ViewIdGenerator vig);
-
- /**
- * Add a specific {@link EditorListener} to this {@link Editor}.
- */
- public void setEditorListener(EditorListener listener);
-
- /**
- * Called internally when the contents of a specific field have changed,
- * allowing advanced editors to persist data in a specific way.
- */
- public void onFieldChanged(String column, String value);
-}
diff --git a/src/com/android/contacts/model/EntityDelta.java b/src/com/android/contacts/model/EntityDelta.java
index cdf2e41..c5d0d9e 100644
--- a/src/com/android/contacts/model/EntityDelta.java
+++ b/src/com/android/contacts/model/EntityDelta.java
@@ -21,24 +21,23 @@
import com.google.android.collect.Sets;
import android.content.ContentProviderOperation;
+import android.content.ContentProviderOperation.Builder;
import android.content.ContentValues;
import android.content.Entity;
-import android.content.ContentProviderOperation.Builder;
import android.content.Entity.NamedContentValues;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.BaseColumns;
-import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.util.Log;
-import android.view.View;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -241,6 +240,18 @@
return entry;
}
+ public ArrayList<ContentValues> getContentValues() {
+ ArrayList<ContentValues> values = Lists.newArrayList();
+ for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
+ for (ValuesDelta entry : mimeEntries) {
+ if (!entry.isDelete()) {
+ values.add(entry.getCompleteValues());
+ }
+ }
+ }
+ return values;
+ }
+
/**
* Find entry with the given {@link BaseColumns#_ID} value.
*/
@@ -578,6 +589,21 @@
}
}
+ public boolean isChanged(String key) {
+ if (mAfter == null || !mAfter.containsKey(key)) {
+ return false;
+ }
+
+ Object newValue = mAfter.get(key);
+ Object oldValue = mBefore.get(key);
+
+ if (oldValue == null) {
+ return newValue != null;
+ }
+
+ return !oldValue.equals(newValue);
+ }
+
public String getMimetype() {
return getAsString(Data.MIMETYPE);
}
@@ -612,33 +638,59 @@
return (mBefore != null && mBefore.containsKey(mIdColumn));
}
+ /**
+ * When "after" is present, then visible
+ */
public boolean isVisible() {
- // When "after" is present, then visible
return (mAfter != null);
}
+ /**
+ * When "after" is wiped, action is "delete"
+ */
public boolean isDelete() {
- // When "after" is wiped, action is "delete"
return beforeExists() && (mAfter == null);
}
+ /**
+ * When no "before" or "after", is transient
+ */
public boolean isTransient() {
- // When no "before" or "after", is transient
return (mBefore == null) && (mAfter == null);
}
+ /**
+ * When "after" has some changes, action is "update"
+ */
public boolean isUpdate() {
- // When "after" has some changes, action is "update"
- return beforeExists() && (mAfter != null && mAfter.size() > 0);
+ if (!beforeExists() || mAfter == null || mAfter.size() == 0) {
+ return false;
+ }
+ for (String key : mAfter.keySet()) {
+ Object newValue = mAfter.get(key);
+ Object oldValue = mBefore.get(key);
+ if (oldValue == null) {
+ if (newValue != null) {
+ return true;
+ }
+ } else if (!oldValue.equals(newValue)) {
+ return true;
+ }
+ }
+ return false;
}
+ /**
+ * When "after" has no changes, action is no-op
+ */
public boolean isNoop() {
- // When "after" has no changes, action is no-op
return beforeExists() && (mAfter != null && mAfter.size() == 0);
}
+ /**
+ * When no "before" id, and has "after", action is "insert"
+ */
public boolean isInsert() {
- // When no "before" id, and has "after", action is "insert"
return !beforeExists() && (mAfter != null);
}
@@ -670,6 +722,16 @@
mAfter.put(key, value);
}
+ public void put(String key, long value) {
+ ensureUpdate();
+ mAfter.put(key, value);
+ }
+
+ public void putNull(String key) {
+ ensureUpdate();
+ mAfter.putNull(key);
+ }
+
/**
* Return set of all keys defined through this object.
*/
diff --git a/src/com/android/contacts/model/EntityDeltaList.java b/src/com/android/contacts/model/EntityDeltaList.java
new file mode 100644
index 0000000..a998a37
--- /dev/null
+++ b/src/com/android/contacts/model/EntityDeltaList.java
@@ -0,0 +1,385 @@
+/*
+ * 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.model;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.Entity;
+import android.content.EntityIterator;
+import android.content.ContentProviderOperation.Builder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.ContactsContract.AggregationExceptions;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.RawContactsEntity;
+
+import com.google.android.collect.Lists;
+
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Container for multiple {@link EntityDelta} objects, usually when editing
+ * together as an entire aggregate. Provides convenience methods for parceling
+ * and applying another {@link EntityDeltaList} over it.
+ */
+public class EntityDeltaList extends ArrayList<EntityDelta> implements Parcelable {
+ private boolean mSplitRawContacts;
+ private long[] mJoinWithRawContactIds;
+
+ private EntityDeltaList() {
+ }
+
+ /**
+ * Create an {@link EntityDeltaList} that contains the given {@link EntityDelta},
+ * usually when inserting a new {@link Contacts} entry.
+ */
+ public static EntityDeltaList fromSingle(EntityDelta delta) {
+ final EntityDeltaList state = new EntityDeltaList();
+ state.add(delta);
+ return state;
+ }
+
+ /**
+ * Create an {@link EntityDeltaList} based on {@link Contacts} specified by the
+ * given query parameters. This closes the {@link EntityIterator} when
+ * finished, so it doesn't subscribe to updates.
+ */
+ public static EntityDeltaList fromQuery(ContentResolver resolver, String selection,
+ String[] selectionArgs, String sortOrder) {
+ final EntityIterator iterator = RawContacts.newEntityIterator(resolver.query(
+ RawContactsEntity.CONTENT_URI, null, selection, selectionArgs,
+ sortOrder));
+ try {
+ return fromIterator(iterator);
+ } finally {
+ iterator.close();
+ }
+ }
+
+ /**
+ * Create an {@link EntityDeltaList} that contains the entities of the Iterator as before
+ * values.
+ */
+ public static EntityDeltaList fromIterator(Iterator<Entity> iterator) {
+ final EntityDeltaList state = new EntityDeltaList();
+ // Perform background query to pull contact details
+ while (iterator.hasNext()) {
+ // Read all contacts into local deltas to prepare for edits
+ final Entity before = iterator.next();
+ final EntityDelta entity = EntityDelta.fromBefore(before);
+ state.add(entity);
+ }
+ return state;
+ }
+
+ /**
+ * Merge the "after" values from the given {@link EntityDeltaList}, discarding any
+ * previous "after" states. This is typically used when re-parenting user
+ * edits onto an updated {@link EntityDeltaList}.
+ */
+ public static EntityDeltaList mergeAfter(EntityDeltaList local, EntityDeltaList remote) {
+ if (local == null) local = new EntityDeltaList();
+
+ // For each entity in the remote set, try matching over existing
+ for (EntityDelta remoteEntity : remote) {
+ final Long rawContactId = remoteEntity.getValues().getId();
+
+ // Find or create local match and merge
+ final EntityDelta localEntity = local.getByRawContactId(rawContactId);
+ final EntityDelta merged = EntityDelta.mergeAfter(localEntity, remoteEntity);
+
+ if (localEntity == null && merged != null) {
+ // No local entry before, so insert
+ local.add(merged);
+ }
+ }
+
+ return local;
+ }
+
+ /**
+ * Build a list of {@link ContentProviderOperation} that will transform all
+ * the "before" {@link Entity} states into the modified state which all
+ * {@link EntityDelta} objects represent. This method specifically creates
+ * any {@link AggregationExceptions} rules needed to groups edits together.
+ */
+ public ArrayList<ContentProviderOperation> buildDiff() {
+ final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
+
+ final long rawContactId = this.findRawContactId();
+ int firstInsertRow = -1;
+
+ // First pass enforces versions remain consistent
+ for (EntityDelta delta : this) {
+ delta.buildAssert(diff);
+ }
+
+ final int assertMark = diff.size();
+ int backRefs[] = new int[size()];
+
+ int rawContactIndex = 0;
+
+ // Second pass builds actual operations
+ for (EntityDelta delta : this) {
+ final int firstBatch = diff.size();
+ final boolean isInsert = delta.isContactInsert();
+ backRefs[rawContactIndex++] = isInsert ? firstBatch : -1;
+
+ delta.buildDiff(diff);
+
+ // If the user chose to join with some other existing raw contact(s) at save time,
+ // add aggregation exceptions for all those raw contacts.
+ if (mJoinWithRawContactIds != null) {
+ for (Long joinedRawContactId : mJoinWithRawContactIds) {
+ final Builder builder = beginKeepTogether();
+ builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, joinedRawContactId);
+ if (rawContactId != -1) {
+ builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId);
+ } else {
+ builder.withValueBackReference(
+ AggregationExceptions.RAW_CONTACT_ID2, firstBatch);
+ }
+ diff.add(builder.build());
+ }
+ }
+
+ // Only create rules for inserts
+ if (!isInsert) continue;
+
+ // If we are going to split all contacts, there is no point in first combining them
+ if (mSplitRawContacts) continue;
+
+ if (rawContactId != -1) {
+ // Has existing contact, so bind to it strongly
+ final Builder builder = beginKeepTogether();
+ builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId);
+ builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, firstBatch);
+ diff.add(builder.build());
+
+ } else if (firstInsertRow == -1) {
+ // First insert case, so record row
+ firstInsertRow = firstBatch;
+
+ } else {
+ // Additional insert case, so point at first insert
+ final Builder builder = beginKeepTogether();
+ builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID1,
+ firstInsertRow);
+ builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, firstBatch);
+ diff.add(builder.build());
+ }
+ }
+
+ if (mSplitRawContacts) {
+ buildSplitContactDiff(diff, backRefs);
+ }
+
+ // No real changes if only left with asserts
+ if (diff.size() == assertMark) {
+ diff.clear();
+ }
+
+ return diff;
+ }
+
+ /**
+ * Start building a {@link ContentProviderOperation} that will keep two
+ * {@link RawContacts} together.
+ */
+ protected Builder beginKeepTogether() {
+ final Builder builder = ContentProviderOperation
+ .newUpdate(AggregationExceptions.CONTENT_URI);
+ builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
+ return builder;
+ }
+
+ /**
+ * Builds {@link AggregationExceptions} to split all constituent raw contacts into
+ * separate contacts.
+ */
+ private void buildSplitContactDiff(final ArrayList<ContentProviderOperation> diff,
+ int[] backRefs) {
+ int count = size();
+ for (int i = 0; i < count; i++) {
+ for (int j = 0; j < count; j++) {
+ if (i != j) {
+ buildSplitContactDiff(diff, i, j, backRefs);
+ }
+ }
+ }
+ }
+
+ /**
+ * Construct a {@link AggregationExceptions#TYPE_KEEP_SEPARATE}.
+ */
+ private void buildSplitContactDiff(ArrayList<ContentProviderOperation> diff, int index1,
+ int index2, int[] backRefs) {
+ Builder builder =
+ ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI);
+ builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_SEPARATE);
+
+ Long rawContactId1 = get(index1).getValues().getAsLong(RawContacts._ID);
+ int backRef1 = backRefs[index1];
+ if (rawContactId1 != null && rawContactId1 >= 0) {
+ builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
+ } else if (backRef1 >= 0) {
+ builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID1, backRef1);
+ } else {
+ return;
+ }
+
+ Long rawContactId2 = get(index2).getValues().getAsLong(RawContacts._ID);
+ int backRef2 = backRefs[index2];
+ if (rawContactId2 != null && rawContactId2 >= 0) {
+ builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
+ } else if (backRef2 >= 0) {
+ builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, backRef2);
+ } else {
+ return;
+ }
+
+ diff.add(builder.build());
+ }
+
+ /**
+ * Search all contained {@link EntityDelta} for the first one with an
+ * existing {@link RawContacts#_ID} value. Usually used when creating
+ * {@link AggregationExceptions} during an update.
+ */
+ public long findRawContactId() {
+ for (EntityDelta delta : this) {
+ final Long rawContactId = delta.getValues().getAsLong(RawContacts._ID);
+ if (rawContactId != null && rawContactId >= 0) {
+ return rawContactId;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Find {@link RawContacts#_ID} of the requested {@link EntityDelta}.
+ */
+ public Long getRawContactId(int index) {
+ if (index >= 0 && index < this.size()) {
+ final EntityDelta delta = this.get(index);
+ final ValuesDelta values = delta.getValues();
+ if (values.isVisible()) {
+ return values.getAsLong(RawContacts._ID);
+ }
+ }
+ return null;
+ }
+
+ public EntityDelta getByRawContactId(Long rawContactId) {
+ final int index = this.indexOfRawContactId(rawContactId);
+ return (index == -1) ? null : this.get(index);
+ }
+
+ /**
+ * Find index of given {@link RawContacts#_ID} when present.
+ */
+ public int indexOfRawContactId(Long rawContactId) {
+ if (rawContactId == null) return -1;
+ final int size = this.size();
+ for (int i = 0; i < size; i++) {
+ final Long currentId = getRawContactId(i);
+ if (rawContactId.equals(currentId)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public ValuesDelta getSuperPrimaryEntry(final String mimeType) {
+ ValuesDelta primary = null;
+ ValuesDelta randomEntry = null;
+ for (EntityDelta delta : this) {
+ final ArrayList<ValuesDelta> mimeEntries = delta.getMimeEntries(mimeType);
+ if (mimeEntries == null) return null;
+
+ for (ValuesDelta entry : mimeEntries) {
+ if (entry.isSuperPrimary()) {
+ return entry;
+ } else if (primary == null && entry.isPrimary()) {
+ primary = entry;
+ } else if (randomEntry == null) {
+ randomEntry = entry;
+ }
+ }
+ }
+ // When no direct super primary, return something
+ if (primary != null) {
+ return primary;
+ }
+ return randomEntry;
+ }
+
+ /**
+ * Sets a flag that will split ("explode") the raw_contacts into seperate contacts
+ */
+ public void markRawContactsForSplitting() {
+ mSplitRawContacts = true;
+ }
+
+ public void setJoinWithRawContacts(long[] rawContactIds) {
+ mJoinWithRawContactIds = rawContactIds;
+ }
+
+ /** {@inheritDoc} */
+ public int describeContents() {
+ // Nothing special about this parcel
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public void writeToParcel(Parcel dest, int flags) {
+ final int size = this.size();
+ dest.writeInt(size);
+ for (EntityDelta delta : this) {
+ dest.writeParcelable(delta, flags);
+ }
+ dest.writeLongArray(mJoinWithRawContactIds);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void readFromParcel(Parcel source) {
+ final ClassLoader loader = getClass().getClassLoader();
+ final int size = source.readInt();
+ for (int i = 0; i < size; i++) {
+ this.add(source.<EntityDelta> readParcelable(loader));
+ }
+ mJoinWithRawContactIds = source.createLongArray();
+ }
+
+ public static final Parcelable.Creator<EntityDeltaList> CREATOR =
+ new Parcelable.Creator<EntityDeltaList>() {
+ public EntityDeltaList createFromParcel(Parcel in) {
+ final EntityDeltaList state = new EntityDeltaList();
+ state.readFromParcel(in);
+ return state;
+ }
+
+ public EntityDeltaList[] newArray(int size) {
+ return new EntityDeltaList[size];
+ }
+ };
+}
diff --git a/src/com/android/contacts/model/EntityDiff.java b/src/com/android/contacts/model/EntityDiff.java
deleted file mode 100644
index ea46567..0000000
--- a/src/com/android/contacts/model/EntityDiff.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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.model;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentValues;
-import android.content.Entity;
-import android.content.ContentProviderOperation.Builder;
-import android.content.Entity.NamedContentValues;
-import android.net.Uri;
-import android.provider.BaseColumns;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-
-/**
- * Describes a set of {@link ContentProviderOperation} that need to be
- * executed to transform a database from one {@link Entity} to another.
- */
-@Deprecated
-public class EntityDiff extends ArrayList<ContentProviderOperation> {
- private EntityDiff() {
- }
-
- /**
- * Build the set of {@link ContentProviderOperation} needed to translate
- * from "before" to "after". Tries its best to keep operations to
- * minimal number required. Assumes that all {@link ContentValues} are
- * keyed using {@link BaseColumns#_ID} values.
- */
- public static EntityDiff buildDiff(Entity before, Entity after, Uri targetUri,
- String childForeignKey) {
- final EntityDiff diff = new EntityDiff();
-
- Builder builder;
- ContentValues values;
-
- if (before == null) {
- // Before doesn't exist, so insert "after" values
- builder = ContentProviderOperation.newInsert(targetUri);
- builder.withValues(after.getEntityValues());
- diff.add(builder.build());
-
- for (NamedContentValues child : after.getSubValues()) {
- // Add builder with reference to original _id when needed
- builder = ContentProviderOperation.newInsert(child.uri);
- builder.withValues(child.values);
- if (childForeignKey != null) {
- builder.withValueBackReference(childForeignKey, 0);
- }
- diff.add(builder.build());
- }
-
- } else if (after == null) {
- // After doesn't exist, so delete "before" values
- for (NamedContentValues child : before.getSubValues()) {
- builder = ContentProviderOperation.newDelete(child.uri);
- builder.withSelection(getSelectIdClause(child.values), null);
- diff.add(builder.build());
- }
-
- builder = ContentProviderOperation.newDelete(targetUri);
- builder.withSelection(getSelectIdClause(before.getEntityValues()), null);
- diff.add(builder.build());
-
- } else {
- // Somewhere between, so update any changed values
- values = after.getEntityValues();
- if (!before.getEntityValues().equals(values)) {
- // Top-level values changed, so update
- builder = ContentProviderOperation.newUpdate(targetUri);
- builder.withSelection(getSelectIdClause(values), null);
- builder.withValues(values);
- diff.add(builder.build());
- }
-
- // Build lookup maps for children on both sides
- final HashMap<String, NamedContentValues> beforeChildren = buildChildrenMap(before);
- final HashMap<String, NamedContentValues> afterChildren = buildChildrenMap(after);
-
- // Walk through "before" children looking for deletes and updates
- for (NamedContentValues beforeChild : beforeChildren.values()) {
- final String key = buildChildKey(beforeChild);
- final NamedContentValues afterChild = afterChildren.get(key);
-
- if (afterChild == null) {
- // After child doesn't exist, so delete "before" child
- builder = ContentProviderOperation.newDelete(beforeChild.uri);
- builder.withSelection(getSelectIdClause(beforeChild.values), null);
- diff.add(builder.build());
- } else if (!beforeChild.values.equals(afterChild.values)) {
- // After child still exists, and is different, so update
- values = afterChild.values;
- builder = ContentProviderOperation.newUpdate(afterChild.uri);
- builder.withSelection(getSelectIdClause(values), null);
- builder.withValues(values);
- diff.add(builder.build());
- }
-
- // Remove the now-handled "after" child
- afterChildren.remove(key);
- }
-
- // Walk through remaining "after" children, which are inserts
- for (NamedContentValues afterChild : afterChildren.values()) {
- builder = ContentProviderOperation.newInsert(afterChild.uri);
- builder.withValues(afterChild.values);
- diff.add(builder.build());
- }
- }
-
- return diff;
- }
-
- private static String buildChildKey(NamedContentValues child) {
- return child.uri.toString() + child.values.getAsString(BaseColumns._ID);
- }
-
- private static String getSelectIdClause(ContentValues values) {
- return BaseColumns._ID + "=" + values.getAsLong(BaseColumns._ID);
- }
-
- private static HashMap<String, NamedContentValues> buildChildrenMap(Entity entity) {
- final ArrayList<NamedContentValues> children = entity.getSubValues();
- final HashMap<String, NamedContentValues> childrenMap = new HashMap<String, NamedContentValues>(
- children.size());
- for (NamedContentValues child : children) {
- final String key = buildChildKey(child);
- childrenMap.put(key, child);
- }
- return childrenMap;
- }
-}
diff --git a/src/com/android/contacts/model/EntityModifier.java b/src/com/android/contacts/model/EntityModifier.java
index 2e6899e..c58d813 100644
--- a/src/com/android/contacts/model/EntityModifier.java
+++ b/src/com/android/contacts/model/EntityModifier.java
@@ -17,9 +17,9 @@
package com.android.contacts.model;
import com.android.contacts.ContactsUtils;
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.model.ContactsSource.EditField;
-import com.android.contacts.model.ContactsSource.EditType;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.AccountType.EditField;
+import com.android.contacts.model.AccountType.EditType;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.google.android.collect.Lists;
@@ -27,6 +27,7 @@
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Intents;
import android.provider.ContactsContract.RawContacts;
@@ -50,7 +51,7 @@
/**
* Helper methods for modifying an {@link EntityDelta}, such as inserting
- * new rows, or enforcing {@link ContactsSource}.
+ * new rows, or enforcing {@link AccountType}.
*/
public class EntityModifier {
private static final String TAG = "EntityModifier";
@@ -58,7 +59,7 @@
/**
* For the given {@link EntityDelta}, determine if the given
* {@link DataKind} could be inserted under specific
- * {@link ContactsSource}.
+ * {@link AccountType}.
*/
public static boolean canInsert(EntityDelta state, DataKind kind) {
// Insert possible when have valid types and under overall maximum
@@ -81,8 +82,9 @@
* Ensure that at least one of the given {@link DataKind} exists in the
* given {@link EntityDelta} state, and try creating one if none exist.
*/
- public static void ensureKindExists(EntityDelta state, ContactsSource source, String mimeType) {
- final DataKind kind = source.getKindForMimetype(mimeType);
+ public static void ensureKindExists(
+ EntityDelta state, AccountType accountType, String mimeType) {
+ final DataKind kind = accountType.getKindForMimetype(mimeType);
final boolean hasChild = state.getMimeEntriesCount(mimeType, true) > 0;
if (!hasChild && kind != null) {
@@ -97,7 +99,7 @@
/**
* For the given {@link EntityDelta} and {@link DataKind}, return the
* list possible {@link EditType} options available based on
- * {@link ContactsSource}.
+ * {@link AccountType}.
*/
public static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind) {
return getValidTypes(state, kind, null, true, null);
@@ -106,7 +108,7 @@
/**
* For the given {@link EntityDelta} and {@link DataKind}, return the
* list possible {@link EditType} options available based on
- * {@link ContactsSource}.
+ * {@link AccountType}.
*
* @param forceInclude Always include this {@link EditType} in the returned
* list, even when an otherwise-invalid choice. This is useful
@@ -120,7 +122,7 @@
/**
* For the given {@link EntityDelta} and {@link DataKind}, return the
* list possible {@link EditType} options available based on
- * {@link ContactsSource}.
+ * {@link AccountType}.
*
* @param forceInclude Always include this {@link EditType} in the returned
* list, even when an otherwise-invalid choice. This is useful
@@ -348,30 +350,29 @@
/**
* Processing to trim any empty {@link ValuesDelta} and {@link EntityDelta}
- * from the given {@link EntitySet}, assuming the given {@link Sources}
+ * from the given {@link EntityDeltaList}, assuming the given {@link AccountTypeManager}
* dictates the structure for various fields. This method ignores rows not
- * described by the {@link ContactsSource}.
+ * described by the {@link AccountType}.
*/
- public static void trimEmpty(EntitySet set, Sources sources) {
+ public static void trimEmpty(EntityDeltaList set, AccountTypeManager accountTypes) {
for (EntityDelta state : set) {
final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final ContactsSource source = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_MIMETYPES);
+ final AccountType source = accountTypes.getAccountType(accountType);
trimEmpty(state, source);
}
}
/**
* Processing to trim any empty {@link ValuesDelta} rows from the given
- * {@link EntityDelta}, assuming the given {@link ContactsSource} dictates
+ * {@link EntityDelta}, assuming the given {@link AccountType} dictates
* the structure for various fields. This method ignores rows not described
- * by the {@link ContactsSource}.
+ * by the {@link AccountType}.
*/
- public static void trimEmpty(EntityDelta state, ContactsSource source) {
+ public static void trimEmpty(EntityDelta state, AccountType accountType) {
boolean hasValues = false;
// Walk through entries for each well-known kind
- for (DataKind kind : source.getSortedDataKinds()) {
+ for (DataKind kind : accountType.getSortedDataKinds()) {
final String mimeType = kind.mimeType;
final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
if (entries == null) continue;
@@ -385,10 +386,10 @@
}
// Test and remove this row if empty and it isn't a photo from google
- final boolean isGoogleSource = TextUtils.equals(GoogleSource.ACCOUNT_TYPE,
+ final boolean isGoogleAccount = TextUtils.equals(GoogleAccountType.ACCOUNT_TYPE,
state.getValues().getAsString(RawContacts.ACCOUNT_TYPE));
final boolean isPhoto = TextUtils.equals(Photo.CONTENT_ITEM_TYPE, kind.mimeType);
- final boolean isGooglePhoto = isPhoto && isGoogleSource;
+ final boolean isGooglePhoto = isPhoto && isGoogleAccount;
if (EntityModifier.isEmpty(entry, kind) && !isGooglePhoto) {
// TODO: remove this verbose logging
@@ -426,10 +427,28 @@
}
/**
+ * Compares corresponding fields in values1 and values2. Only the fields
+ * declared by the DataKind are taken into consideration.
+ */
+ protected static boolean areEqual(ValuesDelta values1, ContentValues values2, DataKind kind) {
+ if (kind.fieldList == null) return false;
+
+ for (EditField field : kind.fieldList) {
+ final String value1 = values1.getAsString(field.column);
+ final String value2 = values2.getAsString(field.column);
+ if (!TextUtils.equals(value1, value2)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
* Parse the given {@link Bundle} into the given {@link EntityDelta} state,
* assuming the extras defined through {@link Intents}.
*/
- public static void parseExtras(Context context, ContactsSource source, EntityDelta state,
+ public static void parseExtras(Context context, AccountType accountType, EntityDelta state,
Bundle extras) {
if (extras == null || extras.size() == 0) {
// Bail early if no useful data
@@ -438,12 +457,12 @@
{
// StructuredName
- EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(state, accountType, StructuredName.CONTENT_ITEM_TYPE);
final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
final String name = extras.getString(Insert.NAME);
if (ContactsUtils.isGraphic(name)) {
- child.put(StructuredName.GIVEN_NAME, name);
+ child.put(StructuredName.DISPLAY_NAME, name);
}
final String phoneticName = extras.getString(Insert.PHONETIC_NAME);
@@ -454,14 +473,14 @@
{
// StructuredPostal
- final DataKind kind = source.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
+ final DataKind kind = accountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
parseExtras(state, kind, extras, Insert.POSTAL_TYPE, Insert.POSTAL,
- StructuredPostal.STREET);
+ StructuredPostal.FORMATTED_ADDRESS);
}
{
// Phone
- final DataKind kind = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
+ final DataKind kind = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
parseExtras(state, kind, extras, Insert.PHONE_TYPE, Insert.PHONE, Phone.NUMBER);
parseExtras(state, kind, extras, Insert.SECONDARY_PHONE_TYPE, Insert.SECONDARY_PHONE,
Phone.NUMBER);
@@ -471,7 +490,7 @@
{
// Email
- final DataKind kind = source.getKindForMimetype(Email.CONTENT_ITEM_TYPE);
+ final DataKind kind = accountType.getKindForMimetype(Email.CONTENT_ITEM_TYPE);
parseExtras(state, kind, extras, Insert.EMAIL_TYPE, Insert.EMAIL, Email.DATA);
parseExtras(state, kind, extras, Insert.SECONDARY_EMAIL_TYPE, Insert.SECONDARY_EMAIL,
Email.DATA);
@@ -481,7 +500,7 @@
{
// Im
- final DataKind kind = source.getKindForMimetype(Im.CONTENT_ITEM_TYPE);
+ final DataKind kind = accountType.getKindForMimetype(Im.CONTENT_ITEM_TYPE);
fixupLegacyImType(extras);
parseExtras(state, kind, extras, Insert.IM_PROTOCOL, Insert.IM_HANDLE, Im.DATA);
}
@@ -489,7 +508,7 @@
// Organization
final boolean hasOrg = extras.containsKey(Insert.COMPANY)
|| extras.containsKey(Insert.JOB_TITLE);
- final DataKind kindOrg = source.getKindForMimetype(Organization.CONTENT_ITEM_TYPE);
+ final DataKind kindOrg = accountType.getKindForMimetype(Organization.CONTENT_ITEM_TYPE);
if (hasOrg && EntityModifier.canInsert(state, kindOrg)) {
final ValuesDelta child = EntityModifier.insertChild(state, kindOrg);
@@ -506,7 +525,7 @@
// Notes
final boolean hasNotes = extras.containsKey(Insert.NOTES);
- final DataKind kindNotes = source.getKindForMimetype(Note.CONTENT_ITEM_TYPE);
+ final DataKind kindNotes = accountType.getKindForMimetype(Note.CONTENT_ITEM_TYPE);
if (hasNotes && EntityModifier.canInsert(state, kindNotes)) {
final ValuesDelta child = EntityModifier.insertChild(state, kindNotes);
@@ -515,6 +534,187 @@
child.put(Note.NOTE, notes);
}
}
+
+ // Arbitrary additional data
+ ArrayList<ContentValues> values = extras.getParcelableArrayList(Insert.DATA);
+ if (values != null) {
+ parseValues(state, accountType, values);
+ }
+ }
+
+ private static void parseValues(
+ EntityDelta state, AccountType accountType, ArrayList<ContentValues> dataValueList) {
+ for (ContentValues values : dataValueList) {
+ String mimeType = values.getAsString(Data.MIMETYPE);
+ if (TextUtils.isEmpty(mimeType)) {
+ Log.e(TAG, "Mimetype is required. Ignoring: " + values);
+ continue;
+ }
+
+ // Won't override the contact name
+ if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ continue;
+ }
+
+ DataKind kind = accountType.getKindForMimetype(mimeType);
+ if (kind == null) {
+ Log.e(TAG, "Mimetype not supported for account type " + accountType.accountType
+ + ". Ignoring: " + values);
+ continue;
+ }
+
+ ValuesDelta entry = ValuesDelta.fromAfter(values);
+ if (isEmpty(entry, kind)) {
+ continue;
+ }
+
+ ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
+
+ if (kind.isList || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ // Check for duplicates
+ boolean addEntry = true;
+ int count = 0;
+ if (entries != null && entries.size() > 0) {
+ for (ValuesDelta delta : entries) {
+ if (!delta.isDelete()) {
+ if (areEqual(delta, values, kind)) {
+ addEntry = false;
+ break;
+ }
+ count++;
+ }
+ }
+ }
+
+ if (kind.typeOverallMax != -1 && count >= kind.typeOverallMax) {
+ Log.e(TAG, "Mimetype allows at most " + kind.typeOverallMax
+ + " entries. Ignoring: " + values);
+ addEntry = false;
+ }
+
+ if (addEntry) {
+ addEntry = adjustType(entry, entries, kind);
+ }
+
+ if (addEntry) {
+ state.addEntry(entry);
+ }
+ } else {
+ // Non-list entries should not be overridden
+ boolean addEntry = true;
+ if (entries != null && entries.size() > 0) {
+ for (ValuesDelta delta : entries) {
+ if (!delta.isDelete() && !isEmpty(delta, kind)) {
+ addEntry = false;
+ break;
+ }
+ }
+ if (addEntry) {
+ for (ValuesDelta delta : entries) {
+ delta.markDeleted();
+ }
+ }
+ }
+
+ if (addEntry) {
+ addEntry = adjustType(entry, entries, kind);
+ }
+
+ if (addEntry) {
+ state.addEntry(entry);
+ } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)){
+ // Note is most likely to contain large amounts of text
+ // that we don't want to drop on the ground.
+ for (ValuesDelta delta : entries) {
+ if (!isEmpty(delta, kind)) {
+ delta.put(Note.NOTE, delta.getAsString(Note.NOTE) + "\n"
+ + values.getAsString(Note.NOTE));
+ break;
+ }
+ }
+ } else {
+ Log.e(TAG, "Will not override mimetype " + mimeType + ". Ignoring: "
+ + values);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if the data kind allows addition of another entry (e.g. Exchange only
+ * supports two "work" phone numbers). If not, tries to switch to one of the
+ * unused types. If successful, returns true.
+ */
+ private static boolean adjustType(
+ ValuesDelta entry, ArrayList<ValuesDelta> entries, DataKind kind) {
+ if (kind.typeColumn == null || kind.typeList == null || kind.typeList.size() == 0) {
+ return true;
+ }
+
+ Integer typeInteger = entry.getAsInteger(kind.typeColumn);
+ int type = typeInteger != null ? typeInteger : kind.typeList.get(0).rawValue;
+
+ if (isTypeAllowed(type, entries, kind)) {
+ entry.put(kind.typeColumn, type);
+ return true;
+ }
+
+ // Specified type is not allowed - choose the first available type that is allowed
+ int size = kind.typeList.size();
+ for (int i = 0; i < size; i++) {
+ EditType editType = kind.typeList.get(i);
+ if (isTypeAllowed(editType.rawValue, entries, kind)) {
+ entry.put(kind.typeColumn, editType.rawValue);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if a new entry of the specified type can be added to the raw
+ * contact. For example, Exchange only supports two "work" phone numbers, so
+ * addition of a third would not be allowed.
+ */
+ private static boolean isTypeAllowed(int type, ArrayList<ValuesDelta> entries, DataKind kind) {
+ int max = 0;
+ int size = kind.typeList.size();
+ for (int i = 0; i < size; i++) {
+ EditType editType = kind.typeList.get(i);
+ if (editType.rawValue == type) {
+ max = editType.specificMax;
+ break;
+ }
+ }
+
+ if (max == 0) {
+ // This type is not allowed at all
+ return false;
+ }
+
+ if (max == -1) {
+ // Unlimited instances of this type are allowed
+ return true;
+ }
+
+ return getEntryCountByType(entries, kind.typeColumn, type) < max;
+ }
+
+ /**
+ * Counts occurrences of the specified type in the supplied entry list.
+ */
+ private static int getEntryCountByType(
+ ArrayList<ValuesDelta> entries, String typeColumn, int type) {
+ int count = 0;
+ int size = entries.size();
+ for (int i = 0; i < size; i++) {
+ Integer typeInteger = entries.get(i).getAsInteger(typeColumn);
+ if (typeInteger != null && typeInteger == type) {
+ count++;
+ }
+ }
+ return count;
}
/**
@@ -552,7 +752,7 @@
String typeExtra, String valueExtra, String valueColumn) {
final CharSequence value = extras.getCharSequence(valueExtra);
- // Bail early if source doesn't handle this type
+ // Bail early if account type doesn't handle this MIME type
if (kind == null) return;
// Bail when can't insert type, or value missing
diff --git a/src/com/android/contacts/model/EntitySet.java b/src/com/android/contacts/model/EntitySet.java
deleted file mode 100644
index 830f8da..0000000
--- a/src/com/android/contacts/model/EntitySet.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * 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.model;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentResolver;
-import android.content.Entity;
-import android.content.EntityIterator;
-import android.content.ContentProviderOperation.Builder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.provider.ContactsContract.AggregationExceptions;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.RawContactsEntity;
-
-import com.google.android.collect.Lists;
-
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-
-import java.util.ArrayList;
-
-/**
- * Container for multiple {@link EntityDelta} objects, usually when editing
- * together as an entire aggregate. Provides convenience methods for parceling
- * and applying another {@link EntitySet} over it.
- */
-public class EntitySet extends ArrayList<EntityDelta> implements Parcelable {
- private boolean mSplitRawContacts;
-
- private EntitySet() {
- }
-
- /**
- * Create an {@link EntitySet} that contains the given {@link EntityDelta},
- * usually when inserting a new {@link Contacts} entry.
- */
- public static EntitySet fromSingle(EntityDelta delta) {
- final EntitySet state = new EntitySet();
- state.add(delta);
- return state;
- }
-
- /**
- * Create an {@link EntitySet} based on {@link Contacts} specified by the
- * given query parameters. This closes the {@link EntityIterator} when
- * finished, so it doesn't subscribe to updates.
- */
- public static EntitySet fromQuery(ContentResolver resolver, String selection,
- String[] selectionArgs, String sortOrder) {
- EntityIterator iterator = RawContacts.newEntityIterator(resolver.query(
- RawContactsEntity.CONTENT_URI, null, selection, selectionArgs,
- sortOrder));
- try {
- final EntitySet state = new EntitySet();
- // Perform background query to pull contact details
- while (iterator.hasNext()) {
- // Read all contacts into local deltas to prepare for edits
- final Entity before = iterator.next();
- final EntityDelta entity = EntityDelta.fromBefore(before);
- state.add(entity);
- }
- return state;
- } finally {
- iterator.close();
- }
- }
-
- /**
- * Merge the "after" values from the given {@link EntitySet}, discarding any
- * previous "after" states. This is typically used when re-parenting user
- * edits onto an updated {@link EntitySet}.
- */
- public static EntitySet mergeAfter(EntitySet local, EntitySet remote) {
- if (local == null) local = new EntitySet();
-
- // For each entity in the remote set, try matching over existing
- for (EntityDelta remoteEntity : remote) {
- final Long rawContactId = remoteEntity.getValues().getId();
-
- // Find or create local match and merge
- final EntityDelta localEntity = local.getByRawContactId(rawContactId);
- final EntityDelta merged = EntityDelta.mergeAfter(localEntity, remoteEntity);
-
- if (localEntity == null && merged != null) {
- // No local entry before, so insert
- local.add(merged);
- }
- }
-
- return local;
- }
-
- /**
- * Build a list of {@link ContentProviderOperation} that will transform all
- * the "before" {@link Entity} states into the modified state which all
- * {@link EntityDelta} objects represent. This method specifically creates
- * any {@link AggregationExceptions} rules needed to groups edits together.
- */
- public ArrayList<ContentProviderOperation> buildDiff() {
- final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
-
- final long rawContactId = this.findRawContactId();
- int firstInsertRow = -1;
-
- // First pass enforces versions remain consistent
- for (EntityDelta delta : this) {
- delta.buildAssert(diff);
- }
-
- final int assertMark = diff.size();
- int backRefs[] = new int[size()];
-
- int rawContactIndex = 0;
-
- // Second pass builds actual operations
- for (EntityDelta delta : this) {
- final int firstBatch = diff.size();
- final boolean isInsert = delta.isContactInsert();
- backRefs[rawContactIndex++] = isInsert ? firstBatch : -1;
-
- delta.buildDiff(diff);
-
- // Only create rules for inserts
- if (!isInsert) continue;
-
- // If we are going to split all contacts, there is no point in first combining them
- if (mSplitRawContacts) continue;
-
- if (rawContactId != -1) {
- // Has existing contact, so bind to it strongly
- final Builder builder = beginKeepTogether();
- builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId);
- builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, firstBatch);
- diff.add(builder.build());
-
- } else if (firstInsertRow == -1) {
- // First insert case, so record row
- firstInsertRow = firstBatch;
-
- } else {
- // Additional insert case, so point at first insert
- final Builder builder = beginKeepTogether();
- builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID1, firstInsertRow);
- builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, firstBatch);
- diff.add(builder.build());
- }
- }
-
- if (mSplitRawContacts) {
- buildSplitContactDiff(diff, backRefs);
- }
-
- // No real changes if only left with asserts
- if (diff.size() == assertMark) {
- diff.clear();
- }
-
- return diff;
- }
-
- /**
- * Start building a {@link ContentProviderOperation} that will keep two
- * {@link RawContacts} together.
- */
- protected Builder beginKeepTogether() {
- final Builder builder = ContentProviderOperation
- .newUpdate(AggregationExceptions.CONTENT_URI);
- builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
- return builder;
- }
-
- /**
- * Builds {@link AggregationExceptions} to split all constituent raw contacts into
- * separate contacts.
- */
- private void buildSplitContactDiff(final ArrayList<ContentProviderOperation> diff,
- int[] backRefs) {
- int count = size();
- for (int i = 0; i < count; i++) {
- for (int j = 0; j < count; j++) {
- if (i != j) {
- buildSplitContactDiff(diff, i, j, backRefs);
- }
- }
- }
- }
-
- /**
- * Construct a {@link AggregationExceptions#TYPE_KEEP_SEPARATE}.
- */
- private void buildSplitContactDiff(ArrayList<ContentProviderOperation> diff, int index1,
- int index2, int[] backRefs) {
- Builder builder =
- ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI);
- builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_SEPARATE);
-
- Long rawContactId1 = get(index1).getValues().getAsLong(RawContacts._ID);
- int backRef1 = backRefs[index1];
- if (rawContactId1 != null && rawContactId1 >= 0) {
- builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
- } else if (backRef1 >= 0) {
- builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID1, backRef1);
- } else {
- return;
- }
-
- Long rawContactId2 = get(index2).getValues().getAsLong(RawContacts._ID);
- int backRef2 = backRefs[index2];
- if (rawContactId2 != null && rawContactId2 >= 0) {
- builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
- } else if (backRef2 >= 0) {
- builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, backRef2);
- } else {
- return;
- }
-
- diff.add(builder.build());
- }
-
- /**
- * Search all contained {@link EntityDelta} for the first one with an
- * existing {@link RawContacts#_ID} value. Usually used when creating
- * {@link AggregationExceptions} during an update.
- */
- public long findRawContactId() {
- for (EntityDelta delta : this) {
- final Long rawContactId = delta.getValues().getAsLong(RawContacts._ID);
- if (rawContactId != null && rawContactId >= 0) {
- return rawContactId;
- }
- }
- return -1;
- }
-
- /**
- * Find {@link RawContacts#_ID} of the requested {@link EntityDelta}.
- */
- public Long getRawContactId(int index) {
- if (index >= 0 && index < this.size()) {
- final EntityDelta delta = this.get(index);
- final ValuesDelta values = delta.getValues();
- if (values.isVisible()) {
- return values.getAsLong(RawContacts._ID);
- }
- }
- return null;
- }
-
- public EntityDelta getByRawContactId(Long rawContactId) {
- final int index = this.indexOfRawContactId(rawContactId);
- return (index == -1) ? null : this.get(index);
- }
-
- /**
- * Find index of given {@link RawContacts#_ID} when present.
- */
- public int indexOfRawContactId(Long rawContactId) {
- if (rawContactId == null) return -1;
- final int size = this.size();
- for (int i = 0; i < size; i++) {
- final Long currentId = getRawContactId(i);
- if (rawContactId.equals(currentId)) {
- return i;
- }
- }
- return -1;
- }
-
- public ValuesDelta getSuperPrimaryEntry(final String mimeType) {
- ValuesDelta primary = null;
- ValuesDelta randomEntry = null;
- for (EntityDelta delta : this) {
- final ArrayList<ValuesDelta> mimeEntries = delta.getMimeEntries(mimeType);
- if (mimeEntries == null) return null;
-
- for (ValuesDelta entry : mimeEntries) {
- if (entry.isSuperPrimary()) {
- return entry;
- } else if (primary == null && entry.isPrimary()) {
- primary = entry;
- } else if (randomEntry == null) {
- randomEntry = entry;
- }
- }
- }
- // When no direct super primary, return something
- if (primary != null) {
- return primary;
- }
- return randomEntry;
- }
-
- public void splitRawContacts() {
- mSplitRawContacts = true;
- }
-
- /** {@inheritDoc} */
- public int describeContents() {
- // Nothing special about this parcel
- return 0;
- }
-
- /** {@inheritDoc} */
- public void writeToParcel(Parcel dest, int flags) {
- final int size = this.size();
- dest.writeInt(size);
- for (EntityDelta delta : this) {
- dest.writeParcelable(delta, flags);
- }
- }
-
- public void readFromParcel(Parcel source) {
- final ClassLoader loader = getClass().getClassLoader();
- final int size = source.readInt();
- for (int i = 0; i < size; i++) {
- this.add(source.<EntityDelta> readParcelable(loader));
- }
- }
-
- public static final Parcelable.Creator<EntitySet> CREATOR = new Parcelable.Creator<EntitySet>() {
- public EntitySet createFromParcel(Parcel in) {
- final EntitySet state = new EntitySet();
- state.readFromParcel(in);
- return state;
- }
-
- public EntitySet[] newArray(int size) {
- return new EntitySet[size];
- }
- };
-}
diff --git a/src/com/android/contacts/model/ExchangeAccountType.java b/src/com/android/contacts/model/ExchangeAccountType.java
new file mode 100644
index 0000000..4442a70
--- /dev/null
+++ b/src/com/android/contacts/model/ExchangeAccountType.java
@@ -0,0 +1,295 @@
+/*
+ * 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.model;
+
+import com.android.contacts.R;
+import com.android.contacts.editor.EventFieldEditorView;
+import com.android.contacts.util.DateUtils;
+import com.google.android.collect.Lists;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+
+import java.util.Locale;
+
+public class ExchangeAccountType extends BaseAccountType {
+
+ public static final String ACCOUNT_TYPE = "com.android.exchange";
+
+ public ExchangeAccountType(Context context, String resPackageName) {
+ this.accountType = ACCOUNT_TYPE;
+ this.resPackageName = null;
+ this.summaryResPackageName = resPackageName;
+
+ addDataKindStructuredName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindEvent(context);
+ addDataKindWebsite(context);
+ addDataKindGroupMembership(context);
+ }
+
+ @Override
+ protected DataKind addDataKindStructuredName(Context context) {
+ final DataKind kind = super.addDataKindStructuredName(context);
+
+ boolean displayOrderPrimary =
+ context.getResources().getBoolean(R.bool.config_editor_field_order_primary);
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(StructuredName.PREFIX, R.string.name_prefix,
+ FLAGS_PERSON_NAME).setOptional(true));
+ if (!displayOrderPrimary) {
+ kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME,
+ R.string.name_family, FLAGS_PERSON_NAME));
+ kind.fieldList.add(new EditField(StructuredName.MIDDLE_NAME,
+ R.string.name_middle, FLAGS_PERSON_NAME).setOptional(true));
+ kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME,
+ R.string.name_given, FLAGS_PERSON_NAME));
+ kind.fieldList.add(new EditField(StructuredName.SUFFIX,
+ R.string.name_suffix, FLAGS_PERSON_NAME).setOptional(true));
+ kind.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
+ R.string.name_phonetic_family, FLAGS_PHONETIC).setOptional(true));
+ kind.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
+ R.string.name_phonetic_given, FLAGS_PHONETIC).setOptional(true));
+ } else {
+ kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME,
+ R.string.name_given, FLAGS_PERSON_NAME));
+ kind.fieldList.add(new EditField(StructuredName.MIDDLE_NAME,
+ R.string.name_middle, FLAGS_PERSON_NAME).setOptional(true));
+ kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME,
+ R.string.name_family, FLAGS_PERSON_NAME));
+ kind.fieldList.add(new EditField(StructuredName.SUFFIX,
+ R.string.name_suffix, FLAGS_PERSON_NAME).setOptional(true));
+ kind.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
+ R.string.name_phonetic_given, FLAGS_PHONETIC).setOptional(true));
+ kind.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
+ R.string.name_phonetic_family, FLAGS_PHONETIC).setOptional(true));
+ }
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindNickname(Context context) {
+ final DataKind kind = super.addDataKindNickname(context);
+
+ kind.isList = false;
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Nickname.NAME, R.string.nicknameLabelsGroup,
+ FLAGS_PERSON_NAME));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindPhone(Context context) {
+ final DataKind kind = super.addDataKindPhone(context);
+
+ kind.typeColumn = Phone.TYPE;
+ kind.typeList = Lists.newArrayList();
+ kind.typeList.add(buildPhoneType(Phone.TYPE_HOME).setSpecificMax(2));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE).setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_WORK).setSpecificMax(2));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true)
+ .setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true)
+ .setSpecificMax(1));
+ kind.typeList
+ .add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true).setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_CAR).setSecondary(true).setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_COMPANY_MAIN).setSecondary(true)
+ .setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MMS).setSecondary(true).setSpecificMax(1));
+ kind.typeList
+ .add(buildPhoneType(Phone.TYPE_RADIO).setSecondary(true).setSpecificMax(1));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true)
+ .setSpecificMax(1));
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindEmail(Context context) {
+ final DataKind kind = super.addDataKindEmail(context);
+
+ kind.typeOverallMax = 3;
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindStructuredPostal(Context context) {
+ final DataKind kind = super.addDataKindStructuredPostal(context);
+
+ final boolean useJapaneseOrder =
+ Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage());
+ kind.typeColumn = StructuredPostal.TYPE;
+ kind.typeList = Lists.newArrayList();
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_WORK).setSpecificMax(1));
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_HOME).setSpecificMax(1));
+ kind.typeList.add(buildPostalType(StructuredPostal.TYPE_OTHER).setSpecificMax(1));
+
+ kind.fieldList = Lists.newArrayList();
+ if (useJapaneseOrder) {
+ kind.fieldList.add(new EditField(StructuredPostal.COUNTRY,
+ R.string.postal_country, FLAGS_POSTAL).setOptional(true));
+ kind.fieldList.add(new EditField(StructuredPostal.POSTCODE,
+ R.string.postal_postcode, FLAGS_POSTAL));
+ kind.fieldList.add(new EditField(StructuredPostal.REGION,
+ R.string.postal_region, FLAGS_POSTAL));
+ kind.fieldList.add(new EditField(StructuredPostal.CITY,
+ R.string.postal_city,FLAGS_POSTAL));
+ kind.fieldList.add(new EditField(StructuredPostal.STREET,
+ R.string.postal_street, FLAGS_POSTAL));
+ } else {
+ kind.fieldList.add(new EditField(StructuredPostal.STREET,
+ R.string.postal_street, FLAGS_POSTAL));
+ kind.fieldList.add(new EditField(StructuredPostal.CITY,
+ R.string.postal_city,FLAGS_POSTAL));
+ kind.fieldList.add(new EditField(StructuredPostal.REGION,
+ R.string.postal_region, FLAGS_POSTAL));
+ kind.fieldList.add(new EditField(StructuredPostal.POSTCODE,
+ R.string.postal_postcode, FLAGS_POSTAL));
+ kind.fieldList.add(new EditField(StructuredPostal.COUNTRY,
+ R.string.postal_country, FLAGS_POSTAL).setOptional(true));
+ }
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindIm(Context context) {
+ final DataKind kind = super.addDataKindIm(context);
+
+ // Types are not supported for IM. There can be 3 IMs, but OWA only shows only the first
+ kind.typeOverallMax = 3;
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Im.TYPE, Im.TYPE_OTHER);
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindOrganization(Context context) {
+ final DataKind kind = super.addDataKindOrganization(context);
+
+ kind.isList = false;
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Organization.COMPANY, R.string.ghostData_company,
+ FLAGS_GENERIC_NAME));
+ kind.fieldList.add(new EditField(Organization.TITLE, R.string.ghostData_title,
+ FLAGS_GENERIC_NAME));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindPhoto(Context context) {
+ final DataKind kind = super.addDataKindPhoto(context);
+
+ kind.typeOverallMax = 1;
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindNote(Context context) {
+ final DataKind kind = super.addDataKindNote(context);
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Note.NOTE, R.string.label_notes, FLAGS_NOTE));
+
+ return kind;
+ }
+
+ protected DataKind addDataKindEvent(Context context) {
+ DataKind kind = addKind(
+ new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, -1, 150, true));
+ kind.actionHeader = new EventActionInflater();
+ kind.actionBody = new SimpleInflater(Event.START_DATE);
+
+ kind.isList = false;
+
+ kind.typeColumn = Event.TYPE;
+ kind.typeList = Lists.newArrayList();
+ kind.typeList.add(buildEventType(Event.TYPE_BIRTHDAY, false).setSpecificMax(1));
+
+ kind.dateFormatWithYear = DateUtils.DATE_AND_TIME_FORMAT;
+ kind.editorClass = EventFieldEditorView.class;
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Event.DATA, R.string.eventLabelsGroup, FLAGS_EVENT));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindWebsite(Context context) {
+ final DataKind kind = super.addDataKindWebsite(context);
+
+ kind.isList = false;
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Website.URL, R.string.websiteLabelsGroup, FLAGS_WEBSITE));
+
+ return kind;
+ }
+
+ @Override
+ public int getHeaderColor(Context context) {
+ return 0xffd5ba96;
+ }
+
+ @Override
+ public int getSideBarColor(Context context) {
+ return 0xffb58e59;
+ }
+}
diff --git a/src/com/android/contacts/model/ExchangeSource.java b/src/com/android/contacts/model/ExchangeSource.java
deleted file mode 100644
index 3f2ab6c..0000000
--- a/src/com/android/contacts/model/ExchangeSource.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * 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.model;
-
-import com.android.contacts.R;
-import com.google.android.collect.Lists;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Nickname;
-import android.provider.ContactsContract.CommonDataKinds.Note;
-import android.provider.ContactsContract.CommonDataKinds.Organization;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Website;
-
-import java.util.Locale;
-
-public class ExchangeSource extends FallbackSource {
-
- public static final String ACCOUNT_TYPE = "com.android.exchange";
-
- public ExchangeSource(String resPackageName) {
- this.accountType = ACCOUNT_TYPE;
- this.resPackageName = null;
- this.summaryResPackageName = resPackageName;
- }
-
- @Override
- protected void inflate(Context context, int inflateLevel) {
-
- inflateStructuredName(context, inflateLevel);
- inflateNickname(context, inflateLevel);
- inflatePhone(context, inflateLevel);
- inflateEmail(context, inflateLevel);
- inflateStructuredPostal(context, inflateLevel);
- inflateIm(context, inflateLevel);
- inflateOrganization(context, inflateLevel);
- inflatePhoto(context, inflateLevel);
- inflateNote(context, inflateLevel);
- inflateWebsite(context, inflateLevel);
-
- setInflatedLevel(inflateLevel);
- }
-
- @Override
- protected DataKind inflateStructuredName(Context context, int inflateLevel) {
- final DataKind kind = super.inflateStructuredName(context, ContactsSource.LEVEL_MIMETYPES);
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- boolean displayOrderPrimary =
- context.getResources().getBoolean(R.bool.config_editor_field_order_primary);
- kind.typeOverallMax = 1;
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(StructuredName.PREFIX, R.string.name_prefix,
- FLAGS_PERSON_NAME).setOptional(true));
- if (!displayOrderPrimary) {
- kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME,
- R.string.name_family, FLAGS_PERSON_NAME));
- kind.fieldList.add(new EditField(StructuredName.MIDDLE_NAME,
- R.string.name_middle, FLAGS_PERSON_NAME).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME,
- R.string.name_given, FLAGS_PERSON_NAME));
- kind.fieldList.add(new EditField(StructuredName.SUFFIX,
- R.string.name_suffix, FLAGS_PERSON_NAME).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
- R.string.name_phonetic_family, FLAGS_PHONETIC).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
- R.string.name_phonetic_given, FLAGS_PHONETIC).setOptional(true));
- } else {
- kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME,
- R.string.name_given, FLAGS_PERSON_NAME));
- kind.fieldList.add(new EditField(StructuredName.MIDDLE_NAME,
- R.string.name_middle, FLAGS_PERSON_NAME).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME,
- R.string.name_family, FLAGS_PERSON_NAME));
- kind.fieldList.add(new EditField(StructuredName.SUFFIX,
- R.string.name_suffix, FLAGS_PERSON_NAME).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
- R.string.name_phonetic_given, FLAGS_PHONETIC).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
- R.string.name_phonetic_family, FLAGS_PHONETIC).setOptional(true));
- }
- }
-
- return kind;
- }
-
- @Override
- protected DataKind inflateNickname(Context context, int inflateLevel) {
- final DataKind kind = super.inflateNickname(context, ContactsSource.LEVEL_MIMETYPES);
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.isList = false;
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Nickname.NAME, R.string.nicknameLabelsGroup,
- FLAGS_PERSON_NAME));
- }
-
- return kind;
- }
-
- @Override
- protected DataKind inflatePhone(Context context, int inflateLevel) {
- final DataKind kind = super.inflatePhone(context, ContactsSource.LEVEL_MIMETYPES);
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.typeColumn = Phone.TYPE;
- kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildPhoneType(Phone.TYPE_HOME).setSpecificMax(2));
- kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE).setSpecificMax(1));
- kind.typeList.add(buildPhoneType(Phone.TYPE_WORK).setSpecificMax(2));
- kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true)
- .setSpecificMax(1));
- kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true)
- .setSpecificMax(1));
- kind.typeList
- .add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true).setSpecificMax(1));
- kind.typeList.add(buildPhoneType(Phone.TYPE_CAR).setSecondary(true).setSpecificMax(1));
- kind.typeList.add(buildPhoneType(Phone.TYPE_COMPANY_MAIN).setSecondary(true)
- .setSpecificMax(1));
- kind.typeList.add(buildPhoneType(Phone.TYPE_MMS).setSecondary(true).setSpecificMax(1));
- kind.typeList
- .add(buildPhoneType(Phone.TYPE_RADIO).setSecondary(true).setSpecificMax(1));
- kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true)
- .setSpecificMax(1).setCustomColumn(Phone.LABEL));
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
- }
-
- return kind;
- }
-
- @Override
- protected DataKind inflateEmail(Context context, int inflateLevel) {
- final DataKind kind = super.inflateEmail(context, ContactsSource.LEVEL_MIMETYPES);
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.typeOverallMax = 3;
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
- }
-
- return kind;
- }
-
- @Override
- protected DataKind inflateStructuredPostal(Context context, int inflateLevel) {
- final DataKind kind = super.inflateStructuredPostal(context, ContactsSource.LEVEL_MIMETYPES);
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- final boolean useJapaneseOrder =
- Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage());
- kind.typeColumn = StructuredPostal.TYPE;
- kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildPostalType(StructuredPostal.TYPE_WORK).setSpecificMax(1));
- kind.typeList.add(buildPostalType(StructuredPostal.TYPE_HOME).setSpecificMax(1));
- kind.typeList.add(buildPostalType(StructuredPostal.TYPE_OTHER).setSpecificMax(1));
-
- kind.fieldList = Lists.newArrayList();
- if (useJapaneseOrder) {
- kind.fieldList.add(new EditField(StructuredPostal.COUNTRY,
- R.string.postal_country, FLAGS_POSTAL).setOptional(true));
- kind.fieldList.add(new EditField(StructuredPostal.POSTCODE,
- R.string.postal_postcode, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.REGION,
- R.string.postal_region, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.CITY,
- R.string.postal_city,FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.STREET,
- R.string.postal_street, FLAGS_POSTAL));
- } else {
- kind.fieldList.add(new EditField(StructuredPostal.STREET,
- R.string.postal_street, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.CITY,
- R.string.postal_city,FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.REGION,
- R.string.postal_region, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.POSTCODE,
- R.string.postal_postcode, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.COUNTRY,
- R.string.postal_country, FLAGS_POSTAL).setOptional(true));
- }
- }
-
- return kind;
- }
-
- @Override
- protected DataKind inflateIm(Context context, int inflateLevel) {
- final DataKind kind = super.inflateIm(context, ContactsSource.LEVEL_MIMETYPES);
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.typeOverallMax = 3;
-
- // NOTE: even though a traditional "type" exists, for editing
- // purposes we're using the protocol to pick labels
-
- kind.defaultValues = new ContentValues();
- kind.defaultValues.put(Im.TYPE, Im.TYPE_OTHER);
-
- kind.typeColumn = Im.PROTOCOL;
- kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildImType(Im.PROTOCOL_AIM));
- kind.typeList.add(buildImType(Im.PROTOCOL_MSN));
- kind.typeList.add(buildImType(Im.PROTOCOL_YAHOO));
- kind.typeList.add(buildImType(Im.PROTOCOL_SKYPE));
- kind.typeList.add(buildImType(Im.PROTOCOL_QQ));
- kind.typeList.add(buildImType(Im.PROTOCOL_GOOGLE_TALK));
- kind.typeList.add(buildImType(Im.PROTOCOL_ICQ));
- kind.typeList.add(buildImType(Im.PROTOCOL_JABBER));
- kind.typeList.add(buildImType(Im.PROTOCOL_CUSTOM).setSecondary(true).setCustomColumn(
- Im.CUSTOM_PROTOCOL));
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL));
- }
-
- return kind;
- }
-
- @Override
- protected DataKind inflateOrganization(Context context, int inflateLevel) {
- final DataKind kind = super.inflateOrganization(context, ContactsSource.LEVEL_MIMETYPES);
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.isList = false;
- kind.typeColumn = Organization.TYPE;
- kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildOrgType(Organization.TYPE_WORK).setSpecificMax(1));
- kind.typeList.add(buildOrgType(Organization.TYPE_OTHER).setSpecificMax(1));
- kind.typeList.add(buildOrgType(Organization.TYPE_CUSTOM).setSecondary(true)
- .setSpecificMax(1));
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Organization.COMPANY, R.string.ghostData_company,
- FLAGS_GENERIC_NAME));
- kind.fieldList.add(new EditField(Organization.TITLE, R.string.ghostData_title,
- FLAGS_GENERIC_NAME));
- }
-
- return kind;
- }
-
- @Override
- protected DataKind inflatePhoto(Context context, int inflateLevel) {
- final DataKind kind = super.inflatePhoto(context, ContactsSource.LEVEL_MIMETYPES);
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.typeOverallMax = 1;
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
- }
-
- return kind;
- }
-
- @Override
- protected DataKind inflateNote(Context context, int inflateLevel) {
- final DataKind kind = super.inflateNote(context, ContactsSource.LEVEL_MIMETYPES);
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Note.NOTE, R.string.label_notes, FLAGS_NOTE));
- }
-
- return kind;
- }
-
- @Override
- protected DataKind inflateWebsite(Context context, int inflateLevel) {
- final DataKind kind = super.inflateWebsite(context, ContactsSource.LEVEL_MIMETYPES);
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.isList = false;
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Website.URL, R.string.websiteLabelsGroup, FLAGS_WEBSITE));
- }
-
- return kind;
- }
-
- @Override
- public int getHeaderColor(Context context) {
- return 0xffd5ba96;
- }
-
- @Override
- public int getSideBarColor(Context context) {
- return 0xffb58e59;
- }
-}
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
new file mode 100644
index 0000000..0b895f6
--- /dev/null
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -0,0 +1,190 @@
+/*
+ * 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.model;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * A general contacts account type descriptor.
+ */
+public class ExternalAccountType extends BaseAccountType {
+ private static final String TAG = "ExternalAccountType";
+
+ private static final String ACTION_SYNC_ADAPTER = "android.content.SyncAdapter";
+ private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
+
+ private static final String TAG_CONTACTS_SOURCE_LEGACY = "ContactsSource";
+ private static final String TAG_CONTACTS_ACCOUNT_TYPE = "ContactsAccountType";
+ private static final String TAG_CONTACTS_DATA_KIND = "ContactsDataKind";
+
+ private static final String ATTR_EDIT_CONTACT_ACTIVITY = "editContactActivity";
+ private static final String ATTR_CREATE_CONTACT_ACTIVITY = "createContactActivity";
+
+ private String mEditContactActivityClassName;
+ private String mCreateContactActivityClassName;
+
+ public ExternalAccountType(Context context, String resPackageName) {
+ this.resPackageName = resPackageName;
+ this.summaryResPackageName = resPackageName;
+
+ // Handle unknown sources by searching their package
+ final PackageManager pm = context.getPackageManager();
+ final Intent syncAdapter = new Intent(ACTION_SYNC_ADAPTER);
+ final List<ResolveInfo> matches = pm.queryIntentServices(syncAdapter,
+ PackageManager.GET_META_DATA);
+ for (ResolveInfo info : matches) {
+ ServiceInfo serviceInfo = info.serviceInfo;
+ if (serviceInfo.packageName.equals(resPackageName)) {
+ final XmlResourceParser parser = serviceInfo.loadXmlMetaData(pm,
+ METADATA_CONTACTS);
+ if (parser == null) continue;
+ inflate(context, parser);
+ }
+ }
+
+ // Bring in name and photo from fallback source, which are non-optional
+ addDataKindStructuredName(context);
+ addDataKindPhoto(context);
+ }
+
+ @Override
+ public boolean isExternal() {
+ return true;
+ }
+
+ @Override
+ public String getEditContactActivityClassName() {
+ return mEditContactActivityClassName;
+ }
+
+ @Override
+ public String getCreateContactActivityClassName() {
+ return mCreateContactActivityClassName;
+ }
+
+ /**
+ * Inflate this {@link AccountType} from the given parser. This may only
+ * load details matching the publicly-defined schema.
+ */
+ protected void inflate(Context context, XmlPullParser parser) {
+ final AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ try {
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ // Drain comments and whitespace
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ throw new IllegalStateException("No start tag found");
+ }
+
+ String rootTag = parser.getName();
+ if (!TAG_CONTACTS_ACCOUNT_TYPE.equals(rootTag) &&
+ !TAG_CONTACTS_SOURCE_LEGACY.equals(rootTag)) {
+ throw new IllegalStateException("Top level element must be "
+ + TAG_CONTACTS_ACCOUNT_TYPE + ", not " + rootTag);
+ }
+
+ int attributeCount = parser.getAttributeCount();
+ for (int i = 0; i < attributeCount; i++) {
+ String attr = parser.getAttributeName(i);
+ if (ATTR_EDIT_CONTACT_ACTIVITY.equals(attr)) {
+ mEditContactActivityClassName = parser.getAttributeValue(i);
+ } else if (ATTR_CREATE_CONTACT_ACTIVITY.equals(attr)) {
+ mCreateContactActivityClassName = parser.getAttributeValue(i);
+ } else {
+ Log.e(TAG, "Unsupported attribute " + attr);
+ }
+ }
+
+ // Parse all children kinds
+ final int depth = parser.getDepth();
+ while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+ String tag = parser.getName();
+ if (type == XmlPullParser.END_TAG || !TAG_CONTACTS_DATA_KIND.equals(tag)) {
+ continue;
+ }
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ android.R.styleable.ContactsDataKind);
+ final DataKind kind = new DataKind();
+
+ kind.mimeType = a
+ .getString(com.android.internal.R.styleable.ContactsDataKind_mimeType);
+ kind.iconRes = a.getResourceId(
+ com.android.internal.R.styleable.ContactsDataKind_icon, -1);
+
+ final String summaryColumn = a
+ .getString(com.android.internal.R.styleable.ContactsDataKind_summaryColumn);
+ if (summaryColumn != null) {
+ // Inflate a specific column as summary when requested
+ kind.actionHeader = new FallbackAccountType.SimpleInflater(summaryColumn);
+ }
+
+ final String detailColumn = a
+ .getString(com.android.internal.R.styleable.ContactsDataKind_detailColumn);
+ final boolean detailSocialSummary = a.getBoolean(
+ com.android.internal.R.styleable.ContactsDataKind_detailSocialSummary,
+ false);
+
+ if (detailSocialSummary) {
+ // Inflate social summary when requested
+ kind.actionBodySocial = true;
+ }
+
+ if (detailColumn != null) {
+ // Inflate specific column as summary
+ kind.actionBody = new FallbackAccountType.SimpleInflater(detailColumn);
+ }
+
+ addKind(kind);
+ }
+ } catch (XmlPullParserException e) {
+ throw new IllegalStateException("Problem reading XML", e);
+ } catch (IOException e) {
+ throw new IllegalStateException("Problem reading XML", e);
+ }
+ }
+
+ @Override
+ public int getHeaderColor(Context context) {
+ return 0xff6d86b4;
+ }
+
+ @Override
+ public int getSideBarColor(Context context) {
+ return 0xff6d86b4;
+ }
+}
diff --git a/src/com/android/contacts/model/ExternalSource.java b/src/com/android/contacts/model/ExternalSource.java
deleted file mode 100644
index aa9d888..0000000
--- a/src/com/android/contacts/model/ExternalSource.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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.model;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.util.AttributeSet;
-import android.util.Xml;
-
-import java.io.IOException;
-import java.util.List;
-
-/*
-
-<!-- example of what SourceConstraints would look like in XML -->
-<!-- NOTE: may not directly match the current structure version -->
-
-<DataKind
- mimeType="vnd.android.cursor.item/email"
- title="@string/title_postal"
- icon="@drawable/icon_postal"
- weight="12"
- editable="true">
-
- <!-- these are defined using string-builder-ish -->
- <ActionHeader></ActionHeader>
- <ActionBody socialSummary="true" /> <!-- can pull together various columns -->
-
- <!-- ordering handles precedence the "insert/add" case -->
- <!-- assume uniform type when missing "column", use title in place -->
- <EditTypes column="data5" overallMax="-1">
- <EditType rawValue="0" label="@string/type_home" specificMax="-1" />
- <EditType rawValue="1" label="@string/type_work" specificMax="-1" secondary="true" />
- <EditType rawValue="4" label="@string/type_custom" customColumn="data6" specificMax="-1" secondary="true" />
- </EditTypes>
-
- <!-- when single edit field, simplifies edit case -->
- <EditField column="data1" title="@string/field_family_name" android:inputType="textCapWords|textPhonetic" />
- <EditField column="data2" title="@string/field_given_name" android:minLines="2" />
- <EditField column="data3" title="@string/field_suffix" />
-
-</DataKind>
-
-*/
-
-/**
- * Internal structure that represents constraints and styles for a specific data
- * source, such as the various data types they support, including details on how
- * those types should be rendered and edited.
- * <p>
- * In the future this may be inflated from XML defined by a data source.
- */
-public class ExternalSource extends FallbackSource {
- private static final String ACTION_SYNC_ADAPTER = "android.content.SyncAdapter";
- private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
-
- private interface InflateTags {
- final String CONTACTS_SOURCE = "ContactsSource";
- final String CONTACTS_DATA_KIND = "ContactsDataKind";
- }
-
- public ExternalSource(String resPackageName) {
- this.resPackageName = resPackageName;
- this.summaryResPackageName = resPackageName;
- }
-
- /**
- * Ensure that the constraint rules behind this {@link ContactsSource} have
- * been inflated. Because this may involve parsing meta-data from
- * {@link PackageManager}, it shouldn't be called from a UI thread.
- */
- @Override
- public void inflate(Context context, int inflateLevel) {
- // Handle unknown sources by searching their package
- final PackageManager pm = context.getPackageManager();
- final Intent syncAdapter = new Intent(ACTION_SYNC_ADAPTER);
- final List<ResolveInfo> matches = pm.queryIntentServices(syncAdapter,
- PackageManager.GET_META_DATA);
- for (ResolveInfo info : matches) {
- final XmlResourceParser parser = info.serviceInfo.loadXmlMetaData(pm,
- METADATA_CONTACTS);
- if (parser == null) continue;
- inflate(context, parser);
- }
-
- // Bring in name and photo from fallback source, which are non-optional
- inflateStructuredName(context, inflateLevel);
- inflatePhoto(context, inflateLevel);
-
- setInflatedLevel(inflateLevel);
- }
-
- /**
- * Inflate this {@link ContactsSource} from the given parser. This may only
- * load details matching the publicly-defined schema.
- */
- protected void inflate(Context context, XmlPullParser parser) {
- final AttributeSet attrs = Xml.asAttributeSet(parser);
-
- try {
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- // Drain comments and whitespace
- }
-
- if (type != XmlPullParser.START_TAG) {
- throw new IllegalStateException("No start tag found");
- }
-
- if (!InflateTags.CONTACTS_SOURCE.equals(parser.getName())) {
- throw new IllegalStateException("Top level element must be "
- + InflateTags.CONTACTS_SOURCE);
- }
-
- // Parse all children kinds
- final int depth = parser.getDepth();
- while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
- && type != XmlPullParser.END_DOCUMENT) {
- if (type == XmlPullParser.END_TAG
- || !InflateTags.CONTACTS_DATA_KIND.equals(parser.getName())) {
- continue;
- }
-
- final TypedArray a = context.obtainStyledAttributes(attrs,
- android.R.styleable.ContactsDataKind);
- final DataKind kind = new DataKind();
-
- kind.mimeType = a
- .getString(com.android.internal.R.styleable.ContactsDataKind_mimeType);
- kind.iconRes = a.getResourceId(
- com.android.internal.R.styleable.ContactsDataKind_icon, -1);
-
- final String summaryColumn = a
- .getString(com.android.internal.R.styleable.ContactsDataKind_summaryColumn);
- if (summaryColumn != null) {
- // Inflate a specific column as summary when requested
- kind.actionHeader = new FallbackSource.SimpleInflater(summaryColumn);
- }
-
- final String detailColumn = a
- .getString(com.android.internal.R.styleable.ContactsDataKind_detailColumn);
- final boolean detailSocialSummary = a.getBoolean(
- com.android.internal.R.styleable.ContactsDataKind_detailSocialSummary,
- false);
-
- if (detailSocialSummary) {
- // Inflate social summary when requested
- kind.actionBodySocial = true;
- }
-
- if (detailColumn != null) {
- // Inflate specific column as summary
- kind.actionBody = new FallbackSource.SimpleInflater(detailColumn);
- }
-
- addKind(kind);
- }
- } catch (XmlPullParserException e) {
- throw new IllegalStateException("Problem reading XML", e);
- } catch (IOException e) {
- throw new IllegalStateException("Problem reading XML", e);
- }
- }
-
- @Override
- public int getHeaderColor(Context context) {
- return 0xff6d86b4;
- }
-
- @Override
- public int getSideBarColor(Context context) {
- return 0xff6d86b4;
- }
-}
diff --git a/src/com/android/contacts/model/FallbackAccountType.java b/src/com/android/contacts/model/FallbackAccountType.java
new file mode 100644
index 0000000..05e6556
--- /dev/null
+++ b/src/com/android/contacts/model/FallbackAccountType.java
@@ -0,0 +1,55 @@
+/*
+ * 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.model;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+
+public class FallbackAccountType extends BaseAccountType {
+
+ public FallbackAccountType(Context context) {
+ this.accountType = null;
+ this.titleRes = R.string.account_phone;
+ this.iconRes = R.mipmap.ic_launcher_contacts;
+
+ this.resPackageName = null;
+ this.summaryResPackageName = resPackageName;
+
+ addDataKindStructuredName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindWebsite(context);
+ addDataKindSipAddress(context);
+ }
+
+ @Override
+ public int getHeaderColor(Context context) {
+ return 0xff7f93bc;
+ }
+
+ @Override
+ public int getSideBarColor(Context context) {
+ return 0xffbdc7b8;
+ }
+}
diff --git a/src/com/android/contacts/model/FallbackSource.java b/src/com/android/contacts/model/FallbackSource.java
deleted file mode 100644
index f36752a..0000000
--- a/src/com/android/contacts/model/FallbackSource.java
+++ /dev/null
@@ -1,713 +0,0 @@
-/*
- * 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.model;
-
-import com.android.contacts.R;
-import com.google.android.collect.Lists;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.provider.ContactsContract.CommonDataKinds.BaseTypes;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Event;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Nickname;
-import android.provider.ContactsContract.CommonDataKinds.Note;
-import android.provider.ContactsContract.CommonDataKinds.Organization;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.SipAddress;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.view.inputmethod.EditorInfo;
-
-import java.util.Locale;
-
-public class FallbackSource extends ContactsSource {
- protected static final int FLAGS_PHONE = EditorInfo.TYPE_CLASS_PHONE;
- protected static final int FLAGS_EMAIL = EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
- protected static final int FLAGS_PERSON_NAME = EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS | EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME;
- protected static final int FLAGS_PHONETIC = EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_VARIATION_PHONETIC;
- protected static final int FLAGS_GENERIC_NAME = EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
- protected static final int FLAGS_NOTE = EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
- protected static final int FLAGS_WEBSITE = EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_VARIATION_URI;
- protected static final int FLAGS_POSTAL = EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_VARIATION_POSTAL_ADDRESS | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS
- | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
- protected static final int FLAGS_SIP_ADDRESS = EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; // since SIP addresses have the same
- // basic format as email addresses
-
- public FallbackSource() {
- this.accountType = null;
- this.titleRes = R.string.account_phone;
- this.iconRes = R.drawable.ic_launcher_contacts;
- }
-
- @Override
- protected void inflate(Context context, int inflateLevel) {
-
- inflateStructuredName(context, inflateLevel);
- inflateNickname(context, inflateLevel);
- inflatePhone(context, inflateLevel);
- inflateEmail(context, inflateLevel);
- inflateStructuredPostal(context, inflateLevel);
- inflateIm(context, inflateLevel);
- inflateOrganization(context, inflateLevel);
- inflatePhoto(context, inflateLevel);
- inflateNote(context, inflateLevel);
- inflateWebsite(context, inflateLevel);
- inflateEvent(context, inflateLevel);
- inflateSipAddress(context, inflateLevel);
-
- setInflatedLevel(inflateLevel);
-
- }
-
- protected EditType buildPhoneType(int type) {
- return new EditType(type, Phone.getTypeLabelResource(type));
- }
-
- protected EditType buildEmailType(int type) {
- return new EditType(type, Email.getTypeLabelResource(type));
- }
-
- protected EditType buildPostalType(int type) {
- return new EditType(type, StructuredPostal.getTypeLabelResource(type));
- }
-
- protected EditType buildImType(int type) {
- return new EditType(type, Im.getProtocolLabelResource(type));
- }
-
- protected EditType buildOrgType(int type) {
- return new EditType(type, Organization.getTypeLabelResource(type));
- }
-
- protected DataKind inflateStructuredName(Context context, int inflateLevel) {
- DataKind kind = getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
- if (kind == null) {
- kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
- R.string.nameLabelsGroup, -1, -1, true));
- }
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- boolean displayOrderPrimary =
- context.getResources().getBoolean(R.bool.config_editor_field_order_primary);
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(StructuredName.PREFIX, R.string.name_prefix,
- FLAGS_PERSON_NAME).setOptional(true));
- if (!displayOrderPrimary) {
- kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family,
- FLAGS_PERSON_NAME));
- kind.fieldList.add(new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle,
- FLAGS_PERSON_NAME).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given,
- FLAGS_PERSON_NAME));
- kind.fieldList.add(new EditField(StructuredName.SUFFIX, R.string.name_suffix,
- FLAGS_PERSON_NAME).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
- R.string.name_phonetic_family, FLAGS_PHONETIC).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
- R.string.name_phonetic_middle, FLAGS_PHONETIC).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
- R.string.name_phonetic_given, FLAGS_PHONETIC).setOptional(true));
- } else {
- kind.fieldList.add(new EditField(StructuredName.GIVEN_NAME, R.string.name_given,
- FLAGS_PERSON_NAME));
- kind.fieldList.add(new EditField(StructuredName.MIDDLE_NAME, R.string.name_middle,
- FLAGS_PERSON_NAME).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.FAMILY_NAME, R.string.name_family,
- FLAGS_PERSON_NAME));
- kind.fieldList.add(new EditField(StructuredName.SUFFIX, R.string.name_suffix,
- FLAGS_PERSON_NAME).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
- R.string.name_phonetic_given, FLAGS_PHONETIC).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
- R.string.name_phonetic_middle, FLAGS_PHONETIC).setOptional(true));
- kind.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
- R.string.name_phonetic_family, FLAGS_PHONETIC).setOptional(true));
- }
- }
-
- return kind;
- }
-
- protected DataKind inflateNickname(Context context, int inflateLevel) {
- DataKind kind = getKindForMimetype(Nickname.CONTENT_ITEM_TYPE);
- if (kind == null) {
- kind = addKind(new DataKind(Nickname.CONTENT_ITEM_TYPE,
- R.string.nicknameLabelsGroup, -1, 115, true));
- kind.secondary = true;
- kind.isList = false;
- kind.actionHeader = new SimpleInflater(R.string.nicknameLabelsGroup);
- kind.actionBody = new SimpleInflater(Nickname.NAME);
- }
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.defaultValues = new ContentValues();
- kind.defaultValues.put(Nickname.TYPE, Nickname.TYPE_DEFAULT);
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Nickname.NAME, R.string.nicknameLabelsGroup,
- FLAGS_PERSON_NAME));
- }
-
- return kind;
- }
-
- protected DataKind inflatePhone(Context context, int inflateLevel) {
- DataKind kind = getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
- if (kind == null) {
- kind = addKind(new DataKind(Phone.CONTENT_ITEM_TYPE, R.string.phoneLabelsGroup,
- android.R.drawable.sym_action_call, 10, true));
- kind.iconAltRes = R.drawable.sym_action_sms;
- kind.actionHeader = new PhoneActionInflater();
- kind.actionAltHeader = new PhoneActionAltInflater();
- kind.actionBody = new SimpleInflater(Phone.NUMBER);
- }
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.typeColumn = Phone.TYPE;
- kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
- kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE));
- kind.typeList.add(buildPhoneType(Phone.TYPE_WORK));
- kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER));
- kind.typeList.add(buildPhoneType(Phone.TYPE_CUSTOM).setSecondary(true).setCustomColumn(
- Phone.LABEL));
- kind.typeList.add(buildPhoneType(Phone.TYPE_CALLBACK).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_CAR).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_COMPANY_MAIN).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_ISDN).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_MAIN).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER_FAX).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_RADIO).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_TELEX).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_TTY_TDD).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_MOBILE).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_PAGER).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true)
- .setCustomColumn(Phone.LABEL));
- kind.typeList.add(buildPhoneType(Phone.TYPE_MMS).setSecondary(true));
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
- }
-
- return kind;
- }
-
- protected DataKind inflateEmail(Context context, int inflateLevel) {
- DataKind kind = getKindForMimetype(Email.CONTENT_ITEM_TYPE);
- if (kind == null) {
- kind = addKind(new DataKind(Email.CONTENT_ITEM_TYPE,
- R.string.emailLabelsGroup, android.R.drawable.sym_action_email, 15, true));
- kind.actionHeader = new EmailActionInflater();
- kind.actionBody = new SimpleInflater(Email.DATA);
- }
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.typeColumn = Email.TYPE;
- kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildEmailType(Email.TYPE_HOME));
- kind.typeList.add(buildEmailType(Email.TYPE_WORK));
- kind.typeList.add(buildEmailType(Email.TYPE_OTHER));
- kind.typeList.add(buildEmailType(Email.TYPE_MOBILE));
- kind.typeList.add(buildEmailType(Email.TYPE_CUSTOM).setSecondary(true).setCustomColumn(
- Email.LABEL));
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
- }
-
- return kind;
- }
-
- protected DataKind inflateStructuredPostal(Context context, int inflateLevel) {
- DataKind kind = getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
- if (kind == null) {
- kind = addKind(new DataKind(StructuredPostal.CONTENT_ITEM_TYPE,
- R.string.postalLabelsGroup, R.drawable.sym_action_map, 25, true));
- kind.actionHeader = new PostalActionInflater();
- kind.actionBody = new SimpleInflater(StructuredPostal.FORMATTED_ADDRESS);
- }
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- final boolean useJapaneseOrder =
- Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage());
- kind.typeColumn = StructuredPostal.TYPE;
- kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildPostalType(StructuredPostal.TYPE_HOME));
- kind.typeList.add(buildPostalType(StructuredPostal.TYPE_WORK));
- kind.typeList.add(buildPostalType(StructuredPostal.TYPE_OTHER));
- kind.typeList.add(buildPostalType(StructuredPostal.TYPE_CUSTOM).setSecondary(true)
- .setCustomColumn(StructuredPostal.LABEL));
-
- kind.fieldList = Lists.newArrayList();
-
- if (useJapaneseOrder) {
- kind.fieldList.add(new EditField(StructuredPostal.COUNTRY,
- R.string.postal_country, FLAGS_POSTAL).setOptional(true));
- kind.fieldList.add(new EditField(StructuredPostal.POSTCODE,
- R.string.postal_postcode, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.REGION,
- R.string.postal_region, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.CITY,
- R.string.postal_city, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.NEIGHBORHOOD,
- R.string.postal_neighborhood, FLAGS_POSTAL).setOptional(true));
- kind.fieldList.add(new EditField(StructuredPostal.STREET,
- R.string.postal_street, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.POBOX,
- R.string.postal_pobox, FLAGS_POSTAL).setOptional(true));
- } else {
- kind.fieldList.add(new EditField(StructuredPostal.STREET,
- R.string.postal_street, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.POBOX,
- R.string.postal_pobox, FLAGS_POSTAL).setOptional(true));
- kind.fieldList.add(new EditField(StructuredPostal.NEIGHBORHOOD,
- R.string.postal_neighborhood, FLAGS_POSTAL).setOptional(true));
- kind.fieldList.add(new EditField(StructuredPostal.CITY,
- R.string.postal_city, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.REGION,
- R.string.postal_region, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.POSTCODE,
- R.string.postal_postcode, FLAGS_POSTAL));
- kind.fieldList.add(new EditField(StructuredPostal.COUNTRY,
- R.string.postal_country, FLAGS_POSTAL).setOptional(true));
- }
- }
-
- return kind;
- }
-
- protected DataKind inflateIm(Context context, int inflateLevel) {
- DataKind kind = getKindForMimetype(Im.CONTENT_ITEM_TYPE);
- if (kind == null) {
- kind = addKind(new DataKind(Im.CONTENT_ITEM_TYPE, R.string.imLabelsGroup,
- android.R.drawable.sym_action_chat, 20, true));
- kind.secondary = true;
- kind.actionHeader = new ImActionInflater();
- kind.actionBody = new SimpleInflater(Im.DATA);
- }
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- // NOTE: even though a traditional "type" exists, for editing
- // purposes we're using the protocol to pick labels
-
- kind.defaultValues = new ContentValues();
- kind.defaultValues.put(Im.TYPE, Im.TYPE_OTHER);
-
- kind.typeColumn = Im.PROTOCOL;
- kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildImType(Im.PROTOCOL_AIM));
- kind.typeList.add(buildImType(Im.PROTOCOL_MSN));
- kind.typeList.add(buildImType(Im.PROTOCOL_YAHOO));
- kind.typeList.add(buildImType(Im.PROTOCOL_SKYPE));
- kind.typeList.add(buildImType(Im.PROTOCOL_QQ));
- kind.typeList.add(buildImType(Im.PROTOCOL_GOOGLE_TALK));
- kind.typeList.add(buildImType(Im.PROTOCOL_ICQ));
- kind.typeList.add(buildImType(Im.PROTOCOL_JABBER));
- kind.typeList.add(buildImType(Im.PROTOCOL_CUSTOM).setSecondary(true).setCustomColumn(
- Im.CUSTOM_PROTOCOL));
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Im.DATA, R.string.imLabelsGroup, FLAGS_EMAIL));
- }
-
- return kind;
- }
-
- protected DataKind inflateOrganization(Context context, int inflateLevel) {
- DataKind kind = getKindForMimetype(Organization.CONTENT_ITEM_TYPE);
- if (kind == null) {
- kind = addKind(new DataKind(Organization.CONTENT_ITEM_TYPE,
- R.string.organizationLabelsGroup, R.drawable.sym_action_organization, 30, true));
- kind.actionHeader = new SimpleInflater(Organization.COMPANY);
- kind.actionBody = new SimpleInflater(Organization.TITLE);
- }
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.typeColumn = Organization.TYPE;
- kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildOrgType(Organization.TYPE_WORK));
- kind.typeList.add(buildOrgType(Organization.TYPE_OTHER));
- kind.typeList.add(buildOrgType(Organization.TYPE_CUSTOM).setSecondary(true)
- .setCustomColumn(Organization.LABEL));
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Organization.COMPANY, R.string.ghostData_company,
- FLAGS_GENERIC_NAME));
- kind.fieldList.add(new EditField(Organization.TITLE, R.string.ghostData_title,
- FLAGS_GENERIC_NAME));
- }
-
- return kind;
- }
-
- protected DataKind inflatePhoto(Context context, int inflateLevel) {
- DataKind kind = getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
- if (kind == null) {
- kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, -1, -1, true));
- }
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
- }
-
- return kind;
- }
-
- protected DataKind inflateNote(Context context, int inflateLevel) {
- DataKind kind = getKindForMimetype(Note.CONTENT_ITEM_TYPE);
- if (kind == null) {
- kind = addKind(new DataKind(Note.CONTENT_ITEM_TYPE,
- R.string.label_notes, R.drawable.sym_note, 110, true));
- kind.isList = false;
- kind.secondary = true;
- kind.actionHeader = new SimpleInflater(R.string.label_notes);
- kind.actionBody = new SimpleInflater(Note.NOTE);
- }
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Note.NOTE, R.string.label_notes, FLAGS_NOTE));
- }
-
- return kind;
- }
-
- protected DataKind inflateWebsite(Context context, int inflateLevel) {
- DataKind kind = getKindForMimetype(Website.CONTENT_ITEM_TYPE);
- if (kind == null) {
- kind = addKind(new DataKind(Website.CONTENT_ITEM_TYPE,
- R.string.websiteLabelsGroup, -1, 120, true));
- kind.secondary = true;
- kind.actionHeader = new SimpleInflater(R.string.websiteLabelsGroup);
- kind.actionBody = new SimpleInflater(Website.URL);
- }
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.defaultValues = new ContentValues();
- kind.defaultValues.put(Website.TYPE, Website.TYPE_OTHER);
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Website.URL, R.string.websiteLabelsGroup, FLAGS_WEBSITE));
- }
-
- return kind;
- }
-
- protected DataKind inflateEvent(Context context, int inflateLevel) {
- DataKind kind = getKindForMimetype(Event.CONTENT_ITEM_TYPE);
- if (kind == null) {
- kind = addKind(new DataKind(Event.CONTENT_ITEM_TYPE,
- R.string.eventLabelsGroup, -1, 150, false));
- kind.secondary = true;
- kind.actionHeader = new EventActionInflater();
- kind.actionBody = new SimpleInflater(Event.START_DATE);
- }
-
- return kind;
- }
-
- protected DataKind inflateSipAddress(Context context, int inflateLevel) {
- DataKind kind = getKindForMimetype(SipAddress.CONTENT_ITEM_TYPE);
- if (kind == null) {
- // The icon specified here is the one that gets displayed for
- // "Internet call" items, in the "view contact" UI within the
- // Contacts app.
- //
- // This is independent of the "SIP call" icon that gets
- // displayed in the Quick Contacts widget, which comes from
- // the android:icon attribute of the SIP-related
- // intent-filters in the Phone app's manifest.
-
- kind = addKind(new DataKind(SipAddress.CONTENT_ITEM_TYPE,
- R.string.label_sip_address, android.R.drawable.sym_action_call, 130, true));
-
- kind.isList = false;
- kind.secondary = true;
- kind.actionHeader = new SimpleInflater(R.string.label_sip_address);
- kind.actionBody = new SimpleInflater(SipAddress.SIP_ADDRESS);
- }
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(SipAddress.SIP_ADDRESS,
- R.string.label_sip_address, FLAGS_SIP_ADDRESS));
- }
-
- return kind;
- }
-
- /**
- * Simple inflater that assumes a string resource has a "%s" that will be
- * filled from the given column.
- */
- public static class SimpleInflater implements StringInflater {
- private final int mStringRes;
- private final String mColumnName;
-
- public SimpleInflater(int stringRes) {
- this(stringRes, null);
- }
-
- public SimpleInflater(String columnName) {
- this(-1, columnName);
- }
-
- public SimpleInflater(int stringRes, String columnName) {
- mStringRes = stringRes;
- mColumnName = columnName;
- }
-
- public CharSequence inflateUsing(Context context, Cursor cursor) {
- final int index = mColumnName != null ? cursor.getColumnIndex(mColumnName) : -1;
- final boolean validString = mStringRes > 0;
- final boolean validColumn = index != -1;
-
- final CharSequence stringValue = validString ? context.getText(mStringRes) : null;
- final CharSequence columnValue = validColumn ? cursor.getString(index) : null;
-
- if (validString && validColumn) {
- return String.format(stringValue.toString(), columnValue);
- } else if (validString) {
- return stringValue;
- } else if (validColumn) {
- return columnValue;
- } else {
- return null;
- }
- }
-
- public CharSequence inflateUsing(Context context, ContentValues values) {
- final boolean validColumn = values.containsKey(mColumnName);
- final boolean validString = mStringRes > 0;
-
- final CharSequence stringValue = validString ? context.getText(mStringRes) : null;
- final CharSequence columnValue = validColumn ? values.getAsString(mColumnName) : null;
-
- if (validString && validColumn) {
- return String.format(stringValue.toString(), columnValue);
- } else if (validString) {
- return stringValue;
- } else if (validColumn) {
- return columnValue;
- } else {
- return null;
- }
- }
- }
-
- public static abstract class CommonInflater implements StringInflater {
- protected abstract int getTypeLabelResource(Integer type);
-
- protected boolean isCustom(Integer type) {
- return type == BaseTypes.TYPE_CUSTOM;
- }
-
- protected String getTypeColumn() {
- return Phone.TYPE;
- }
-
- protected String getLabelColumn() {
- return Phone.LABEL;
- }
-
- protected CharSequence getTypeLabel(Resources res, Integer type, CharSequence label) {
- final int labelRes = getTypeLabelResource(type);
- if (type == null) {
- return res.getText(labelRes);
- } else if (isCustom(type)) {
- return res.getString(labelRes, label == null ? "" : label);
- } else {
- return res.getText(labelRes);
- }
- }
-
- public CharSequence inflateUsing(Context context, Cursor cursor) {
- final Integer type = cursor.getInt(cursor.getColumnIndex(getTypeColumn()));
- final String label = cursor.getString(cursor.getColumnIndex(getLabelColumn()));
- return getTypeLabel(context.getResources(), type, label);
- }
-
- public CharSequence inflateUsing(Context context, ContentValues values) {
- final Integer type = values.getAsInteger(getTypeColumn());
- final String label = values.getAsString(getLabelColumn());
- return getTypeLabel(context.getResources(), type, label);
- }
- }
-
- public static class PhoneActionInflater extends CommonInflater {
- @Override
- protected boolean isCustom(Integer type) {
- return type == Phone.TYPE_CUSTOM || type == Phone.TYPE_ASSISTANT;
- }
-
- @Override
- protected int getTypeLabelResource(Integer type) {
- if (type == null) return R.string.call_other;
- switch (type) {
- case Phone.TYPE_HOME: return R.string.call_home;
- case Phone.TYPE_MOBILE: return R.string.call_mobile;
- case Phone.TYPE_WORK: return R.string.call_work;
- case Phone.TYPE_FAX_WORK: return R.string.call_fax_work;
- case Phone.TYPE_FAX_HOME: return R.string.call_fax_home;
- case Phone.TYPE_PAGER: return R.string.call_pager;
- case Phone.TYPE_OTHER: return R.string.call_other;
- case Phone.TYPE_CALLBACK: return R.string.call_callback;
- case Phone.TYPE_CAR: return R.string.call_car;
- case Phone.TYPE_COMPANY_MAIN: return R.string.call_company_main;
- case Phone.TYPE_ISDN: return R.string.call_isdn;
- case Phone.TYPE_MAIN: return R.string.call_main;
- case Phone.TYPE_OTHER_FAX: return R.string.call_other_fax;
- case Phone.TYPE_RADIO: return R.string.call_radio;
- case Phone.TYPE_TELEX: return R.string.call_telex;
- case Phone.TYPE_TTY_TDD: return R.string.call_tty_tdd;
- case Phone.TYPE_WORK_MOBILE: return R.string.call_work_mobile;
- case Phone.TYPE_WORK_PAGER: return R.string.call_work_pager;
- case Phone.TYPE_ASSISTANT: return R.string.call_assistant;
- case Phone.TYPE_MMS: return R.string.call_mms;
- default: return R.string.call_custom;
- }
- }
- }
-
- public static class PhoneActionAltInflater extends CommonInflater {
- @Override
- protected boolean isCustom(Integer type) {
- return (type == Phone.TYPE_CUSTOM || type == Phone.TYPE_ASSISTANT);
- }
-
- @Override
- protected int getTypeLabelResource(Integer type) {
- if (type == null) return R.string.sms_other;
- switch (type) {
- case Phone.TYPE_HOME: return R.string.sms_home;
- case Phone.TYPE_MOBILE: return R.string.sms_mobile;
- case Phone.TYPE_WORK: return R.string.sms_work;
- case Phone.TYPE_FAX_WORK: return R.string.sms_fax_work;
- case Phone.TYPE_FAX_HOME: return R.string.sms_fax_home;
- case Phone.TYPE_PAGER: return R.string.sms_pager;
- case Phone.TYPE_OTHER: return R.string.sms_other;
- case Phone.TYPE_CALLBACK: return R.string.sms_callback;
- case Phone.TYPE_CAR: return R.string.sms_car;
- case Phone.TYPE_COMPANY_MAIN: return R.string.sms_company_main;
- case Phone.TYPE_ISDN: return R.string.sms_isdn;
- case Phone.TYPE_MAIN: return R.string.sms_main;
- case Phone.TYPE_OTHER_FAX: return R.string.sms_other_fax;
- case Phone.TYPE_RADIO: return R.string.sms_radio;
- case Phone.TYPE_TELEX: return R.string.sms_telex;
- case Phone.TYPE_TTY_TDD: return R.string.sms_tty_tdd;
- case Phone.TYPE_WORK_MOBILE: return R.string.sms_work_mobile;
- case Phone.TYPE_WORK_PAGER: return R.string.sms_work_pager;
- case Phone.TYPE_ASSISTANT: return R.string.sms_assistant;
- case Phone.TYPE_MMS: return R.string.sms_mms;
- default: return R.string.sms_custom;
- }
- }
- }
-
- public static class EmailActionInflater extends CommonInflater {
- @Override
- protected int getTypeLabelResource(Integer type) {
- if (type == null) return R.string.email;
- switch (type) {
- case Email.TYPE_HOME: return R.string.email_home;
- case Email.TYPE_WORK: return R.string.email_work;
- case Email.TYPE_OTHER: return R.string.email_other;
- case Email.TYPE_MOBILE: return R.string.email_mobile;
- default: return R.string.email_custom;
- }
- }
- }
-
- public static class EventActionInflater extends CommonInflater {
- @Override
- protected int getTypeLabelResource(Integer type) {
- return Event.getTypeResource(type);
- }
- }
-
- public static class PostalActionInflater extends CommonInflater {
- @Override
- protected int getTypeLabelResource(Integer type) {
- if (type == null) return R.string.map_other;
- switch (type) {
- case StructuredPostal.TYPE_HOME: return R.string.map_home;
- case StructuredPostal.TYPE_WORK: return R.string.map_work;
- case StructuredPostal.TYPE_OTHER: return R.string.map_other;
- default: return R.string.map_custom;
- }
- }
- }
-
- public static class ImActionInflater extends CommonInflater {
- @Override
- protected String getTypeColumn() {
- return Im.PROTOCOL;
- }
-
- @Override
- protected String getLabelColumn() {
- return Im.CUSTOM_PROTOCOL;
- }
-
- @Override
- protected int getTypeLabelResource(Integer type) {
- if (type == null) return R.string.chat;
- switch (type) {
- case Im.PROTOCOL_AIM: return R.string.chat_aim;
- case Im.PROTOCOL_MSN: return R.string.chat_msn;
- case Im.PROTOCOL_YAHOO: return R.string.chat_yahoo;
- case Im.PROTOCOL_SKYPE: return R.string.chat_skype;
- case Im.PROTOCOL_QQ: return R.string.chat_qq;
- case Im.PROTOCOL_GOOGLE_TALK: return R.string.chat_gtalk;
- case Im.PROTOCOL_ICQ: return R.string.chat_icq;
- case Im.PROTOCOL_JABBER: return R.string.chat_jabber;
- case Im.PROTOCOL_NETMEETING: return R.string.chat;
- default: return R.string.chat;
- }
- }
- }
-
- @Override
- public int getHeaderColor(Context context) {
- return 0xff7f93bc;
- }
-
- @Override
- public int getSideBarColor(Context context) {
- return 0xffbdc7b8;
- }
-}
diff --git a/src/com/android/contacts/model/GoogleAccountType.java b/src/com/android/contacts/model/GoogleAccountType.java
new file mode 100644
index 0000000..d369b28
--- /dev/null
+++ b/src/com/android/contacts/model/GoogleAccountType.java
@@ -0,0 +1,168 @@
+/*
+ * 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.model;
+
+import com.android.contacts.R;
+import com.android.contacts.editor.EventFieldEditorView;
+import com.android.contacts.util.DateUtils;
+import com.google.android.collect.Lists;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.view.inputmethod.EditorInfo;
+
+public class GoogleAccountType extends BaseAccountType {
+ public static final String ACCOUNT_TYPE = "com.google";
+ protected static final int FLAGS_RELATION = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS | EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME;
+
+ public GoogleAccountType(Context context, String resPackageName) {
+ this.accountType = ACCOUNT_TYPE;
+ this.resPackageName = null;
+ this.summaryResPackageName = resPackageName;
+
+ addDataKindStructuredName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindWebsite(context);
+ addDataKindSipAddress(context);
+ addDataKindGroupMembership(context);
+ addDataKindRelation(context);
+ addDataKindEvent(context);
+ }
+
+ @Override
+ protected DataKind addDataKindPhone(Context context) {
+ final DataKind kind = super.addDataKindPhone(context);
+
+ kind.typeColumn = Phone.TYPE;
+ kind.typeList = Lists.newArrayList();
+ kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_WORK));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_CUSTOM).setSecondary(true)
+ .setCustomColumn(Phone.LABEL));
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
+
+ return kind;
+ }
+
+ @Override
+ protected DataKind addDataKindEmail(Context context) {
+ final DataKind kind = super.addDataKindEmail(context);
+
+ kind.typeColumn = Email.TYPE;
+ kind.typeList = Lists.newArrayList();
+ kind.typeList.add(buildEmailType(Email.TYPE_HOME));
+ kind.typeList.add(buildEmailType(Email.TYPE_WORK));
+ kind.typeList.add(buildEmailType(Email.TYPE_OTHER));
+ kind.typeList.add(buildEmailType(Email.TYPE_CUSTOM).setSecondary(true).setCustomColumn(
+ Email.LABEL));
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
+
+ return kind;
+ }
+
+ private DataKind addDataKindRelation(Context context) {
+ DataKind kind = addKind(new DataKind(Relation.CONTENT_ITEM_TYPE,
+ R.string.relationLabelsGroup, -1, 160, true));
+ kind.actionHeader = new RelationActionInflater();
+ kind.actionBody = new SimpleInflater(Relation.NAME);
+
+ kind.typeColumn = Relation.TYPE;
+ kind.typeList = Lists.newArrayList();
+ kind.typeList.add(buildRelationType(Relation.TYPE_ASSISTANT));
+ kind.typeList.add(buildRelationType(Relation.TYPE_BROTHER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_CHILD));
+ kind.typeList.add(buildRelationType(Relation.TYPE_DOMESTIC_PARTNER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_FATHER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_FRIEND));
+ kind.typeList.add(buildRelationType(Relation.TYPE_MANAGER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_MOTHER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_PARENT));
+ kind.typeList.add(buildRelationType(Relation.TYPE_PARTNER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_REFERRED_BY));
+ kind.typeList.add(buildRelationType(Relation.TYPE_RELATIVE));
+ kind.typeList.add(buildRelationType(Relation.TYPE_SISTER));
+ kind.typeList.add(buildRelationType(Relation.TYPE_SPOUSE));
+ kind.typeList.add(buildRelationType(Relation.TYPE_CUSTOM).setSecondary(true)
+ .setCustomColumn(Relation.LABEL));
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Relation.TYPE, Relation.TYPE_SPOUSE);
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Relation.DATA, R.string.relationLabelsGroup,
+ FLAGS_RELATION));
+
+ return kind;
+ }
+
+ private DataKind addDataKindEvent(Context context) {
+ DataKind kind = addKind(new DataKind(Event.CONTENT_ITEM_TYPE,
+ R.string.eventLabelsGroup, -1, 150, true));
+ kind.actionHeader = new EventActionInflater();
+ kind.actionBody = new SimpleInflater(Event.START_DATE);
+ kind.editorClass = EventFieldEditorView.class;
+
+ kind.typeColumn = Event.TYPE;
+ kind.typeList = Lists.newArrayList();
+ kind.dateFormatWithoutYear = DateUtils.NO_YEAR_DATE_FORMAT;
+ kind.dateFormatWithYear = DateUtils.FULL_DATE_FORMAT;
+ kind.typeList.add(buildEventType(Event.TYPE_BIRTHDAY, true).setSpecificMax(1));
+ kind.typeList.add(buildEventType(Event.TYPE_ANNIVERSARY, false));
+ kind.typeList.add(buildEventType(Event.TYPE_OTHER, false));
+ kind.typeList.add(buildEventType(Event.TYPE_CUSTOM, false).setSecondary(true)
+ .setCustomColumn(Event.LABEL));
+
+ kind.defaultValues = new ContentValues();
+ kind.defaultValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
+
+ kind.fieldList = Lists.newArrayList();
+ kind.fieldList.add(new EditField(Event.DATA, R.string.eventLabelsGroup, FLAGS_EVENT));
+
+ return kind;
+ }
+
+ @Override
+ public int getHeaderColor(Context context) {
+ return 0xff89c2c2;
+ }
+
+ @Override
+ public int getSideBarColor(Context context) {
+ return 0xff5bb4b4;
+ }
+}
diff --git a/src/com/android/contacts/model/GoogleSource.java b/src/com/android/contacts/model/GoogleSource.java
deleted file mode 100644
index d6dfbb6..0000000
--- a/src/com/android/contacts/model/GoogleSource.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * 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.model;
-
-import com.android.contacts.R;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.google.android.collect.Lists;
-
-import android.accounts.Account;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.OperationApplicationException;
-import android.database.Cursor;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts.Data;
-
-import java.util.ArrayList;
-
-public class GoogleSource extends FallbackSource {
- public static final String ACCOUNT_TYPE = "com.google";
-
- private static final String SELECTION_GROUPS_BY_TITLE_AND_ACCOUNT =
- Groups.TITLE + "=? AND " + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?";
-
- public GoogleSource(String resPackageName) {
- this.accountType = ACCOUNT_TYPE;
- this.resPackageName = null;
- this.summaryResPackageName = resPackageName;
- }
-
- @Override
- protected void inflate(Context context, int inflateLevel) {
-
- inflateStructuredName(context, inflateLevel);
- inflateNickname(context, inflateLevel);
- inflatePhone(context, inflateLevel);
- inflateEmail(context, inflateLevel);
- inflateStructuredPostal(context, inflateLevel);
- inflateIm(context, inflateLevel);
- inflateOrganization(context, inflateLevel);
- inflatePhoto(context, inflateLevel);
- inflateNote(context, inflateLevel);
- inflateWebsite(context, inflateLevel);
- inflateEvent(context, inflateLevel);
- inflateSipAddress(context, inflateLevel);
-
- // TODO: GOOGLE: GROUPMEMBERSHIP
-
- setInflatedLevel(inflateLevel);
-
- }
-
- @Override
- protected DataKind inflatePhone(Context context, int inflateLevel) {
- final DataKind kind = super.inflatePhone(context, ContactsSource.LEVEL_MIMETYPES);
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.typeColumn = Phone.TYPE;
- kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
- kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE));
- kind.typeList.add(buildPhoneType(Phone.TYPE_WORK));
- kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_PAGER).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_OTHER));
- kind.typeList.add(buildPhoneType(Phone.TYPE_CUSTOM).setSecondary(true).setCustomColumn(
- Phone.LABEL));
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Phone.NUMBER, R.string.phoneLabelsGroup, FLAGS_PHONE));
- }
-
- return kind;
- }
-
- @Override
- protected DataKind inflateEmail(Context context, int inflateLevel) {
- final DataKind kind = super.inflateEmail(context, ContactsSource.LEVEL_MIMETYPES);
-
- if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
- kind.typeColumn = Email.TYPE;
- kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildEmailType(Email.TYPE_HOME));
- kind.typeList.add(buildEmailType(Email.TYPE_WORK));
- kind.typeList.add(buildEmailType(Email.TYPE_OTHER));
- kind.typeList.add(buildEmailType(Email.TYPE_CUSTOM).setSecondary(true).setCustomColumn(
- Email.LABEL));
-
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Email.DATA, R.string.emailLabelsGroup, FLAGS_EMAIL));
- }
-
- return kind;
- }
-
- // TODO: this should come from resource in the future
- // Note that frameworks/base/core/java/android/pim/vcard/VCardEntry.java also wants
- // this String.
- private static final String GOOGLE_MY_CONTACTS_GROUP = "System Group: My Contacts";
-
- public static final void attemptMyContactsMembership(EntityDelta state, Context context) {
- final ValuesDelta stateValues = state.getValues();
- stateValues.setFromTemplate(true);
- final String accountName = stateValues.getAsString(RawContacts.ACCOUNT_NAME);
- final String accountType = stateValues.getAsString(RawContacts.ACCOUNT_TYPE);
- attemptMyContactsMembership(state, accountName, accountType, context, true);
- }
-
- public static final void createMyContactsIfNotExist(Account account, Context context) {
- attemptMyContactsMembership(null, account.name, account.type, context, true);
- }
-
- /**
- *
- * @param allowRecur If the group is created between querying/about to create, we recur. But
- * to prevent excess recursion, we provide a flag to make sure we only do the recursion loop
- * once
- */
- private static final void attemptMyContactsMembership(EntityDelta state,
- final String accountName, final String accountType, Context context,
- boolean allowRecur) {
- final ContentResolver resolver = context.getContentResolver();
-
- Cursor cursor = resolver.query(Groups.CONTENT_URI,
- new String[] {Groups.TITLE, Groups.SOURCE_ID, Groups.SHOULD_SYNC},
- Groups.ACCOUNT_NAME + " =? AND " + Groups.ACCOUNT_TYPE + " =?",
- new String[] {accountName, accountType}, null);
-
- boolean myContactsExists = false;
- long assignToGroupSourceId = -1;
- while (cursor.moveToNext()) {
- if (GOOGLE_MY_CONTACTS_GROUP.equals(cursor.getString(0))) {
- myContactsExists = true;
- }
- if (assignToGroupSourceId == -1 && cursor.getInt(2) != 0) {
- assignToGroupSourceId = cursor.getInt(1);
- }
-
- if (myContactsExists && assignToGroupSourceId != -1) {
- break;
- }
- }
-
- if (myContactsExists && state == null) {
- return;
- }
-
- try {
- final ContentValues values = new ContentValues();
- values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
-
- if (!myContactsExists) {
- // create the group if it doesn't exist
- final ContentValues newGroup = new ContentValues();
- newGroup.put(Groups.TITLE, GOOGLE_MY_CONTACTS_GROUP);
-
- newGroup.put(Groups.ACCOUNT_NAME, accountName);
- newGroup.put(Groups.ACCOUNT_TYPE, accountType);
- newGroup.put(Groups.GROUP_VISIBLE, "1");
-
- ArrayList<ContentProviderOperation> operations =
- new ArrayList<ContentProviderOperation>();
-
- operations.add(ContentProviderOperation
- .newAssertQuery(Groups.CONTENT_URI)
- .withSelection(SELECTION_GROUPS_BY_TITLE_AND_ACCOUNT,
- new String[] {GOOGLE_MY_CONTACTS_GROUP, accountName, accountType})
- .withExpectedCount(0).build());
- operations.add(ContentProviderOperation
- .newInsert(Groups.CONTENT_URI)
- .withValues(newGroup)
- .build());
- try {
- ContentProviderResult[] results = resolver.applyBatch(
- ContactsContract.AUTHORITY, operations);
- values.put(GroupMembership.GROUP_ROW_ID, ContentUris.parseId(results[1].uri));
- } catch (RemoteException e) {
- throw new IllegalStateException("Problem querying for groups", e);
- } catch (OperationApplicationException e) {
- // the group was created after the query but before we tried to create it
- if (allowRecur) {
- attemptMyContactsMembership(
- state, accountName, accountType, context, false);
- }
- return;
- }
- } else {
- if (assignToGroupSourceId != -1) {
- values.put(GroupMembership.GROUP_SOURCE_ID, assignToGroupSourceId);
- } else {
- // there are no Groups to add this contact to, so don't apply any membership
- // TODO: alert user that their contact will be dropped?
- }
- }
- if (state != null) {
- state.addEntry(ValuesDelta.fromAfter(values));
- }
- } finally {
- cursor.close();
- }
- }
-
- @Override
- public int getHeaderColor(Context context) {
- return 0xff89c2c2;
- }
-
- @Override
- public int getSideBarColor(Context context) {
- return 0xff5bb4b4;
- }
-}
diff --git a/src/com/android/contacts/model/Sources.java b/src/com/android/contacts/model/Sources.java
deleted file mode 100644
index 06d7551..0000000
--- a/src/com/android/contacts/model/Sources.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * 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.model;
-
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-import com.google.android.collect.Sets;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AuthenticatorDescription;
-import android.accounts.OnAccountsUpdateListener;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.IContentService;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SyncAdapterType;
-import android.content.pm.PackageManager;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.lang.ref.SoftReference;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Locale;
-
-/**
- * Singleton holder for all parsed {@link ContactsSource} available on the
- * system, typically filled through {@link PackageManager} queries.
- */
-public class Sources extends BroadcastReceiver implements OnAccountsUpdateListener {
- private static final String TAG = "Sources";
-
- private Context mContext;
- private Context mApplicationContext;
- private AccountManager mAccountManager;
-
- private ContactsSource mFallbackSource = null;
-
- private HashMap<String, ContactsSource> mSources = Maps.newHashMap();
- private HashSet<String> mKnownPackages = Sets.newHashSet();
-
- private static SoftReference<Sources> sInstance = null;
-
- /**
- * Requests the singleton instance of {@link Sources} with data bound from
- * the available authenticators. This method blocks until its interaction
- * with {@link AccountManager} is finished, so don't call from a UI thread.
- */
- public static synchronized Sources getInstance(Context context) {
- Sources sources = sInstance == null ? null : sInstance.get();
- if (sources == null) {
- sources = new Sources(context);
- sInstance = new SoftReference<Sources>(sources);
- }
- return sources;
- }
-
- /**
- * Internal constructor that only performs initial parsing.
- */
- private Sources(Context context) {
- mContext = context;
- mApplicationContext = context.getApplicationContext();
- mAccountManager = AccountManager.get(mApplicationContext);
-
- // Create fallback contacts source for on-phone contacts
- mFallbackSource = new FallbackSource();
-
- queryAccounts();
-
- // Request updates when packages or accounts change
- IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addDataScheme("package");
- mApplicationContext.registerReceiver(this, filter);
- IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
- sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mApplicationContext.registerReceiver(this, sdFilter);
-
- // Request updates when locale is changed so that the order of each field will
- // be able to be changed on the locale change.
- filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
- mApplicationContext.registerReceiver(this, filter);
-
- mAccountManager.addOnAccountsUpdatedListener(this, null, false);
- }
-
- /** @hide exposed for unit tests */
- public Sources(ContactsSource... sources) {
- for (ContactsSource source : sources) {
- addSource(source);
- }
- }
-
- protected void addSource(ContactsSource source) {
- mSources.put(source.accountType, source);
- mKnownPackages.add(source.resPackageName);
- }
-
- /** {@inheritDoc} */
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
-
- if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
- || Intent.ACTION_PACKAGE_ADDED.equals(action)
- || Intent.ACTION_PACKAGE_CHANGED.equals(action) ||
- Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) ||
- Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- String[] pkgList = null;
- // Handle applications on sdcard.
- if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) ||
- Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- } else {
- final String packageName = intent.getData().getSchemeSpecificPart();
- pkgList = new String[] { packageName };
- }
- if (pkgList != null) {
- for (String packageName : pkgList) {
- final boolean knownPackage = mKnownPackages.contains(packageName);
- if (knownPackage) {
- // Invalidate cache of existing source
- invalidateCache(packageName);
- } else {
- // Unknown source, so reload from scratch
- queryAccounts();
- }
- }
- }
- } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
- invalidateAllCache();
- }
- }
-
- protected void invalidateCache(String packageName) {
- for (ContactsSource source : mSources.values()) {
- if (TextUtils.equals(packageName, source.resPackageName)) {
- // Invalidate any cache for the changed package
- source.invalidateCache();
- }
- }
- }
-
- protected void invalidateAllCache() {
- mFallbackSource.invalidateCache();
- for (ContactsSource source : mSources.values()) {
- source.invalidateCache();
- }
- }
-
- /** {@inheritDoc} */
- public void onAccountsUpdated(Account[] accounts) {
- // Refresh to catch any changed accounts
- queryAccounts();
- }
-
- /**
- * Blocking call to load all {@link AuthenticatorDescription} known by the
- * {@link AccountManager} on the system.
- */
- protected synchronized void queryAccounts() {
- mSources.clear();
- mKnownPackages.clear();
-
- final AccountManager am = mAccountManager;
- final IContentService cs = ContentResolver.getContentService();
-
- try {
- final SyncAdapterType[] syncs = cs.getSyncAdapterTypes();
- final AuthenticatorDescription[] auths = am.getAuthenticatorTypes();
-
- for (SyncAdapterType sync : syncs) {
- if (!ContactsContract.AUTHORITY.equals(sync.authority)) {
- // Skip sync adapters that don't provide contact data.
- continue;
- }
-
- // Look for the formatting details provided by each sync
- // adapter, using the authenticator to find general resources.
- final String accountType = sync.accountType;
- final AuthenticatorDescription auth = findAuthenticator(auths, accountType);
- if (auth == null) {
- Log.w(TAG, "No authenticator found for type=" + accountType + ", ignoring it.");
- continue;
- }
-
- ContactsSource source;
- if (GoogleSource.ACCOUNT_TYPE.equals(accountType)) {
- source = new GoogleSource(auth.packageName);
- } else if (ExchangeSource.ACCOUNT_TYPE.equals(accountType)) {
- source = new ExchangeSource(auth.packageName);
- } else {
- // TODO: use syncadapter package instead, since it provides resources
- Log.d(TAG, "Creating external source for type=" + accountType
- + ", packageName=" + auth.packageName);
- source = new ExternalSource(auth.packageName);
- source.readOnly = !sync.supportsUploading();
- }
-
- source.accountType = auth.type;
- source.titleRes = auth.labelId;
- source.iconRes = auth.iconId;
-
- addSource(source);
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Problem loading accounts: " + e.toString());
- }
- }
-
- /**
- * Find a specific {@link AuthenticatorDescription} in the provided list
- * that matches the given account type.
- */
- protected static AuthenticatorDescription findAuthenticator(AuthenticatorDescription[] auths,
- String accountType) {
- for (AuthenticatorDescription auth : auths) {
- if (accountType.equals(auth.type)) {
- return auth;
- }
- }
- return null;
- }
-
- /**
- * Return list of all known, writable {@link ContactsSource}. Sources
- * returned may require inflation before they can be used.
- */
- public ArrayList<Account> getAccounts(boolean writableOnly) {
- final AccountManager am = mAccountManager;
- final Account[] accounts = am.getAccounts();
- final ArrayList<Account> matching = Lists.newArrayList();
-
- for (Account account : accounts) {
- // Ensure we have details loaded for each account
- final ContactsSource source = getInflatedSource(account.type,
- ContactsSource.LEVEL_SUMMARY);
- final boolean hasContacts = source != null;
- final boolean matchesWritable = (!writableOnly || (writableOnly && !source.readOnly));
- if (hasContacts && matchesWritable) {
- matching.add(account);
- }
- }
- return matching;
- }
-
- /**
- * Find the best {@link DataKind} matching the requested
- * {@link ContactsSource#accountType} and {@link DataKind#mimeType}. If no
- * direct match found, we try searching {@link #mFallbackSource}.
- * When fourceRefresh is set to true, cache is refreshed and inflation of each
- * EditField will occur.
- */
- public DataKind getKindOrFallback(String accountType, String mimeType, Context context,
- int inflateLevel) {
- DataKind kind = null;
-
- // Try finding source and kind matching request
- final ContactsSource source = mSources.get(accountType);
- if (source != null) {
- source.ensureInflated(context, inflateLevel);
- kind = source.getKindForMimetype(mimeType);
- }
-
- if (kind == null) {
- // Nothing found, so try fallback as last resort
- mFallbackSource.ensureInflated(context, inflateLevel);
- kind = mFallbackSource.getKindForMimetype(mimeType);
- }
-
- if (kind == null) {
- Log.w(TAG, "Unknown type=" + accountType + ", mime=" + mimeType);
- }
-
- return kind;
- }
-
- /**
- * Return {@link ContactsSource} for the given account type.
- */
- public ContactsSource getInflatedSource(String accountType, int inflateLevel) {
- // Try finding specific source, otherwise use fallback
- ContactsSource source = mSources.get(accountType);
- if (source == null) source = mFallbackSource;
-
- if (source.isInflated(inflateLevel)) {
- // Already inflated, so return directly
- return source;
- } else {
- // Not inflated, but requested that we force-inflate
- source.ensureInflated(mContext, inflateLevel);
- return source;
- }
- }
-}
diff --git a/src/com/android/contacts/preference/ContactsPreferenceActivity.java b/src/com/android/contacts/preference/ContactsPreferenceActivity.java
new file mode 100644
index 0000000..7f7b750
--- /dev/null
+++ b/src/com/android/contacts/preference/ContactsPreferenceActivity.java
@@ -0,0 +1,78 @@
+/*
+ * 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.preference;
+
+import com.android.contacts.R;
+import com.android.contacts.activities.ContactBrowserActivity;
+
+import android.app.ActionBar;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.view.MenuItem;
+
+import java.util.List;
+
+/**
+ * Contacts settings.
+ */
+public final class ContactsPreferenceActivity extends PreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // This Activity will always fall back to the "top" Contacts screen when touched on the
+ // app up icon, regardless of launch context.
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
+ }
+ }
+
+ /**
+ * Populate the activity with the top-level headers.
+ */
+ @Override
+ public void onBuildHeaders(List<Header> target) {
+ loadHeadersFromResource(R.xml.preference_headers, target);
+ }
+
+ /**
+ * Returns true if there are no preferences to display and therefore the
+ * corresponding menu item can be removed.
+ */
+ public static boolean isEmpty(Context context) {
+ return !context.getResources().getBoolean(R.bool.config_sort_order_user_changeable)
+ && !context.getResources().getBoolean(R.bool.config_display_order_user_changeable);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home: {
+ Intent intent = new Intent(this, ContactBrowserActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ finish();
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/contacts/preference/ContactsPreferences.java b/src/com/android/contacts/preference/ContactsPreferences.java
new file mode 100644
index 0000000..540158d
--- /dev/null
+++ b/src/com/android/contacts/preference/ContactsPreferences.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2010 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.preference;
+
+import com.android.contacts.R;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.ContactsContract;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+
+/**
+ * Manages user preferences for contacts.
+ */
+public final class ContactsPreferences extends ContentObserver {
+
+ public static final String PREF_DISPLAY_ONLY_PHONES = "only_phones";
+ public static final boolean PREF_DISPLAY_ONLY_PHONES_DEFAULT = false;
+
+ private Context mContext;
+ private int mSortOrder = -1;
+ private int mDisplayOrder = -1;
+ private ChangeListener mListener = null;
+ private Handler mHandler;
+
+ public ContactsPreferences(Context context) {
+ super(null);
+ mContext = context;
+ mHandler = new Handler();
+ }
+
+ public boolean isSortOrderUserChangeable() {
+ return mContext.getResources().getBoolean(R.bool.config_sort_order_user_changeable);
+ }
+
+ public int getDefaultSortOrder() {
+ if (mContext.getResources().getBoolean(R.bool.config_default_sort_order_primary)) {
+ return ContactsContract.Preferences.SORT_ORDER_PRIMARY;
+ } else {
+ return ContactsContract.Preferences.SORT_ORDER_ALTERNATIVE;
+ }
+ }
+
+ public int getSortOrder() {
+ if (!isSortOrderUserChangeable()) {
+ return getDefaultSortOrder();
+ }
+
+ if (mSortOrder == -1) {
+ try {
+ mSortOrder = Settings.System.getInt(mContext.getContentResolver(),
+ ContactsContract.Preferences.SORT_ORDER);
+ } catch (SettingNotFoundException e) {
+ mSortOrder = getDefaultSortOrder();
+ }
+ }
+ return mSortOrder;
+ }
+
+ public void setSortOrder(int sortOrder) {
+ mSortOrder = sortOrder;
+ Settings.System.putInt(mContext.getContentResolver(),
+ ContactsContract.Preferences.SORT_ORDER, sortOrder);
+ }
+
+ public boolean isDisplayOrderUserChangeable() {
+ return mContext.getResources().getBoolean(R.bool.config_display_order_user_changeable);
+ }
+
+ public int getDefaultDisplayOrder() {
+ if (mContext.getResources().getBoolean(R.bool.config_default_display_order_primary)) {
+ return ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY;
+ } else {
+ return ContactsContract.Preferences.DISPLAY_ORDER_ALTERNATIVE;
+ }
+ }
+
+ public int getDisplayOrder() {
+ if (!isDisplayOrderUserChangeable()) {
+ return getDefaultDisplayOrder();
+ }
+
+ if (mDisplayOrder == -1) {
+ try {
+ mDisplayOrder = Settings.System.getInt(mContext.getContentResolver(),
+ ContactsContract.Preferences.DISPLAY_ORDER);
+ } catch (SettingNotFoundException e) {
+ mDisplayOrder = getDefaultDisplayOrder();
+ }
+ }
+ return mDisplayOrder;
+ }
+
+ public void setDisplayOrder(int displayOrder) {
+ mDisplayOrder = displayOrder;
+ Settings.System.putInt(mContext.getContentResolver(),
+ ContactsContract.Preferences.DISPLAY_ORDER, displayOrder);
+ }
+
+ public void registerChangeListener(ChangeListener listener) {
+ if (mListener != null) unregisterChangeListener();
+
+ mListener = listener;
+
+ // Reset preferences to "unknown" because they may have changed while the
+ // observer was unregistered.
+ mDisplayOrder = -1;
+ mSortOrder = -1;
+
+ final ContentResolver contentResolver = mContext.getContentResolver();
+ contentResolver.registerContentObserver(
+ Settings.System.getUriFor(
+ ContactsContract.Preferences.SORT_ORDER), false, this);
+ contentResolver.registerContentObserver(
+ Settings.System.getUriFor(
+ ContactsContract.Preferences.DISPLAY_ORDER), false, this);
+ }
+
+ public void unregisterChangeListener() {
+ if (mListener != null) {
+ mContext.getContentResolver().unregisterContentObserver(this);
+ mListener = null;
+ }
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ // This notification is not sent on the Ui thread. Use the previously created Handler
+ // to switch to the Ui thread
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSortOrder = -1;
+ mDisplayOrder = -1;
+ if (mListener != null) mListener.onChange();
+ }
+ });
+ }
+
+ public interface ChangeListener {
+ void onChange();
+ }
+}
diff --git a/src/com/android/contacts/preference/DisplayOptionsPreferenceFragment.java b/src/com/android/contacts/preference/DisplayOptionsPreferenceFragment.java
new file mode 100644
index 0000000..db4a94e
--- /dev/null
+++ b/src/com/android/contacts/preference/DisplayOptionsPreferenceFragment.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010 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.preference;
+
+import com.android.contacts.R;
+
+import android.os.Bundle;
+import android.preference.PreferenceFragment;
+
+/**
+ * This fragment shows the preferences for the first header.
+ */
+public class DisplayOptionsPreferenceFragment extends PreferenceFragment {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Load the preferences from an XML resource
+ addPreferencesFromResource(R.xml.preference_display_options);
+ }
+}
+
diff --git a/src/com/android/contacts/preference/DisplayOrderPreference.java b/src/com/android/contacts/preference/DisplayOrderPreference.java
new file mode 100644
index 0000000..fea01c8
--- /dev/null
+++ b/src/com/android/contacts/preference/DisplayOrderPreference.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 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.preference;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.preference.ListPreference;
+import android.provider.ContactsContract;
+import android.util.AttributeSet;
+
+/**
+ * Custom preference: view-name-as (first name first or last name first).
+ */
+public final class DisplayOrderPreference extends ListPreference {
+
+ private ContactsPreferences mPreferences;
+ private Context mContext;
+
+ public DisplayOrderPreference(Context context) {
+ super(context);
+ prepare();
+ }
+
+ public DisplayOrderPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ prepare();
+ }
+
+ private void prepare() {
+ mContext = getContext();
+ mPreferences = new ContactsPreferences(mContext);
+ setEntries(new String[]{
+ mContext.getString(R.string.display_options_view_given_name_first),
+ mContext.getString(R.string.display_options_view_family_name_first),
+ });
+ setEntryValues(new String[]{
+ String.valueOf(ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY),
+ String.valueOf(ContactsContract.Preferences.DISPLAY_ORDER_ALTERNATIVE),
+ });
+ setValue(String.valueOf(mPreferences.getDisplayOrder()));
+ }
+
+ @Override
+ protected boolean shouldPersist() {
+ return false; // This preference takes care of its own storage
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ switch (mPreferences.getDisplayOrder()) {
+ case ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY:
+ return mContext.getString(R.string.display_options_view_given_name_first);
+ case ContactsContract.Preferences.DISPLAY_ORDER_ALTERNATIVE:
+ return mContext.getString(R.string.display_options_view_family_name_first);
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean persistString(String value) {
+ int newValue = Integer.parseInt(value);
+ if (newValue != mPreferences.getDisplayOrder()) {
+ mPreferences.setDisplayOrder(newValue);
+ notifyChanged();
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/contacts/preference/SortOrderPreference.java b/src/com/android/contacts/preference/SortOrderPreference.java
new file mode 100644
index 0000000..81b034c
--- /dev/null
+++ b/src/com/android/contacts/preference/SortOrderPreference.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 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.preference;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.preference.ListPreference;
+import android.provider.ContactsContract;
+import android.util.AttributeSet;
+
+/**
+ * Custom preference: sort-by.
+ */
+public final class SortOrderPreference extends ListPreference {
+
+ private ContactsPreferences mPreferences;
+ private Context mContext;
+
+ public SortOrderPreference(Context context) {
+ super(context);
+ prepare();
+ }
+
+ public SortOrderPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ prepare();
+ }
+
+ private void prepare() {
+ mContext = getContext();
+ mPreferences = new ContactsPreferences(mContext);
+ setEntries(new String[]{
+ mContext.getString(R.string.display_options_sort_by_given_name),
+ mContext.getString(R.string.display_options_sort_by_family_name),
+ });
+ setEntryValues(new String[]{
+ String.valueOf(ContactsContract.Preferences.SORT_ORDER_PRIMARY),
+ String.valueOf(ContactsContract.Preferences.SORT_ORDER_ALTERNATIVE),
+ });
+ setValue(String.valueOf(mPreferences.getSortOrder()));
+ }
+
+ @Override
+ protected boolean shouldPersist() {
+ return false; // This preference takes care of its own storage
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ switch (mPreferences.getSortOrder()) {
+ case ContactsContract.Preferences.SORT_ORDER_PRIMARY:
+ return mContext.getString(R.string.display_options_sort_by_given_name);
+ case ContactsContract.Preferences.SORT_ORDER_ALTERNATIVE:
+ return mContext.getString(R.string.display_options_sort_by_family_name);
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean persistString(String value) {
+ int newValue = Integer.parseInt(value);
+ if (newValue != mPreferences.getSortOrder()) {
+ mPreferences.setSortOrder(newValue);
+ notifyChanged();
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/Action.java b/src/com/android/contacts/quickcontact/Action.java
new file mode 100644
index 0000000..bdfbe48
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/Action.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 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.quickcontact;
+
+import com.android.contacts.Collapser;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+
+/**
+ * Abstract definition of an action that could be performed, along with
+ * string description and icon.
+ */
+public interface Action extends Collapser.Collapsible<Action> {
+ public CharSequence getHeader();
+ public CharSequence getBody();
+
+ public String getMimeType();
+ public Drawable getFallbackIcon();
+
+ /**
+ * Build an {@link Intent} that will perform this action.
+ */
+ public Intent getIntent();
+
+ /**
+ * Checks if the contact data for this action is primary.
+ */
+ public Boolean isPrimary();
+
+ /**
+ * Returns a lookup (@link Uri) for the contact data item or null if there is no data item
+ * corresponding to this row
+ */
+ public Uri getDataUri();
+
+ /**
+ * Returns the id of the contact data item or -1 of there is no data item corresponding to this
+ * row
+ */
+ public long getDataId();
+}
diff --git a/src/com/android/contacts/quickcontact/ActionMultiMap.java b/src/com/android/contacts/quickcontact/ActionMultiMap.java
new file mode 100644
index 0000000..21234ce
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/ActionMultiMap.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010 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.quickcontact;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Provide a simple way of collecting one or more {@link Action} objects
+ * under a MIME-type key.
+ */
+public class ActionMultiMap extends HashMap<String, ArrayList<Action>> {
+ public void put(String mimeType, Action info) {
+ // Create list for this MIME-type when needed
+ ArrayList<Action> collectList = get(mimeType);
+ if (collectList == null) {
+ collectList = new ArrayList<Action>();
+ put(mimeType, collectList);
+ }
+ collectList.add(info);
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/CheckableImageView.java b/src/com/android/contacts/quickcontact/CheckableImageView.java
new file mode 100644
index 0000000..d1b75cb
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/CheckableImageView.java
@@ -0,0 +1,63 @@
+/*
+ * 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.quickcontact;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Checkable;
+import android.widget.ImageView;
+
+/**
+ * A special variation of ImageView that can be used as a checkable object.
+ * This is used as the background view of quickcontact chiclet, which is in checked state
+ * when disambig list is shown. Otherwise, it works identically to a ImageView.
+ */
+public class CheckableImageView extends ImageView implements Checkable {
+ private boolean mChecked;
+
+ private static final int[] CHECKED_STATE_SET = {
+ android.R.attr.state_checked
+ };
+
+ public CheckableImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public int[] onCreateDrawableState(int extraSpace) {
+ final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+ if (isChecked()) {
+ mergeDrawableStates(drawableState, CHECKED_STATE_SET);
+ }
+ return drawableState;
+ }
+
+ public void toggle() {
+ setChecked(!mChecked);
+ }
+
+ public boolean isChecked() {
+ return mChecked;
+ }
+
+ public void setChecked(boolean checked) {
+ if (mChecked != checked) {
+ mChecked = checked;
+ refreshDrawableState();
+ }
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/ClearDefaultsAction.java b/src/com/android/contacts/quickcontact/ClearDefaultsAction.java
new file mode 100644
index 0000000..d1acc6b
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/ClearDefaultsAction.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 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.quickcontact;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+
+/**
+ * Action that expands to show and allow clearing the currently selected defaults.
+ */
+public class ClearDefaultsAction implements Action {
+ /**
+ * This is a pseudo-mimetype that is only needed for the action list. It has to be
+ * different from the real mime-types used
+ */
+ public static final String PSEUDO_MIME_TYPE = "__clear_defaults_mime_type";
+
+ @Override
+ public boolean collapseWith(Action t) {
+ return false;
+ }
+
+ @Override
+ public boolean shouldCollapseWith(Action t) {
+ return false;
+ }
+
+ @Override
+ public CharSequence getHeader() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getBody() {
+ return null;
+ }
+
+ @Override
+ public String getMimeType() {
+ return PSEUDO_MIME_TYPE;
+ }
+
+ @Override
+ public Drawable getFallbackIcon() {
+ return null;
+ }
+
+ @Override
+ public Intent getIntent() {
+ return null;
+ }
+
+ @Override
+ public Boolean isPrimary() {
+ return null;
+ }
+
+ @Override
+ public Uri getDataUri() {
+ return null;
+ }
+
+ @Override
+ public long getDataId() {
+ return -1;
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/DataAction.java b/src/com/android/contacts/quickcontact/DataAction.java
new file mode 100644
index 0000000..bd59856
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/DataAction.java
@@ -0,0 +1,285 @@
+package com.android.contacts.quickcontact;
+
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.util.Constants;
+import com.android.contacts.util.PhoneCapabilityTester;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.net.WebAddress;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Description of a specific {@link Data#_ID} item, with style information
+ * defined by a {@link DataKind}.
+ */
+public class DataAction implements Action {
+ private static final String TAG = "DataAction";
+
+ private final Context mContext;
+ private final DataKind mKind;
+ private final String mMimeType;
+
+ private CharSequence mHeader;
+ private CharSequence mBody;
+ private Intent mIntent;
+
+ private boolean mAlternate;
+ private Uri mDataUri;
+ private long mDataId;
+ private boolean mIsPrimary;
+
+ /**
+ * Create an action from common {@link Data} elements.
+ */
+ public DataAction(Context context, String mimeType, DataKind kind,
+ long dataId, Cursor cursor) {
+ mContext = context;
+ mKind = kind;
+ mMimeType = mimeType;
+
+ // Inflate strings from cursor
+ mAlternate = Constants.MIME_TYPE_SMS_ADDRESS.equals(mimeType);
+ if (mAlternate && mKind.actionAltHeader != null) {
+ mHeader = mKind.actionAltHeader.inflateUsing(context, cursor);
+ } else if (mKind.actionHeader != null) {
+ mHeader = mKind.actionHeader.inflateUsing(context, cursor);
+ }
+
+ if (getAsInt(cursor, Data.IS_SUPER_PRIMARY) != 0) {
+ mIsPrimary = true;
+ }
+
+ if (mKind.actionBody != null) {
+ mBody = mKind.actionBody.inflateUsing(context, cursor);
+ }
+
+ mDataId = dataId;
+ mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
+
+ // Handle well-known MIME-types with special care
+ if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ if (PhoneCapabilityTester.isPhone(mContext)) {
+ final String number = getAsString(cursor, Phone.NUMBER);
+ if (!TextUtils.isEmpty(number)) {
+ final Uri callUri = Uri.fromParts(Constants.SCHEME_TEL, number, null);
+ mIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, callUri);
+ }
+ }
+ } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ if (PhoneCapabilityTester.isSipPhone(mContext)) {
+ final String address = getAsString(cursor, SipAddress.SIP_ADDRESS);
+ if (!TextUtils.isEmpty(address)) {
+ final Uri callUri = Uri.fromParts(Constants.SCHEME_SIP, address, null);
+ mIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, callUri);
+ // Note that this item will get a SIP-specific variant
+ // of the "call phone" icon, rather than the standard
+ // app icon for the Phone app (which we show for
+ // regular phone numbers.) That's because the phone
+ // app explicitly specifies an android:icon attribute
+ // for the SIP-related intent-filters in its manifest.
+ }
+ }
+ } else if (Constants.MIME_TYPE_SMS_ADDRESS.equals(mimeType)) {
+ if (PhoneCapabilityTester.isSmsIntentRegistered(mContext)) {
+ final String number = getAsString(cursor, Phone.NUMBER);
+ if (!TextUtils.isEmpty(number)) {
+ final Uri smsUri = Uri.fromParts(Constants.SCHEME_SMSTO, number, null);
+ mIntent = new Intent(Intent.ACTION_SENDTO, smsUri);
+ }
+ }
+ } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ final String address = getAsString(cursor, Email.DATA);
+ if (!TextUtils.isEmpty(address)) {
+ final Uri mailUri = Uri.fromParts(Constants.SCHEME_MAILTO, address, null);
+ mIntent = new Intent(Intent.ACTION_SENDTO, mailUri);
+ }
+
+ } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ final String url = getAsString(cursor, Website.URL);
+ if (!TextUtils.isEmpty(url)) {
+ WebAddress webAddress = new WebAddress(url);
+ mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webAddress.toString()));
+ }
+
+ } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)
+ || Constants.MIME_TYPE_VIDEO_CHAT.equals(mimeType)) {
+ final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(
+ getAsString(cursor, Data.MIMETYPE));
+ if (isEmail || isProtocolValid(cursor)) {
+ final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK :
+ getAsInt(cursor, Im.PROTOCOL);
+
+ if (isEmail) {
+ // Use Google Talk string when using Email, and clear data
+ // Uri so we don't try saving Email as primary.
+ mHeader = context.getText(R.string.chat_gtalk);
+ mDataUri = null;
+ }
+
+ String host = getAsString(cursor, Im.CUSTOM_PROTOCOL);
+ String data = getAsString(cursor,
+ isEmail ? Email.DATA : Im.DATA);
+ if (protocol != Im.PROTOCOL_CUSTOM) {
+ // Try bringing in a well-known host for specific protocols
+ host = ContactsUtils.lookupProviderNameFromId(protocol);
+ }
+
+ if (Constants.MIME_TYPE_VIDEO_CHAT.equals(mimeType)) {
+ if (!TextUtils.isEmpty(data)) {
+ mIntent = new Intent(Intent.ACTION_SENDTO);
+ mIntent.setDataAndType(Uri.parse("xmpp:" + data + "?call"),
+ Constants.MIME_TYPE_VIDEO_CHAT);
+ }
+ } else if (!TextUtils.isEmpty(host) && !TextUtils.isEmpty(data)) {
+ final String authority = host.toLowerCase();
+ final Uri imUri = new Uri.Builder().scheme(Constants.SCHEME_IMTO).authority(
+ authority).appendPath(data).build();
+ mIntent = new Intent(Intent.ACTION_SENDTO, imUri);
+ }
+ }
+ }
+
+ if (mIntent == null) {
+ // Otherwise fall back to default VIEW action
+ mIntent = new Intent(Intent.ACTION_VIEW);
+ mIntent.setDataAndType(mDataUri, mimeType);
+ }
+
+ // Always launch as new task, since we're like a launcher
+ mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ }
+
+ /** Read {@link String} from the given {@link Cursor}. */
+ private static String getAsString(Cursor cursor, String columnName) {
+ final int index = cursor.getColumnIndex(columnName);
+ return cursor.getString(index);
+ }
+
+ /** Read {@link Integer} from the given {@link Cursor}. */
+ private static int getAsInt(Cursor cursor, String columnName) {
+ final int index = cursor.getColumnIndex(columnName);
+ return cursor.getInt(index);
+ }
+
+ private boolean isProtocolValid(Cursor cursor) {
+ final int columnIndex = cursor.getColumnIndex(Im.PROTOCOL);
+ if (cursor.isNull(columnIndex)) {
+ return false;
+ }
+ try {
+ Integer.valueOf(cursor.getString(columnIndex));
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public CharSequence getHeader() {
+ return mHeader;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public CharSequence getBody() {
+ return mBody;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMimeType() {
+ return mMimeType;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Uri getDataUri() {
+ return mDataUri;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public long getDataId() {
+ return mDataId;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Boolean isPrimary() {
+ return mIsPrimary;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Drawable getFallbackIcon() {
+ // Bail early if no valid resources
+ final String resPackageName = mKind.resPackageName;
+ if (resPackageName == null) return null;
+
+ final PackageManager pm = mContext.getPackageManager();
+ if (mAlternate && mKind.iconAltRes != -1) {
+ return pm.getDrawable(resPackageName, mKind.iconAltRes, null);
+ } else if (mKind.iconRes != -1) {
+ return pm.getDrawable(resPackageName, mKind.iconRes, null);
+ } else {
+ return null;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Intent getIntent() {
+ return mIntent;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean collapseWith(Action other) {
+ if (!shouldCollapseWith(other)) {
+ return false;
+ }
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean shouldCollapseWith(Action t) {
+ if (t == null) {
+ return false;
+ }
+ if (!(t instanceof DataAction)) {
+ Log.e(TAG, "t must be DataAction");
+ return false;
+ }
+ DataAction other = (DataAction)t;
+ if (!ContactsUtils.areObjectsEqual(mKind, other.mKind)) {
+ return false;
+ }
+ if (!ContactsUtils.shouldCollapse(mContext, mMimeType, mBody, other.mMimeType,
+ other.mBody)) {
+ return false;
+ }
+ if (!TextUtils.equals(mMimeType, other.mMimeType)
+ || !ContactsUtils.areIntentActionEqual(mIntent, other.mIntent)
+ ) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/PackageIntentReceiver.java b/src/com/android/contacts/quickcontact/PackageIntentReceiver.java
new file mode 100644
index 0000000..7af4005
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/PackageIntentReceiver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 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.quickcontact;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Package intent receiver that flushes the {@link ResolveCache} so that Packages are rescanned next
+ * time
+ */
+public class PackageIntentReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ ResolveCache.flush();
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/ProfileAction.java b/src/com/android/contacts/quickcontact/ProfileAction.java
new file mode 100644
index 0000000..a04e522
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/ProfileAction.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 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.quickcontact;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
+
+/**
+ * Specific action that launches the profile card.
+ */
+public class ProfileAction implements Action {
+ private final Context mContext;
+ private final Uri mLookupUri;
+
+ public ProfileAction(Context context, Uri lookupUri) {
+ mContext = context;
+ mLookupUri = lookupUri;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public CharSequence getHeader() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public CharSequence getBody() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String getMimeType() {
+ return Contacts.CONTENT_ITEM_TYPE;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Drawable getFallbackIcon() {
+ return mContext.getResources().getDrawable(R.drawable.ic_contacts_details);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Intent getIntent() {
+ final Intent intent = new Intent(Intent.ACTION_VIEW, mLookupUri);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ return intent;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Boolean isPrimary() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Uri getDataUri() {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public long getDataId() {
+ return -1;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean collapseWith(Action t) {
+ return false; // Never dup.
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean shouldCollapseWith(Action t) {
+ return false; // Never dup.
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
new file mode 100644
index 0000000..5032386
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -0,0 +1,121 @@
+/*
+ * 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.quickcontact;
+
+import com.android.contacts.ContactsActivity;
+
+import android.content.ContentUris;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.QuickContact;
+import android.provider.ContactsContract.RawContacts;
+import android.util.Log;
+
+/**
+ * Stub translucent activity that just shows {@link QuickContactWindow} floating
+ * above the caller. This temporary hack should eventually be replaced with
+ * direct framework support.
+ */
+public final class QuickContactActivity extends ContactsActivity
+ implements QuickContactWindow.OnDismissListener {
+ private static final String TAG = "QuickContactActivity";
+
+ static final boolean LOGV = false;
+ static final boolean FORCE_CREATE = true;
+
+ private QuickContactWindow mQuickContact;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ if (LOGV) Log.d(TAG, "onCreate");
+
+ this.onNewIntent(getIntent());
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ if (LOGV) Log.d(TAG, "onNewIntent");
+
+ if (QuickContactWindow.TRACE_LAUNCH) {
+ android.os.Debug.startMethodTracing(QuickContactWindow.TRACE_TAG);
+ }
+
+ if (mQuickContact == null || FORCE_CREATE) {
+ if (LOGV) Log.d(TAG, "Preparing window");
+ mQuickContact = new QuickContactWindow(this, this);
+ }
+
+ // Use our local window token for now
+ Uri lookupUri = intent.getData();
+ // Check to see whether it comes from the old version.
+ if (android.provider.Contacts.AUTHORITY.equals(lookupUri.getAuthority())) {
+ final long rawContactId = ContentUris.parseId(lookupUri);
+ lookupUri = RawContacts.getContactLookupUri(getContentResolver(),
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
+ }
+ final Bundle extras = intent.getExtras();
+
+ // Read requested parameters for displaying
+ final Rect target = intent.getSourceBounds();
+ final int mode = extras.getInt(QuickContact.EXTRA_MODE, QuickContact.MODE_MEDIUM);
+ final String[] excludeMimes = extras.getStringArray(QuickContact.EXTRA_EXCLUDE_MIMES);
+
+ mQuickContact.show(lookupUri, target, mode, excludeMimes);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onBackPressed() {
+ if (LOGV) Log.w(TAG, "Unexpected back captured by stub activity");
+ finish();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (LOGV) Log.d(TAG, "onPause");
+
+ // Dismiss any dialog when pausing
+ mQuickContact.dismiss();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (LOGV) Log.d(TAG, "onDestroy");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onDismiss(QuickContactWindow dialog) {
+ if (LOGV) Log.d(TAG, "onDismiss");
+
+ if (isTaskRoot() && !FORCE_CREATE) {
+ // 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);
+ } else {
+ finish();
+ }
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/QuickContactBackgroundDrawable.java b/src/com/android/contacts/quickcontact/QuickContactBackgroundDrawable.java
new file mode 100644
index 0000000..9118480
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/QuickContactBackgroundDrawable.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2010 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.quickcontact;
+
+import com.android.contacts.R;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Drawable that draws three pictures for the QuickContact-Background. ColorFilter is ignored
+ */
+public class QuickContactBackgroundDrawable extends Drawable {
+ private Drawable mLeftDrawable;
+ private Drawable mMiddleDrawable;
+ private Drawable mRightDrawable;
+ private int mRequestedX = Integer.MIN_VALUE;
+ private boolean mBoundsSet = false;
+ private int mAlpha = -1;
+ private int mBottomOverride = Integer.MIN_VALUE;
+
+ @Override
+ public void setAlpha(int alpha) {
+ mAlpha = alpha;
+ setChildAlpha();
+ }
+
+ /**
+ * Overrides the bottom bounds. This is used for the animation when the QuickContact
+ * expands/collapses options
+ */
+ public void setBottomOverride(int value) {
+ mBottomOverride = value;
+ setChildBounds();
+ invalidateSelf();
+ }
+
+ public void clearBottomOverride() {
+ mBottomOverride = Integer.MIN_VALUE;
+ invalidateSelf();
+ setChildBounds();
+ }
+
+ public float getBottomOverride() {
+ return mBottomOverride;
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ public void configure(Resources resources, boolean arrowUp, int requestedX) {
+ mLeftDrawable = resources.getDrawable(arrowUp
+ ? R.drawable.quickactions_arrowup_left_holo_light
+ : R.drawable.quickactions_arrowdown_left_holo_light);
+ mMiddleDrawable = resources.getDrawable(arrowUp
+ ? R.drawable.quickactions_arrowup_middle_holo_light
+ : R.drawable.quickactions_arrowdown_middle_holo_light);
+ mRightDrawable = resources.getDrawable(arrowUp
+ ? R.drawable.quickactions_arrowup_right_holo_light
+ : R.drawable.quickactions_arrowdown_right_holo_light);
+
+ mRequestedX = requestedX;
+
+ setChildAlpha();
+ setChildBounds();
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ mBoundsSet = true;
+ setChildBounds();
+ }
+
+ private void setChildAlpha() {
+ if (mAlpha == -1) return;
+
+ if (mLeftDrawable != null) mLeftDrawable.setAlpha(mAlpha);
+ if (mMiddleDrawable != null) mMiddleDrawable.setAlpha(mAlpha);
+ if (mRightDrawable != null) mRightDrawable.setAlpha(mAlpha);
+ }
+
+ private void setChildBounds() {
+ if (mRequestedX == Integer.MIN_VALUE) return;
+ if (!mBoundsSet) return;
+
+ final Rect bounds = getBounds();
+ int middleLeft = mRequestedX - mMiddleDrawable.getIntrinsicWidth() / 2;
+ int middleRight = mRequestedX + mMiddleDrawable.getIntrinsicWidth() / 2;
+
+ // ensure left drawable is not smaller than its Intrinsic Width
+ final int leftExtra = (middleLeft - bounds.left) - mLeftDrawable.getIntrinsicWidth();
+ if (leftExtra < 0) {
+ middleLeft -= leftExtra;
+ middleRight -= leftExtra;
+ }
+
+ // ensure right drawable is not smaller than its Intrinsic Width
+ final int rightExtra = (bounds.right - middleRight) - mRightDrawable.getIntrinsicWidth();
+ if (rightExtra < 0) {
+ middleLeft += rightExtra;
+ middleRight += rightExtra;
+ }
+
+ final int bottom = mBottomOverride == Integer.MIN_VALUE ? bounds.bottom : mBottomOverride;
+ mLeftDrawable.setBounds(bounds.left, bounds.top, middleLeft, bottom);
+ mMiddleDrawable.setBounds(middleLeft, bounds.top, middleRight, bottom);
+ mRightDrawable.setBounds(middleRight, bounds.top, bounds.right, bottom);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ mLeftDrawable.draw(canvas);
+ mMiddleDrawable.draw(canvas);
+ mRightDrawable.draw(canvas);
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/QuickContactRootLayout.java b/src/com/android/contacts/quickcontact/QuickContactRootLayout.java
new file mode 100644
index 0000000..007783a
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/QuickContactRootLayout.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 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.quickcontact;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.LinearLayout;
+
+/**
+ * Custom layout for Quick Contact. It intercepts the BACK key and
+ * close QC even when the soft keyboard is open.
+ */
+public class QuickContactRootLayout extends LinearLayout {
+ private Listener mListener;
+
+ public QuickContactRootLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setListener(Listener value) {
+ mListener = value;
+ }
+
+ /**
+ * Intercepts the BACK key event and dismisses QuickContact window.
+ */
+ @Override
+ public boolean dispatchKeyEventPreIme(KeyEvent event) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ if (mListener != null) mListener.onBackPressed();
+ return true;
+ }
+ return super.dispatchKeyEventPreIme(event);
+ }
+
+ public interface Listener {
+ void onBackPressed();
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/QuickContactWindow.java b/src/com/android/contacts/quickcontact/QuickContactWindow.java
new file mode 100644
index 0000000..063d446
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/QuickContactWindow.java
@@ -0,0 +1,1439 @@
+/*
+ * 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.quickcontact;
+
+import com.android.contacts.Collapser;
+import com.android.contacts.ContactPresenceIconUtil;
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.util.Constants;
+import com.android.contacts.util.DataStatus;
+import com.android.contacts.util.NotifyingAsyncQueryHandler;
+import com.android.internal.policy.PolicyManager;
+import com.google.android.collect.Lists;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.ActivityNotFoundException;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.QuickContact;
+import android.provider.ContactsContract.RawContacts;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.ActionMode;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.FrameLayout;
+import android.widget.HorizontalScrollView;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Window that shows QuickContact dialog for a specific {@link Contacts#_ID}.
+ */
+public class QuickContactWindow implements Window.Callback,
+ NotifyingAsyncQueryHandler.AsyncQueryListener, View.OnClickListener,
+ AbsListView.OnItemClickListener, KeyEvent.Callback, OnGlobalLayoutListener,
+ QuickContactRootLayout.Listener {
+ private static final String TAG = "QuickContactWindow";
+
+ /**
+ * Interface used to allow the person showing a {@link QuickContactWindow} to
+ * know when the window has been dismissed.
+ */
+ public interface OnDismissListener {
+ public void onDismiss(QuickContactWindow dialog);
+ }
+
+ private final static int ANIMATION_FADE_IN_TIME = 100;
+ private final static int ANIMATION_FADE_OUT_TIME = 100;
+ private final static int ANIMATION_EXPAND_TIME = 100;
+ private final static int ANIMATION_COLLAPSE_TIME = 100;
+
+ private final Context mContext;
+ private final LayoutInflater mInflater;
+ private final WindowManager mWindowManager;
+ private Window mWindow;
+ private View mDecor;
+ private final Rect mRect = new Rect();
+
+ private boolean mDismissed = false;
+ private boolean mQuerying = false;
+ private boolean mShowing = false;
+
+ private NotifyingAsyncQueryHandler mHandler;
+ private OnDismissListener mDismissListener;
+
+ private Uri mLookupUri;
+ private Rect mAnchor;
+
+ private int mScreenWidth;
+ private int mUseableScreenHeight;
+ private int mRequestedY;
+
+ private boolean mHasValidSocial = false;
+
+ private int mMode;
+ private QuickContactRootLayout mRootView;
+ private QuickContactBackgroundDrawable mBackground;
+ private View mHeader;
+ private HorizontalScrollView mTrackScroll;
+ private ViewGroup mTrack;
+
+ private FrameLayout mFooter;
+ private LinearLayout mFooterDisambig;
+ private LinearLayout mFooterClearDefaults;
+ private ListView mResolveList;
+ private CheckableImageView mLastAction;
+ private CheckBox mSetPrimaryCheckBox;
+ private ListView mDefaultsListView;
+ private Button mClearDefaultsButton;
+
+ /**
+ * Keeps the default action per mimetype. Empty if no default actions are set
+ */
+ private HashMap<String, Action> mDefaultsMap = new HashMap<String, Action>();
+
+ private int mWindowRecycled = 0;
+ private int mActionRecycled = 0;
+
+ /**
+ * Set of {@link Action} that are associated with the aggregate currently
+ * displayed by this dialog, represented as a map from {@link String}
+ * MIME-type to a list of {@link Action}.
+ */
+ private ActionMultiMap mActions = new ActionMultiMap();
+
+ /**
+ * Pool of unused {@link CheckableImageView} that have previously been
+ * inflated, and are ready to be recycled through {@link #obtainView()}.
+ */
+ private LinkedList<CheckableImageView> mActionPool = new LinkedList<CheckableImageView>();
+
+ private String[] mExcludeMimes;
+
+ /**
+ * {@link #PRECEDING_MIMETYPES} and {@link #FOLLOWING_MIMETYPES} are used to sort MIME-types.
+ *
+ * <p>The MIME-types in {@link #PRECEDING_MIMETYPES} appear in the front of the dialog,
+ * in the order in the array.
+ *
+ * <p>The ones in {@link #FOLLOWING_MIMETYPES} appear in the end of the dialog, in alphabetical
+ * order.
+ *
+ * <p>The rest go between them, in the order in the array.
+ */
+ private static final String[] PRECEDING_MIMETYPES = new String[] {
+ Phone.CONTENT_ITEM_TYPE,
+ SipAddress.CONTENT_ITEM_TYPE,
+ Contacts.CONTENT_ITEM_TYPE,
+ Constants.MIME_TYPE_SMS_ADDRESS,
+ Email.CONTENT_ITEM_TYPE,
+ };
+
+ /**
+ * See {@link #PRECEDING_MIMETYPES}.
+ */
+ private static final String[] FOLLOWING_MIMETYPES = new String[] {
+ StructuredPostal.CONTENT_ITEM_TYPE,
+ Website.CONTENT_ITEM_TYPE,
+ };
+
+ /**
+ * List of MIMETYPES that do not represent real data rows and can therefore not be set
+ * as defaults
+ */
+ private static final ArrayList<String> VIRTUAL_MIMETYPES = Lists.newArrayList(new String[] {
+ Im.CONTENT_ITEM_TYPE,
+ Constants.MIME_TYPE_SMS_ADDRESS,
+ Constants.MIME_TYPE_VIDEO_CHAT,
+ });
+ private static final int TOKEN_DATA = 1;
+
+ static final boolean TRACE_LAUNCH = false;
+ static final String TRACE_TAG = "quickcontact";
+
+ /**
+ * Prepare a dialog to show in the given {@link Context}.
+ */
+ public QuickContactWindow(Context context) {
+ mContext = new ContextThemeWrapper(context, R.style.QuickContact);
+ mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+
+ mWindow = PolicyManager.makeNewWindow(mContext);
+ mWindow.setCallback(this);
+ mWindow.setWindowManager(mWindowManager, null, null);
+ mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED);
+
+ mWindow.setContentView(R.layout.quickcontact);
+
+ mRootView = (QuickContactRootLayout)mWindow.findViewById(R.id.root);
+ mRootView.setListener(this);
+ mRootView.setFocusable(true);
+ mRootView.setFocusableInTouchMode(true);
+ mRootView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+
+ mBackground = new QuickContactBackgroundDrawable();
+ mRootView.setBackgroundDrawable(mBackground);
+
+ mScreenWidth = mWindowManager.getDefaultDisplay().getWidth();
+ // Status bar height
+ final int screenMarginBottom = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.screen_margin_bottom);
+ mUseableScreenHeight = mWindowManager.getDefaultDisplay().getHeight() - screenMarginBottom;
+
+ mTrack = (ViewGroup) mWindow.findViewById(R.id.quickcontact);
+ mTrackScroll = (HorizontalScrollView) mWindow.findViewById(R.id.scroll);
+
+ mFooter = (FrameLayout) mWindow.findViewById(R.id.footer);
+ mFooterDisambig = (LinearLayout) mWindow.findViewById(R.id.footer_disambig);
+ mFooterClearDefaults = (LinearLayout) mWindow.findViewById(R.id.footer_clear_defaults);
+ mResolveList = (ListView) mWindow.findViewById(android.R.id.list);
+ mSetPrimaryCheckBox = (CheckBox) mWindow.findViewById(android.R.id.checkbox);
+
+ mDefaultsListView = (ListView) mWindow.findViewById(R.id.defaults_list);
+ mClearDefaultsButton = (Button) mWindow.findViewById(R.id.clear_defaults_button);
+ mClearDefaultsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ clearDefaults();
+ }
+ });
+
+ mResolveList.setOnItemClickListener(QuickContactWindow.this);
+
+ mHandler = new NotifyingAsyncQueryHandler(mContext, this);
+ }
+
+ /**
+ * Prepare a dialog to show in the given {@link Context}, and notify the
+ * given {@link OnDismissListener} each time this dialog is dismissed.
+ */
+ public QuickContactWindow(Context context, OnDismissListener dismissListener) {
+ this(context);
+ mDismissListener = dismissListener;
+ }
+
+ private View getHeaderView(int mode) {
+ View header = null;
+ switch (mode) {
+ case QuickContact.MODE_SMALL:
+ header = mWindow.findViewById(R.id.header_small);
+ break;
+ case QuickContact.MODE_MEDIUM:
+ header = mWindow.findViewById(R.id.header_medium);
+ break;
+ case QuickContact.MODE_LARGE:
+ header = mWindow.findViewById(R.id.header_large);
+ break;
+ }
+
+ if (header instanceof ViewStub) {
+ // Inflate actual header if we picked a stub
+ final ViewStub stub = (ViewStub)header;
+ header = stub.inflate();
+ } else if (header != null) {
+ header.setVisibility(View.VISIBLE);
+ }
+
+ return header;
+ }
+
+ /**
+ * Start showing a dialog for the given {@link Contacts#_ID} pointing
+ * towards the given location.
+ */
+ public synchronized void show(Uri lookupUri, Rect anchor, int mode, String[] excludeMimes) {
+ if (mQuerying || mShowing) {
+ Log.w(TAG, "dismissing before showing");
+ dismissInternal();
+ }
+
+ if (TRACE_LAUNCH && !android.os.Debug.isMethodTracingActive()) {
+ android.os.Debug.startMethodTracing(TRACE_TAG);
+ }
+
+ // Validate incoming parameters
+ final boolean validMode = (mode == QuickContact.MODE_SMALL
+ || mode == QuickContact.MODE_MEDIUM || mode == QuickContact.MODE_LARGE);
+ if (!validMode) {
+ throw new IllegalArgumentException("Invalid mode, expecting MODE_LARGE, "
+ + "MODE_MEDIUM, or MODE_SMALL");
+ }
+
+ if (anchor == null) {
+ throw new IllegalArgumentException("Missing anchor rectangle");
+ }
+
+ // Prepare header view for requested mode
+ mLookupUri = lookupUri;
+ mAnchor = new Rect(anchor);
+ mMode = mode;
+ mExcludeMimes = excludeMimes;
+
+ mHeader = getHeaderView(mode);
+
+ setHeaderText(R.id.name, R.string.quickcontact_missing_name);
+
+ setHeaderText(R.id.status, null);
+ setHeaderText(R.id.timestamp, null);
+
+ setHeaderImage(R.id.presence, null);
+
+ resetTrack();
+
+ // We need to have a focused view inside the QuickContact window so
+ // that the BACK key event can be intercepted
+ mRootView.requestFocus();
+
+ mHasValidSocial = false;
+ mDismissed = false;
+ mQuerying = true;
+
+ // Start background query for data, but only select photo rows when they
+ // directly match the super-primary PHOTO_ID.
+ final Uri dataUri = getDataUri(lookupUri);
+ mHandler.cancelOperation(TOKEN_DATA);
+
+ // Only request photo data when required by mode
+ if (mMode == QuickContact.MODE_LARGE) {
+ // Select photos, but only super-primary
+ mHandler.startQuery(TOKEN_DATA, lookupUri, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
+ + "!=? OR (" + Data.MIMETYPE + "=? AND " + Data._ID + "=" + Contacts.PHOTO_ID
+ + ")", new String[] { Photo.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE }, null);
+ } else {
+ // Exclude all photos from cursor
+ mHandler.startQuery(TOKEN_DATA, lookupUri, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
+ + "!=?", new String[] { Photo.CONTENT_ITEM_TYPE }, null);
+ }
+ }
+
+ /**
+ * Build a {@link Uri} into the {@link Data} table for the requested
+ * {@link Contacts#CONTENT_LOOKUP_URI} style {@link Uri}.
+ */
+ private Uri getDataUri(Uri lookupUri) {
+ // TODO: Formalize method of extracting LOOKUP_KEY
+ final List<String> path = lookupUri.getPathSegments();
+ final boolean validLookup = path.size() >= 3 && "lookup".equals(path.get(1));
+ if (!validLookup) {
+ // We only accept valid lookup-style Uris
+ throw new IllegalArgumentException("Expecting lookup-style Uri");
+ } else if (path.size() == 3) {
+ // No direct _ID provided, so force a lookup
+ lookupUri = Contacts.lookupContact(mContext.getContentResolver(), lookupUri);
+ }
+
+ final long contactId = ContentUris.parseId(lookupUri);
+ return Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
+ Contacts.Data.CONTENT_DIRECTORY);
+ }
+
+ /**
+ * Creates and configures the background resource
+ */
+ private void configureBackground(boolean arrowUp, int requestedX) {
+ mBackground.configure(mContext.getResources(), arrowUp, requestedX);
+ }
+
+ /**
+ * Actual internal method to show this dialog. Called only by
+ * {@link #considerShowing()} when all data requirements have been met.
+ */
+ private void showInternal() {
+ mDecor = mWindow.getDecorView();
+ mDecor.getViewTreeObserver().addOnGlobalLayoutListener(this);
+ WindowManager.LayoutParams layoutParams = mWindow.getAttributes();
+
+ layoutParams.width = mContext.getResources().getDimensionPixelSize(
+ R.dimen.quick_contact_width);
+ layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+
+ // Try to left align with the anchor control
+ if (mAnchor.left + layoutParams.width <= mScreenWidth) {
+ layoutParams.x = mAnchor.left;
+ } else {
+ // Not enough space. Try to right align to the anchor
+ if (mAnchor.right - layoutParams.width >= 0) {
+ layoutParams.x = mAnchor.right - layoutParams.width;
+ } else {
+ // Also not enough space. Use the whole screen width available
+ layoutParams.x = 0;
+ layoutParams.width = mScreenWidth;
+ }
+ }
+
+ // Force layout measuring pass so we have baseline numbers
+ mDecor.measure(layoutParams.width, layoutParams.height);
+ final int blockHeight = mDecor.getMeasuredHeight();
+
+ layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
+
+ if (mUseableScreenHeight - mAnchor.bottom > blockHeight) {
+ // Show downwards callout when enough room, aligning block top with bottom of
+ // anchor area, and adjusting to inset arrow.
+ configureBackground(true, mAnchor.centerX() - layoutParams.x);
+ layoutParams.y = mAnchor.bottom;
+ layoutParams.windowAnimations = R.style.QuickContactBelowAnimation;
+ } else {
+ // Show upwards callout, aligning bottom block
+ // edge with top of anchor area, and adjusting to inset arrow.
+ configureBackground(false, mAnchor.centerX() - layoutParams.x);
+ layoutParams.y = mAnchor.top - blockHeight;
+ layoutParams.windowAnimations = R.style.QuickContactAboveAnimation;
+ }
+
+ layoutParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+
+ mRequestedY = layoutParams.y;
+ mWindowManager.addView(mDecor, layoutParams);
+ mShowing = true;
+ mQuerying = false;
+ mDismissed = false;
+
+ if (TRACE_LAUNCH) {
+ android.os.Debug.stopMethodTracing();
+ Log.d(TAG, "Window recycled " + mWindowRecycled + " times, chiclets "
+ + mActionRecycled + " times");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onGlobalLayout() {
+ layoutInScreen();
+ }
+
+ /**
+ * Adjust vertical {@link WindowManager.LayoutParams} to fit window as best
+ * as possible, shifting up to display content as needed.
+ */
+ private void layoutInScreen() {
+ if (!mShowing) return;
+
+ final WindowManager.LayoutParams l = mWindow.getAttributes();
+ final int originalY = l.y;
+
+ final int blockHeight = mDecor.getHeight();
+
+ l.y = mRequestedY;
+ if (mRequestedY + blockHeight > mUseableScreenHeight) {
+ // Shift up from bottom when overflowing
+ l.y = mUseableScreenHeight - blockHeight;
+ }
+
+ if (originalY != l.y) {
+ // Only update when value is changed
+ mWindow.setAttributes(l);
+ }
+ }
+
+ /**
+ * Dismiss this dialog if showing.
+ */
+ public synchronized void dismiss() {
+ // Notify any listeners that we've been dismissed
+ if (mDismissListener != null) {
+ mDismissListener.onDismiss(this);
+ }
+
+ dismissInternal();
+ }
+
+ private void dismissInternal() {
+ // Remove any attached window decor for recycling
+ boolean hadDecor = mDecor != null;
+ if (hadDecor) {
+ mWindowManager.removeView(mDecor);
+ mWindowRecycled++;
+ mDecor.getViewTreeObserver().removeGlobalOnLayoutListener(this);
+ mDecor = null;
+ mWindow.closeAllPanels();
+ }
+ mShowing = false;
+ mDismissed = true;
+
+ // Cancel any pending queries
+ mHandler.cancelOperation(TOKEN_DATA);
+ mQuerying = false;
+
+ // Completely hide header and reset track
+ mHeader.setVisibility(View.GONE);
+ resetTrack();
+ }
+
+ /**
+ * Reset track to initial state, recycling any chiclets.
+ */
+ private void resetTrack() {
+ // Clear background height-animation override
+ mBackground.clearBottomOverride();
+
+ // Release reference to last chiclet
+ mLastAction = null;
+
+ // Clear track actions and scroll to hard left
+ mActions.clear();
+
+ // Recycle any chiclets in use
+ for (int i = mTrack.getChildCount() - 1; i >= 0; i--) {
+ releaseView((CheckableImageView)mTrack.getChildAt(i));
+ mTrack.removeViewAt(i);
+ }
+
+ mTrackScroll.fullScroll(View.FOCUS_LEFT);
+
+ // Clear any primary requests
+ mSetPrimaryCheckBox.setChecked(false);
+
+ setNewActionViewChecked(null);
+ mFooter.setVisibility(View.GONE);
+ }
+
+ /**
+ * Consider showing this window, which will only call through to
+ * {@link #showInternal()} when all data items are present.
+ */
+ private void considerShowing() {
+ if (!mShowing && !mDismissed) {
+ if (mMode == QuickContact.MODE_MEDIUM && !mHasValidSocial) {
+ // Missing valid social, swap medium for small header
+ mHeader.setVisibility(View.GONE);
+ mHeader = getHeaderView(QuickContact.MODE_SMALL);
+ }
+
+ // All queries have returned, pull curtain
+ showInternal();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public synchronized void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ // Bail early when query is stale
+ if (cookie != mLookupUri) return;
+
+ if (cursor == null) {
+ // Problem while running query, so bail without showing
+ Log.w(TAG, "Missing cursor for token=" + token);
+ this.dismiss();
+ return;
+ }
+
+ handleData(cursor);
+
+ if (!cursor.isClosed()) {
+ cursor.close();
+ }
+
+ considerShowing();
+ }
+
+ /** Assign this string to the view, if found in {@link #mHeader}. */
+ private void setHeaderText(int id, int resId) {
+ setHeaderText(id, mContext.getResources().getText(resId));
+ }
+
+ /** Assign this string to the view, if found in {@link #mHeader}. */
+ private void setHeaderText(int id, CharSequence value) {
+ final View view = mHeader.findViewById(id);
+ if (view instanceof TextView) {
+ ((TextView)view).setText(value);
+ view.setVisibility(TextUtils.isEmpty(value) ? View.GONE : View.VISIBLE);
+ }
+ }
+
+ /** Assign this image to the view, if found in {@link #mHeader}. */
+ private void setHeaderImage(int id, Drawable drawable) {
+ final View view = mHeader.findViewById(id);
+ if (view instanceof ImageView) {
+ ((ImageView)view).setImageDrawable(drawable);
+ view.setVisibility(drawable == null ? View.GONE : View.VISIBLE);
+ }
+ }
+
+ /**
+ * Check if the given MIME-type appears in the list of excluded MIME-types
+ * that the most-recent caller requested.
+ */
+ private boolean isMimeExcluded(String mimeType) {
+ if (mExcludeMimes == null) return false;
+ for (String excludedMime : mExcludeMimes) {
+ if (TextUtils.equals(excludedMime, mimeType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Handle the result from the {@link #TOKEN_DATA} query.
+ */
+ private void handleData(Cursor cursor) {
+ final ResolveCache cache = ResolveCache.getInstance(mContext.getPackageManager());
+ if (cursor == null) return;
+ if (cursor.getCount() == 0) {
+ Toast.makeText(mContext, R.string.invalidContactMessage, Toast.LENGTH_LONG).show();
+ dismiss();
+ return;
+ }
+
+ if (!isMimeExcluded(Contacts.CONTENT_ITEM_TYPE)) {
+ // Add the profile shortcut action
+ final Action action = new ProfileAction(mContext, mLookupUri);
+ mActions.put(Contacts.CONTENT_ITEM_TYPE, action);
+ }
+
+ mDefaultsMap.clear();
+
+ final DataStatus status = new DataStatus();
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+ final ImageView photoView = (ImageView)mHeader.findViewById(R.id.photo);
+
+ Bitmap photoBitmap = null;
+ while (cursor.moveToNext()) {
+ final long dataId = cursor.getLong(DataQuery._ID);
+ final String accountType = cursor.getString(DataQuery.ACCOUNT_TYPE);
+ final String mimeType = cursor.getString(DataQuery.MIMETYPE);
+ final boolean isPrimary = cursor.getInt(DataQuery.IS_PRIMARY) != 0;
+ final boolean isSuperPrimary = cursor.getInt(DataQuery.IS_SUPER_PRIMARY) != 0;
+
+ // Handle any social status updates from this row
+ status.possibleUpdate(cursor);
+
+ // Skip this data item if MIME-type excluded
+ if (isMimeExcluded(mimeType)) continue;
+
+ // Handle photos included as data row
+ if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ final int colPhoto = cursor.getColumnIndex(Photo.PHOTO);
+ final byte[] photoBlob = cursor.getBlob(colPhoto);
+ if (photoBlob != null) {
+ photoBitmap = BitmapFactory.decodeByteArray(photoBlob, 0, photoBlob.length);
+ }
+ continue;
+ }
+
+ final DataKind kind = accountTypes.getKindOrFallback(accountType, mimeType);
+
+ if (kind != null) {
+ // Build an action for this data entry, find a mapping to a UI
+ // element, build its summary from the cursor, and collect it
+ // along with all others of this MIME-type.
+ final Action action = new DataAction(mContext, mimeType, kind, dataId, cursor);
+ final boolean wasAdded = considerAdd(action, cache);
+ if (wasAdded) {
+ // Remember the default
+ if (isSuperPrimary || (isPrimary && (mDefaultsMap.get(mimeType) == null))) {
+ mDefaultsMap.put(mimeType, action);
+ }
+ }
+ }
+
+ // If phone number, also insert as text message action
+ if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && kind != null) {
+ final DataAction action = new DataAction(mContext, Constants.MIME_TYPE_SMS_ADDRESS,
+ kind, dataId, cursor);
+ considerAdd(action, cache);
+ }
+
+ boolean isIm = Im.CONTENT_ITEM_TYPE.equals(mimeType);
+
+ // Handle Email rows with presence data as Im entry
+ final boolean hasPresence = !cursor.isNull(DataQuery.PRESENCE);
+ if (hasPresence && Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ final DataKind imKind = accountTypes.getKindOrFallback(accountType,
+ Im.CONTENT_ITEM_TYPE);
+ if (imKind != null) {
+ final DataAction action = new DataAction(mContext, Im.CONTENT_ITEM_TYPE, imKind,
+ dataId, cursor);
+ considerAdd(action, cache);
+ isIm = true;
+ }
+ }
+
+ if (hasPresence && isIm) {
+ int chatCapability = cursor.getInt(DataQuery.CHAT_CAPABILITY);
+ if ((chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) {
+ final DataKind imKind = accountTypes.getKindOrFallback(accountType,
+ Im.CONTENT_ITEM_TYPE);
+ if (imKind != null) {
+ final DataAction chatAction = new DataAction(mContext,
+ Constants.MIME_TYPE_VIDEO_CHAT, imKind, dataId, cursor);
+ considerAdd(chatAction, cache);
+ }
+ }
+ }
+ }
+
+ if (mDefaultsMap.size() != 0) {
+ final Action action = new ClearDefaultsAction();
+ mActions.put(action.getMimeType(), action);
+ }
+
+ if (cursor.moveToLast()) {
+ // Read contact information from last data row
+ final String name = cursor.getString(DataQuery.DISPLAY_NAME);
+ final int presence = cursor.getInt(DataQuery.CONTACT_PRESENCE);
+ final int chatCapability = cursor.getInt(DataQuery.CONTACT_CHAT_CAPABILITY);
+ final Drawable statusIcon = ContactPresenceIconUtil.getChatCapabilityIcon(
+ mContext, presence, chatCapability);
+
+ setHeaderText(R.id.name, name);
+ setHeaderImage(R.id.presence, statusIcon);
+ }
+
+ if (photoView != null) {
+ // Place photo when discovered in data, otherwise hide
+ photoView.setVisibility(photoBitmap != null ? View.VISIBLE : View.GONE);
+ photoView.setImageBitmap(photoBitmap);
+ }
+
+ mHasValidSocial = status.isValid();
+ if (mHasValidSocial && mMode != QuickContact.MODE_SMALL) {
+ // Update status when valid was found
+ setHeaderText(R.id.status, status.getStatus());
+ setHeaderText(R.id.timestamp, status.getTimestampLabel(mContext));
+ }
+
+ // Turn our list of actions into UI elements
+
+ // All the mime-types to add.
+ final Set<String> containedTypes = new HashSet<String>(mActions.keySet());
+
+ boolean hasData = false;
+
+ // First, add PRECEDING_MIMETYPES, which are most common.
+ for (String mimeType : PRECEDING_MIMETYPES) {
+ if (containedTypes.contains(mimeType)) {
+ hasData = true;
+ mTrack.addView(inflateAction(mimeType, cache));
+ containedTypes.remove(mimeType);
+ }
+ }
+
+ // Keep the current index to append non PRECEDING/FOLLOWING items.
+ final int indexAfterPreceding = mTrack.getChildCount() - 1;
+
+ // Then, add FOLLOWING_MIMETYPES, which are least common.
+ for (String mimeType : FOLLOWING_MIMETYPES) {
+ if (containedTypes.contains(mimeType)) {
+ hasData = true;
+ mTrack.addView(inflateAction(mimeType, cache));
+ containedTypes.remove(mimeType);
+ }
+ }
+
+ // Show the clear-defaults button? If yes, it goes to the end of the list
+ if (containedTypes.contains(ClearDefaultsAction.PSEUDO_MIME_TYPE)) {
+ final ClearDefaultsAction action = (ClearDefaultsAction) mActions.get(
+ ClearDefaultsAction.PSEUDO_MIME_TYPE).get(0);
+ final CheckableImageView view = obtainView();
+ view.setChecked(false);
+ final String description = mContext.getResources().getString(
+ R.string.quickcontact_clear_defaults_description);
+ view.setContentDescription(description);
+ view.setImageResource(R.drawable.ic_menu_settings_holo_light);
+ view.setOnClickListener(this);
+ view.setTag(action);
+ mTrack.addView(view);
+ containedTypes.remove(ClearDefaultsAction.PSEUDO_MIME_TYPE);
+ }
+
+ // Go back to just after PRECEDING_MIMETYPES, and append the rest.
+ int index = indexAfterPreceding;
+ final String[] remainingTypes = containedTypes.toArray(new String[containedTypes.size()]);
+ if (remainingTypes.length > 0) hasData = true;
+ Arrays.sort(remainingTypes);
+ for (String mimeType : remainingTypes) {
+ mTrack.addView(inflateAction(mimeType, cache), index++);
+ }
+
+ if (!hasData) {
+ // When there is no data to display, add a TextView to show the user there's no data
+ View view = mInflater.inflate(R.layout.quickcontact_item_nodata, mTrack, false);
+ mTrack.addView(view, index++);
+ }
+ }
+
+ /**
+ * Clears the defaults currently set on the Contact
+ */
+ private void clearDefaults() {
+ final Set<String> mimeTypesKeySet = mDefaultsMap.keySet();
+ // Copy to array so that we can modify the HashMap below
+ final String[] mimeTypes = new String[mimeTypesKeySet.size()];
+ mimeTypesKeySet.toArray(mimeTypes);
+
+ // Send clear default Intents, one by one
+ for (String mimeType : mimeTypes) {
+ final Action action = mDefaultsMap.get(mimeType);
+ final Intent intent =
+ ContactSaveService.createClearPrimaryIntent(mContext, action.getDataId());
+ mContext.startService(intent);
+ mDefaultsMap.remove(mimeType);
+ }
+
+ // Close up and remove the configure default button
+ animateCollapse(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = mTrack.getChildCount() - 1; i >= 0; i--) {
+ final CheckableImageView button = (CheckableImageView) mTrack.getChildAt(i);
+ if (button.getTag() instanceof ClearDefaultsAction) {
+ releaseView(button);
+ mTrack.removeViewAt(i);
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Consider adding the given {@link Action}, which will only happen if
+ * {@link PackageManager} finds an application to handle
+ * {@link Action#getIntent()}.
+ * @return true if action has been added
+ */
+ private boolean considerAdd(Action action, ResolveCache resolveCache) {
+ if (resolveCache.hasResolve(action)) {
+ mActions.put(action.getMimeType(), action);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Obtain a new {@link CheckableImageView} for a new chiclet, either by
+ * recycling one from {@link #mActionPool}, or by inflating a new one. When
+ * finished, use {@link #releaseView(CheckableImageView)} to return back into the pool for
+ * later recycling.
+ */
+ private synchronized CheckableImageView obtainView() {
+ CheckableImageView view = mActionPool.poll();
+ if (view == null || QuickContactActivity.FORCE_CREATE) {
+ view = (CheckableImageView) mInflater.inflate(R.layout.quickcontact_item, mTrack,
+ false);
+ }
+ return view;
+ }
+
+ /**
+ * Return the given {@link CheckableImageView} into our internal pool for
+ * possible recycling during another pass.
+ */
+ private synchronized void releaseView(CheckableImageView view) {
+ mActionPool.offer(view);
+ mActionRecycled++;
+ }
+
+ /**
+ * Inflate the in-track view for the action of the given MIME-type, collapsing duplicate values.
+ * Will use the icon provided by the {@link DataKind}.
+ */
+ private View inflateAction(String mimeType, ResolveCache resolveCache) {
+ final CheckableImageView view = obtainView();
+
+ // Add direct intent if single child, otherwise flag for multiple
+ List<Action> children = mActions.get(mimeType);
+ if (children.size() > 1) {
+ Collapser.collapseList(children);
+ }
+ view.setTag(mimeType);
+ final Action firstInfo = children.get(0);
+
+ // Set icon and listen for clicks
+ final CharSequence descrip = resolveCache.getDescription(firstInfo);
+ final Drawable icon = resolveCache.getIcon(firstInfo);
+ view.setChecked(false);
+ view.setContentDescription(descrip);
+ view.setImageDrawable(icon);
+ view.setOnClickListener(this);
+ return view;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ // Pass list item clicks along so that Intents are handled uniformly
+ onClick(view);
+ }
+
+ /**
+ * Helper for checking an action view
+ */
+ private void setNewActionViewChecked(CheckableImageView actionView) {
+ if (mLastAction != null) mLastAction.setChecked(false);
+ if (actionView != null) actionView.setChecked(true);
+ mLastAction = actionView;
+ }
+
+ /**
+ * Animates collpasing of the disambig area. When done, it expands again to the new size
+ */
+ private void animateCollapse(final Runnable whenDone) {
+ final int oldBottom = mBackground.getBounds().bottom;
+ mBackground.setBottomOverride(oldBottom);
+
+ final ObjectAnimator fadeOutAnimator = ObjectAnimator.ofFloat(mFooter, "alpha",
+ 1.0f, 0.0f);
+ fadeOutAnimator.setDuration(ANIMATION_FADE_OUT_TIME);
+ fadeOutAnimator.start();
+
+ final ObjectAnimator collapseAnimator = ObjectAnimator.ofInt(mBackground,
+ "bottomOverride", oldBottom, oldBottom - mFooter.getHeight());
+ collapseAnimator.setDuration(ANIMATION_COLLAPSE_TIME);
+ collapseAnimator.setStartDelay(ANIMATION_FADE_OUT_TIME);
+ collapseAnimator.start();
+
+ collapseAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mFooter.setVisibility(View.GONE);
+ new Handler().post(whenDone);
+ }
+ });
+ }
+
+ /**
+ * Animates expansion of the disambig area.
+ * @param showClearDefaults true to expand to clear defaults. false to expand to intent disambig
+ */
+ private void animateExpand(boolean showClearDefaults) {
+ mFooter.setVisibility(View.VISIBLE);
+ mFooterDisambig.setVisibility(showClearDefaults ? View.GONE : View.VISIBLE);
+ mFooterClearDefaults.setVisibility(showClearDefaults ? View.VISIBLE : View.GONE);
+ final int oldBottom = mBackground.getBounds().bottom;
+ mBackground.setBottomOverride(oldBottom);
+ mFooter.setAlpha(0.0f);
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ final int newBottom = mBackground.getBounds().bottom;
+ final ObjectAnimator expandAnimator = ObjectAnimator.ofInt(mBackground,
+ "bottomOverride", oldBottom, newBottom);
+ expandAnimator.setDuration(ANIMATION_EXPAND_TIME);
+ expandAnimator.start();
+
+ final ObjectAnimator fadeInAnimator = ObjectAnimator.ofFloat(mFooter,
+ "alpha", 0.0f, 1.0f);
+ fadeInAnimator.setDuration(ANIMATION_FADE_IN_TIME);
+ fadeInAnimator.setStartDelay(ANIMATION_EXPAND_TIME);
+ fadeInAnimator.start();
+ }
+ });
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onClick(View view) {
+ final boolean isActionView = (view instanceof CheckableImageView);
+ final CheckableImageView actionView = isActionView ? (CheckableImageView)view : null;
+ final Object tag = view.getTag();
+ if (tag instanceof ClearDefaultsAction) {
+ // Do nothing if already open
+ if (actionView == mLastAction) return;
+ // collapse any disambig that may already be open. the open clearing-defaults
+ final Runnable expandClearDefaultsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Show resolution list and set adapter
+ setNewActionViewChecked(actionView);
+ final Action[] actions = new Action[mDefaultsMap.size()];
+ mDefaultsMap.values().toArray(actions);
+
+ mDefaultsListView.setAdapter(new BaseAdapter() {
+ @Override
+ public int getCount() {
+ return actions.length;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return actions[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return false;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final View result = convertView != null ? convertView :
+ mInflater.inflate(R.layout.quickcontact_default_item,
+ parent, false);
+ // Set action title based on summary value
+ final Action defaultAction = actions[position];
+
+ TextView text1 = (TextView)result.findViewById(android.R.id.text1);
+ TextView text2 = (TextView)result.findViewById(android.R.id.text2);
+
+ text1.setText(defaultAction.getHeader());
+ text2.setText(defaultAction.getBody());
+
+ result.setTag(defaultAction);
+ return result;
+ }
+ });
+
+ animateExpand(true);
+ // Make sure we resize to make room for ListView
+ if (mDecor != null) {
+ mDecor.forceLayout();
+ mDecor.invalidate();
+ }
+ }
+ };
+ if (mFooter.getVisibility() == View.VISIBLE) {
+ animateCollapse(expandClearDefaultsRunnable);
+ } else {
+ new Handler().post(expandClearDefaultsRunnable);
+ }
+ return;
+ }
+
+ // Determine whether to launch a specific Action or show a disambig-List
+ final Action action;
+ final List<Action> actionList;
+ final boolean fromDisambigList;
+ final String mimeType;
+ if (tag instanceof Action) {
+ action = (Action) tag;
+ actionList = null;
+ fromDisambigList = true;
+ mimeType = action.getMimeType();
+ } else if (tag instanceof String) {
+ mimeType = (String) tag;
+ actionList = mActions.get(mimeType);
+
+ if (actionList.size() == 1) {
+ // Just one item? Pick that one
+ action = actionList.get(0);
+ } else if (mDefaultsMap.containsKey(mimeType)) {
+ // Default item? pick that one
+ action = mDefaultsMap.get(mimeType);
+ } else {
+ // Several actions and none is default.
+ action = null;
+ }
+ fromDisambigList = false;
+ } else {
+ throw new IllegalStateException("tag is neither Action nor (mimetype-) String");
+ }
+
+ if (action != null) {
+ final boolean isVirtual = VIRTUAL_MIMETYPES.contains(mimeType);
+ final boolean makePrimary = fromDisambigList && mSetPrimaryCheckBox.isChecked() &&
+ !isVirtual;
+ final Runnable startAppRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Incoming tag is concrete intent, so try launching
+ try {
+ mContext.startActivity(action.getIntent());
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(mContext, R.string.quickcontact_missing_app,
+ Toast.LENGTH_SHORT).show();
+ }
+
+ // Hide the resolution list, if present
+ setNewActionViewChecked(null);
+ dismiss();
+ mFooter.setVisibility(View.GONE);
+
+ // Set default?
+ final long dataId = action.getDataId();
+ if (makePrimary && dataId != -1) {
+ Intent serviceIntent = ContactSaveService.createSetSuperPrimaryIntent(
+ mContext, dataId);
+ mContext.startService(serviceIntent);
+ }
+ }
+ };
+ if (isActionView && mFooter.getVisibility() == View.VISIBLE) {
+ // If the footer is currently opened, animate its collapse and then
+ // execute the target app
+ animateCollapse(startAppRunnable);
+ } else {
+ // Defer the action to make the window properly repaint
+ new Handler().post(startAppRunnable);
+ }
+ return;
+ }
+
+ // This was not a specific Action. Expand the disambig-list
+ if (actionList == null) throw new IllegalStateException();
+
+ // Don't do anything if already open
+ if (actionView == mLastAction) return;
+ final Runnable configureListRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Show resolution list and set adapter
+ setNewActionViewChecked(actionView);
+ final boolean isVirtual = VIRTUAL_MIMETYPES.contains(mimeType);
+ mSetPrimaryCheckBox.setVisibility(isVirtual ? View.GONE : View.VISIBLE);
+
+ mResolveList.setAdapter(new BaseAdapter() {
+ @Override
+ public int getCount() {
+ return actionList.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return actionList.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final View result = convertView != null ? convertView :
+ mInflater.inflate(R.layout.quickcontact_resolve_item,
+ parent, false);
+ // Set action title based on summary value
+ final Action listAction = actionList.get(position);
+
+ TextView text1 = (TextView)result.findViewById(android.R.id.text1);
+ TextView text2 = (TextView)result.findViewById(android.R.id.text2);
+
+ text1.setText(listAction.getHeader());
+ text2.setText(listAction.getBody());
+
+ result.setTag(listAction);
+ return result;
+ }
+ });
+
+ animateExpand(false);
+ // Make sure we resize to make room for ListView
+ if (mDecor != null) {
+ mDecor.forceLayout();
+ mDecor.invalidate();
+ }
+ }
+ };
+ if (mFooter.getVisibility() == View.VISIBLE) {
+ // If the expansion list is currently opened, animate its collapse and then
+ // execute the target app
+ animateCollapse(configureListRunnable);
+ } else {
+ // Defer the action to make the window properly repaint
+ configureListRunnable.run();
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ dismiss();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (mWindow.superDispatchKeyEvent(event)) {
+ return true;
+ }
+ return event.dispatch(this, mDecor != null
+ ? mDecor.getKeyDispatcherState() : null, this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ event.startTracking();
+ return true;
+ }
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
+ && !event.isCanceled()) {
+ onBackPressed();
+ return true;
+ }
+
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean dispatchKeyShortcutEvent(KeyEvent event) {
+ return mWindow.superDispatchKeyShortcutEvent(event);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ // TODO: make this window accessible
+ return false;
+ }
+
+ /**
+ * Detect if the given {@link MotionEvent} is outside the boundaries of this
+ * window, which usually means we should dismiss.
+ */
+ protected void detectEventOutside(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN && mDecor != null) {
+ // Only try detecting outside events on down-press
+ mDecor.getHitRect(mRect);
+ final int x = (int)event.getX();
+ final int y = (int)event.getY();
+ if (!mRect.contains(x, y)) {
+ event.setAction(MotionEvent.ACTION_OUTSIDE);
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ detectEventOutside(event);
+ if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+ dismiss();
+ return true;
+ }
+ return mWindow.superDispatchTouchEvent(event);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean dispatchTrackballEvent(MotionEvent event) {
+ return mWindow.superDispatchTrackballEvent(event);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onContentChanged() {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onCreatePanelMenu(int featureId, Menu menu) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public View onCreatePanelView(int featureId) {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onMenuOpened(int featureId, Menu menu) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onPanelClosed(int featureId, Menu menu) {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onPreparePanel(int featureId, View view, Menu menu) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean onSearchRequested() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams attrs) {
+ if (mDecor != null) {
+ mWindowManager.updateViewLayout(mDecor, attrs);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onAttachedToWindow() {
+ // No actions
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onDetachedFromWindow() {
+ // No actions
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
+ return null;
+ }
+
+ @Override
+ public void onActionModeStarted(ActionMode mode) {
+ }
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {
+ }
+
+ private interface DataQuery {
+ final String[] PROJECTION = new String[] {
+ Data._ID,
+
+ RawContacts.ACCOUNT_TYPE,
+ Contacts.STARRED,
+ Contacts.DISPLAY_NAME,
+ Contacts.CONTACT_PRESENCE,
+ Contacts.CONTACT_CHAT_CAPABILITY,
+
+ Data.STATUS,
+ Data.STATUS_RES_PACKAGE,
+ Data.STATUS_ICON,
+ Data.STATUS_LABEL,
+ Data.STATUS_TIMESTAMP,
+ Data.PRESENCE,
+ Data.CHAT_CAPABILITY,
+
+ Data.RES_PACKAGE,
+ Data.MIMETYPE,
+ Data.IS_PRIMARY,
+ Data.IS_SUPER_PRIMARY,
+ Data.RAW_CONTACT_ID,
+
+ Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, Data.DATA5,
+ Data.DATA6, Data.DATA7, Data.DATA8, Data.DATA9, Data.DATA10, Data.DATA11,
+ Data.DATA12, Data.DATA13, Data.DATA14, Data.DATA15,
+ };
+
+ final int _ID = 0;
+
+ final int ACCOUNT_TYPE = 1;
+ final int STARRED = 2;
+ final int DISPLAY_NAME = 3;
+ final int CONTACT_PRESENCE = 4;
+ final int CONTACT_CHAT_CAPABILITY = 5;
+
+ final int STATUS = 6;
+ final int STATUS_RES_PACKAGE = 7;
+ final int STATUS_ICON = 8;
+ final int STATUS_LABEL = 9;
+ final int STATUS_TIMESTAMP = 10;
+ final int PRESENCE = 11;
+ final int CHAT_CAPABILITY = 12;
+
+ final int RES_PACKAGE = 13;
+ final int MIMETYPE = 14;
+ final int IS_PRIMARY = 15;
+ final int IS_SUPER_PRIMARY = 16;
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/ResolveCache.java b/src/com/android/contacts/quickcontact/ResolveCache.java
new file mode 100644
index 0000000..5d84253
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/ResolveCache.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2010 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.quickcontact;
+
+import com.google.android.collect.Sets;
+
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+
+import java.lang.ref.SoftReference;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Internally hold a cache of scaled icons based on {@link PackageManager}
+ * queries, keyed internally on MIME-type.
+ */
+public class ResolveCache {
+ /**
+ * Specific list {@link ApplicationInfo#packageName} of apps that are
+ * prefered <strong>only</strong> for the purposes of default icons when
+ * multiple {@link ResolveInfo} are found to match. This only happens when
+ * the user has not selected a default app yet, and they will still be
+ * presented with the system disambiguation dialog.
+ */
+ private static final HashSet<String> sPreferResolve = Sets.newHashSet(
+ "com.android.email",
+ "com.android.calendar",
+ "com.android.contacts",
+ "com.android.mms",
+ "com.android.phone",
+ "com.android.browser");
+
+ private final PackageManager mPackageManager;
+
+ private static ResolveCache sInstance;
+
+ /**
+ * Returns an instance of the ResolveCache. Only one internal instance is kept, so
+ * the argument packageManagers is ignored for all but the first call
+ */
+ public synchronized static ResolveCache getInstance(PackageManager packageManager) {
+ if (sInstance == null) {
+ return sInstance = new ResolveCache(packageManager);
+ }
+ return sInstance;
+ }
+
+ public synchronized static void flush() {
+ sInstance = null;
+ }
+
+ /**
+ * Cached entry holding the best {@link ResolveInfo} for a specific
+ * MIME-type, along with a {@link SoftReference} to its icon.
+ */
+ private static class Entry {
+ public ResolveInfo bestResolve;
+ public Drawable icon;
+ }
+
+ private HashMap<String, Entry> mCache = new HashMap<String, Entry>();
+
+ private ResolveCache(PackageManager packageManager) {
+ mPackageManager = packageManager;
+ }
+
+ /**
+ * Get the {@link Entry} best associated with the given {@link Action},
+ * or create and populate a new one if it doesn't exist.
+ */
+ protected Entry getEntry(Action action) {
+ final String mimeType = action.getMimeType();
+ Entry entry = mCache.get(mimeType);
+ if (entry != null) return entry;
+ entry = new Entry();
+
+ final Intent intent = action.getIntent();
+ if (intent != null) {
+ final List<ResolveInfo> matches = mPackageManager.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+
+ // Pick first match, otherwise best found
+ ResolveInfo bestResolve = null;
+ final int size = matches.size();
+ if (size == 1) {
+ bestResolve = matches.get(0);
+ } else if (size > 1) {
+ bestResolve = getBestResolve(intent, matches);
+ }
+
+ if (bestResolve != null) {
+ final Drawable icon = bestResolve.loadIcon(mPackageManager);
+
+ entry.bestResolve = bestResolve;
+ entry.icon = icon;
+ }
+ }
+
+ mCache.put(mimeType, entry);
+ return entry;
+ }
+
+ /**
+ * Best {@link ResolveInfo} when multiple found. Ties are broken by
+ * selecting first from the {QuickContactWindow#sPreferResolve} list of
+ * preferred packages, second by apps that live on the system partition,
+ * otherwise the app from the top of the list. This is
+ * <strong>only</strong> used for selecting a default icon for
+ * displaying in the track, and does not shortcut the system
+ * {@link Intent} disambiguation dialog.
+ */
+ protected ResolveInfo getBestResolve(Intent intent, List<ResolveInfo> matches) {
+ // Try finding preferred activity, otherwise detect disambig
+ final ResolveInfo foundResolve = mPackageManager.resolveActivity(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ final boolean foundDisambig = (foundResolve.match &
+ IntentFilter.MATCH_CATEGORY_MASK) == 0;
+
+ if (!foundDisambig) {
+ // Found concrete match, so return directly
+ return foundResolve;
+ }
+
+ // Accept any package from prefer list, otherwise first system app
+ ResolveInfo firstSystem = null;
+ for (ResolveInfo info : matches) {
+ final boolean isSystem = (info.activityInfo.applicationInfo.flags
+ & ApplicationInfo.FLAG_SYSTEM) != 0;
+ final boolean isPrefer = sPreferResolve
+ .contains(info.activityInfo.applicationInfo.packageName);
+
+ if (isPrefer) return info;
+ if (isSystem && firstSystem == null) firstSystem = info;
+ }
+
+ // Return first system found, otherwise first from list
+ return firstSystem != null ? firstSystem : matches.get(0);
+ }
+
+ /**
+ * Check {@link PackageManager} to see if any apps offer to handle the
+ * given {@link Action}.
+ */
+ public boolean hasResolve(Action action) {
+ return getEntry(action).bestResolve != null;
+ }
+
+ /**
+ * Find the best description for the given {@link Action}, usually used
+ * for accessibility purposes.
+ */
+ public CharSequence getDescription(Action action) {
+ final CharSequence actionHeader = action.getHeader();
+ final ResolveInfo info = getEntry(action).bestResolve;
+ if (!TextUtils.isEmpty(actionHeader)) {
+ return actionHeader;
+ } else if (info != null) {
+ return info.loadLabel(mPackageManager);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return the best icon for the given {@link Action}, which is usually
+ * based on the {@link ResolveInfo} found through a
+ * {@link PackageManager} query.
+ */
+ public Drawable getIcon(Action action) {
+ return getEntry(action).icon;
+ }
+
+ public void clear() {
+ mCache.clear();
+ }
+}
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetConfigureActivity.java b/src/com/android/contacts/socialwidget/SocialWidgetConfigureActivity.java
new file mode 100644
index 0000000..4201cee
--- /dev/null
+++ b/src/com/android/contacts/socialwidget/SocialWidgetConfigureActivity.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 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.socialwidget;
+
+import android.app.Activity;
+import android.appwidget.AppWidgetManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+
+public class SocialWidgetConfigureActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // If the user presses back, we want to cancel
+ setResult(RESULT_CANCELED);
+
+ // Forward the Intent to the picker
+ final Intent pickerIntent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
+ pickerIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivityForResult(pickerIntent, 0);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // We came back from the Picker. If the user actually selected a contact,
+ // return it now
+ if (resultCode == Activity.RESULT_OK) {
+ final Bundle extras = getIntent().getExtras();
+ if (extras == null) throw new IllegalStateException("Intent extras are null");
+ final int widgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+
+ // Save the setting
+ final SocialWidgetConfigureActivity context = SocialWidgetConfigureActivity.this;
+ SocialWidgetSettings.getInstance().setContactUri(context, widgetId, data.getData());
+
+ // Update the widget
+ SocialWidgetProvider.loadWidgetData(
+ context, AppWidgetManager.getInstance(this), widgetId);
+
+ // Return OK so that the system won't remove the widget
+ final Intent resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
+ setResult(RESULT_OK, resultValue);
+ }
+ finish();
+ }
+}
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
new file mode 100644
index 0000000..0330c89
--- /dev/null
+++ b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2010 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.socialwidget;
+
+import com.android.contacts.ContactLoader;
+import com.android.contacts.R;
+import com.android.contacts.util.ContactBadgeUtil;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.content.Loader;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Typeface;
+import android.net.Uri;
+import android.provider.ContactsContract.QuickContact;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.StyleSpan;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.View;
+import android.widget.RemoteViews;
+
+public class SocialWidgetProvider extends AppWidgetProvider {
+ private static final String TAG = "SocialWidgetProvider";
+
+ /**
+ * Max length of a snippet that is considered "short" and displayed in
+ * a separate line.
+ */
+ private static final int SHORT_SNIPPET_LENGTH = 48;
+
+ private static SparseArray<ContactLoader> sLoaders = new SparseArray<ContactLoader>();
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ for (int appWidgetId : appWidgetIds) {
+ Log.d(TAG, "onUpdate called for " + appWidgetId);
+ }
+
+ for (int appWidgetId : appWidgetIds) {
+ loadWidgetData(context, appWidgetManager, appWidgetId);
+ }
+ }
+
+ @Override
+ public void onDeleted(Context context, int[] appWidgetIds) {
+ for (int appWidgetId : appWidgetIds) {
+ ContactLoader loader = sLoaders.get(appWidgetId);
+ if (loader != null) {
+ Log.d(TAG, "Stopping loader for widget with id=" + appWidgetId);
+ loader.stopLoading();
+ sLoaders.delete(appWidgetId);
+ }
+ }
+ SocialWidgetSettings.getInstance().remove(context, appWidgetIds);
+ }
+
+ public static void loadWidgetData(
+ final Context context, final AppWidgetManager appWidgetManager, final int widgetId) {
+ final ContactLoader previousLoader = sLoaders.get(widgetId);
+
+ if (previousLoader != null) {
+ previousLoader.startLoading();
+ } else {
+ // Show that we are loading
+ final RemoteViews loadingViews =
+ new RemoteViews(context.getPackageName(), R.layout.social_widget);
+ loadingViews.setTextViewText(R.id.name,
+ context.getString(R.string.social_widget_loading));
+ loadingViews.setViewVisibility(R.id.name, View.VISIBLE);
+ loadingViews.setViewVisibility(R.id.name_and_snippet, View.GONE);
+ appWidgetManager.updateAppWidget(widgetId, loadingViews);
+
+ // Load
+ final Uri contactUri =
+ SocialWidgetSettings.getInstance().getContactUri(context, widgetId);
+ if (contactUri == null) {
+ // Not yet set-up (this can happen while the Configuration activity is visible)
+ return;
+ }
+ final ContactLoader contactLoader = new ContactLoader(context, contactUri);
+ contactLoader.registerListener(0,
+ new ContactLoader.OnLoadCompleteListener<ContactLoader.Result>() {
+ @Override
+ public void onLoadComplete(Loader<ContactLoader.Result> loader,
+ ContactLoader.Result contactData) {
+ bindRemoteViews(context, widgetId, appWidgetManager, contactData);
+ }
+ });
+ contactLoader.startLoading();
+ sLoaders.append(widgetId, contactLoader);
+ }
+ }
+
+ private static void bindRemoteViews(final Context context, final int widgetId,
+ final AppWidgetManager widgetManager, ContactLoader.Result contactData) {
+ Log.d(TAG, "Loaded " + contactData.getLookupKey()
+ + " for widget with id=" + widgetId);
+ final RemoteViews views = new RemoteViews(context.getPackageName(),
+ R.layout.social_widget);
+
+ if (contactData == ContactLoader.Result.ERROR ||
+ contactData == ContactLoader.Result.NOT_FOUND) {
+ setDisplayNameAndSnippet(context, views,
+ context.getString(R.string.invalidContactMessage), null, null);
+ setPhoto(views, ContactBadgeUtil.loadPlaceholderPhoto(context));
+ setStatusAttribution(views, null);
+ } else {
+ setDisplayNameAndSnippet(context, views, contactData.getDisplayName(),
+ contactData.getPhoneticName(), contactData.getSocialSnippet());
+
+ byte[] photo = contactData.getPhotoBinaryData();
+ setPhoto(views, photo != null
+ ? BitmapFactory.decodeByteArray(photo, 0, photo.length)
+ : ContactBadgeUtil.loadPlaceholderPhoto(context));
+ setStatusAttribution(views, ContactBadgeUtil.getSocialDate(
+ contactData, context));
+
+ // OnClick launch QuickContact
+ final Intent intent = new Intent(QuickContact.ACTION_QUICK_CONTACT);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+ intent.setData(contactData.getLookupUri());
+ intent.putExtra(QuickContact.EXTRA_MODE, QuickContact.MODE_SMALL);
+
+ final PendingIntent pendingIntent = PendingIntent.getActivity(context,
+ 0, intent, 0);
+ views.setOnClickPendingIntent(R.id.border, pendingIntent);
+ }
+
+ // Configure UI
+ widgetManager.updateAppWidget(widgetId, views);
+ }
+
+
+ private static void setPhoto(RemoteViews views, Bitmap photo) {
+ views.setImageViewBitmap(R.id.image, photo);
+ }
+
+ /**
+ * Set the display name, phonetic name and the social snippet.
+ */
+ private static void setDisplayNameAndSnippet(Context context, RemoteViews views,
+ CharSequence displayName, CharSequence phoneticName,
+ CharSequence snippet) {
+ SpannableStringBuilder sb = new SpannableStringBuilder();
+
+ CharSequence name = displayName;
+ if (!TextUtils.isEmpty(phoneticName)) {
+ name = context.getString(R.string.widget_name_and_phonetic,
+ name, phoneticName);
+ }
+ sb.append(name);
+
+ AbsoluteSizeSpan sizeSpan = new AbsoluteSizeSpan(
+ context.getResources().getDimensionPixelSize(R.dimen.widget_text_size_name));
+ StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);
+ sb.setSpan(sizeSpan, 0, name.length(), 0);
+ sb.setSpan(styleSpan, 0, name.length(), 0);
+
+ if (TextUtils.isEmpty(snippet)) {
+ views.setTextViewText(R.id.name, sb);
+ views.setViewVisibility(R.id.name, View.VISIBLE);
+ views.setViewVisibility(R.id.name_and_snippet, View.GONE);
+ } else {
+ if (snippet.length() <= SHORT_SNIPPET_LENGTH) {
+ sb.append("\n");
+ } else {
+ sb.append(" ");
+ }
+ sb.append(snippet);
+ views.setTextViewText(R.id.name_and_snippet, sb);
+ views.setViewVisibility(R.id.name, View.GONE);
+ views.setViewVisibility(R.id.name_and_snippet, View.VISIBLE);
+ }
+ }
+
+ /**
+ * Set the status attribution text to display in the header.
+ */
+ private static void setStatusAttribution(RemoteViews views,
+ CharSequence attribution) {
+ if (attribution == null) {
+ views.setViewVisibility(R.id.status_date, View.GONE);
+ } else {
+ views.setTextViewText(R.id.status_date, attribution);
+ views.setViewVisibility(R.id.status_date, View.VISIBLE);
+ }
+ }
+}
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetSettings.java b/src/com/android/contacts/socialwidget/SocialWidgetSettings.java
new file mode 100644
index 0000000..18b5041
--- /dev/null
+++ b/src/com/android/contacts/socialwidget/SocialWidgetSettings.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 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.socialwidget;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.net.Uri;
+import android.util.Log;
+
+public class SocialWidgetSettings {
+ private static final String TAG = "SocialWidgetSettings";
+
+ private static final String PREFS_NAME = "WidgetSettings";
+ private static final String CONTACT_URI_PREFIX = "CONTACT_URI_";
+
+ private static final SocialWidgetSettings sInstance = new SocialWidgetSettings();
+
+ public static SocialWidgetSettings getInstance() {
+ return sInstance;
+ }
+
+ private final String getSettingsString(int widgetId) {
+ return CONTACT_URI_PREFIX + Integer.toString(widgetId);
+ }
+
+ public void remove(Context context, int[] widgetIds) {
+ final SharedPreferences settings =
+ context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
+ final Editor editor = settings.edit();
+ for (int widgetId : widgetIds) {
+ Log.d(TAG, "remove(" + widgetId + ")");
+ editor.remove(getSettingsString(widgetId));
+ }
+ editor.apply();
+ }
+
+ public Uri getContactUri(Context context, int widgetId) {
+ final SharedPreferences settings =
+ context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
+ final String resultString = settings.getString(getSettingsString(widgetId), null);
+ final Uri result = resultString == null ? null : Uri.parse(resultString);
+ Log.d(TAG, "getContactUri(" + widgetId + ") --> " + result);
+ return result;
+ }
+
+ public void setContactUri(Context context, int widgetId, Uri contactLookupUri) {
+ Log.d(TAG, "setContactUri(" + widgetId + ", " + contactLookupUri + ")");
+ final SharedPreferences settings =
+ context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
+ final Editor editor = settings.edit();
+ if (contactLookupUri == null) {
+ editor.remove(getSettingsString(widgetId));
+ } else {
+ editor.putString(getSettingsString(widgetId), contactLookupUri.toString());
+ }
+ editor.apply();
+ }
+}
diff --git a/src/com/android/contacts/test/FragmentTestActivity.java b/src/com/android/contacts/test/FragmentTestActivity.java
new file mode 100644
index 0000000..34b1556
--- /dev/null
+++ b/src/com/android/contacts/test/FragmentTestActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 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.test;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.R;
+
+import android.os.Bundle;
+
+/**
+ * An activity that is used for testing fragments. A unit test starts this
+ * activity, adds a fragment and then tests the fragment.
+ */
+public class FragmentTestActivity extends ContactsActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.empty);
+ }
+}
diff --git a/src/com/android/contacts/test/InjectedServices.java b/src/com/android/contacts/test/InjectedServices.java
new file mode 100644
index 0000000..b74481c
--- /dev/null
+++ b/src/com/android/contacts/test/InjectedServices.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 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.test;
+
+import com.google.android.collect.Maps;
+
+import android.content.ContentResolver;
+import android.content.SharedPreferences;
+
+import java.util.HashMap;
+
+/**
+ * A mechanism for providing alternative (mock) services to the application
+ * while running tests. Activities, Services and the Application should check
+ * with this class to see if a particular service has been overridden.
+ */
+public class InjectedServices {
+
+ private ContentResolver mContentResolver;
+ private SharedPreferences mSharedPreferences;
+ private HashMap<String, Object> mSystemServices;
+
+ public void setContentResolver(ContentResolver contentResolver) {
+ this.mContentResolver = contentResolver;
+ }
+
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ public void setSharedPreferences(SharedPreferences sharedPreferences) {
+ this.mSharedPreferences = sharedPreferences;
+ }
+
+ public SharedPreferences getSharedPreferences() {
+ return mSharedPreferences;
+ }
+
+ public void setSystemService(String name, Object service) {
+ if (mSystemServices == null) {
+ mSystemServices = Maps.newHashMap();
+ }
+
+ mSystemServices.put(name, service);
+ }
+
+ public Object getSystemService(String name) {
+ if (mSystemServices != null) {
+ return mSystemServices.get(name);
+ }
+ return null;
+ }
+}
diff --git a/src/com/android/contacts/ui/ContactsPreferences.java b/src/com/android/contacts/ui/ContactsPreferences.java
deleted file mode 100644
index 14c6e05..0000000
--- a/src/com/android/contacts/ui/ContactsPreferences.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2010 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.ui;
-
-import com.android.contacts.R;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.provider.ContactsContract;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-
-/**
- * Manages user preferences for contacts.
- */
-public final class ContactsPreferences extends ContentObserver {
- private Context mContext;
- private int mSortOrder = -1;
- private int mDisplayOrder = -1;
- private ChangeListener mListener = null;
- private Handler mHandler;
-
- public ContactsPreferences(Context context) {
- super(null);
- mContext = context;
- mHandler = new Handler();
- }
-
- public boolean isSortOrderUserChangeable() {
- return mContext.getResources().getBoolean(R.bool.config_sort_order_user_changeable);
- }
-
- private int getDefaultSortOrder() {
- if (mContext.getResources().getBoolean(R.bool.config_default_sort_order_primary)) {
- return ContactsContract.Preferences.SORT_ORDER_PRIMARY;
- } else {
- return ContactsContract.Preferences.SORT_ORDER_ALTERNATIVE;
- }
- }
-
- public int getSortOrder() {
- if (!isSortOrderUserChangeable()) {
- return getDefaultSortOrder();
- }
-
- if (mSortOrder == -1) {
- try {
- mSortOrder = Settings.System.getInt(mContext.getContentResolver(),
- ContactsContract.Preferences.SORT_ORDER);
- } catch (SettingNotFoundException e) {
- mSortOrder = getDefaultSortOrder();
- }
- }
- return mSortOrder;
- }
-
- public void setSortOrder(int sortOrder) {
- mSortOrder = sortOrder;
- Settings.System.putInt(mContext.getContentResolver(),
- ContactsContract.Preferences.SORT_ORDER, sortOrder);
- }
-
- public boolean isDisplayOrderUserChangeable() {
- return mContext.getResources().getBoolean(R.bool.config_display_order_user_changeable);
- }
-
- private int getDefaultDisplayOrder() {
- if (mContext.getResources().getBoolean(R.bool.config_default_display_order_primary)) {
- return ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY;
- } else {
- return ContactsContract.Preferences.DISPLAY_ORDER_ALTERNATIVE;
- }
- }
-
- public int getDisplayOrder() {
- if (!isDisplayOrderUserChangeable()) {
- return getDefaultDisplayOrder();
- }
-
- if (mDisplayOrder == -1) {
- try {
- mDisplayOrder = Settings.System.getInt(mContext.getContentResolver(),
- ContactsContract.Preferences.DISPLAY_ORDER);
- } catch (SettingNotFoundException e) {
- mDisplayOrder = getDefaultDisplayOrder();
- }
- }
- return mDisplayOrder;
- }
-
- public void setDisplayOrder(int displayOrder) {
- mDisplayOrder = displayOrder;
- Settings.System.putInt(mContext.getContentResolver(),
- ContactsContract.Preferences.DISPLAY_ORDER, displayOrder);
- }
-
- public void registerChangeListener(ChangeListener listener) {
- if (mListener != null) unregisterChangeListener();
-
- // We didn't watch before, so ensure that we actually forget our cache here
- mSortOrder = -1;
- mDisplayOrder = -1;
-
- mListener = listener;
- final ContentResolver contentResolver = mContext.getContentResolver();
- contentResolver.registerContentObserver(
- Settings.System.getUriFor(
- ContactsContract.Preferences.SORT_ORDER), false, this);
- contentResolver.registerContentObserver(
- Settings.System.getUriFor(
- ContactsContract.Preferences.DISPLAY_ORDER), false, this);
- }
-
- public void unregisterChangeListener() {
- if (mListener != null) {
- mContext.getContentResolver().unregisterContentObserver(this);
- mListener = null;
- }
- }
-
- @Override
- public void onChange(boolean selfChange) {
- // This notification is not sent on the Ui thread. Use the previously created Handler
- // to switch to the Ui thread
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mSortOrder = -1;
- mDisplayOrder = -1;
- if (mListener != null) mListener.onChange();
- }
- });
- }
-
- public interface ChangeListener {
- void onChange();
- }
-}
diff --git a/src/com/android/contacts/ui/ContactsPreferencesActivity.java b/src/com/android/contacts/ui/ContactsPreferencesActivity.java
deleted file mode 100644
index 6bc9be8..0000000
--- a/src/com/android/contacts/ui/ContactsPreferencesActivity.java
+++ /dev/null
@@ -1,1075 +0,0 @@
-/*
- * 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.ui;
-
-import com.android.contacts.ContactsSearchManager;
-import com.android.contacts.R;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.GoogleSource;
-import com.android.contacts.model.Sources;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.util.EmptyService;
-import com.android.contacts.util.WeakAsyncTask;
-import com.google.android.collect.Lists;
-
-import android.accounts.Account;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ExpandableListActivity;
-import android.app.ProgressDialog;
-import android.content.ContentProviderOperation;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.EntityIterator;
-import android.content.Intent;
-import android.content.OperationApplicationException;
-import android.content.SharedPreferences;
-import android.content.ContentProviderOperation.Builder;
-import android.content.SharedPreferences.Editor;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.preference.PreferenceManager;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Groups;
-import android.provider.ContactsContract.Settings;
-import android.util.Log;
-import android.view.ContextMenu;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.MenuItem.OnMenuItemClickListener;
-import android.widget.AdapterView;
-import android.widget.BaseExpandableListAdapter;
-import android.widget.CheckBox;
-import android.widget.ExpandableListAdapter;
-import android.widget.ExpandableListView;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-
-/**
- * Shows a list of all available {@link Groups} available, letting the user
- * select which ones they want to be visible.
- */
-public final class ContactsPreferencesActivity extends ExpandableListActivity implements
- AdapterView.OnItemClickListener, View.OnClickListener {
- private static final String TAG = "DisplayGroupsActivity";
-
- public interface Prefs {
- public static final String DISPLAY_ONLY_PHONES = "only_phones";
- public static final boolean DISPLAY_ONLY_PHONES_DEFAULT = false;
-
- }
-
- private static final int DIALOG_SORT_ORDER = 1;
- private static final int DIALOG_DISPLAY_ORDER = 2;
-
- private ExpandableListView mList;
- private DisplayAdapter mAdapter;
-
- private SharedPreferences mPrefs;
- private ContactsPreferences mContactsPrefs;
-
- private CheckBox mDisplayPhones;
-
- private View mHeaderPhones;
- private View mHeaderSeparator;
-
- private View mSortOrderView;
- private TextView mSortOrderTextView;
- private int mSortOrder;
-
- private View mDisplayOrderView;
- private TextView mDisplayOrderTextView;
- private int mDisplayOrder;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.contacts_preferences);
-
- mList = getExpandableListView();
- mList.setHeaderDividersEnabled(true);
- mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
- mContactsPrefs = new ContactsPreferences(this);
- mAdapter = new DisplayAdapter(this);
-
- final LayoutInflater inflater = getLayoutInflater();
-
- createWithPhonesOnlyPreferenceView(inflater);
- createSortOrderPreferenceView(inflater);
- createDisplayOrderPreferenceView(inflater);
- createDisplayGroupHeader(inflater);
-
- findViewById(R.id.btn_done).setOnClickListener(this);
- findViewById(R.id.btn_discard).setOnClickListener(this);
-
- // Catch clicks on the header views
- mList.setOnItemClickListener(this);
- mList.setOnCreateContextMenuListener(this);
-
- mSortOrder = mContactsPrefs.getSortOrder();
- mDisplayOrder = mContactsPrefs.getDisplayOrder();
- }
-
- private void createWithPhonesOnlyPreferenceView(LayoutInflater inflater) {
- // Add the "Only contacts with phones" header modifier.
- mHeaderPhones = inflater.inflate(R.layout.display_options_phones_only, mList, false);
- mHeaderPhones.setId(R.id.header_phones);
- mDisplayPhones = (CheckBox) mHeaderPhones.findViewById(android.R.id.checkbox);
- mDisplayPhones.setChecked(mPrefs.getBoolean(Prefs.DISPLAY_ONLY_PHONES,
- Prefs.DISPLAY_ONLY_PHONES_DEFAULT));
- {
- final TextView text1 = (TextView)mHeaderPhones.findViewById(android.R.id.text1);
- final TextView text2 = (TextView)mHeaderPhones.findViewById(android.R.id.text2);
- text1.setText(R.string.showFilterPhones);
- text2.setText(R.string.showFilterPhonesDescrip);
- }
- }
-
- private void createSortOrderPreferenceView(LayoutInflater inflater) {
- mSortOrderView = inflater.inflate(R.layout.preference_with_more_button, mList, false);
-
- View preferenceLayout = mSortOrderView.findViewById(R.id.preference);
-
- TextView label = (TextView)preferenceLayout.findViewById(R.id.label);
- label.setText(getString(R.string.display_options_sort_list_by));
-
- mSortOrderTextView = (TextView)preferenceLayout.findViewById(R.id.data);
- }
-
- private void createDisplayOrderPreferenceView(LayoutInflater inflater) {
- mDisplayOrderView = inflater.inflate(R.layout.preference_with_more_button, mList, false);
- View preferenceLayout = mDisplayOrderView.findViewById(R.id.preference);
-
- TextView label = (TextView)preferenceLayout.findViewById(R.id.label);
- label.setText(getString(R.string.display_options_view_names_as));
-
- mDisplayOrderTextView = (TextView)preferenceLayout.findViewById(R.id.data);
- }
-
- private void createDisplayGroupHeader(LayoutInflater inflater) {
- // Add the separator before showing the detailed group list.
- mHeaderSeparator = inflater.inflate(R.layout.list_separator, mList, false);
- {
- final TextView text1 = (TextView)mHeaderSeparator;
- text1.setText(R.string.headerContactGroups);
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mList.removeHeaderView(mHeaderPhones);
- mList.removeHeaderView(mSortOrderView);
- mList.removeHeaderView(mDisplayOrderView);
- mList.removeHeaderView(mHeaderSeparator);
-
- // List adapter needs to be reset, because header views cannot be added
- // to a list with an existing adapter.
- setListAdapter(null);
-
- mList.addHeaderView(mHeaderPhones, null, true);
- if (mContactsPrefs.isSortOrderUserChangeable()) {
- mList.addHeaderView(mSortOrderView, null, true);
- }
-
- if (mContactsPrefs.isSortOrderUserChangeable()) {
- mList.addHeaderView(mDisplayOrderView, null, true);
- }
-
- mList.addHeaderView(mHeaderSeparator, null, false);
-
- setListAdapter(mAdapter);
-
- bindView();
-
- // Start background query to find account details
- new QueryGroupsTask(this).execute();
- }
-
- private void bindView() {
- mSortOrderTextView.setText(
- mSortOrder == ContactsContract.Preferences.SORT_ORDER_PRIMARY
- ? getString(R.string.display_options_sort_by_given_name)
- : getString(R.string.display_options_sort_by_family_name));
-
- mDisplayOrderTextView.setText(
- mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY
- ? getString(R.string.display_options_view_given_name_first)
- : getString(R.string.display_options_view_family_name_first));
- }
-
- @Override
- protected Dialog onCreateDialog(int id, Bundle args) {
- switch (id) {
- case DIALOG_SORT_ORDER:
- return createSortOrderDialog();
- case DIALOG_DISPLAY_ORDER:
- return createDisplayOrderDialog();
- }
-
- return null;
- }
-
- private Dialog createSortOrderDialog() {
- String[] items = new String[] {
- getString(R.string.display_options_sort_by_given_name),
- getString(R.string.display_options_sort_by_family_name),
- };
-
- return new AlertDialog.Builder(this)
- .setTitle(R.string.display_options_sort_list_by)
- .setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- setSortOrder(dialog);
- dialog.dismiss();
- }
- })
- .setNegativeButton(android.R.string.cancel, null)
- .create();
- }
-
- private Dialog createDisplayOrderDialog() {
- String[] items = new String[] {
- getString(R.string.display_options_view_given_name_first),
- getString(R.string.display_options_view_family_name_first),
- };
-
- return new AlertDialog.Builder(this)
- .setTitle(R.string.display_options_view_names_as)
- .setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- setDisplayOrder(dialog);
- dialog.dismiss();
- }
- })
- .setNegativeButton(android.R.string.cancel, null)
- .create();
- }
-
- @Override
- protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
- switch (id) {
- case DIALOG_SORT_ORDER:
- setCheckedItem(dialog,
- mSortOrder == ContactsContract.Preferences.SORT_ORDER_PRIMARY ? 0 : 1);
- break;
- case DIALOG_DISPLAY_ORDER:
- setCheckedItem(dialog,
- mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY
- ? 0 : 1);
- break;
- }
- }
-
- private void setCheckedItem(Dialog dialog, int position) {
- ListView listView = ((AlertDialog)dialog).getListView();
- listView.setItemChecked(position, true);
- listView.setSelection(position);
- }
-
- protected void setSortOrder(DialogInterface dialog) {
- ListView listView = ((AlertDialog)dialog).getListView();
- int checked = listView.getCheckedItemPosition();
- mSortOrder = checked == 0
- ? ContactsContract.Preferences.SORT_ORDER_PRIMARY
- : ContactsContract.Preferences.SORT_ORDER_ALTERNATIVE;
-
- bindView();
- }
-
- protected void setDisplayOrder(DialogInterface dialog) {
- ListView listView = ((AlertDialog)dialog).getListView();
- int checked = listView.getCheckedItemPosition();
- mDisplayOrder = checked == 0
- ? ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY
- : ContactsContract.Preferences.DISPLAY_ORDER_ALTERNATIVE;
-
- bindView();
- }
-
- /**
- * Background operation to build set of {@link AccountDisplay} for each
- * {@link Sources#getAccounts(boolean)} that provides groups.
- */
- private static class QueryGroupsTask extends
- WeakAsyncTask<Void, Void, AccountSet, ContactsPreferencesActivity> {
- public QueryGroupsTask(ContactsPreferencesActivity target) {
- super(target);
- }
-
- @Override
- protected AccountSet doInBackground(ContactsPreferencesActivity target,
- Void... params) {
- final Context context = target;
- final Sources sources = Sources.getInstance(context);
- final ContentResolver resolver = context.getContentResolver();
-
- // Inflate groups entry for each account
- final AccountSet accounts = new AccountSet();
- for (Account account : sources.getAccounts(false)) {
- accounts.add(new AccountDisplay(resolver, account.name, account.type));
- }
-
- return accounts;
- }
-
- @Override
- protected void onPostExecute(ContactsPreferencesActivity target, AccountSet result) {
- target.mAdapter.setAccounts(result);
- }
- }
-
- private static final int DEFAULT_SHOULD_SYNC = 1;
- private static final int DEFAULT_VISIBLE = 0;
-
- /**
- * Entry holding any changes to {@link Groups} or {@link Settings} rows,
- * such as {@link Groups#SHOULD_SYNC} or {@link Groups#GROUP_VISIBLE}.
- */
- protected static class GroupDelta extends ValuesDelta {
- private boolean mUngrouped = false;
- private boolean mAccountHasGroups;
-
- private GroupDelta() {
- super();
- }
-
- /**
- * Build {@link GroupDelta} from the {@link Settings} row for the given
- * {@link Settings#ACCOUNT_NAME} and {@link Settings#ACCOUNT_TYPE}.
- */
- public static GroupDelta fromSettings(ContentResolver resolver, String accountName,
- String accountType, boolean accountHasGroups) {
- final Uri settingsUri = Settings.CONTENT_URI.buildUpon()
- .appendQueryParameter(Settings.ACCOUNT_NAME, accountName)
- .appendQueryParameter(Settings.ACCOUNT_TYPE, accountType).build();
- final Cursor cursor = resolver.query(settingsUri, new String[] {
- Settings.SHOULD_SYNC, Settings.UNGROUPED_VISIBLE
- }, null, null, null);
-
- try {
- final ContentValues values = new ContentValues();
- values.put(Settings.ACCOUNT_NAME, accountName);
- values.put(Settings.ACCOUNT_TYPE, accountType);
-
- if (cursor != null && cursor.moveToFirst()) {
- // Read existing values when present
- values.put(Settings.SHOULD_SYNC, cursor.getInt(0));
- values.put(Settings.UNGROUPED_VISIBLE, cursor.getInt(1));
- return fromBefore(values).setUngrouped(accountHasGroups);
- } else {
- // Nothing found, so treat as create
- values.put(Settings.SHOULD_SYNC, DEFAULT_SHOULD_SYNC);
- values.put(Settings.UNGROUPED_VISIBLE, DEFAULT_VISIBLE);
- return fromAfter(values).setUngrouped(accountHasGroups);
- }
- } finally {
- if (cursor != null) cursor.close();
- }
- }
-
- public static GroupDelta fromBefore(ContentValues before) {
- final GroupDelta entry = new GroupDelta();
- entry.mBefore = before;
- entry.mAfter = new ContentValues();
- return entry;
- }
-
- public static GroupDelta fromAfter(ContentValues after) {
- final GroupDelta entry = new GroupDelta();
- entry.mBefore = null;
- entry.mAfter = after;
- return entry;
- }
-
- protected GroupDelta setUngrouped(boolean accountHasGroups) {
- mUngrouped = true;
- mAccountHasGroups = accountHasGroups;
- return this;
- }
-
- @Override
- public boolean beforeExists() {
- return mBefore != null;
- }
-
- public boolean getShouldSync() {
- return getAsInteger(mUngrouped ? Settings.SHOULD_SYNC : Groups.SHOULD_SYNC,
- DEFAULT_SHOULD_SYNC) != 0;
- }
-
- public boolean getVisible() {
- return getAsInteger(mUngrouped ? Settings.UNGROUPED_VISIBLE : Groups.GROUP_VISIBLE,
- DEFAULT_VISIBLE) != 0;
- }
-
- public void putShouldSync(boolean shouldSync) {
- put(mUngrouped ? Settings.SHOULD_SYNC : Groups.SHOULD_SYNC, shouldSync ? 1 : 0);
- }
-
- public void putVisible(boolean visible) {
- put(mUngrouped ? Settings.UNGROUPED_VISIBLE : Groups.GROUP_VISIBLE, visible ? 1 : 0);
- }
-
- public CharSequence getTitle(Context context) {
- if (mUngrouped) {
- if (mAccountHasGroups) {
- return context.getText(R.string.display_ungrouped);
- } else {
- return context.getText(R.string.display_all_contacts);
- }
- } else {
- final Integer titleRes = getAsInteger(Groups.TITLE_RES);
- if (titleRes != null) {
- final String packageName = getAsString(Groups.RES_PACKAGE);
- return context.getPackageManager().getText(packageName, titleRes, null);
- } else {
- return getAsString(Groups.TITLE);
- }
- }
- }
-
- /**
- * Build a possible {@link ContentProviderOperation} to persist any
- * changes to the {@link Groups} or {@link Settings} row described by
- * this {@link GroupDelta}.
- */
- public ContentProviderOperation buildDiff() {
- if (isNoop()) {
- return null;
- } else if (isUpdate()) {
- // When has changes and "before" exists, then "update"
- final Builder builder = ContentProviderOperation
- .newUpdate(mUngrouped ? Settings.CONTENT_URI : addCallerIsSyncAdapterParameter(Groups.CONTENT_URI));
- if (mUngrouped) {
- builder.withSelection(Settings.ACCOUNT_NAME + "=? AND " + Settings.ACCOUNT_TYPE
- + "=?", new String[] {
- this.getAsString(Settings.ACCOUNT_NAME),
- this.getAsString(Settings.ACCOUNT_TYPE)
- });
- } else {
- builder.withSelection(Groups._ID + "=" + this.getId(), null);
- }
- builder.withValues(mAfter);
- return builder.build();
- } else if (isInsert() && mUngrouped) {
- // Only allow inserts for Settings
- mAfter.remove(mIdColumn);
- final Builder builder = ContentProviderOperation.newInsert(Settings.CONTENT_URI);
- builder.withValues(mAfter);
- return builder.build();
- } else {
- throw new IllegalStateException("Unexpected delete or insert");
- }
- }
- }
-
- private static Uri addCallerIsSyncAdapterParameter(Uri uri) {
- return uri.buildUpon()
- .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
- .build();
- }
-
- /**
- * {@link Comparator} to sort by {@link Groups#_ID}.
- */
- private static Comparator<GroupDelta> sIdComparator = new Comparator<GroupDelta>() {
- public int compare(GroupDelta object1, GroupDelta object2) {
- final Long id1 = object1.getId();
- final Long id2 = object2.getId();
- if (id1 == null && id2 == null) {
- return 0;
- } else if (id1 == null) {
- return -1;
- } else if (id2 == null) {
- return 1;
- } else if (id1 < id2) {
- return -1;
- } else if (id1 > id2) {
- return 1;
- } else {
- return 0;
- }
- }
- };
-
- /**
- * Set of all {@link AccountDisplay} entries, one for each source.
- */
- protected static class AccountSet extends ArrayList<AccountDisplay> {
- public ArrayList<ContentProviderOperation> buildDiff() {
- final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
- for (AccountDisplay account : this) {
- account.buildDiff(diff);
- }
- return diff;
- }
- }
-
- /**
- * {@link GroupDelta} details for a single {@link Account}, usually shown as
- * children under a single expandable group.
- */
- protected static class AccountDisplay {
- public String mName;
- public String mType;
-
- public GroupDelta mUngrouped;
- public ArrayList<GroupDelta> mSyncedGroups = Lists.newArrayList();
- public ArrayList<GroupDelta> mUnsyncedGroups = Lists.newArrayList();
-
- /**
- * Build an {@link AccountDisplay} covering all {@link Groups} under the
- * given {@link Account}.
- */
- public AccountDisplay(ContentResolver resolver, String accountName, String accountType) {
- mName = accountName;
- mType = accountType;
-
- final Uri groupsUri = Groups.CONTENT_URI.buildUpon()
- .appendQueryParameter(Groups.ACCOUNT_NAME, accountName)
- .appendQueryParameter(Groups.ACCOUNT_TYPE, accountType).build();
- EntityIterator iterator = ContactsContract.Groups.newEntityIterator(resolver.query(
- groupsUri, null, null, null, null));
- try {
- boolean hasGroups = false;
-
- // Create entries for each known group
- while (iterator.hasNext()) {
- final ContentValues values = iterator.next().getEntityValues();
- final GroupDelta group = GroupDelta.fromBefore(values);
- addGroup(group);
- hasGroups = true;
- }
- // Create single entry handling ungrouped status
- mUngrouped = GroupDelta.fromSettings(resolver, accountName, accountType, hasGroups);
- addGroup(mUngrouped);
- } finally {
- iterator.close();
- }
- }
-
- /**
- * Add the given {@link GroupDelta} internally, filing based on its
- * {@link GroupDelta#getShouldSync()} status.
- */
- private void addGroup(GroupDelta group) {
- if (group.getShouldSync()) {
- mSyncedGroups.add(group);
- } else {
- mUnsyncedGroups.add(group);
- }
- }
-
- /**
- * Set the {@link GroupDelta#putShouldSync(boolean)} value for all
- * children {@link GroupDelta} rows.
- */
- public void setShouldSync(boolean shouldSync) {
- final Iterator<GroupDelta> oppositeChildren = shouldSync ?
- mUnsyncedGroups.iterator() : mSyncedGroups.iterator();
- while (oppositeChildren.hasNext()) {
- final GroupDelta child = oppositeChildren.next();
- setShouldSync(child, shouldSync, false);
- oppositeChildren.remove();
- }
- }
-
- public void setShouldSync(GroupDelta child, boolean shouldSync) {
- setShouldSync(child, shouldSync, true);
- }
-
- /**
- * Set {@link GroupDelta#putShouldSync(boolean)}, and file internally
- * based on updated state.
- */
- public void setShouldSync(GroupDelta child, boolean shouldSync, boolean attemptRemove) {
- child.putShouldSync(shouldSync);
- if (shouldSync) {
- if (attemptRemove) {
- mUnsyncedGroups.remove(child);
- }
- mSyncedGroups.add(child);
- Collections.sort(mSyncedGroups, sIdComparator);
- } else {
- if (attemptRemove) {
- mSyncedGroups.remove(child);
- }
- mUnsyncedGroups.add(child);
- }
- }
-
- /**
- * Build set of {@link ContentProviderOperation} to persist any user
- * changes to {@link GroupDelta} rows under this {@link Account}.
- */
- public void buildDiff(ArrayList<ContentProviderOperation> diff) {
- for (GroupDelta group : mSyncedGroups) {
- final ContentProviderOperation oper = group.buildDiff();
- if (oper != null) diff.add(oper);
- }
- for (GroupDelta group : mUnsyncedGroups) {
- final ContentProviderOperation oper = group.buildDiff();
- if (oper != null) diff.add(oper);
- }
- }
- }
-
- /**
- * {@link ExpandableListAdapter} that shows {@link GroupDelta} settings,
- * grouped by {@link Account} source. Shows footer row when any groups are
- * unsynced, as determined through {@link AccountDisplay#mUnsyncedGroups}.
- */
- protected static class DisplayAdapter extends BaseExpandableListAdapter {
- private Context mContext;
- private LayoutInflater mInflater;
- private Sources mSources;
- private AccountSet mAccounts;
-
- private boolean mChildWithPhones = false;
-
- public DisplayAdapter(Context context) {
- mContext = context;
- mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mSources = Sources.getInstance(context);
- }
-
- public void setAccounts(AccountSet accounts) {
- mAccounts = accounts;
- notifyDataSetChanged();
- }
-
- /**
- * In group descriptions, show the number of contacts with phone
- * numbers, in addition to the total contacts.
- */
- public void setChildDescripWithPhones(boolean withPhones) {
- mChildWithPhones = withPhones;
- }
-
- /** {@inheritDoc} */
- public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
- View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.display_child, parent, false);
- }
-
- final TextView text1 = (TextView)convertView.findViewById(android.R.id.text1);
- final TextView text2 = (TextView)convertView.findViewById(android.R.id.text2);
- final CheckBox checkbox = (CheckBox)convertView.findViewById(android.R.id.checkbox);
-
- final AccountDisplay account = mAccounts.get(groupPosition);
- final GroupDelta child = (GroupDelta)this.getChild(groupPosition, childPosition);
- if (child != null) {
- // Handle normal group, with title and checkbox
- final boolean groupVisible = child.getVisible();
- checkbox.setVisibility(View.VISIBLE);
- checkbox.setChecked(groupVisible);
-
- final CharSequence groupTitle = child.getTitle(mContext);
- text1.setText(groupTitle);
-
-// final int count = cursor.getInt(GroupsQuery.SUMMARY_COUNT);
-// final int withPhones = cursor.getInt(GroupsQuery.SUMMARY_WITH_PHONES);
-
-// final CharSequence descrip = mContext.getResources().getQuantityString(
-// mChildWithPhones ? R.plurals.groupDescripPhones : R.plurals.groupDescrip,
-// count, count, withPhones);
-
-// text2.setText(descrip);
- text2.setVisibility(View.GONE);
- } else {
- // When unknown child, this is "more" footer view
- checkbox.setVisibility(View.GONE);
- text1.setText(R.string.display_more_groups);
- text2.setVisibility(View.GONE);
- }
-
- return convertView;
- }
-
- /** {@inheritDoc} */
- public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
- ViewGroup parent) {
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.display_group, parent, false);
- }
-
- final TextView text1 = (TextView)convertView.findViewById(android.R.id.text1);
- final TextView text2 = (TextView)convertView.findViewById(android.R.id.text2);
-
- final AccountDisplay account = (AccountDisplay)this.getGroup(groupPosition);
-
- final ContactsSource source = mSources.getInflatedSource(account.mType,
- ContactsSource.LEVEL_SUMMARY);
-
- text1.setText(account.mName);
- text2.setText(source.getDisplayLabel(mContext));
- text2.setVisibility(account.mName == null ? View.GONE : View.VISIBLE);
-
- return convertView;
- }
-
- /** {@inheritDoc} */
- public Object getChild(int groupPosition, int childPosition) {
- final AccountDisplay account = mAccounts.get(groupPosition);
- final boolean validChild = childPosition >= 0
- && childPosition < account.mSyncedGroups.size();
- if (validChild) {
- return account.mSyncedGroups.get(childPosition);
- } else {
- return null;
- }
- }
-
- /** {@inheritDoc} */
- public long getChildId(int groupPosition, int childPosition) {
- final GroupDelta child = (GroupDelta)getChild(groupPosition, childPosition);
- if (child != null) {
- final Long childId = child.getId();
- return childId != null ? childId : Long.MIN_VALUE;
- } else {
- return Long.MIN_VALUE;
- }
- }
-
- /** {@inheritDoc} */
- public int getChildrenCount(int groupPosition) {
- // Count is any synced groups, plus possible footer
- final AccountDisplay account = mAccounts.get(groupPosition);
- final boolean anyHidden = account.mUnsyncedGroups.size() > 0;
- return account.mSyncedGroups.size() + (anyHidden ? 1 : 0);
- }
-
- /** {@inheritDoc} */
- public Object getGroup(int groupPosition) {
- return mAccounts.get(groupPosition);
- }
-
- /** {@inheritDoc} */
- public int getGroupCount() {
- if (mAccounts == null) {
- return 0;
- }
- return mAccounts.size();
- }
-
- /** {@inheritDoc} */
- public long getGroupId(int groupPosition) {
- return groupPosition;
- }
-
- /** {@inheritDoc} */
- public boolean hasStableIds() {
- return true;
- }
-
- /** {@inheritDoc} */
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
- }
-
- /**
- * Handle any clicks on header views added to our {@link #mAdapter}, which
- * are usually the global modifier checkboxes.
- */
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Log.d(TAG, "OnItemClick, position=" + position + ", id=" + id);
- if (view == mHeaderPhones) {
- mDisplayPhones.toggle();
- return;
- }
- if (view == mDisplayOrderView) {
- Log.d(TAG, "Showing Display Order dialog");
- showDialog(DIALOG_DISPLAY_ORDER);
- return;
- }
- if (view == mSortOrderView) {
- Log.d(TAG, "Showing Sort Order dialog");
- showDialog(DIALOG_SORT_ORDER);
- return;
- }
- }
-
- /** {@inheritDoc} */
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.btn_done: {
- this.doSaveAction();
- break;
- }
- case R.id.btn_discard: {
- this.finish();
- break;
- }
- }
- }
-
- /**
- * Assign a specific value to {@link Prefs#DISPLAY_ONLY_PHONES}, refreshing
- * the visible list as needed.
- */
- protected void setDisplayOnlyPhones(boolean displayOnlyPhones) {
- mDisplayPhones.setChecked(displayOnlyPhones);
-
- Editor editor = mPrefs.edit();
- editor.putBoolean(Prefs.DISPLAY_ONLY_PHONES, displayOnlyPhones);
- editor.apply();
-
- mAdapter.setChildDescripWithPhones(displayOnlyPhones);
- mAdapter.notifyDataSetChanged();
- }
-
- /**
- * Handle any clicks on {@link ExpandableListAdapter} children, which
- * usually mean toggling its visible state.
- */
- @Override
- public boolean onChildClick(ExpandableListView parent, View view, int groupPosition,
- int childPosition, long id) {
- final CheckBox checkbox = (CheckBox)view.findViewById(android.R.id.checkbox);
-
- final AccountDisplay account = (AccountDisplay)mAdapter.getGroup(groupPosition);
- final GroupDelta child = (GroupDelta)mAdapter.getChild(groupPosition, childPosition);
- if (child != null) {
- checkbox.toggle();
- child.putVisible(checkbox.isChecked());
- } else {
- // Open context menu for bringing back unsynced
- this.openContextMenu(view);
- }
- return true;
- }
-
- // TODO: move these definitions to framework constants when we begin
- // defining this mode through <sync-adapter> tags
- private static final int SYNC_MODE_UNSUPPORTED = 0;
- private static final int SYNC_MODE_UNGROUPED = 1;
- private static final int SYNC_MODE_EVERYTHING = 2;
-
- protected int getSyncMode(AccountDisplay account) {
- // TODO: read sync mode through <sync-adapter> definition
- if (GoogleSource.ACCOUNT_TYPE.equals(account.mType)) {
- return SYNC_MODE_EVERYTHING;
- } else {
- return SYNC_MODE_UNSUPPORTED;
- }
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view,
- ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, view, menuInfo);
-
- // Bail if not working with expandable long-press, or if not child
- if (!(menuInfo instanceof ExpandableListContextMenuInfo)) return;
-
- final ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuInfo;
- final int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
- final int childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition);
-
- // Skip long-press on expandable parents
- if (childPosition == -1) return;
-
- final AccountDisplay account = (AccountDisplay)mAdapter.getGroup(groupPosition);
- final GroupDelta child = (GroupDelta)mAdapter.getChild(groupPosition, childPosition);
-
- // Ignore when selective syncing unsupported
- final int syncMode = getSyncMode(account);
- if (syncMode == SYNC_MODE_UNSUPPORTED) return;
-
- if (child != null) {
- showRemoveSync(menu, account, child, syncMode);
- } else {
- showAddSync(menu, account, syncMode);
- }
- }
-
- protected void showRemoveSync(ContextMenu menu, final AccountDisplay account,
- final GroupDelta child, final int syncMode) {
- final CharSequence title = child.getTitle(this);
-
- menu.setHeaderTitle(title);
- menu.add(R.string.menu_sync_remove).setOnMenuItemClickListener(
- new OnMenuItemClickListener() {
- public boolean onMenuItemClick(MenuItem item) {
- handleRemoveSync(account, child, syncMode, title);
- return true;
- }
- });
- }
-
- protected void handleRemoveSync(final AccountDisplay account, final GroupDelta child,
- final int syncMode, CharSequence title) {
- final boolean shouldSyncUngrouped = account.mUngrouped.getShouldSync();
- if (syncMode == SYNC_MODE_EVERYTHING && shouldSyncUngrouped
- && !child.equals(account.mUngrouped)) {
- // Warn before removing this group when it would cause ungrouped to stop syncing
- final AlertDialog.Builder builder = new AlertDialog.Builder(this);
- final CharSequence removeMessage = this.getString(
- R.string.display_warn_remove_ungrouped, title);
- builder.setTitle(R.string.menu_sync_remove);
- builder.setMessage(removeMessage);
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- // Mark both this group and ungrouped to stop syncing
- account.setShouldSync(account.mUngrouped, false);
- account.setShouldSync(child, false);
- mAdapter.notifyDataSetChanged();
- }
- });
- builder.show();
- } else {
- // Mark this group to not sync
- account.setShouldSync(child, false);
- mAdapter.notifyDataSetChanged();
- }
- }
-
- protected void showAddSync(ContextMenu menu, final AccountDisplay account, final int syncMode) {
- menu.setHeaderTitle(R.string.dialog_sync_add);
-
- // Create item for each available, unsynced group
- for (final GroupDelta child : account.mUnsyncedGroups) {
- if (!child.getShouldSync()) {
- final CharSequence title = child.getTitle(this);
- menu.add(title).setOnMenuItemClickListener(new OnMenuItemClickListener() {
- public boolean onMenuItemClick(MenuItem item) {
- // Adding specific group for syncing
- if (child.mUngrouped && syncMode == SYNC_MODE_EVERYTHING) {
- account.setShouldSync(true);
- } else {
- account.setShouldSync(child, true);
- }
- mAdapter.notifyDataSetChanged();
- return true;
- }
- });
- }
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void onBackPressed() {
- doSaveAction();
- }
-
- private void doSaveAction() {
- mContactsPrefs.setSortOrder(mSortOrder);
- mContactsPrefs.setDisplayOrder(mDisplayOrder);
-
- if (mAdapter == null || mAdapter.mAccounts == null) {
- return;
- }
- setDisplayOnlyPhones(mDisplayPhones.isChecked());
- new UpdateTask(this).execute(mAdapter.mAccounts);
- }
-
- /**
- * Background task that persists changes to {@link Groups#GROUP_VISIBLE},
- * showing spinner dialog to user while updating.
- */
- public static class UpdateTask extends
- WeakAsyncTask<AccountSet, Void, Void, Activity> {
- private WeakReference<ProgressDialog> mProgress;
-
- public UpdateTask(Activity target) {
- super(target);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onPreExecute(Activity target) {
- final Context context = target;
-
- mProgress = new WeakReference<ProgressDialog>(ProgressDialog.show(context, null,
- context.getText(R.string.savingDisplayGroups)));
-
- // Before starting this task, start an empty service to protect our
- // process from being reclaimed by the system.
- context.startService(new Intent(context, EmptyService.class));
- }
-
- /** {@inheritDoc} */
- @Override
- protected Void doInBackground(Activity target, AccountSet... params) {
- final Context context = target;
- final ContentValues values = new ContentValues();
- final ContentResolver resolver = context.getContentResolver();
-
- try {
- // Build changes and persist in transaction
- final AccountSet set = params[0];
- final ArrayList<ContentProviderOperation> diff = set.buildDiff();
- resolver.applyBatch(ContactsContract.AUTHORITY, diff);
- } catch (RemoteException e) {
- Log.e(TAG, "Problem saving display groups", e);
- } catch (OperationApplicationException e) {
- Log.e(TAG, "Problem saving display groups", e);
- }
-
- return null;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onPostExecute(Activity target, Void result) {
- final Context context = target;
-
- final ProgressDialog dialog = mProgress.get();
- if (dialog != null) {
- try {
- dialog.dismiss();
- } catch (Exception e) {
- Log.e(TAG, "Error dismissing progress dialog", e);
- }
- }
-
- target.finish();
-
- // Stop the service that was protecting us
- context.stopService(new Intent(context, EmptyService.class));
- }
- }
-
- @Override
- public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
- boolean globalSearch) {
- if (globalSearch) {
- super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
- } else {
- ContactsSearchManager.startSearch(this, initialQuery);
- }
- }
-}
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
deleted file mode 100644
index e37aade..0000000
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ /dev/null
@@ -1,1434 +0,0 @@
-/*
- * 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.ui;
-
-import com.android.contacts.ContactsListActivity;
-import com.android.contacts.ContactsSearchManager;
-import com.android.contacts.ContactsUtils;
-import com.android.contacts.R;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.Editor;
-import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.EntitySet;
-import com.android.contacts.model.GoogleSource;
-import com.android.contacts.model.Sources;
-import com.android.contacts.model.ContactsSource.EditType;
-import com.android.contacts.model.Editor.EditorListener;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.ui.widget.BaseContactEditorView;
-import com.android.contacts.ui.widget.PhotoEditorView;
-import com.android.contacts.util.EmptyService;
-import com.android.contacts.util.WeakAsyncTask;
-import com.google.android.collect.Lists;
-
-import android.accounts.Account;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.ActivityNotFoundException;
-import android.content.ContentProviderOperation;
-import android.content.ContentProviderResult;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Entity;
-import android.content.Intent;
-import android.content.OperationApplicationException;
-import android.content.ContentProviderOperation.Builder;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.media.MediaScannerConnection;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.RemoteException;
-import android.provider.ContactsContract;
-import android.provider.MediaStore;
-import android.provider.ContactsContract.AggregationExceptions;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts.Data;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.LinearLayout;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.io.File;
-import java.lang.ref.WeakReference;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-
-/**
- * Activity for editing or inserting a contact.
- */
-public final class EditContactActivity extends Activity
- implements View.OnClickListener, Comparator<EntityDelta> {
-
- private static final String TAG = "EditContactActivity";
-
- /** 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 a contact to join with is returned */
- private static final int REQUEST_JOIN_CONTACT = 3022;
-
- /** The launch code when taking a picture */
- private static final int CAMERA_WITH_DATA = 3023;
-
- private static final String KEY_EDIT_STATE = "state";
- private static final String KEY_RAW_CONTACT_ID_REQUESTING_PHOTO = "photorequester";
- private static final String KEY_VIEW_ID_GENERATOR = "viewidgenerator";
- private static final String KEY_CURRENT_PHOTO_FILE = "currentphotofile";
- private static final String KEY_QUERY_SELECTION = "queryselection";
- private static final String KEY_CONTACT_ID_FOR_JOIN = "contactidforjoin";
-
- /** The result code when view activity should close after edit returns */
- public static final int RESULT_CLOSE_VIEW_ACTIVITY = 777;
-
- public static final int SAVE_MODE_DEFAULT = 0;
- public static final int SAVE_MODE_SPLIT = 1;
- public static final int SAVE_MODE_JOIN = 2;
-
- private long mRawContactIdRequestingPhoto = -1;
-
- private static final int DIALOG_CONFIRM_DELETE = 1;
- private static final int DIALOG_CONFIRM_READONLY_DELETE = 2;
- private static final int DIALOG_CONFIRM_MULTIPLE_DELETE = 3;
- private static final int DIALOG_CONFIRM_READONLY_HIDE = 4;
-
- private static final int ICON_SIZE = 96;
-
- private static final File PHOTO_DIR = new File(
- Environment.getExternalStorageDirectory() + "/DCIM/Camera");
-
- private File mCurrentPhotoFile;
-
- String mQuerySelection;
-
- private long mContactIdForJoin;
-
- private static final int STATUS_LOADING = 0;
- private static final int STATUS_EDITING = 1;
- private static final int STATUS_SAVING = 2;
-
- private int mStatus;
- private boolean mActivityActive; // true after onCreate/onResume, false at onPause
-
- EntitySet mState;
-
- /** The linear layout holding the ContactEditorViews */
- LinearLayout mContent;
-
- private ArrayList<Dialog> mManagedDialogs = Lists.newArrayList();
-
- private ViewIdGenerator mViewIdGenerator;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- final Intent intent = getIntent();
- final String action = intent.getAction();
-
- setContentView(R.layout.act_edit);
-
- // Build editor and listen for photo requests
- mContent = (LinearLayout) findViewById(R.id.editors);
-
- findViewById(R.id.btn_done).setOnClickListener(this);
- findViewById(R.id.btn_discard).setOnClickListener(this);
-
- // Handle initial actions only when existing state missing
- final boolean hasIncomingState = icicle != null && icicle.containsKey(KEY_EDIT_STATE);
-
- mActivityActive = true;
-
- if (Intent.ACTION_EDIT.equals(action) && !hasIncomingState) {
- setTitle(R.string.editContact_title_edit);
- mStatus = STATUS_LOADING;
-
- // Read initial state from database
- new QueryEntitiesTask(this).execute(intent);
- } else if (Intent.ACTION_INSERT.equals(action) && !hasIncomingState) {
- setTitle(R.string.editContact_title_insert);
- mStatus = STATUS_EDITING;
- // Trigger dialog to pick account type
- doAddAction();
- }
-
- if (icicle == null) {
- // If icicle is non-null, onRestoreInstanceState() will restore the generator.
- mViewIdGenerator = new ViewIdGenerator();
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mActivityActive = true;
- }
-
- @Override
- protected void onPause() {
- super.onResume();
- mActivityActive = false;
- }
-
- private static class QueryEntitiesTask extends
- WeakAsyncTask<Intent, Void, EntitySet, EditContactActivity> {
-
- private String mSelection;
-
- public QueryEntitiesTask(EditContactActivity target) {
- super(target);
- }
-
- @Override
- protected EntitySet doInBackground(EditContactActivity target, Intent... params) {
- final Intent intent = params[0];
-
- final ContentResolver resolver = target.getContentResolver();
-
- // Handle both legacy and new authorities
- final Uri data = intent.getData();
- final String authority = data.getAuthority();
- final String mimeType = intent.resolveType(resolver);
-
- mSelection = "0";
- if (ContactsContract.AUTHORITY.equals(authority)) {
- if (Contacts.CONTENT_ITEM_TYPE.equals(mimeType)) {
- // Handle selected aggregate
- final long contactId = ContentUris.parseId(data);
- mSelection = RawContacts.CONTACT_ID + "=" + contactId;
- } else if (RawContacts.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final long rawContactId = ContentUris.parseId(data);
- final long contactId = ContactsUtils.queryForContactId(resolver, rawContactId);
- mSelection = RawContacts.CONTACT_ID + "=" + contactId;
- }
- } else if (android.provider.Contacts.AUTHORITY.equals(authority)) {
- final long rawContactId = ContentUris.parseId(data);
- mSelection = Data.RAW_CONTACT_ID + "=" + rawContactId;
- }
-
- return EntitySet.fromQuery(target.getContentResolver(), mSelection, null, null);
- }
-
- @Override
- protected void onPostExecute(EditContactActivity target, EntitySet entitySet) {
- target.mQuerySelection = mSelection;
-
- // Load edit details in background
- final Context context = target;
- final Sources sources = Sources.getInstance(context);
-
- // Handle any incoming values that should be inserted
- final Bundle extras = target.getIntent().getExtras();
- final boolean hasExtras = extras != null && extras.size() > 0;
- final boolean hasState = entitySet.size() > 0;
- if (hasExtras && hasState) {
- // Find source defining the first RawContact found
- final EntityDelta state = entitySet.get(0);
- final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final ContactsSource source = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_CONSTRAINTS);
- EntityModifier.parseExtras(context, source, state, extras);
- }
-
- target.mState = entitySet;
-
- // Bind UI to new background state
- target.bindEditors();
- }
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- if (hasValidState()) {
- // Store entities with modifications
- outState.putParcelable(KEY_EDIT_STATE, mState);
- }
-
- outState.putLong(KEY_RAW_CONTACT_ID_REQUESTING_PHOTO, mRawContactIdRequestingPhoto);
- outState.putParcelable(KEY_VIEW_ID_GENERATOR, mViewIdGenerator);
- if (mCurrentPhotoFile != null) {
- outState.putString(KEY_CURRENT_PHOTO_FILE, mCurrentPhotoFile.toString());
- }
- outState.putString(KEY_QUERY_SELECTION, mQuerySelection);
- outState.putLong(KEY_CONTACT_ID_FOR_JOIN, mContactIdForJoin);
- super.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- // Read modifications from instance
- mState = savedInstanceState.<EntitySet> getParcelable(KEY_EDIT_STATE);
- mRawContactIdRequestingPhoto = savedInstanceState.getLong(
- KEY_RAW_CONTACT_ID_REQUESTING_PHOTO);
- mViewIdGenerator = savedInstanceState.getParcelable(KEY_VIEW_ID_GENERATOR);
- String fileName = savedInstanceState.getString(KEY_CURRENT_PHOTO_FILE);
- if (fileName != null) {
- mCurrentPhotoFile = new File(fileName);
- }
- mQuerySelection = savedInstanceState.getString(KEY_QUERY_SELECTION);
- mContactIdForJoin = savedInstanceState.getLong(KEY_CONTACT_ID_FOR_JOIN);
-
- bindEditors();
-
- super.onRestoreInstanceState(savedInstanceState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
-
- for (Dialog dialog : mManagedDialogs) {
- dismissDialog(dialog);
- }
- }
-
- @Override
- protected Dialog onCreateDialog(int id, Bundle bundle) {
- 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, new DeleteClickListener())
- .setCancelable(false)
- .create();
- case DIALOG_CONFIRM_READONLY_DELETE:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.readOnlyContactDeleteConfirmation)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, new DeleteClickListener())
- .setCancelable(false)
- .create();
- case DIALOG_CONFIRM_MULTIPLE_DELETE:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.multipleContactDeleteConfirmation)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, new DeleteClickListener())
- .setCancelable(false)
- .create();
- case DIALOG_CONFIRM_READONLY_HIDE:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.deleteConfirmation_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.readOnlyContactWarning)
- .setPositiveButton(android.R.string.ok, new DeleteClickListener())
- .setCancelable(false)
- .create();
- }
- return null;
- }
-
- /**
- * Start managing this {@link Dialog} along with the {@link Activity}.
- */
- private void startManagingDialog(Dialog dialog) {
- synchronized (mManagedDialogs) {
- mManagedDialogs.add(dialog);
- }
- }
-
- /**
- * Show this {@link Dialog} and manage with the {@link Activity}.
- */
- void showAndManageDialog(Dialog dialog) {
- startManagingDialog(dialog);
- dialog.show();
- }
-
- /**
- * Dismiss the given {@link Dialog}.
- */
- static void dismissDialog(Dialog dialog) {
- try {
- // Only dismiss when valid reference and still showing
- if (dialog != null && dialog.isShowing()) {
- dialog.dismiss();
- }
- } catch (Exception e) {
- Log.w(TAG, "Ignoring exception while dismissing dialog: " + e.toString());
- }
- }
-
- /**
- * Check if our internal {@link #mState} is valid, usually checked before
- * performing user actions.
- */
- protected boolean hasValidState() {
- return mStatus == STATUS_EDITING && mState != null && mState.size() > 0;
- }
-
- /**
- * Rebuild the editors to match our underlying {@link #mState} object, usually
- * called once we've parsed {@link Entity} data or have inserted a new
- * {@link RawContacts}.
- */
- protected void bindEditors() {
- if (mState == null) {
- return;
- }
-
- final LayoutInflater inflater = (LayoutInflater) getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- final Sources sources = Sources.getInstance(this);
-
- // Sort the editors
- Collections.sort(mState, this);
-
- // Remove any existing editors and rebuild any visible
- mContent.removeAllViews();
- int size = mState.size();
- for (int i = 0; i < size; i++) {
- // TODO ensure proper ordering of entities in the list
- EntityDelta entity = mState.get(i);
- final ValuesDelta values = entity.getValues();
- if (!values.isVisible()) continue;
-
- final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
- final ContactsSource source = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_CONSTRAINTS);
- final long rawContactId = values.getAsLong(RawContacts._ID);
-
- BaseContactEditorView editor;
- if (!source.readOnly) {
- editor = (BaseContactEditorView) inflater.inflate(R.layout.item_contact_editor,
- mContent, false);
- } else {
- editor = (BaseContactEditorView) inflater.inflate(
- R.layout.item_read_only_contact_editor, mContent, false);
- }
- PhotoEditorView photoEditor = editor.getPhotoEditor();
- photoEditor.setEditorListener(new PhotoListener(rawContactId, source.readOnly,
- photoEditor));
-
- mContent.addView(editor);
- editor.setState(entity, source, mViewIdGenerator);
- }
-
- // Show editor now that we've loaded state
- mContent.setVisibility(View.VISIBLE);
- mStatus = STATUS_EDITING;
- }
-
- /**
- * Class that listens to requests coming from photo editors
- */
- private class PhotoListener implements EditorListener, DialogInterface.OnClickListener {
- private long mRawContactId;
- private boolean mReadOnly;
- private PhotoEditorView mEditor;
-
- public PhotoListener(long rawContactId, boolean readOnly, PhotoEditorView editor) {
- mRawContactId = rawContactId;
- mReadOnly = readOnly;
- mEditor = editor;
- }
-
- public void onDeleted(Editor editor) {
- // Do nothing
- }
-
- public void onRequest(int request) {
- if (!hasValidState()) return;
-
- if (request == EditorListener.REQUEST_PICK_PHOTO) {
- if (mEditor.hasSetPhoto()) {
- // There is an existing photo, offer to remove, replace, or promoto to primary
- createPhotoDialog().show();
- } else if (!mReadOnly) {
- // No photo set and not read-only, try to set the photo
- doPickPhotoAction(mRawContactId);
- }
- }
- }
-
- /**
- * Prepare dialog for picking a new {@link EditType} or entering a
- * custom label. This dialog is limited to the valid types as determined
- * by {@link EntityModifier}.
- */
- public Dialog createPhotoDialog() {
- Context context = EditContactActivity.this;
-
- // Wrap our context to inflate list items using correct theme
- final Context dialogContext = new ContextThemeWrapper(context,
- android.R.style.Theme_Light);
-
- String[] choices;
- if (mReadOnly) {
- choices = new String[1];
- choices[0] = getString(R.string.use_photo_as_primary);
- } else {
- choices = new String[3];
- choices[0] = getString(R.string.use_photo_as_primary);
- choices[1] = getString(R.string.removePicture);
- choices[2] = getString(R.string.changePicture);
- }
- final ListAdapter adapter = new ArrayAdapter<String>(dialogContext,
- android.R.layout.simple_list_item_1, choices);
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(dialogContext);
- builder.setTitle(R.string.attachToContact);
- builder.setSingleChoiceItems(adapter, -1, this);
- return builder.create();
- }
-
- /**
- * Called when something in the dialog is clicked
- */
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
-
- switch (which) {
- case 0:
- // Set the photo as super primary
- mEditor.setSuperPrimary(true);
-
- // And set all other photos as not super primary
- int count = mContent.getChildCount();
- for (int i = 0; i < count; i++) {
- View childView = mContent.getChildAt(i);
- if (childView instanceof BaseContactEditorView) {
- BaseContactEditorView editor = (BaseContactEditorView) childView;
- PhotoEditorView photoEditor = editor.getPhotoEditor();
- if (!photoEditor.equals(mEditor)) {
- photoEditor.setSuperPrimary(false);
- }
- }
- }
- break;
-
- case 1:
- // Remove the photo
- mEditor.setPhotoBitmap(null);
- break;
-
- case 2:
- // Pick a new photo for the contact
- doPickPhotoAction(mRawContactId);
- break;
- }
- }
- }
-
- /** {@inheritDoc} */
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.btn_done:
- doSaveAction(SAVE_MODE_DEFAULT);
- break;
- case R.id.btn_discard:
- doRevertAction();
- break;
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public void onBackPressed() {
- doSaveAction(SAVE_MODE_DEFAULT);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- // Ignore failed requests
- if (resultCode != RESULT_OK) return;
-
- switch (requestCode) {
- case PHOTO_PICKED_WITH_DATA: {
- BaseContactEditorView requestingEditor = null;
- for (int i = 0; i < mContent.getChildCount(); i++) {
- View childView = mContent.getChildAt(i);
- if (childView instanceof BaseContactEditorView) {
- BaseContactEditorView editor = (BaseContactEditorView) childView;
- if (editor.getRawContactId() == mRawContactIdRequestingPhoto) {
- requestingEditor = editor;
- break;
- }
- }
- }
-
- if (requestingEditor != null) {
- final Bitmap photo = data.getParcelableExtra("data");
- requestingEditor.setPhotoBitmap(photo);
- mRawContactIdRequestingPhoto = -1;
- } else {
- // The contact that requested the photo is no longer present.
- // TODO: Show error message
- }
-
- break;
- }
-
- case CAMERA_WITH_DATA: {
- doCropPhoto(mCurrentPhotoFile);
- break;
- }
-
- case REQUEST_JOIN_CONTACT: {
- if (resultCode == RESULT_OK && data != null) {
- final long contactId = ContentUris.parseId(data.getData());
- joinAggregate(contactId);
- }
- }
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
-
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.edit, menu);
-
-
- return true;
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- menu.findItem(R.id.menu_split).setVisible(mState != null && mState.size() > 1);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_done:
- return doSaveAction(SAVE_MODE_DEFAULT);
- case R.id.menu_discard:
- return doRevertAction();
- case R.id.menu_add:
- return doAddAction();
- case R.id.menu_delete:
- return doDeleteAction();
- case R.id.menu_split:
- return doSplitContactAction();
- case R.id.menu_join:
- return doJoinContactAction();
- }
- return false;
- }
-
- /**
- * Background task for persisting edited contact data, using the changes
- * defined by a set of {@link EntityDelta}. This task starts
- * {@link EmptyService} to make sure the background thread can finish
- * persisting in cases where the system wants to reclaim our process.
- */
- public static class PersistTask extends
- WeakAsyncTask<EntitySet, Void, Integer, EditContactActivity> {
- private static final int PERSIST_TRIES = 3;
-
- private static final int RESULT_UNCHANGED = 0;
- private static final int RESULT_SUCCESS = 1;
- private static final int RESULT_FAILURE = 2;
-
- private WeakReference<ProgressDialog> mProgress;
-
- private int mSaveMode;
- private Uri mContactLookupUri = null;
-
- public PersistTask(EditContactActivity target, int saveMode) {
- super(target);
- mSaveMode = saveMode;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onPreExecute(EditContactActivity target) {
- mProgress = new WeakReference<ProgressDialog>(ProgressDialog.show(target, null,
- target.getText(R.string.savingContact)));
-
- // Before starting this task, start an empty service to protect our
- // process from being reclaimed by the system.
- final Context context = target;
- context.startService(new Intent(context, EmptyService.class));
- }
-
- /** {@inheritDoc} */
- @Override
- protected Integer doInBackground(EditContactActivity target, EntitySet... params) {
- final Context context = target;
- final ContentResolver resolver = context.getContentResolver();
-
- EntitySet state = params[0];
-
- // Trim any empty fields, and RawContacts, before persisting
- final Sources sources = Sources.getInstance(context);
- EntityModifier.trimEmpty(state, sources);
-
- // Attempt to persist changes
- int tries = 0;
- Integer result = RESULT_FAILURE;
- while (tries++ < PERSIST_TRIES) {
- try {
- // Build operations and try applying
- final ArrayList<ContentProviderOperation> diff = state.buildDiff();
- ContentProviderResult[] results = null;
- if (!diff.isEmpty()) {
- results = resolver.applyBatch(ContactsContract.AUTHORITY, diff);
- }
-
- final long rawContactId = getRawContactId(state, diff, results);
- if (rawContactId != -1) {
- final Uri rawContactUri = ContentUris.withAppendedId(
- RawContacts.CONTENT_URI, rawContactId);
-
- // convert the raw contact URI to a contact URI
- mContactLookupUri = RawContacts.getContactLookupUri(resolver,
- rawContactUri);
- }
- result = (diff.size() > 0) ? RESULT_SUCCESS : RESULT_UNCHANGED;
- break;
-
- } catch (RemoteException e) {
- // Something went wrong, bail without success
- Log.e(TAG, "Problem persisting user edits", e);
- break;
-
- } catch (OperationApplicationException e) {
- // Version consistency failed, re-parent change and try again
- Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
- final EntitySet newState = EntitySet.fromQuery(resolver,
- target.mQuerySelection, null, null);
- state = EntitySet.mergeAfter(newState, state);
- }
- }
-
- return result;
- }
-
- private long getRawContactId(EntitySet state,
- final ArrayList<ContentProviderOperation> diff,
- final ContentProviderResult[] results) {
- long rawContactId = state.findRawContactId();
- if (rawContactId != -1) {
- return rawContactId;
- }
-
- // we gotta do some searching for the id
- final int diffSize = diff.size();
- for (int i = 0; i < diffSize; i++) {
- ContentProviderOperation operation = diff.get(i);
- if (operation.getType() == ContentProviderOperation.TYPE_INSERT
- && operation.getUri().getEncodedPath().contains(
- RawContacts.CONTENT_URI.getEncodedPath())) {
- return ContentUris.parseId(results[i].uri);
- }
- }
- return -1;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onPostExecute(EditContactActivity target, Integer result) {
- final Context context = target;
- final ProgressDialog progress = mProgress.get();
-
- if (result == RESULT_SUCCESS && mSaveMode != SAVE_MODE_JOIN) {
- Toast.makeText(context, R.string.contactSavedToast, Toast.LENGTH_SHORT).show();
- } else if (result == RESULT_FAILURE) {
- Toast.makeText(context, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
- }
-
- dismissDialog(progress);
-
- // Stop the service that was protecting us
- context.stopService(new Intent(context, EmptyService.class));
-
- target.onSaveCompleted(result != RESULT_FAILURE, mSaveMode, mContactLookupUri);
- }
- }
-
- /**
- * Saves or creates the contact based on the mode, and if successful
- * finishes the activity.
- */
- boolean doSaveAction(int saveMode) {
- if (!hasValidState()) {
- return false;
- }
-
- mStatus = STATUS_SAVING;
- final PersistTask task = new PersistTask(this, saveMode);
- task.execute(mState);
-
- return true;
- }
-
- private class DeleteClickListener implements DialogInterface.OnClickListener {
-
- public void onClick(DialogInterface dialog, int which) {
- Sources sources = Sources.getInstance(EditContactActivity.this);
- // Mark all raw contacts for deletion
- for (EntityDelta delta : mState) {
- delta.markDeleted();
- }
- // Save the deletes
- doSaveAction(SAVE_MODE_DEFAULT);
- finish();
- }
- }
-
- private void onSaveCompleted(boolean success, int saveMode, Uri contactLookupUri) {
- switch (saveMode) {
- case SAVE_MODE_DEFAULT:
- if (success && contactLookupUri != null) {
- final Intent resultIntent = new Intent();
-
- final Uri requestData = getIntent().getData();
- final String requestAuthority = requestData == null ? null : requestData
- .getAuthority();
-
- if (android.provider.Contacts.AUTHORITY.equals(requestAuthority)) {
- // Build legacy Uri when requested by caller
- final long contactId = ContentUris.parseId(Contacts.lookupContact(
- getContentResolver(), contactLookupUri));
- final Uri legacyUri = ContentUris.withAppendedId(
- android.provider.Contacts.People.CONTENT_URI, contactId);
- resultIntent.setData(legacyUri);
- } else {
- // Otherwise pass back a lookup-style Uri
- resultIntent.setData(contactLookupUri);
- }
-
- setResult(RESULT_OK, resultIntent);
- } else {
- setResult(RESULT_CANCELED, null);
- }
- finish();
- break;
-
- case SAVE_MODE_SPLIT:
- if (success) {
- Intent intent = new Intent();
- intent.setData(contactLookupUri);
- setResult(RESULT_CLOSE_VIEW_ACTIVITY, intent);
- }
- finish();
- break;
-
- case SAVE_MODE_JOIN:
- mStatus = STATUS_EDITING;
- if (success) {
- showJoinAggregateActivity(contactLookupUri);
- }
- break;
- }
- }
-
- /**
- * Shows a list of aggregates that can be joined into the currently viewed aggregate.
- *
- * @param contactLookupUri the fresh URI for the currently edited contact (after saving it)
- */
- public void showJoinAggregateActivity(Uri contactLookupUri) {
- if (contactLookupUri == null) {
- return;
- }
-
- mContactIdForJoin = ContentUris.parseId(contactLookupUri);
- Intent intent = new Intent(ContactsListActivity.JOIN_AGGREGATE);
- intent.putExtra(ContactsListActivity.EXTRA_AGGREGATE_ID, mContactIdForJoin);
- startActivityForResult(intent, REQUEST_JOIN_CONTACT);
- }
-
- private interface JoinContactQuery {
- String[] PROJECTION = {
- RawContacts._ID,
- RawContacts.CONTACT_ID,
- RawContacts.NAME_VERIFIED,
- };
-
- String SELECTION = RawContacts.CONTACT_ID + "=? OR " + RawContacts.CONTACT_ID + "=?";
-
- int _ID = 0;
- int CONTACT_ID = 1;
- int NAME_VERIFIED = 2;
- }
-
- /**
- * Performs aggregation with the contact selected by the user from suggestions or A-Z list.
- */
- private void joinAggregate(final long contactId) {
- ContentResolver resolver = getContentResolver();
-
- // Load raw contact IDs for all raw contacts involved - currently edited and selected
- // in the join UIs
- Cursor c = resolver.query(RawContacts.CONTENT_URI,
- JoinContactQuery.PROJECTION,
- JoinContactQuery.SELECTION,
- new String[]{String.valueOf(contactId), String.valueOf(mContactIdForJoin)}, null);
-
- long rawContactIds[];
- long verifiedNameRawContactId = -1;
- try {
- rawContactIds = new long[c.getCount()];
- for (int i = 0; i < rawContactIds.length; i++) {
- c.moveToNext();
- long rawContactId = c.getLong(JoinContactQuery._ID);
- rawContactIds[i] = rawContactId;
- if (c.getLong(JoinContactQuery.CONTACT_ID) == mContactIdForJoin) {
- if (verifiedNameRawContactId == -1
- || c.getInt(JoinContactQuery.NAME_VERIFIED) != 0) {
- verifiedNameRawContactId = rawContactId;
- }
- }
- }
- } finally {
- c.close();
- }
-
- // For each pair of raw contacts, insert an aggregation exception
- ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
- for (int i = 0; i < rawContactIds.length; i++) {
- for (int j = 0; j < rawContactIds.length; j++) {
- if (i != j) {
- buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]);
- }
- }
- }
-
- // Mark the original contact as "name verified" to make sure that the contact
- // display name does not change as a result of the join
- Builder builder = ContentProviderOperation.newUpdate(
- ContentUris.withAppendedId(RawContacts.CONTENT_URI, verifiedNameRawContactId));
- builder.withValue(RawContacts.NAME_VERIFIED, 1);
- operations.add(builder.build());
-
- // Apply all aggregation exceptions as one batch
- try {
- getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations);
-
- // We can use any of the constituent raw contacts to refresh the UI - why not the first
- Intent intent = new Intent();
- intent.setData(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactIds[0]));
-
- // Reload the new state from database
- new QueryEntitiesTask(this).execute(intent);
-
- Toast.makeText(this, R.string.contactsJoinedMessage, Toast.LENGTH_LONG).show();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to apply aggregation exception batch", e);
- Toast.makeText(this, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
- } catch (OperationApplicationException e) {
- Log.e(TAG, "Failed to apply aggregation exception batch", e);
- Toast.makeText(this, R.string.contactSavedErrorToast, Toast.LENGTH_LONG).show();
- }
- }
-
- /**
- * Construct a {@link AggregationExceptions#TYPE_KEEP_TOGETHER} ContentProviderOperation.
- */
- private void buildJoinContactDiff(ArrayList<ContentProviderOperation> operations,
- long rawContactId1, long rawContactId2) {
- Builder builder =
- ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI);
- builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
- builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
- builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
- operations.add(builder.build());
- }
-
- /**
- * Revert any changes the user has made, and finish the activity.
- */
- private boolean doRevertAction() {
- finish();
- return true;
- }
-
- /**
- * Create a new {@link RawContacts} which will exist as another
- * {@link EntityDelta} under the currently edited {@link Contacts}.
- */
- private boolean doAddAction() {
- if (mStatus != STATUS_EDITING) {
- return false;
- }
-
- // Adding is okay when missing state
- new AddContactTask(this).execute();
- return true;
- }
-
- /**
- * Delete the entire contact currently being edited, which usually asks for
- * user confirmation before continuing.
- */
- private boolean doDeleteAction() {
- if (!hasValidState())
- return false;
- int readOnlySourcesCnt = 0;
- int writableSourcesCnt = 0;
- Sources sources = Sources.getInstance(EditContactActivity.this);
- for (EntityDelta delta : mState) {
- final String accountType = delta.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final ContactsSource contactsSource = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_CONSTRAINTS);
- if (contactsSource != null && contactsSource.readOnly) {
- readOnlySourcesCnt += 1;
- } else {
- writableSourcesCnt += 1;
- }
- }
-
- if (readOnlySourcesCnt > 0 && writableSourcesCnt > 0) {
- showDialog(DIALOG_CONFIRM_READONLY_DELETE);
- } else if (readOnlySourcesCnt > 0 && writableSourcesCnt == 0) {
- showDialog(DIALOG_CONFIRM_READONLY_HIDE);
- } else if (readOnlySourcesCnt == 0 && writableSourcesCnt > 1) {
- showDialog(DIALOG_CONFIRM_MULTIPLE_DELETE);
- } else {
- showDialog(DIALOG_CONFIRM_DELETE);
- }
- return true;
- }
-
- /**
- * Pick a specific photo to be added under the currently selected tab.
- */
- boolean doPickPhotoAction(long rawContactId) {
- if (!hasValidState()) return false;
-
- mRawContactIdRequestingPhoto = rawContactId;
-
- showAndManageDialog(createPickPhotoDialog());
-
- return true;
- }
-
- /**
- * Creates a dialog offering two options: take a photo or pick a photo from the gallery.
- */
- private Dialog createPickPhotoDialog() {
- Context context = EditContactActivity.this;
-
- // Wrap our context to inflate list items using correct theme
- final Context dialogContext = new ContextThemeWrapper(context,
- android.R.style.Theme_Light);
-
- String[] choices;
- choices = new String[2];
- choices[0] = getString(R.string.take_photo);
- choices[1] = getString(R.string.pick_photo);
- final ListAdapter adapter = new ArrayAdapter<String>(dialogContext,
- android.R.layout.simple_list_item_1, choices);
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(dialogContext);
- builder.setTitle(R.string.attachToContact);
- builder.setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- switch(which) {
- case 0:
- doTakePhoto();
- break;
- case 1:
- doPickPhotoFromGallery();
- break;
- }
- }
- });
- return builder.create();
- }
-
- /**
- * Create a file name for the icon photo using current time.
- */
- private String getPhotoFileName() {
- Date date = new Date(System.currentTimeMillis());
- SimpleDateFormat dateFormat = new SimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");
- return dateFormat.format(date) + ".jpg";
- }
-
- /**
- * Launches Camera to take a picture and store it in a file.
- */
- protected void doTakePhoto() {
- try {
- // Launch camera to take photo for selected contact
- PHOTO_DIR.mkdirs();
- mCurrentPhotoFile = new File(PHOTO_DIR, getPhotoFileName());
- final Intent intent = getTakePickIntent(mCurrentPhotoFile);
- startActivityForResult(intent, CAMERA_WITH_DATA);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
- }
- }
-
- /**
- * Constructs an intent for capturing a photo and storing it in a temporary file.
- */
- public static Intent getTakePickIntent(File f) {
- Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null);
- intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
- return intent;
- }
-
- /**
- * Sends a newly acquired photo to Gallery for cropping
- */
- protected void doCropPhoto(File f) {
- try {
-
- // Add the image to the media store
- MediaScannerConnection.scanFile(
- this,
- new String[] { f.getAbsolutePath() },
- new String[] { null },
- null);
-
- // Launch gallery to crop the photo
- final Intent intent = getCropImageIntent(Uri.fromFile(f));
- startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
- } catch (Exception e) {
- Log.e(TAG, "Cannot crop image", e);
- Toast.makeText(this, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
- }
- }
-
- /**
- * Constructs an intent for image cropping.
- */
- public static Intent getCropImageIntent(Uri photoUri) {
- Intent intent = new Intent("com.android.camera.action.CROP");
- intent.setDataAndType(photoUri, "image/*");
- intent.putExtra("crop", "true");
- intent.putExtra("aspectX", 1);
- intent.putExtra("aspectY", 1);
- intent.putExtra("outputX", ICON_SIZE);
- intent.putExtra("outputY", ICON_SIZE);
- intent.putExtra("return-data", true);
- return intent;
- }
-
- /**
- * Launches Gallery to pick a photo.
- */
- protected void doPickPhotoFromGallery() {
- try {
- // Launch picker to choose photo for selected contact
- final Intent intent = getPhotoPickIntent();
- startActivityForResult(intent, PHOTO_PICKED_WITH_DATA);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(this, R.string.photoPickerNotFoundText, Toast.LENGTH_LONG).show();
- }
- }
-
- /**
- * Constructs an intent for picking a photo from Gallery, cropping it and returning the bitmap.
- */
- public static Intent getPhotoPickIntent() {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
- intent.setType("image/*");
- intent.putExtra("crop", "true");
- intent.putExtra("aspectX", 1);
- intent.putExtra("aspectY", 1);
- intent.putExtra("outputX", ICON_SIZE);
- intent.putExtra("outputY", ICON_SIZE);
- intent.putExtra("return-data", true);
- return intent;
- }
-
- /** {@inheritDoc} */
- public void onDeleted(Editor editor) {
- // Ignore any editor deletes
- }
-
- private boolean doSplitContactAction() {
- if (!hasValidState()) return false;
-
- showAndManageDialog(createSplitDialog());
- return true;
- }
-
- private Dialog createSplitDialog() {
- final AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.splitConfirmation_title);
- builder.setIcon(android.R.drawable.ic_dialog_alert);
- builder.setMessage(R.string.splitConfirmation);
- builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- // Split the contacts
- mState.splitRawContacts();
- doSaveAction(SAVE_MODE_SPLIT);
- }
- });
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.setCancelable(false);
- return builder.create();
- }
-
- private boolean doJoinContactAction() {
- return doSaveAction(SAVE_MODE_JOIN);
- }
-
- /**
- * Build dialog that handles adding a new {@link RawContacts} after the user
- * picks a specific {@link ContactsSource}.
- */
- private static class AddContactTask extends
- WeakAsyncTask<Void, Void, ArrayList<Account>, EditContactActivity> {
-
- public AddContactTask(EditContactActivity target) {
- super(target);
- }
-
- @Override
- protected ArrayList<Account> doInBackground(final EditContactActivity target,
- Void... params) {
- return Sources.getInstance(target).getAccounts(true);
- }
-
- @Override
- protected void onPostExecute(final EditContactActivity target, ArrayList<Account> accounts) {
- if (!target.mActivityActive) {
- // A monkey or very fast user.
- return;
- }
- target.selectAccountAndCreateContact(accounts);
- }
- }
-
- public void selectAccountAndCreateContact(ArrayList<Account> accounts) {
- // No Accounts available. Create a phone-local contact.
- if (accounts.isEmpty()) {
- createContact(null);
- return; // Don't show a dialog.
- }
-
- // In the common case of a single account being writable, auto-select
- // it without showing a dialog.
- if (accounts.size() == 1) {
- createContact(accounts.get(0));
- return; // Don't show a dialog.
- }
-
- // Wrap our context to inflate list items using correct theme
- final Context dialogContext = new ContextThemeWrapper(this, android.R.style.Theme_Light);
- final LayoutInflater dialogInflater =
- (LayoutInflater)dialogContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- final Sources sources = Sources.getInstance(this);
-
- final ArrayAdapter<Account> accountAdapter = new ArrayAdapter<Account>(this,
- android.R.layout.simple_list_item_2, accounts) {
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = dialogInflater.inflate(android.R.layout.simple_list_item_2,
- parent, false);
- }
-
- // TODO: show icon along with title
- final TextView text1 = (TextView)convertView.findViewById(android.R.id.text1);
- final TextView text2 = (TextView)convertView.findViewById(android.R.id.text2);
-
- final Account account = this.getItem(position);
- final ContactsSource source = sources.getInflatedSource(account.type,
- ContactsSource.LEVEL_SUMMARY);
-
- text1.setText(account.name);
- text2.setText(source.getDisplayLabel(EditContactActivity.this));
-
- return convertView;
- }
- };
-
- final DialogInterface.OnClickListener clickListener =
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
-
- // Create new contact based on selected source
- final Account account = accountAdapter.getItem(which);
- createContact(account);
- }
- };
-
- final DialogInterface.OnCancelListener cancelListener =
- new DialogInterface.OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- // If nothing remains, close activity
- if (!hasValidState()) {
- finish();
- }
- }
- };
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.dialog_new_contact_account);
- builder.setSingleChoiceItems(accountAdapter, 0, clickListener);
- builder.setOnCancelListener(cancelListener);
- showAndManageDialog(builder.create());
- }
-
- /**
- * @param account may be null to signal a device-local contact should
- * be created.
- */
- private void createContact(Account account) {
- final Sources sources = Sources.getInstance(this);
- final ContentValues values = new ContentValues();
- if (account != null) {
- values.put(RawContacts.ACCOUNT_NAME, account.name);
- values.put(RawContacts.ACCOUNT_TYPE, account.type);
- } else {
- values.putNull(RawContacts.ACCOUNT_NAME);
- values.putNull(RawContacts.ACCOUNT_TYPE);
- }
-
- // Parse any values from incoming intent
- EntityDelta insert = new EntityDelta(ValuesDelta.fromAfter(values));
- final ContactsSource source = sources.getInflatedSource(
- account != null ? account.type : null,
- ContactsSource.LEVEL_CONSTRAINTS);
- final Bundle extras = getIntent().getExtras();
- EntityModifier.parseExtras(this, source, insert, extras);
-
- // Ensure we have some default fields
- EntityModifier.ensureKindExists(insert, source, Phone.CONTENT_ITEM_TYPE);
- EntityModifier.ensureKindExists(insert, source, Email.CONTENT_ITEM_TYPE);
-
- // Create "My Contacts" membership for Google contacts
- // TODO: move this off into "templates" for each given source
- if (GoogleSource.ACCOUNT_TYPE.equals(source.accountType)) {
- GoogleSource.attemptMyContactsMembership(insert, this);
- }
-
- if (mState == null) {
- // Create state if none exists yet
- mState = EntitySet.fromSingle(insert);
- } else {
- // Add contact onto end of existing state
- mState.add(insert);
- }
-
- bindEditors();
- }
-
- /**
- * Compare EntityDeltas for sorting the stack of editors.
- */
- public int compare(EntityDelta one, EntityDelta two) {
- // Check direct equality
- if (one.equals(two)) {
- return 0;
- }
-
- final Sources sources = Sources.getInstance(this);
- String accountType = one.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final ContactsSource oneSource = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_SUMMARY);
- accountType = two.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final ContactsSource twoSource = sources.getInflatedSource(accountType,
- ContactsSource.LEVEL_SUMMARY);
-
- // Check read-only
- if (oneSource.readOnly && !twoSource.readOnly) {
- return 1;
- } else if (twoSource.readOnly && !oneSource.readOnly) {
- return -1;
- }
-
- // Check account type
- boolean skipAccountTypeCheck = false;
- boolean oneIsGoogle = oneSource instanceof GoogleSource;
- boolean twoIsGoogle = twoSource instanceof GoogleSource;
- if (oneIsGoogle && !twoIsGoogle) {
- return -1;
- } else if (twoIsGoogle && !oneIsGoogle) {
- return 1;
- } else if (oneIsGoogle && twoIsGoogle){
- skipAccountTypeCheck = true;
- }
-
- int value;
- if (!skipAccountTypeCheck) {
- if (oneSource.accountType == null) {
- return 1;
- }
- value = oneSource.accountType.compareTo(twoSource.accountType);
- if (value != 0) {
- return value;
- }
- }
-
- // Check account name
- ValuesDelta oneValues = one.getValues();
- String oneAccount = oneValues.getAsString(RawContacts.ACCOUNT_NAME);
- if (oneAccount == null) oneAccount = "";
- ValuesDelta twoValues = two.getValues();
- String twoAccount = twoValues.getAsString(RawContacts.ACCOUNT_NAME);
- if (twoAccount == null) twoAccount = "";
- value = oneAccount.compareTo(twoAccount);
- if (value != 0) {
- return value;
- }
-
- // Both are in the same account, fall back to contact ID
- Long oneId = oneValues.getAsLong(RawContacts._ID);
- Long twoId = twoValues.getAsLong(RawContacts._ID);
- if (oneId == null) {
- return -1;
- } else if (twoId == null) {
- return 1;
- }
-
- return (int)(oneId - twoId);
- }
-
- @Override
- public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
- boolean globalSearch) {
- if (globalSearch) {
- super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
- } else {
- ContactsSearchManager.startSearch(this, initialQuery);
- }
- }
-}
diff --git a/src/com/android/contacts/ui/QuickContactActivity.java b/src/com/android/contacts/ui/QuickContactActivity.java
deleted file mode 100644
index ec8f44d..0000000
--- a/src/com/android/contacts/ui/QuickContactActivity.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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.ui;
-
-import com.android.contacts.StickyTabs;
-
-import android.app.Activity;
-import android.content.ContentUris;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract.QuickContact;
-import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
-
-/**
- * Stub translucent activity that just shows {@link QuickContactWindow} floating
- * above the caller. This temporary hack should eventually be replaced with
- * direct framework support.
- */
-public final class QuickContactActivity extends Activity implements
- QuickContactWindow.OnDismissListener {
- private static final String TAG = "QuickContactActivity";
-
- static final boolean LOGV = false;
- static final boolean FORCE_CREATE = false;
-
- private QuickContactWindow mQuickContact;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- if (LOGV) Log.d(TAG, "onCreate");
-
- this.onNewIntent(getIntent());
- }
-
- @Override
- public void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- if (LOGV) Log.d(TAG, "onNewIntent");
-
- if (QuickContactWindow.TRACE_LAUNCH) {
- android.os.Debug.startMethodTracing(QuickContactWindow.TRACE_TAG);
- }
-
- if (mQuickContact == null || FORCE_CREATE) {
- if (LOGV) Log.d(TAG, "Preparing window");
- mQuickContact = new QuickContactWindow(this, this);
- }
- mQuickContact.setLastSelectedContactsAppTab(StickyTabs.getTab(intent));
-
- // Use our local window token for now
- Uri lookupUri = intent.getData();
- // Check to see whether it comes from the old version.
- if (android.provider.Contacts.AUTHORITY.equals(lookupUri.getAuthority())) {
- final long rawContactId = ContentUris.parseId(lookupUri);
- lookupUri = RawContacts.getContactLookupUri(getContentResolver(),
- ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
- }
- final Bundle extras = intent.getExtras();
-
- // Read requested parameters for displaying
- final Rect target = intent.getSourceBounds();
- final int mode = extras.getInt(QuickContact.EXTRA_MODE, QuickContact.MODE_MEDIUM);
- final String[] excludeMimes = extras.getStringArray(QuickContact.EXTRA_EXCLUDE_MIMES);
-
- mQuickContact.show(lookupUri, target, mode, excludeMimes);
- }
-
- /** {@inheritDoc} */
- @Override
- public void onBackPressed() {
- if (LOGV) Log.w(TAG, "Unexpected back captured by stub activity");
- finish();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- if (LOGV) Log.d(TAG, "onPause");
-
- // Dismiss any dialog when pausing
- mQuickContact.dismiss();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (LOGV) Log.d(TAG, "onDestroy");
- }
-
- /** {@inheritDoc} */
- public void onDismiss(QuickContactWindow dialog) {
- if (LOGV) Log.d(TAG, "onDismiss");
-
- if (isTaskRoot() && !FORCE_CREATE) {
- // 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);
- } else {
- finish();
- }
- }
-}
diff --git a/src/com/android/contacts/ui/QuickContactWindow.java b/src/com/android/contacts/ui/QuickContactWindow.java
deleted file mode 100644
index 889749e..0000000
--- a/src/com/android/contacts/ui/QuickContactWindow.java
+++ /dev/null
@@ -1,1719 +0,0 @@
-/*
- * 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.ui;
-
-import com.android.contacts.Collapser;
-import com.android.contacts.ContactPresenceIconUtil;
-import com.android.contacts.ContactsUtils;
-import com.android.contacts.R;
-import com.android.contacts.StickyTabs;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.Sources;
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.ui.widget.CheckableImageView;
-import com.android.contacts.util.Constants;
-import com.android.contacts.util.DataStatus;
-import com.android.contacts.util.NotifyingAsyncQueryHandler;
-import com.android.internal.policy.PolicyManager;
-import com.google.android.collect.Sets;
-
-import android.content.ActivityNotFoundException;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.QuickContact;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.StatusUpdates;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.SipAddress;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.HorizontalScrollView;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.lang.ref.SoftReference;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Window that shows QuickContact dialog for a specific {@link Contacts#_ID}.
- */
-public class QuickContactWindow implements Window.Callback,
- NotifyingAsyncQueryHandler.AsyncQueryListener, View.OnClickListener,
- AbsListView.OnItemClickListener, CompoundButton.OnCheckedChangeListener, KeyEvent.Callback,
- OnGlobalLayoutListener {
- private static final String TAG = "QuickContactWindow";
-
- /**
- * Interface used to allow the person showing a {@link QuickContactWindow} to
- * know when the window has been dismissed.
- */
- public interface OnDismissListener {
- public void onDismiss(QuickContactWindow dialog);
- }
-
- /**
- * Custom layout the sole purpose of which is to intercept the BACK key and
- * close QC even when the soft keyboard is open.
- */
- public static class RootLayout extends RelativeLayout {
-
- QuickContactWindow mQuickContactWindow;
-
- public RootLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- /**
- * Intercepts the BACK key event and dismisses QuickContact window.
- */
- @Override
- public boolean dispatchKeyEventPreIme(KeyEvent event) {
- if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
- mQuickContactWindow.onBackPressed();
- return true;
- } else {
- return super.dispatchKeyEventPreIme(event);
- }
- }
- }
-
- private final Context mContext;
- private final LayoutInflater mInflater;
- private final WindowManager mWindowManager;
- private Window mWindow;
- private View mDecor;
- private final Rect mRect = new Rect();
-
- private boolean mDismissed = false;
- private boolean mQuerying = false;
- private boolean mShowing = false;
-
- private NotifyingAsyncQueryHandler mHandler;
- private OnDismissListener mDismissListener;
- private ResolveCache mResolveCache;
-
- /** Last selected tab of the Dialtacs-Activity. This is -1 if not called out of contacts app */
- private int mLastSelectedContactsAppTab;
-
- private Uri mLookupUri;
- private Rect mAnchor;
-
- private int mShadowHoriz;
- private int mShadowVert;
- private int mShadowTouch;
-
- private int mScreenWidth;
- private int mScreenHeight;
- private int mRequestedY;
-
- private boolean mHasValidSocial = false;
- private boolean mMakePrimary = false;
-
- private ImageView mArrowUp;
- private ImageView mArrowDown;
-
- private int mMode;
- private RootLayout mRootView;
- private View mHeader;
- private HorizontalScrollView mTrackScroll;
- private ViewGroup mTrack;
- private Animation mTrackAnim;
-
- private View mFooter;
- private View mFooterDisambig;
- private ListView mResolveList;
- private CheckableImageView mLastAction;
- private CheckBox mSetPrimaryCheckBox;
-
- private int mWindowRecycled = 0;
- private int mActionRecycled = 0;
-
- /**
- * Set of {@link Action} that are associated with the aggregate currently
- * displayed by this dialog, represented as a map from {@link String}
- * MIME-type to {@link ActionList}.
- */
- private ActionMap mActions = new ActionMap();
-
- /**
- * Pool of unused {@link CheckableImageView} that have previously been
- * inflated, and are ready to be recycled through {@link #obtainView()}.
- */
- private LinkedList<View> mActionPool = new LinkedList<View>();
-
- private String[] mExcludeMimes;
-
- /**
- * {@link #PRECEDING_MIMETYPES} and {@link #FOLLOWING_MIMETYPES} are used to sort MIME-types.
- *
- * <p>The MIME-types in {@link #PRECEDING_MIMETYPES} appear in the front of the dialog,
- * in the order in the array.
- *
- * <p>The ones in {@link #FOLLOWING_MIMETYPES} appear in the end of the dialog, in alphabetical
- * order.
- *
- * <p>The rest go between them, in the order in the array.
- */
- private static final String[] PRECEDING_MIMETYPES = new String[] {
- Phone.CONTENT_ITEM_TYPE,
- SipAddress.CONTENT_ITEM_TYPE,
- Contacts.CONTENT_ITEM_TYPE,
- Constants.MIME_SMS_ADDRESS,
- Email.CONTENT_ITEM_TYPE,
- };
-
- /**
- * See {@link #PRECEDING_MIMETYPES}.
- */
- private static final String[] FOLLOWING_MIMETYPES = new String[] {
- StructuredPostal.CONTENT_ITEM_TYPE,
- Website.CONTENT_ITEM_TYPE,
- };
-
- /**
- * Specific list {@link ApplicationInfo#packageName} of apps that are
- * prefered <strong>only</strong> for the purposes of default icons when
- * multiple {@link ResolveInfo} are found to match. This only happens when
- * the user has not selected a default app yet, and they will still be
- * presented with the system disambiguation dialog.
- */
- private static final HashSet<String> sPreferResolve = Sets.newHashSet(
- "com.android.email",
- "com.android.calendar",
- "com.android.contacts",
- "com.android.mms",
- "com.android.phone",
- "com.android.browser");
-
- private static final int TOKEN_DATA = 1;
-
- static final boolean LOGD = false;
-
- static final boolean TRACE_LAUNCH = false;
- static final String TRACE_TAG = "quickcontact";
-
- /**
- * Prepare a dialog to show in the given {@link Context}.
- */
- public QuickContactWindow(Context context) {
- mContext = new ContextThemeWrapper(context, R.style.QuickContact);
- mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
-
- mWindow = PolicyManager.makeNewWindow(mContext);
- mWindow.setCallback(this);
- mWindow.setWindowManager(mWindowManager, null, null);
- mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED);
-
- mWindow.setContentView(R.layout.quickcontact);
-
- mRootView = (RootLayout)mWindow.findViewById(R.id.root);
- mRootView.mQuickContactWindow = this;
- mRootView.setFocusable(true);
- mRootView.setFocusableInTouchMode(true);
- mRootView.setDescendantFocusability(RootLayout.FOCUS_AFTER_DESCENDANTS);
-
- mArrowUp = (ImageView)mWindow.findViewById(R.id.arrow_up);
- mArrowDown = (ImageView)mWindow.findViewById(R.id.arrow_down);
-
- mResolveCache = new ResolveCache(mContext);
-
- final Resources res = mContext.getResources();
- mShadowHoriz = res.getDimensionPixelSize(R.dimen.quickcontact_shadow_horiz);
- mShadowVert = res.getDimensionPixelSize(R.dimen.quickcontact_shadow_vert);
- mShadowTouch = res.getDimensionPixelSize(R.dimen.quickcontact_shadow_touch);
-
- mScreenWidth = mWindowManager.getDefaultDisplay().getWidth();
- mScreenHeight = mWindowManager.getDefaultDisplay().getHeight();
-
- mTrack = (ViewGroup)mWindow.findViewById(R.id.quickcontact);
- mTrackScroll = (HorizontalScrollView)mWindow.findViewById(R.id.scroll);
-
- mFooter = mWindow.findViewById(R.id.footer);
- mFooterDisambig = mWindow.findViewById(R.id.footer_disambig);
- mResolveList = (ListView)mWindow.findViewById(android.R.id.list);
- mSetPrimaryCheckBox = (CheckBox)mWindow.findViewById(android.R.id.checkbox);
-
- mSetPrimaryCheckBox.setOnCheckedChangeListener(this);
-
- // Prepare track entrance animation
- mTrackAnim = AnimationUtils.loadAnimation(mContext, R.anim.quickcontact);
- mTrackAnim.setInterpolator(new Interpolator() {
- public float getInterpolation(float t) {
- // Pushes past the target area, then snaps back into place.
- // Equation for graphing: 1.2-((x*1.6)-1.1)^2
- final float inner = (t * 1.55f) - 1.1f;
- return 1.2f - inner * inner;
- }
- });
-
- mHandler = new NotifyingAsyncQueryHandler(mContext, this);
- }
-
- /**
- * Prepare a dialog to show in the given {@link Context}, and notify the
- * given {@link OnDismissListener} each time this dialog is dismissed.
- */
- public QuickContactWindow(Context context, OnDismissListener dismissListener) {
- this(context);
- mDismissListener = dismissListener;
- }
-
- public void setLastSelectedContactsAppTab(int value) {
- mLastSelectedContactsAppTab = value;
- }
-
- private View getHeaderView(int mode) {
- View header = null;
- switch (mode) {
- case QuickContact.MODE_SMALL:
- header = mWindow.findViewById(R.id.header_small);
- break;
- case QuickContact.MODE_MEDIUM:
- header = mWindow.findViewById(R.id.header_medium);
- break;
- case QuickContact.MODE_LARGE:
- header = mWindow.findViewById(R.id.header_large);
- break;
- }
-
- if (header instanceof ViewStub) {
- // Inflate actual header if we picked a stub
- final ViewStub stub = (ViewStub)header;
- header = stub.inflate();
- } else if (header != null) {
- header.setVisibility(View.VISIBLE);
- }
-
- return header;
- }
-
- /**
- * Start showing a dialog for the given {@link Contacts#_ID} pointing
- * towards the given location.
- */
- public synchronized void show(Uri lookupUri, Rect anchor, int mode, String[] excludeMimes) {
- if (mQuerying || mShowing) {
- Log.w(TAG, "dismissing before showing");
- dismissInternal();
- }
-
- if (TRACE_LAUNCH && !android.os.Debug.isMethodTracingActive()) {
- android.os.Debug.startMethodTracing(TRACE_TAG);
- }
-
- // Validate incoming parameters
- final boolean validMode = (mode == QuickContact.MODE_SMALL
- || mode == QuickContact.MODE_MEDIUM || mode == QuickContact.MODE_LARGE);
- if (!validMode) {
- throw new IllegalArgumentException("Invalid mode, expecting MODE_LARGE, "
- + "MODE_MEDIUM, or MODE_SMALL");
- }
-
- if (anchor == null) {
- throw new IllegalArgumentException("Missing anchor rectangle");
- }
-
- // Prepare header view for requested mode
- mLookupUri = lookupUri;
- mAnchor = new Rect(anchor);
- mMode = mode;
- mExcludeMimes = excludeMimes;
-
- mHeader = getHeaderView(mode);
-
- setHeaderText(R.id.name, R.string.quickcontact_missing_name);
-
- setHeaderText(R.id.status, null);
- setHeaderText(R.id.timestamp, null);
-
- setHeaderImage(R.id.presence, null);
-
- resetTrack();
-
- // We need to have a focused view inside the QuickContact window so
- // that the BACK key event can be intercepted
- mRootView.requestFocus();
-
- mHasValidSocial = false;
- mDismissed = false;
- mQuerying = true;
-
- // Start background query for data, but only select photo rows when they
- // directly match the super-primary PHOTO_ID.
- final Uri dataUri = getDataUri(lookupUri);
- mHandler.cancelOperation(TOKEN_DATA);
-
- // Only request photo data when required by mode
- if (mMode == QuickContact.MODE_LARGE) {
- // Select photos, but only super-primary
- mHandler.startQuery(TOKEN_DATA, lookupUri, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
- + "!=? OR (" + Data.MIMETYPE + "=? AND " + Data._ID + "=" + Contacts.PHOTO_ID
- + ")", new String[] { Photo.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE }, null);
- } else {
- // Exclude all photos from cursor
- mHandler.startQuery(TOKEN_DATA, lookupUri, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
- + "!=?", new String[] { Photo.CONTENT_ITEM_TYPE }, null);
- }
- }
-
- /**
- * Build a {@link Uri} into the {@link Data} table for the requested
- * {@link Contacts#CONTENT_LOOKUP_URI} style {@link Uri}.
- */
- private Uri getDataUri(Uri lookupUri) {
- // TODO: Formalize method of extracting LOOKUP_KEY
- final List<String> path = lookupUri.getPathSegments();
- final boolean validLookup = path.size() >= 3 && "lookup".equals(path.get(1));
- if (!validLookup) {
- // We only accept valid lookup-style Uris
- throw new IllegalArgumentException("Expecting lookup-style Uri");
- } else if (path.size() == 3) {
- // No direct _ID provided, so force a lookup
- lookupUri = Contacts.lookupContact(mContext.getContentResolver(), lookupUri);
- }
-
- final long contactId = ContentUris.parseId(lookupUri);
- return Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId),
- Contacts.Data.CONTENT_DIRECTORY);
- }
-
- /**
- * Show the correct call-out arrow based on a {@link R.id} reference.
- */
- private void showArrow(int whichArrow, int requestedX) {
- final View showArrow = (whichArrow == R.id.arrow_up) ? mArrowUp : mArrowDown;
- final View hideArrow = (whichArrow == R.id.arrow_up) ? mArrowDown : mArrowUp;
-
- final int arrowWidth = mArrowUp.getMeasuredWidth();
-
- showArrow.setVisibility(View.VISIBLE);
- ViewGroup.MarginLayoutParams param = (ViewGroup.MarginLayoutParams)showArrow.getLayoutParams();
- param.leftMargin = requestedX - arrowWidth / 2;
-
- hideArrow.setVisibility(View.INVISIBLE);
- }
-
- /**
- * Actual internal method to show this dialog. Called only by
- * {@link #considerShowing()} when all data requirements have been met.
- */
- private void showInternal() {
- mDecor = mWindow.getDecorView();
- mDecor.getViewTreeObserver().addOnGlobalLayoutListener(this);
- WindowManager.LayoutParams l = mWindow.getAttributes();
-
- l.width = mScreenWidth + mShadowHoriz + mShadowHoriz;
- l.height = WindowManager.LayoutParams.WRAP_CONTENT;
-
- // Force layout measuring pass so we have baseline numbers
- mDecor.measure(l.width, l.height);
- final int blockHeight = mDecor.getMeasuredHeight();
-
- l.gravity = Gravity.TOP | Gravity.LEFT;
- l.x = -mShadowHoriz;
-
- if (mAnchor.top > blockHeight) {
- // Show downwards callout when enough room, aligning bottom block
- // edge with top of anchor area, and adjusting to inset arrow.
- showArrow(R.id.arrow_down, mAnchor.centerX());
- l.y = mAnchor.top - blockHeight + mShadowVert;
- l.windowAnimations = R.style.QuickContactAboveAnimation;
-
- } else {
- // Otherwise show upwards callout, aligning block top with bottom of
- // anchor area, and adjusting to inset arrow.
- showArrow(R.id.arrow_up, mAnchor.centerX());
- l.y = mAnchor.bottom - mShadowVert;
- l.windowAnimations = R.style.QuickContactBelowAnimation;
-
- }
-
- l.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
- | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-
- mRequestedY = l.y;
- mWindowManager.addView(mDecor, l);
- mShowing = true;
- mQuerying = false;
- mDismissed = false;
-
- mTrack.startAnimation(mTrackAnim);
-
- if (TRACE_LAUNCH) {
- android.os.Debug.stopMethodTracing();
- Log.d(TAG, "Window recycled " + mWindowRecycled + " times, chiclets "
- + mActionRecycled + " times");
- }
- }
-
- /** {@inheritDoc} */
- public void onGlobalLayout() {
- layoutInScreen();
- }
-
- /**
- * Adjust vertical {@link WindowManager.LayoutParams} to fit window as best
- * as possible, shifting up to display content as needed.
- */
- private void layoutInScreen() {
- if (!mShowing) return;
-
- final WindowManager.LayoutParams l = mWindow.getAttributes();
- final int originalY = l.y;
-
- final int blockHeight = mDecor.getHeight();
-
- l.y = mRequestedY;
- if (mRequestedY + blockHeight > mScreenHeight) {
- // Shift up from bottom when overflowing
- l.y = mScreenHeight - blockHeight;
- }
-
- if (originalY != l.y) {
- // Only update when value is changed
- mWindow.setAttributes(l);
- }
- }
-
- /**
- * Dismiss this dialog if showing.
- */
- public synchronized void dismiss() {
- // Notify any listeners that we've been dismissed
- if (mDismissListener != null) {
- mDismissListener.onDismiss(this);
- }
-
- dismissInternal();
- }
-
- private void dismissInternal() {
- // Remove any attached window decor for recycling
- boolean hadDecor = mDecor != null;
- if (hadDecor) {
- mWindowManager.removeView(mDecor);
- mWindowRecycled++;
- mDecor.getViewTreeObserver().removeGlobalOnLayoutListener(this);
- mDecor = null;
- mWindow.closeAllPanels();
- }
- mShowing = false;
- mDismissed = true;
-
- // Cancel any pending queries
- mHandler.cancelOperation(TOKEN_DATA);
- mQuerying = false;
-
- // Completely hide header and reset track
- mHeader.setVisibility(View.GONE);
- resetTrack();
- }
-
- /**
- * Reset track to initial state, recycling any chiclets.
- */
- private void resetTrack() {
- // Release reference to last chiclet
- mLastAction = null;
-
- // Clear track actions and scroll to hard left
- mResolveCache.clear();
- mActions.clear();
-
- // Recycle any chiclets in use
- while (mTrack.getChildCount() > 2) {
- this.releaseView(mTrack.getChildAt(1));
- mTrack.removeViewAt(1);
- }
-
- mTrackScroll.fullScroll(View.FOCUS_LEFT);
- mWasDownArrow = false;
-
- // Clear any primary requests
- mMakePrimary = false;
- mSetPrimaryCheckBox.setChecked(false);
-
- setResolveVisible(false, null);
- }
-
- /**
- * Consider showing this window, which will only call through to
- * {@link #showInternal()} when all data items are present.
- */
- private void considerShowing() {
- if (!mShowing && !mDismissed) {
- if (mMode == QuickContact.MODE_MEDIUM && !mHasValidSocial) {
- // Missing valid social, swap medium for small header
- mHeader.setVisibility(View.GONE);
- mHeader = getHeaderView(QuickContact.MODE_SMALL);
- }
-
- // All queries have returned, pull curtain
- showInternal();
- }
- }
-
- /** {@inheritDoc} */
- public synchronized void onQueryComplete(int token, Object cookie, Cursor cursor) {
- // Bail early when query is stale
- if (cookie != mLookupUri) return;
-
- if (cursor == null) {
- // Problem while running query, so bail without showing
- Log.w(TAG, "Missing cursor for token=" + token);
- this.dismiss();
- return;
- }
-
- handleData(cursor);
-
- if (!cursor.isClosed()) {
- cursor.close();
- }
-
- considerShowing();
- }
-
- /** Assign this string to the view, if found in {@link #mHeader}. */
- private void setHeaderText(int id, int resId) {
- setHeaderText(id, mContext.getResources().getText(resId));
- }
-
- /** Assign this string to the view, if found in {@link #mHeader}. */
- private void setHeaderText(int id, CharSequence value) {
- final View view = mHeader.findViewById(id);
- if (view instanceof TextView) {
- ((TextView)view).setText(value);
- view.setVisibility(TextUtils.isEmpty(value) ? View.GONE : View.VISIBLE);
- }
- }
-
- /** Assign this image to the view, if found in {@link #mHeader}. */
- private void setHeaderImage(int id, Drawable drawable) {
- final View view = mHeader.findViewById(id);
- if (view instanceof ImageView) {
- ((ImageView)view).setImageDrawable(drawable);
- view.setVisibility(drawable == null ? View.GONE : View.VISIBLE);
- }
- }
-
- /**
- * Find the QuickContact-specific presence icon for showing in chiclets.
- */
- private Drawable getTrackPresenceIcon(int status) {
- int resId;
- switch (status) {
- case StatusUpdates.AVAILABLE:
- resId = R.drawable.quickcontact_slider_presence_active;
- break;
- case StatusUpdates.IDLE:
- case StatusUpdates.AWAY:
- resId = R.drawable.quickcontact_slider_presence_away;
- break;
- case StatusUpdates.DO_NOT_DISTURB:
- resId = R.drawable.quickcontact_slider_presence_busy;
- break;
- case StatusUpdates.INVISIBLE:
- resId = R.drawable.quickcontact_slider_presence_inactive;
- break;
- case StatusUpdates.OFFLINE:
- default:
- resId = R.drawable.quickcontact_slider_presence_inactive;
- }
- return mContext.getResources().getDrawable(resId);
- }
-
- /** Read {@link String} from the given {@link Cursor}. */
- private static String getAsString(Cursor cursor, String columnName) {
- final int index = cursor.getColumnIndex(columnName);
- return cursor.getString(index);
- }
-
- /** Read {@link Integer} from the given {@link Cursor}. */
- private static int getAsInt(Cursor cursor, String columnName) {
- final int index = cursor.getColumnIndex(columnName);
- return cursor.getInt(index);
- }
-
- /**
- * Abstract definition of an action that could be performed, along with
- * string description and icon.
- */
- private interface Action extends Collapser.Collapsible<Action> {
- public CharSequence getHeader();
- public CharSequence getBody();
-
- public String getMimeType();
- public Drawable getFallbackIcon();
-
- /**
- * Build an {@link Intent} that will perform this action.
- */
- public Intent getIntent();
-
- /**
- * Checks if the contact data for this action is primary.
- */
- public Boolean isPrimary();
-
- /**
- * Returns a lookup (@link Uri) for the contact data item.
- */
- public Uri getDataUri();
- }
-
- /**
- * Description of a specific {@link Data#_ID} item, with style information
- * defined by a {@link DataKind}.
- */
- private static class DataAction implements Action {
- private final Context mContext;
- private final DataKind mKind;
- private final String mMimeType;
-
- private CharSequence mHeader;
- private CharSequence mBody;
- private Intent mIntent;
-
- private boolean mAlternate;
- private Uri mDataUri;
- private boolean mIsPrimary;
-
- /**
- * Create an action from common {@link Data} elements.
- */
- public DataAction(Context context, String mimeType, DataKind kind,
- long dataId, Cursor cursor) {
- mContext = context;
- mKind = kind;
- mMimeType = mimeType;
-
- // Inflate strings from cursor
- mAlternate = Constants.MIME_SMS_ADDRESS.equals(mimeType);
- if (mAlternate && mKind.actionAltHeader != null) {
- mHeader = mKind.actionAltHeader.inflateUsing(context, cursor);
- } else if (mKind.actionHeader != null) {
- mHeader = mKind.actionHeader.inflateUsing(context, cursor);
- }
-
- if (getAsInt(cursor, Data.IS_SUPER_PRIMARY) != 0) {
- mIsPrimary = true;
- }
-
- if (mKind.actionBody != null) {
- mBody = mKind.actionBody.inflateUsing(context, cursor);
- }
-
- mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
-
- // Handle well-known MIME-types with special care
- if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final String number = getAsString(cursor, Phone.NUMBER);
- if (!TextUtils.isEmpty(number)) {
- final Uri callUri = Uri.fromParts(Constants.SCHEME_TEL, number, null);
- mIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, callUri);
- }
- } else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final String address = getAsString(cursor, SipAddress.SIP_ADDRESS);
- if (!TextUtils.isEmpty(address)) {
- final Uri callUri = Uri.fromParts(Constants.SCHEME_SIP, address, null);
- mIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, callUri);
- // Note that this item will get a SIP-specific variant
- // of the "call phone" icon, rather than the standard
- // app icon for the Phone app (which we show for
- // regular phone numbers.) That's because the phone
- // app explicitly specifies an android:icon attribute
- // for the SIP-related intent-filters in its manifest.
- }
- } else if (Constants.MIME_SMS_ADDRESS.equals(mimeType)) {
- final String number = getAsString(cursor, Phone.NUMBER);
- if (!TextUtils.isEmpty(number)) {
- final Uri smsUri = Uri.fromParts(Constants.SCHEME_SMSTO, number, null);
- mIntent = new Intent(Intent.ACTION_SENDTO, smsUri);
- }
-
- } else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final String address = getAsString(cursor, Email.DATA);
- if (!TextUtils.isEmpty(address)) {
- final Uri mailUri = Uri.fromParts(Constants.SCHEME_MAILTO, address, null);
- mIntent = new Intent(Intent.ACTION_SENDTO, mailUri);
- }
-
- } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final String url = getAsString(cursor, Website.URL);
- if (!TextUtils.isEmpty(url)) {
- mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- }
-
- } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(
- getAsString(cursor, Data.MIMETYPE));
- if (isEmail || isProtocolValid(cursor)) {
- final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK :
- getAsInt(cursor, Im.PROTOCOL);
-
- if (isEmail) {
- // Use Google Talk string when using Email, and clear data
- // Uri so we don't try saving Email as primary.
- mHeader = context.getText(R.string.chat_gtalk);
- mDataUri = null;
- }
-
- String host = getAsString(cursor, Im.CUSTOM_PROTOCOL);
- String data = getAsString(cursor, isEmail ? Email.DATA : Im.DATA);
- if (protocol != Im.PROTOCOL_CUSTOM) {
- // Try bringing in a well-known host for specific protocols
- host = ContactsUtils.lookupProviderNameFromId(protocol);
- }
-
- if (!TextUtils.isEmpty(host) && !TextUtils.isEmpty(data)) {
- final String authority = host.toLowerCase();
- final Uri imUri = new Uri.Builder().scheme(Constants.SCHEME_IMTO).authority(
- authority).appendPath(data).build();
- mIntent = new Intent(Intent.ACTION_SENDTO, imUri);
- }
- }
- }
-
- if (mIntent == null) {
- // Otherwise fall back to default VIEW action
- mIntent = new Intent(Intent.ACTION_VIEW, mDataUri);
- }
-
- // Always launch as new task, since we're like a launcher
- mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- }
-
- private boolean isProtocolValid(Cursor cursor) {
- final int columnIndex = cursor.getColumnIndex(Im.PROTOCOL);
- if (cursor.isNull(columnIndex)) {
- return false;
- }
- try {
- Integer.valueOf(cursor.getString(columnIndex));
- } catch (NumberFormatException e) {
- return false;
- }
- return true;
- }
-
- /** {@inheritDoc} */
- public CharSequence getHeader() {
- return mHeader;
- }
-
- /** {@inheritDoc} */
- public CharSequence getBody() {
- return mBody;
- }
-
- /** {@inheritDoc} */
- public String getMimeType() {
- return mMimeType;
- }
-
- /** {@inheritDoc} */
- public Uri getDataUri() {
- return mDataUri;
- }
-
- /** {@inheritDoc} */
- public Boolean isPrimary() {
- return mIsPrimary;
- }
-
- /** {@inheritDoc} */
- public Drawable getFallbackIcon() {
- // Bail early if no valid resources
- final String resPackageName = mKind.resPackageName;
- if (resPackageName == null) return null;
-
- final PackageManager pm = mContext.getPackageManager();
- if (mAlternate && mKind.iconAltRes != -1) {
- return pm.getDrawable(resPackageName, mKind.iconAltRes, null);
- } else if (mKind.iconRes != -1) {
- return pm.getDrawable(resPackageName, mKind.iconRes, null);
- } else {
- return null;
- }
- }
-
- /** {@inheritDoc} */
- public Intent getIntent() {
- return mIntent;
- }
-
- /** {@inheritDoc} */
- public boolean collapseWith(Action other) {
- if (!shouldCollapseWith(other)) {
- return false;
- }
- return true;
- }
-
- /** {@inheritDoc} */
- public boolean shouldCollapseWith(Action t) {
- if (t == null) {
- return false;
- }
- if (!(t instanceof DataAction)) {
- Log.e(TAG, "t must be DataAction");
- return false;
- }
- DataAction other = (DataAction)t;
- if (!ContactsUtils.areObjectsEqual(mKind, other.mKind)) {
- return false;
- }
- if (!ContactsUtils.shouldCollapse(mContext, mMimeType, mBody, other.mMimeType,
- other.mBody)) {
- return false;
- }
- if (!TextUtils.equals(mMimeType, other.mMimeType)
- || !ContactsUtils.areIntentActionEqual(mIntent, other.mIntent)
- ) {
- return false;
- }
- return true;
- }
- }
-
- /**
- * Specific action that launches the profile card.
- */
- private static class ProfileAction implements Action {
- private final Context mContext;
- private final Uri mLookupUri;
-
- public ProfileAction(Context context, Uri lookupUri) {
- mContext = context;
- mLookupUri = lookupUri;
- }
-
- /** {@inheritDoc} */
- public CharSequence getHeader() {
- return null;
- }
-
- /** {@inheritDoc} */
- public CharSequence getBody() {
- return null;
- }
-
- /** {@inheritDoc} */
- public String getMimeType() {
- return Contacts.CONTENT_ITEM_TYPE;
- }
-
- /** {@inheritDoc} */
- public Drawable getFallbackIcon() {
- return mContext.getResources().getDrawable(R.drawable.ic_contacts_details);
- }
-
- /** {@inheritDoc} */
- public Intent getIntent() {
- final Intent intent = new Intent(Intent.ACTION_VIEW, mLookupUri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- return intent;
- }
-
- /** {@inheritDoc} */
- public Boolean isPrimary() {
- return null;
- }
-
- /** {@inheritDoc} */
- public Uri getDataUri() {
- return null;
- }
-
- /** {@inheritDoc} */
- public boolean collapseWith(Action t) {
- return false; // Never dup.
- }
-
- /** {@inheritDoc} */
- public boolean shouldCollapseWith(Action t) {
- return false; // Never dup.
- }
- }
-
- /**
- * Internally hold a cache of scaled icons based on {@link PackageManager}
- * queries, keyed internally on MIME-type.
- */
- private static class ResolveCache {
- private PackageManager mPackageManager;
-
- /**
- * Cached entry holding the best {@link ResolveInfo} for a specific
- * MIME-type, along with a {@link SoftReference} to its icon.
- */
- private static class Entry {
- public ResolveInfo bestResolve;
- public SoftReference<Drawable> icon;
- }
-
- private HashMap<String, Entry> mCache = new HashMap<String, Entry>();
-
- public ResolveCache(Context context) {
- mPackageManager = context.getPackageManager();
- }
-
- /**
- * Get the {@link Entry} best associated with the given {@link Action},
- * or create and populate a new one if it doesn't exist.
- */
- protected Entry getEntry(Action action) {
- final String mimeType = action.getMimeType();
- Entry entry = mCache.get(mimeType);
- if (entry != null) return entry;
- entry = new Entry();
-
- final Intent intent = action.getIntent();
- if (intent != null) {
- final List<ResolveInfo> matches = mPackageManager.queryIntentActivities(intent,
- PackageManager.MATCH_DEFAULT_ONLY);
-
- // Pick first match, otherwise best found
- ResolveInfo bestResolve = null;
- final int size = matches.size();
- if (size == 1) {
- bestResolve = matches.get(0);
- } else if (size > 1) {
- bestResolve = getBestResolve(intent, matches);
- }
-
- if (bestResolve != null) {
- final Drawable icon = bestResolve.loadIcon(mPackageManager);
-
- entry.bestResolve = bestResolve;
- entry.icon = new SoftReference<Drawable>(icon);
- }
- }
-
- mCache.put(mimeType, entry);
- return entry;
- }
-
- /**
- * Best {@link ResolveInfo} when multiple found. Ties are broken by
- * selecting first from the {QuickContactWindow#sPreferResolve} list of
- * preferred packages, second by apps that live on the system partition,
- * otherwise the app from the top of the list. This is
- * <strong>only</strong> used for selecting a default icon for
- * displaying in the track, and does not shortcut the system
- * {@link Intent} disambiguation dialog.
- */
- protected ResolveInfo getBestResolve(Intent intent, List<ResolveInfo> matches) {
- // Try finding preferred activity, otherwise detect disambig
- final ResolveInfo foundResolve = mPackageManager.resolveActivity(intent,
- PackageManager.MATCH_DEFAULT_ONLY);
- final boolean foundDisambig = (foundResolve.match &
- IntentFilter.MATCH_CATEGORY_MASK) == 0;
-
- if (!foundDisambig) {
- // Found concrete match, so return directly
- return foundResolve;
- }
-
- // Accept any package from prefer list, otherwise first system app
- ResolveInfo firstSystem = null;
- for (ResolveInfo info : matches) {
- final boolean isSystem = (info.activityInfo.applicationInfo.flags
- & ApplicationInfo.FLAG_SYSTEM) != 0;
- final boolean isPrefer = QuickContactWindow.sPreferResolve
- .contains(info.activityInfo.applicationInfo.packageName);
-
-
-
- if (isPrefer) return info;
- if (isSystem && firstSystem != null) firstSystem = info;
- }
-
- // Return first system found, otherwise first from list
- return firstSystem != null ? firstSystem : matches.get(0);
- }
-
- /**
- * Check {@link PackageManager} to see if any apps offer to handle the
- * given {@link Action}.
- */
- public boolean hasResolve(Action action) {
- return getEntry(action).bestResolve != null;
- }
-
- /**
- * Find the best description for the given {@link Action}, usually used
- * for accessibility purposes.
- */
- public CharSequence getDescription(Action action) {
- final CharSequence actionHeader = action.getHeader();
- final ResolveInfo info = getEntry(action).bestResolve;
- if (!TextUtils.isEmpty(actionHeader)) {
- return actionHeader;
- } else if (info != null) {
- return info.loadLabel(mPackageManager);
- } else {
- return null;
- }
- }
-
- /**
- * Return the best icon for the given {@link Action}, which is usually
- * based on the {@link ResolveInfo} found through a
- * {@link PackageManager} query.
- */
- public Drawable getIcon(Action action) {
- final SoftReference<Drawable> iconRef = getEntry(action).icon;
- return (iconRef == null) ? null : iconRef.get();
- }
-
- public void clear() {
- mCache.clear();
- }
- }
-
- /**
- * Provide a strongly-typed {@link LinkedList} that holds a list of
- * {@link Action} objects.
- */
- private class ActionList extends ArrayList<Action> {
- }
-
- /**
- * Provide a simple way of collecting one or more {@link Action} objects
- * under a MIME-type key.
- */
- private class ActionMap extends HashMap<String, ActionList> {
- private void collect(String mimeType, Action info) {
- // Create list for this MIME-type when needed
- ActionList collectList = get(mimeType);
- if (collectList == null) {
- collectList = new ActionList();
- put(mimeType, collectList);
- }
- collectList.add(info);
- }
- }
-
- /**
- * Check if the given MIME-type appears in the list of excluded MIME-types
- * that the most-recent caller requested.
- */
- private boolean isMimeExcluded(String mimeType) {
- if (mExcludeMimes == null) return false;
- for (String excludedMime : mExcludeMimes) {
- if (TextUtils.equals(excludedMime, mimeType)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Handle the result from the {@link #TOKEN_DATA} query.
- */
- private void handleData(Cursor cursor) {
- if (cursor == null) return;
-
- if (!isMimeExcluded(Contacts.CONTENT_ITEM_TYPE)) {
- // Add the profile shortcut action
- final Action action = new ProfileAction(mContext, mLookupUri);
- mActions.collect(Contacts.CONTENT_ITEM_TYPE, action);
- }
-
- final DataStatus status = new DataStatus();
- final Sources sources = Sources.getInstance(mContext);
- final ImageView photoView = (ImageView)mHeader.findViewById(R.id.photo);
-
- Bitmap photoBitmap = null;
- while (cursor.moveToNext()) {
- final long dataId = cursor.getLong(DataQuery._ID);
- final String accountType = cursor.getString(DataQuery.ACCOUNT_TYPE);
- final String mimeType = cursor.getString(DataQuery.MIMETYPE);
-
- // Handle any social status updates from this row
- status.possibleUpdate(cursor);
-
- // Skip this data item if MIME-type excluded
- if (isMimeExcluded(mimeType)) continue;
-
- // Handle photos included as data row
- if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final int colPhoto = cursor.getColumnIndex(Photo.PHOTO);
- final byte[] photoBlob = cursor.getBlob(colPhoto);
- if (photoBlob != null) {
- photoBitmap = BitmapFactory.decodeByteArray(photoBlob, 0, photoBlob.length);
- }
- continue;
- }
-
- final DataKind kind = sources.getKindOrFallback(accountType, mimeType, mContext,
- ContactsSource.LEVEL_MIMETYPES);
-
- if (kind != null) {
- // Build an action for this data entry, find a mapping to a UI
- // element, build its summary from the cursor, and collect it
- // along with all others of this MIME-type.
- final Action action = new DataAction(mContext, mimeType, kind, dataId, cursor);
- considerAdd(action, mimeType);
- }
-
- // If phone number, also insert as text message action
- if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && kind != null) {
- final Action action = new DataAction(mContext, Constants.MIME_SMS_ADDRESS,
- kind, dataId, cursor);
- considerAdd(action, Constants.MIME_SMS_ADDRESS);
- }
-
- // Handle Email rows with presence data as Im entry
- final boolean hasPresence = !cursor.isNull(DataQuery.PRESENCE);
- if (hasPresence && Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final DataKind imKind = sources.getKindOrFallback(accountType,
- Im.CONTENT_ITEM_TYPE, mContext, ContactsSource.LEVEL_MIMETYPES);
- if (imKind != null) {
- final Action action = new DataAction(mContext, Im.CONTENT_ITEM_TYPE, imKind,
- dataId, cursor);
- considerAdd(action, Im.CONTENT_ITEM_TYPE);
- }
- }
- }
-
- if (cursor.moveToLast()) {
- // Read contact information from last data row
- final String name = cursor.getString(DataQuery.DISPLAY_NAME);
- final int presence = cursor.getInt(DataQuery.CONTACT_PRESENCE);
- final Drawable statusIcon = ContactPresenceIconUtil.getPresenceIcon(mContext, presence);
-
- setHeaderText(R.id.name, name);
- setHeaderImage(R.id.presence, statusIcon);
- }
-
- if (photoView != null) {
- // Place photo when discovered in data, otherwise hide
- photoView.setVisibility(photoBitmap != null ? View.VISIBLE : View.GONE);
- photoView.setImageBitmap(photoBitmap);
- }
-
- mHasValidSocial = status.isValid();
- if (mHasValidSocial && mMode != QuickContact.MODE_SMALL) {
- // Update status when valid was found
- setHeaderText(R.id.status, status.getStatus());
- setHeaderText(R.id.timestamp, status.getTimestampLabel(mContext));
- }
-
- // Turn our list of actions into UI elements
-
- // Index where we start adding child views.
- int index = mTrack.getChildCount() - 1;
-
- // All the mime-types to add.
- final Set<String> containedTypes = new HashSet<String>(mActions.keySet());
-
- boolean hasData = false;
-
- // First, add PRECEDING_MIMETYPES, which are most common.
- for (String mimeType : PRECEDING_MIMETYPES) {
- if (containedTypes.contains(mimeType)) {
- hasData = true;
- mTrack.addView(inflateAction(mimeType), index++);
- containedTypes.remove(mimeType);
- }
- }
-
- // Keep the current index to append non PRECEDING/FOLLOWING items.
- final int indexAfterPreceding = index;
-
- // Then, add FOLLOWING_MIMETYPES, which are least common.
- for (String mimeType : FOLLOWING_MIMETYPES) {
- if (containedTypes.contains(mimeType)) {
- hasData = true;
- mTrack.addView(inflateAction(mimeType), index++);
- containedTypes.remove(mimeType);
- }
- }
-
- // Go back to just after PRECEDING_MIMETYPES, and append the rest.
- index = indexAfterPreceding;
- final String[] remainingTypes = containedTypes.toArray(new String[containedTypes.size()]);
- if (remainingTypes.length > 0) hasData = true;
- Arrays.sort(remainingTypes);
- for (String mimeType : remainingTypes) {
- mTrack.addView(inflateAction(mimeType), index++);
- }
-
- // When there is no data to display, add a TextView to show the user there's no data
- if (!hasData) {
- View view = mInflater.inflate(R.layout.quickcontact_item_nodata, mTrack, false);
- mTrack.addView(view, index++);
- }
- }
-
- /**
- * Consider adding the given {@link Action}, which will only happen if
- * {@link PackageManager} finds an application to handle
- * {@link Action#getIntent()}.
- */
- private void considerAdd(Action action, String mimeType) {
- if (mResolveCache.hasResolve(action)) {
- mActions.collect(mimeType, action);
- }
- }
-
- /**
- * Obtain a new {@link CheckableImageView} for a new chiclet, either by
- * recycling one from {@link #mActionPool}, or by inflating a new one. When
- * finished, use {@link #releaseView(View)} to return back into the pool for
- * later recycling.
- */
- private synchronized View obtainView() {
- View view = mActionPool.poll();
- if (view == null || QuickContactActivity.FORCE_CREATE) {
- view = mInflater.inflate(R.layout.quickcontact_item, mTrack, false);
- }
- return view;
- }
-
- /**
- * Return the given {@link CheckableImageView} into our internal pool for
- * possible recycling during another pass.
- */
- private synchronized void releaseView(View view) {
- // Only add CheckableImageViews
- if (!(view instanceof CheckableImageView)) {
- return;
- }
- mActionPool.offer(view);
- mActionRecycled++;
- }
-
- /**
- * Inflate the in-track view for the action of the given MIME-type, collapsing duplicate values.
- * Will use the icon provided by the {@link DataKind}.
- */
- private View inflateAction(String mimeType) {
- final CheckableImageView view = (CheckableImageView)obtainView();
- boolean isActionSet = false;
-
- // Add direct intent if single child, otherwise flag for multiple
- ActionList children = mActions.get(mimeType);
- if (children.size() > 1) {
- Collapser.collapseList(children);
- }
- Action firstInfo = children.get(0);
- if (children.size() == 1) {
- view.setTag(firstInfo);
- } else {
- for (Action action : children) {
- if (action.isPrimary()) {
- view.setTag(action);
- isActionSet = true;
- break;
- }
- }
- if (!isActionSet) {
- view.setTag(children);
- }
- }
-
- // Set icon and listen for clicks
- final CharSequence descrip = mResolveCache.getDescription(firstInfo);
- final Drawable icon = mResolveCache.getIcon(firstInfo);
- view.setChecked(false);
- view.setContentDescription(descrip);
- view.setImageDrawable(icon);
- view.setOnClickListener(this);
- return view;
- }
-
- /** {@inheritDoc} */
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- // Pass list item clicks along so that Intents are handled uniformly
- onClick(view);
- }
-
- /**
- * Flag indicating if {@link #mArrowDown} was visible during the last call
- * to {@link #setResolveVisible(boolean, CheckableImageView)}. Used to
- * decide during a later call if the arrow should be restored.
- */
- private boolean mWasDownArrow = false;
-
- /**
- * Helper for showing and hiding {@link #mFooterDisambig}, which will
- * correctly manage {@link #mArrowDown} as needed.
- */
- private void setResolveVisible(boolean visible, CheckableImageView actionView) {
- // Show or hide the resolve list if needed
- boolean visibleNow = mFooterDisambig.getVisibility() == View.VISIBLE;
-
- if (mLastAction != null) mLastAction.setChecked(false);
- if (actionView != null) actionView.setChecked(true);
- mLastAction = actionView;
-
- // Bail early if already in desired state
- if (visible == visibleNow) return;
-
- mFooter.setVisibility(visible ? View.GONE : View.VISIBLE);
- mFooterDisambig.setVisibility(visible ? View.VISIBLE : View.GONE);
-
- if (visible) {
- // If showing list, then hide and save state of down arrow
- mWasDownArrow = mWasDownArrow || (mArrowDown.getVisibility() == View.VISIBLE);
- mArrowDown.setVisibility(View.INVISIBLE);
- } else {
- // If hiding list, restore any down arrow state
- mArrowDown.setVisibility(mWasDownArrow ? View.VISIBLE : View.INVISIBLE);
- }
- }
-
- /** {@inheritDoc} */
- public void onClick(View view) {
- final boolean isActionView = (view instanceof CheckableImageView);
- final CheckableImageView actionView = isActionView ? (CheckableImageView)view : null;
- final Object tag = view.getTag();
- if (tag instanceof Action) {
- // Incoming tag is concrete intent, so try launching
- final Action action = (Action)tag;
- final boolean makePrimary = mMakePrimary;
-
- if (Intent.ACTION_CALL_PRIVILEGED.equals(action.getIntent().getAction())) {
- StickyTabs.saveTab(mContext, mLastSelectedContactsAppTab);
- }
-
- try {
- mContext.startActivity(action.getIntent());
- } catch (ActivityNotFoundException e) {
- Toast.makeText(mContext, R.string.quickcontact_missing_app, Toast.LENGTH_SHORT)
- .show();
- }
-
- // Hide the resolution list, if present
- setResolveVisible(false, null);
- this.dismiss();
-
- if (makePrimary) {
- ContentValues values = new ContentValues(1);
- values.put(Data.IS_SUPER_PRIMARY, 1);
- final Uri dataUri = action.getDataUri();
- if (dataUri != null) {
- mContext.getContentResolver().update(dataUri, values, null, null);
- }
- }
- } else if (tag instanceof ActionList) {
- // Incoming tag is a MIME-type, so show resolution list
- final ActionList children = (ActionList)tag;
-
- // Show resolution list and set adapter
- setResolveVisible(true, actionView);
-
- mResolveList.setOnItemClickListener(this);
- mResolveList.setAdapter(new BaseAdapter() {
- public int getCount() {
- return children.size();
- }
-
- public Object getItem(int position) {
- return children.get(position);
- }
-
- public long getItemId(int position) {
- return position;
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = mInflater.inflate(
- R.layout.quickcontact_resolve_item, parent, false);
- }
-
- // Set action title based on summary value
- final Action action = (Action)getItem(position);
-
- TextView text1 = (TextView)convertView.findViewById(android.R.id.text1);
- TextView text2 = (TextView)convertView.findViewById(android.R.id.text2);
-
- text1.setText(action.getHeader());
- text2.setText(action.getBody());
-
- convertView.setTag(action);
- return convertView;
- }
- });
-
- // Make sure we resize to make room for ListView
- mDecor.forceLayout();
- mDecor.invalidate();
-
- }
- }
-
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- mMakePrimary = isChecked;
- }
-
- private void onBackPressed() {
- // Back key will first dismiss any expanded resolve list, otherwise
- // it will close the entire dialog.
- if (mFooterDisambig.getVisibility() == View.VISIBLE) {
- setResolveVisible(false, null);
- mDecor.forceLayout();
- mDecor.invalidate();
- } else {
- dismiss();
- }
- }
-
- /** {@inheritDoc} */
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (mWindow.superDispatchKeyEvent(event)) {
- return true;
- }
- return event.dispatch(this, mDecor != null
- ? mDecor.getKeyDispatcherState() : null, this);
- }
-
- /** {@inheritDoc} */
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- event.startTracking();
- return true;
- }
-
- return false;
- }
-
- /** {@inheritDoc} */
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
- && !event.isCanceled()) {
- onBackPressed();
- return true;
- }
-
- return false;
- }
-
- /** {@inheritDoc} */
- public boolean onKeyLongPress(int keyCode, KeyEvent event) {
- return false;
- }
-
- /** {@inheritDoc} */
- public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
- return false;
- }
-
- /** {@inheritDoc} */
- public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- // TODO: make this window accessible
- return false;
- }
-
- /**
- * Detect if the given {@link MotionEvent} is outside the boundaries of this
- * window, which usually means we should dismiss.
- */
- protected void detectEventOutside(MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN && mDecor != null) {
- // Only try detecting outside events on down-press
- mDecor.getHitRect(mRect);
- mRect.top = mRect.top + mShadowTouch;
- mRect.bottom = mRect.bottom - mShadowTouch;
- final int x = (int)event.getX();
- final int y = (int)event.getY();
- if (!mRect.contains(x, y)) {
- event.setAction(MotionEvent.ACTION_OUTSIDE);
- }
- }
- }
-
- /** {@inheritDoc} */
- public boolean dispatchTouchEvent(MotionEvent event) {
- detectEventOutside(event);
- if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- dismiss();
- return true;
- } else {
- return mWindow.superDispatchTouchEvent(event);
- }
- }
-
- /** {@inheritDoc} */
- public boolean dispatchTrackballEvent(MotionEvent event) {
- return mWindow.superDispatchTrackballEvent(event);
- }
-
- /** {@inheritDoc} */
- public void onContentChanged() {
- }
-
- /** {@inheritDoc} */
- public boolean onCreatePanelMenu(int featureId, Menu menu) {
- return false;
- }
-
- /** {@inheritDoc} */
- public View onCreatePanelView(int featureId) {
- return null;
- }
-
- /** {@inheritDoc} */
- public boolean onMenuItemSelected(int featureId, MenuItem item) {
- return false;
- }
-
- /** {@inheritDoc} */
- public boolean onMenuOpened(int featureId, Menu menu) {
- return false;
- }
-
- /** {@inheritDoc} */
- public void onPanelClosed(int featureId, Menu menu) {
- }
-
- /** {@inheritDoc} */
- public boolean onPreparePanel(int featureId, View view, Menu menu) {
- return false;
- }
-
- /** {@inheritDoc} */
- public boolean onSearchRequested() {
- return false;
- }
-
- /** {@inheritDoc} */
- public void onWindowAttributesChanged(android.view.WindowManager.LayoutParams attrs) {
- if (mDecor != null) {
- mWindowManager.updateViewLayout(mDecor, attrs);
- }
- }
-
- /** {@inheritDoc} */
- public void onWindowFocusChanged(boolean hasFocus) {
- }
-
- /** {@inheritDoc} */
- public void onAttachedToWindow() {
- // No actions
- }
-
- /** {@inheritDoc} */
- public void onDetachedFromWindow() {
- // No actions
- }
-
- private interface DataQuery {
- final String[] PROJECTION = new String[] {
- Data._ID,
-
- RawContacts.ACCOUNT_TYPE,
- Contacts.STARRED,
- Contacts.DISPLAY_NAME,
- Contacts.CONTACT_PRESENCE,
-
- Data.STATUS,
- Data.STATUS_RES_PACKAGE,
- Data.STATUS_ICON,
- Data.STATUS_LABEL,
- Data.STATUS_TIMESTAMP,
- Data.PRESENCE,
-
- Data.RES_PACKAGE,
- Data.MIMETYPE,
- Data.IS_PRIMARY,
- Data.IS_SUPER_PRIMARY,
- Data.RAW_CONTACT_ID,
-
- Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, Data.DATA5,
- Data.DATA6, Data.DATA7, Data.DATA8, Data.DATA9, Data.DATA10, Data.DATA11,
- Data.DATA12, Data.DATA13, Data.DATA14, Data.DATA15,
- };
-
- final int _ID = 0;
-
- final int ACCOUNT_TYPE = 1;
- final int STARRED = 2;
- final int DISPLAY_NAME = 3;
- final int CONTACT_PRESENCE = 4;
-
- final int STATUS = 5;
- final int STATUS_RES_PACKAGE = 6;
- final int STATUS_ICON = 7;
- final int STATUS_LABEL = 8;
- final int STATUS_TIMESTAMP = 9;
- final int PRESENCE = 10;
-
- final int RES_PACKAGE = 11;
- final int MIMETYPE = 12;
- final int IS_PRIMARY = 13;
- final int IS_SUPER_PRIMARY = 14;
- }
-}
diff --git a/src/com/android/contacts/ui/ShowOrCreateActivity.java b/src/com/android/contacts/ui/ShowOrCreateActivity.java
deleted file mode 100755
index e0781d2..0000000
--- a/src/com/android/contacts/ui/ShowOrCreateActivity.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * 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.ui;
-
-import com.android.contacts.ContactsSearchManager;
-import com.android.contacts.ContactsListActivity;
-import com.android.contacts.R;
-import com.android.contacts.util.Constants;
-import com.android.contacts.util.NotifyingAsyncQueryHandler;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.ComponentName;
-import android.content.ContentUris;
-import android.content.DialogInterface;
-import android.content.EntityIterator;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Intents;
-import android.provider.ContactsContract.PhoneLookup;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.util.Log;
-
-/**
- * Handle several edge cases around showing or possibly creating contacts in
- * connected with a specific E-mail address or phone number. Will search based
- * on incoming {@link Intent#getData()} as described by
- * {@link Intents#SHOW_OR_CREATE_CONTACT}.
- * <ul>
- * <li>If no matching contacts found, will prompt user with dialog to add to a
- * contact, then will use {@link Intent#ACTION_INSERT_OR_EDIT} to let create new
- * contact or edit new data into an existing one.
- * <li>If one matching contact found, directly show {@link Intent#ACTION_VIEW}
- * that specific contact.
- * <li>If more than one matching found, show list of matching contacts using
- * {@link Intent#ACTION_SEARCH}.
- * </ul>
- */
-public final class ShowOrCreateActivity extends Activity implements
- NotifyingAsyncQueryHandler.AsyncQueryListener {
- static final String TAG = "ShowOrCreateActivity";
- static final boolean LOGD = false;
-
- static final String[] PHONES_PROJECTION = new String[] {
- PhoneLookup._ID,
- };
-
- static final String[] CONTACTS_PROJECTION = new String[] {
- RawContacts.CONTACT_ID,
- };
-
- static final int CONTACT_ID_INDEX = 0;
-
- static final int CREATE_CONTACT_DIALOG = 1;
-
- static final int QUERY_TOKEN = 42;
-
- private NotifyingAsyncQueryHandler mQueryHandler;
-
- private Bundle mCreateExtras;
- private String mCreateDescrip;
- private boolean mCreateForce;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- // Create handler if doesn't exist, otherwise cancel any running
- if (mQueryHandler == null) {
- mQueryHandler = new NotifyingAsyncQueryHandler(this, this);
- } else {
- mQueryHandler.cancelOperation(QUERY_TOKEN);
- }
-
- final Intent intent = getIntent();
- final Uri data = intent.getData();
-
- // Unpack scheme and target data from intent
- String scheme = null;
- String ssp = null;
- if (data != null) {
- scheme = data.getScheme();
- ssp = data.getSchemeSpecificPart();
- }
-
- // Build set of extras for possible use when creating contact
- mCreateExtras = new Bundle();
- Bundle originalExtras = intent.getExtras();
- if (originalExtras != null) {
- mCreateExtras.putAll(originalExtras);
- }
-
- // Read possible extra with specific title
- mCreateDescrip = intent.getStringExtra(Intents.EXTRA_CREATE_DESCRIPTION);
- if (mCreateDescrip == null) {
- mCreateDescrip = ssp;
- }
-
- // Allow caller to bypass dialog prompt
- mCreateForce = intent.getBooleanExtra(Intents.EXTRA_FORCE_CREATE, false);
-
- // Handle specific query request
- if (Constants.SCHEME_MAILTO.equals(scheme)) {
- mCreateExtras.putString(Intents.Insert.EMAIL, ssp);
-
- Uri uri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, Uri.encode(ssp));
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri, CONTACTS_PROJECTION, null, null, null);
-
- } else if (Constants.SCHEME_TEL.equals(scheme)) {
- mCreateExtras.putString(Intents.Insert.PHONE, ssp);
-
- Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, ssp);
- mQueryHandler.startQuery(QUERY_TOKEN, null, uri, PHONES_PROJECTION, null, null, null);
-
- } else {
- Log.w(TAG, "Invalid intent:" + getIntent());
- finish();
- }
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- if (mQueryHandler != null) {
- mQueryHandler.cancelOperation(QUERY_TOKEN);
- }
- }
-
- /** {@inheritDoc} */
- public void onQueryComplete(int token, Object cookie, Cursor cursor) {
- if (cursor == null) {
- // Bail when problem running query in background
- finish();
- return;
- }
-
- // Count contacts found by query
- int count = 0;
- long contactId = -1;
- try {
- count = cursor.getCount();
- if (count == 1 && cursor.moveToFirst()) {
- // Try reading ID if only one contact returned
- contactId = cursor.getLong(CONTACT_ID_INDEX);
- }
- } finally {
- cursor.close();
- }
-
- if (count == 1 && contactId != -1) {
- // If we only found one item, jump right to viewing it
- final Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
- final Intent viewIntent = new Intent(Intent.ACTION_VIEW, contactUri);
- startActivity(viewIntent);
- finish();
-
- } else if (count > 1) {
- // If more than one, show pick list
- Intent listIntent = new Intent(Intent.ACTION_SEARCH);
- listIntent.setComponent(new ComponentName(this, ContactsListActivity.class));
- listIntent.putExtras(mCreateExtras);
- startActivity(listIntent);
- finish();
-
- } else {
- // No matching contacts found
- if (mCreateForce) {
- // Forced to create new contact
- Intent createIntent = new Intent(Intent.ACTION_INSERT, RawContacts.CONTENT_URI);
- createIntent.putExtras(mCreateExtras);
- createIntent.setType(RawContacts.CONTENT_TYPE);
-
- startActivity(createIntent);
- finish();
-
- } else {
- showDialog(CREATE_CONTACT_DIALOG);
- }
- }
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- switch(id) {
- case CREATE_CONTACT_DIALOG:
- // Prompt user to insert or edit contact
- final Intent createIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
- createIntent.putExtras(mCreateExtras);
- createIntent.setType(RawContacts.CONTENT_ITEM_TYPE);
-
- final CharSequence message = getResources().getString(
- R.string.add_contact_dlg_message_fmt, mCreateDescrip);
-
- return new AlertDialog.Builder(this)
- .setTitle(R.string.add_contact_dlg_title)
- .setMessage(message)
- .setPositiveButton(android.R.string.ok,
- new IntentClickListener(this, createIntent))
- .setNegativeButton(android.R.string.cancel,
- new IntentClickListener(this, null))
- .create();
- }
- return super.onCreateDialog(id);
- }
-
- /**
- * Listener for {@link DialogInterface} that launches a given {@link Intent}
- * when clicked. When clicked, this also closes the parent using
- * {@link Activity#finish()}.
- */
- private static class IntentClickListener implements DialogInterface.OnClickListener {
- private Activity mParent;
- private Intent mIntent;
-
- /**
- * @param parent {@link Activity} to use for launching target.
- * @param intent Target {@link Intent} to launch when clicked.
- */
- public IntentClickListener(Activity parent, Intent intent) {
- mParent = parent;
- mIntent = intent;
- }
-
- public void onClick(DialogInterface dialog, int which) {
- if (mIntent != null) {
- mParent.startActivity(mIntent);
- }
- mParent.finish();
- }
- }
-
- @Override
- public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
- boolean globalSearch) {
- if (globalSearch) {
- super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
- } else {
- ContactsSearchManager.startSearch(this, initialQuery);
- }
- }
-}
diff --git a/src/com/android/contacts/ui/ViewIdGenerator.java b/src/com/android/contacts/ui/ViewIdGenerator.java
deleted file mode 100644
index f7281d4..0000000
--- a/src/com/android/contacts/ui/ViewIdGenerator.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.ui;
-
-import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.ui.widget.GenericEditorView;
-import com.android.contacts.ui.widget.KindSectionView;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A class that provides unique view ids for {@link ContentEditorView}, {@link KindSectionView},
- * {@link GenericEditorView} and {@link EditView} on {@link EditContactActivity}.
- * It is used to assign a unique but consistent id to each view across {@link EditContactActivity}'s
- * lifecycle, so that we can re-construct view state (e.g. focused view) when the screen rotates.
- *
- * <p>This class is not thread safe.
- */
-public final class ViewIdGenerator implements Parcelable {
- private static final int INVALID_VIEW_ID = 0;
- private static final int INITIAL_VIEW_ID = 1;
-
- public static final int NO_VIEW_INDEX = -1;
-
- private int mNextId;
-
- /**
- * Used as a map from the "key" of the views to actual ids. {@link #getId()} generates keys for
- * the views.
- */
- private Bundle mIdMap = new Bundle();
-
- private static final char KEY_SEPARATOR = '*';
-
- private final static StringBuilder sWorkStringBuilder = new StringBuilder();
-
- public ViewIdGenerator() {
- mNextId = INITIAL_VIEW_ID;
- }
-
- /** {@inheritDoc} */
- public int describeContents() {
- return 0;
- }
-
- /**
- * Returns an id for a view associated with specified contact field.
- *
- * @param entity {@link EntityDelta} associated with the view
- * @param kind {@link DataKind} associated with the view, or null if none exists.
- * @param values {@link ValuesDelta} associated with the view, or null if none exists.
- * @param viewIndex index of the view in the parent {@link Editor}, if it's a leave view.
- * Otherwise, pass {@link #NO_VIEW_INDEX}.
- */
- public int getId(EntityDelta entity, DataKind kind, ValuesDelta values,
- int viewIndex) {
- final String k = getMapKey(entity, kind, values, viewIndex);
-
- int id = mIdMap.getInt(k, INVALID_VIEW_ID);
- if (id == INVALID_VIEW_ID) {
- // Make sure the new id won't conflict with auto-generated ids by masking with 0xffff.
- id = (mNextId++) & 0xFFFF;
- mIdMap.putInt(k, id);
- }
- return id;
- }
-
- private static String getMapKey(EntityDelta entity, DataKind kind, ValuesDelta values,
- int viewIndex) {
- sWorkStringBuilder.setLength(0);
- if (entity != null) {
- sWorkStringBuilder.append(entity.getValues().getId());
-
- if (kind != null) {
- sWorkStringBuilder.append(KEY_SEPARATOR);
- sWorkStringBuilder.append(kind.mimeType);
-
- if (values != null) {
- sWorkStringBuilder.append(KEY_SEPARATOR);
- sWorkStringBuilder.append(values.getId());
-
- if (viewIndex != NO_VIEW_INDEX) {
- sWorkStringBuilder.append(KEY_SEPARATOR);
- sWorkStringBuilder.append(viewIndex);
- }
- }
- }
- }
- return sWorkStringBuilder.toString();
- }
-
- /** {@Override} */
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mNextId);
- dest.writeBundle(mIdMap);
- }
-
- private void readFromParcel(Parcel src) {
- mNextId = src.readInt();
- mIdMap = src.readBundle();
- }
-
- public static final Parcelable.Creator<ViewIdGenerator> CREATOR =
- new Parcelable.Creator<ViewIdGenerator>() {
- public ViewIdGenerator createFromParcel(Parcel in) {
- final ViewIdGenerator vig = new ViewIdGenerator();
- vig.readFromParcel(in);
- return vig;
- }
-
- public ViewIdGenerator[] newArray(int size) {
- return new ViewIdGenerator[size];
- }
- };
-}
diff --git a/src/com/android/contacts/ui/widget/BaseContactEditorView.java b/src/com/android/contacts/ui/widget/BaseContactEditorView.java
deleted file mode 100644
index c2f9136..0000000
--- a/src/com/android/contacts/ui/widget/BaseContactEditorView.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.ui.widget;
-
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.ContactsSource.EditType;
-import com.android.contacts.model.Editor.EditorListener;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.ui.ViewIdGenerator;
-
-import android.content.Context;
-import android.content.Entity;
-import android.graphics.Bitmap;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.widget.LinearLayout;
-
-/**
- * Base view that provides common code for the editor interaction for a specific
- * RawContact represented through an {@link EntityDelta}. Callers can
- * reuse this view and quickly rebuild its contents through
- * {@link #setState(EntityDelta, ContactsSource)}.
- * <p>
- * Internal updates are performed against {@link ValuesDelta} so that the
- * source {@link Entity} can be swapped out. Any state-based changes, such as
- * adding {@link Data} rows or changing {@link EditType}, are performed through
- * {@link EntityModifier} to ensure that {@link ContactsSource} are enforced.
- */
-public abstract class BaseContactEditorView extends LinearLayout {
- protected LayoutInflater mInflater;
-
- protected PhotoEditorView mPhoto;
- protected boolean mHasPhotoEditor = false;
-
- public BaseContactEditorView(Context context) {
- super(context);
- }
-
- public BaseContactEditorView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- /**
- * Assign the given {@link Bitmap} to the internal {@link PhotoEditorView}
- * for the {@link EntityDelta} currently being edited.
- */
- public void setPhotoBitmap(Bitmap bitmap) {
- mPhoto.setPhotoBitmap(bitmap);
- }
-
- /**
- * Return true if the current {@link RawContacts} supports {@link Photo},
- * which means that {@link PhotoEditorView} is enabled.
- */
- public boolean hasPhotoEditor() {
- return mHasPhotoEditor;
- }
-
- /**
- * Return true if internal {@link PhotoEditorView} has a {@link Photo} set.
- */
- public boolean hasSetPhoto() {
- return mPhoto.hasSetPhoto();
- }
-
- public PhotoEditorView getPhotoEditor() {
- return mPhoto;
- }
-
- /**
- * @return the RawContact ID that this editor is editing.
- */
- public abstract long getRawContactId();
-
- /**
- * Set the internal state for this view, given a current
- * {@link EntityDelta} state and the {@link ContactsSource} that
- * apply to that state.
- */
- public abstract void setState(EntityDelta state, ContactsSource source, ViewIdGenerator vig);
-
- /**
- * Sets the {@link EditorListener} on the name field
- */
- public abstract void setNameEditorListener(EditorListener listener);
-}
diff --git a/src/com/android/contacts/ui/widget/CheckableImageView.java b/src/com/android/contacts/ui/widget/CheckableImageView.java
deleted file mode 100644
index ff5abc0..0000000
--- a/src/com/android/contacts/ui/widget/CheckableImageView.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.ui.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.Checkable;
-import android.widget.ImageView;
-
-/**
- * A special variation of ImageView that can be used as a checkable object.
- * This is used as the background view of quickcontact chiclet, which is in checked state
- * when disambig list is shown. Otherwise, it works identically to a ImageView.
- */
-public class CheckableImageView extends ImageView implements Checkable {
- private boolean mChecked;
-
- private static final int[] CHECKED_STATE_SET = {
- android.R.attr.state_checked
- };
-
- public CheckableImageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public int[] onCreateDrawableState(int extraSpace) {
- final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
- if (isChecked()) {
- mergeDrawableStates(drawableState, CHECKED_STATE_SET);
- }
- return drawableState;
- }
-
- public void toggle() {
- setChecked(!mChecked);
- }
-
- public boolean isChecked() {
- return mChecked;
- }
-
- public void setChecked(boolean checked) {
- if (mChecked != checked) {
- mChecked = checked;
- refreshDrawableState();
- }
- }
-}
diff --git a/src/com/android/contacts/ui/widget/ContactEditorView.java b/src/com/android/contacts/ui/widget/ContactEditorView.java
deleted file mode 100644
index 83bf2fb..0000000
--- a/src/com/android/contacts/ui/widget/ContactEditorView.java
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * 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.ui.widget;
-
-import com.android.contacts.R;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.model.ContactsSource.EditType;
-import com.android.contacts.model.Editor.EditorListener;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.ui.ViewIdGenerator;
-
-import android.content.Context;
-import android.content.Entity;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.View.OnClickListener;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-/**
- * Custom view that provides all the editor interaction for a specific
- * {@link Contacts} represented through an {@link EntityDelta}. Callers can
- * reuse this view and quickly rebuild its contents through
- * {@link #setState(EntityDelta, ContactsSource)}.
- * <p>
- * Internal updates are performed against {@link ValuesDelta} so that the
- * source {@link Entity} can be swapped out. Any state-based changes, such as
- * adding {@link Data} rows or changing {@link EditType}, are performed through
- * {@link EntityModifier} to ensure that {@link ContactsSource} are enforced.
- */
-public class ContactEditorView extends BaseContactEditorView implements OnClickListener {
- private TextView mReadOnly;
- private TextView mReadOnlyName;
-
- private View mPhotoStub;
- private GenericEditorView mName;
-
- private boolean mIsSourceReadOnly;
- private ViewGroup mGeneral;
- private ViewGroup mSecondary;
- private boolean mSecondaryVisible;
-
- private TextView mSecondaryHeader;
-
- private Drawable mSecondaryOpen;
- private Drawable mSecondaryClosed;
-
- private View mHeaderColorBar;
- private View mSideBar;
- private ImageView mHeaderIcon;
- private TextView mHeaderAccountType;
- private TextView mHeaderAccountName;
-
- private long mRawContactId = -1;
-
- public ContactEditorView(Context context) {
- super(context);
- }
-
- public ContactEditorView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mInflater = (LayoutInflater)getContext().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
-
- mPhoto = (PhotoEditorView)findViewById(R.id.edit_photo);
- mPhotoStub = findViewById(R.id.stub_photo);
-
- final int photoSize = getResources().getDimensionPixelSize(R.dimen.edit_photo_size);
-
- mReadOnly = (TextView)findViewById(R.id.edit_read_only);
-
- mName = (GenericEditorView)findViewById(R.id.edit_name);
- mName.setMinimumHeight(photoSize);
- mName.setDeletable(false);
-
- mReadOnlyName = (TextView) findViewById(R.id.read_only_name);
-
- mGeneral = (ViewGroup)findViewById(R.id.sect_general);
- mSecondary = (ViewGroup)findViewById(R.id.sect_secondary);
-
- mHeaderColorBar = findViewById(R.id.header_color_bar);
- mSideBar = findViewById(R.id.color_bar);
- mHeaderIcon = (ImageView) findViewById(R.id.header_icon);
- mHeaderAccountType = (TextView) findViewById(R.id.header_account_type);
- mHeaderAccountName = (TextView) findViewById(R.id.header_account_name);
-
- mSecondaryHeader = (TextView)findViewById(R.id.head_secondary);
- mSecondaryHeader.setOnClickListener(this);
-
- final Resources res = getResources();
- mSecondaryOpen = res.getDrawable(com.android.internal.R.drawable.expander_ic_maximized);
- mSecondaryClosed = res.getDrawable(com.android.internal.R.drawable.expander_ic_minimized);
-
- this.setSecondaryVisible(false);
- }
-
- /** {@inheritDoc} */
- public void onClick(View v) {
- // Toggle visibility of secondary kinds
- final boolean makeVisible = mSecondary.getVisibility() != View.VISIBLE;
- this.setSecondaryVisible(makeVisible);
- }
-
- /**
- * Set the visibility of secondary sections, along with header icon.
- *
- * <p>If the source is read-only and there's no secondary fields, the entire secondary section
- * will be hidden.
- */
- private void setSecondaryVisible(boolean makeVisible) {
- mSecondaryVisible = makeVisible;
-
- if (!mIsSourceReadOnly && mSecondary.getChildCount() > 0) {
- mSecondaryHeader.setVisibility(View.VISIBLE);
- mSecondaryHeader.setCompoundDrawablesWithIntrinsicBounds(
- makeVisible ? mSecondaryOpen : mSecondaryClosed, null, null, null);
- mSecondary.setVisibility(makeVisible ? View.VISIBLE : View.GONE);
- } else {
- mSecondaryHeader.setVisibility(View.GONE);
- mSecondary.setVisibility(View.GONE);
- }
- }
-
- /**
- * Set the internal state for this view, given a current
- * {@link EntityDelta} state and the {@link ContactsSource} that
- * apply to that state.
- */
- @Override
- public void setState(EntityDelta state, ContactsSource source, ViewIdGenerator vig) {
- // Remove any existing sections
- mGeneral.removeAllViews();
- mSecondary.removeAllViews();
-
- // Bail if invalid state or source
- if (state == null || source == null) return;
-
- setId(vig.getId(state, null, null, ViewIdGenerator.NO_VIEW_INDEX));
-
- mIsSourceReadOnly = source.readOnly;
-
- // Make sure we have StructuredName
- EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE);
-
- // Fill in the header info
- ValuesDelta values = state.getValues();
- String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
- CharSequence accountType = source.getDisplayLabel(mContext);
- if (TextUtils.isEmpty(accountType)) {
- accountType = mContext.getString(R.string.account_phone);
- }
- if (!TextUtils.isEmpty(accountName)) {
- mHeaderAccountName.setText(
- mContext.getString(R.string.from_account_format, accountName));
- }
- mHeaderAccountType.setText(mContext.getString(R.string.account_type_format, accountType));
- mHeaderIcon.setImageDrawable(source.getDisplayIcon(mContext));
-
- mRawContactId = values.getAsLong(RawContacts._ID);
-
- // Show photo editor when supported
- EntityModifier.ensureKindExists(state, source, Photo.CONTENT_ITEM_TYPE);
- mHasPhotoEditor = (source.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null);
- mPhoto.setVisibility(mHasPhotoEditor ? View.VISIBLE : View.GONE);
- mPhoto.setEnabled(!mIsSourceReadOnly);
- mName.setEnabled(!mIsSourceReadOnly);
-
- // Show and hide the appropriate views
- if (mIsSourceReadOnly) {
- mGeneral.setVisibility(View.GONE);
- mName.setVisibility(View.GONE);
- mReadOnly.setVisibility(View.VISIBLE);
- mReadOnly.setText(mContext.getString(R.string.contact_read_only, accountType));
- mReadOnlyName.setVisibility(View.VISIBLE);
- } else {
- mGeneral.setVisibility(View.VISIBLE);
- mName.setVisibility(View.VISIBLE);
- mReadOnly.setVisibility(View.GONE);
- mReadOnlyName.setVisibility(View.GONE);
- }
-
- boolean anySecondaryFieldFilled = false;
- // Create editor sections for each possible data kind
- for (DataKind kind : source.getSortedDataKinds()) {
- // Skip kind of not editable
- if (!kind.editable) continue;
-
- final String mimeType = kind.mimeType;
- if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
- // Handle special case editor for structured name
- final ValuesDelta primary = state.getPrimaryEntry(mimeType);
- if (!mIsSourceReadOnly) {
- mName.setValues(kind, primary, state, mIsSourceReadOnly, vig);
- } else {
- String displayName = primary.getAsString(StructuredName.DISPLAY_NAME);
- mReadOnlyName.setText(displayName);
- }
- } else if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
- // Handle special case editor for photos
- final ValuesDelta primary = state.getPrimaryEntry(mimeType);
- mPhoto.setValues(kind, primary, state, mIsSourceReadOnly, vig);
- if (mIsSourceReadOnly && !mPhoto.hasSetPhoto()) {
- mPhotoStub.setVisibility(View.GONE);
- } else {
- mPhotoStub.setVisibility(View.VISIBLE);
- }
- } else if (!mIsSourceReadOnly) {
- // Otherwise use generic section-based editors
- if (kind.fieldList == null) continue;
- final ViewGroup parent = kind.secondary ? mSecondary : mGeneral;
- final KindSectionView section = (KindSectionView)mInflater.inflate(
- R.layout.item_kind_section, parent, false);
- section.setState(kind, state, mIsSourceReadOnly, vig);
- if (kind.secondary && section.isAnyEditorFilledOut()) {
- anySecondaryFieldFilled = true;
- }
- parent.addView(section);
- }
- }
-
- setSecondaryVisible(anySecondaryFieldFilled);
- }
-
- /**
- * Sets the {@link EditorListener} on the name field
- */
- @Override
- public void setNameEditorListener(EditorListener listener) {
- mName.setEditorListener(listener);
- }
-
- @Override
- public long getRawContactId() {
- return mRawContactId;
- }
-
- private static class SavedState extends BaseSavedState {
- public boolean mSecondaryVisible;
-
- SavedState(Parcelable superState) {
- super(superState);
- }
-
- private SavedState(Parcel in) {
- super(in);
- mSecondaryVisible = (in.readInt() == 0 ? false : true);
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeInt(mSecondaryVisible ? 1 : 0);
- }
-
- public static final Parcelable.Creator<SavedState> CREATOR
- = new Parcelable.Creator<SavedState>() {
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- /**
- * Saves the visibility of the secondary field.
- */
- @Override
- protected Parcelable onSaveInstanceState() {
- Parcelable superState = super.onSaveInstanceState();
- SavedState ss = new SavedState(superState);
-
- ss.mSecondaryVisible = mSecondaryVisible;
- return ss;
- }
-
- /**
- * Restores the visibility of the secondary field.
- */
- @Override
- protected void onRestoreInstanceState(Parcelable state) {
- SavedState ss = (SavedState) state;
- super.onRestoreInstanceState(ss.getSuperState());
-
- setSecondaryVisible(ss.mSecondaryVisible);
- }
-}
diff --git a/src/com/android/contacts/ui/widget/DontPressWithParentImageView.java b/src/com/android/contacts/ui/widget/DontPressWithParentImageView.java
deleted file mode 100644
index bdb0e0a..0000000
--- a/src/com/android/contacts/ui/widget/DontPressWithParentImageView.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.ui.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-
-/**
- * Special class to to allow the parent to be pressed without being pressed itself.
- * This way the line of a tab can be pressed, but the image itself is not.
- */
-public class DontPressWithParentImageView extends ImageView {
-
- public DontPressWithParentImageView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- public void setPressed(boolean pressed) {
- // If the parent is pressed, do not set to pressed.
- if (pressed && ((View) getParent()).isPressed()) {
- return;
- }
- super.setPressed(pressed);
- }
-}
diff --git a/src/com/android/contacts/ui/widget/GenericEditorView.java b/src/com/android/contacts/ui/widget/GenericEditorView.java
deleted file mode 100644
index 24262bb..0000000
--- a/src/com/android/contacts/ui/widget/GenericEditorView.java
+++ /dev/null
@@ -1,472 +0,0 @@
-/*
- * 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.ui.widget;
-
-import com.android.contacts.ContactsUtils;
-import com.android.contacts.R;
-import com.android.contacts.model.Editor;
-import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.model.ContactsSource.EditField;
-import com.android.contacts.model.ContactsSource.EditType;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.ui.ViewIdGenerator;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Entity;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.PhoneNumberFormattingTextWatcher;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.EditorInfo;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.ListAdapter;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import java.util.List;
-
-/**
- * Simple editor that handles labels and any {@link EditField} defined for
- * the entry. Uses {@link ValuesDelta} to read any existing
- * {@link Entity} values, and to correctly write any changes values.
- */
-public class GenericEditorView extends RelativeLayout implements Editor, View.OnClickListener {
- protected static final int RES_FIELD = R.layout.item_editor_field;
- protected static final int RES_LABEL_ITEM = android.R.layout.simple_list_item_1;
-
- protected LayoutInflater mInflater;
-
- protected static final int INPUT_TYPE_CUSTOM = EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
-
- protected TextView mLabel;
- protected ViewGroup mFields;
- protected View mDelete;
- protected View mMore;
- protected View mLess;
-
- protected DataKind mKind;
- protected ValuesDelta mEntry;
- protected EntityDelta mState;
- protected boolean mReadOnly;
-
- protected boolean mHideOptional = true;
-
- protected EditType mType;
- // Used only when a user tries to use custom label.
- private EditType mPendingType;
-
- private ViewIdGenerator mViewIdGenerator;
-
- public GenericEditorView(Context context) {
- super(context);
- }
-
- public GenericEditorView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onFinishInflate() {
- mInflater = (LayoutInflater)getContext().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
-
- mLabel = (TextView)findViewById(R.id.edit_label);
- mLabel.setOnClickListener(this);
-
- mFields = (ViewGroup)findViewById(R.id.edit_fields);
-
- mDelete = findViewById(R.id.edit_delete);
- mDelete.setOnClickListener(this);
-
- mMore = findViewById(R.id.edit_more);
- mMore.setOnClickListener(this);
-
- mLess = findViewById(R.id.edit_less);
- mLess.setOnClickListener(this);
- }
-
- protected EditorListener mListener;
-
- public void setEditorListener(EditorListener listener) {
- mListener = listener;
- }
-
- public void setDeletable(boolean deletable) {
- mDelete.setVisibility(deletable ? View.VISIBLE : View.INVISIBLE);
- }
-
- @Override
- public void setEnabled(boolean enabled) {
- mLabel.setEnabled(enabled);
- final int count = mFields.getChildCount();
- for (int pos = 0; pos < count; pos++) {
- final View v = mFields.getChildAt(pos);
- v.setEnabled(enabled);
- }
- mMore.setEnabled(enabled);
- mLess.setEnabled(enabled);
- }
-
- /**
- * Build the current label state based on selected {@link EditType} and
- * possible custom label string.
- */
- private void rebuildLabel() {
- // Handle undetected types
- if (mType == null) {
- mLabel.setText(R.string.unknown);
- return;
- }
-
- if (mType.customColumn != null) {
- // Use custom label string when present
- final String customText = mEntry.getAsString(mType.customColumn);
- if (customText != null) {
- mLabel.setText(customText);
- return;
- }
- }
-
- // Otherwise fall back to using default label
- mLabel.setText(mType.labelRes);
- }
-
- /** {@inheritDoc} */
- public void onFieldChanged(String column, String value) {
- // Field changes are saved directly
- mEntry.put(column, value);
- if (mListener != null) {
- mListener.onRequest(EditorListener.FIELD_CHANGED);
- }
- }
-
- public boolean isAnyFieldFilledOut() {
- int childCount = mFields.getChildCount();
- for (int i = 0; i < childCount; i++) {
- EditText editorView = (EditText) mFields.getChildAt(i);
- if (!TextUtils.isEmpty(editorView.getText())) {
- return true;
- }
- }
- return false;
- }
-
- private void rebuildValues() {
- setValues(mKind, mEntry, mState, mReadOnly, mViewIdGenerator);
- }
-
- /**
- * Prepare this editor using the given {@link DataKind} for defining
- * structure and {@link ValuesDelta} describing the content to edit.
- */
- public void setValues(DataKind kind, ValuesDelta entry, EntityDelta state, boolean readOnly,
- ViewIdGenerator vig) {
- mKind = kind;
- mEntry = entry;
- mState = state;
- mReadOnly = readOnly;
- mViewIdGenerator = vig;
- setId(vig.getId(state, kind, entry, ViewIdGenerator.NO_VIEW_INDEX));
-
- final boolean enabled = !readOnly;
-
- if (!entry.isVisible()) {
- // Hide ourselves entirely if deleted
- setVisibility(View.GONE);
- return;
- } else {
- setVisibility(View.VISIBLE);
- }
-
- // Display label selector if multiple types available
- final boolean hasTypes = EntityModifier.hasEditTypes(kind);
- mLabel.setVisibility(hasTypes ? View.VISIBLE : View.GONE);
- mLabel.setEnabled(enabled);
- if (hasTypes) {
- mType = EntityModifier.getCurrentType(entry, kind);
- rebuildLabel();
- }
-
- // Build out set of fields
- mFields.removeAllViews();
- boolean hidePossible = false;
- int n = 0;
- for (EditField field : kind.fieldList) {
- // Inflate field from definition
- EditText fieldView = (EditText)mInflater.inflate(RES_FIELD, mFields, false);
- fieldView.setId(vig.getId(state, kind, entry, n++));
- if (field.titleRes > 0) {
- fieldView.setHint(field.titleRes);
- }
- int inputType = field.inputType;
- fieldView.setInputType(inputType);
- if (inputType == InputType.TYPE_CLASS_PHONE) {
- fieldView.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
- }
- fieldView.setMinLines(field.minLines);
-
- // Read current value from state
- final String column = field.column;
- final String value = entry.getAsString(column);
- fieldView.setText(value);
-
- // Prepare listener for writing changes
- fieldView.addTextChangedListener(new TextWatcher() {
- public void afterTextChanged(Editable s) {
- // Trigger event for newly changed value
- onFieldChanged(column, s.toString());
- }
-
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
- });
-
- // Hide field when empty and optional value
- final boolean couldHide = (!ContactsUtils.isGraphic(value) && field.optional);
- final boolean willHide = (mHideOptional && couldHide);
- fieldView.setVisibility(willHide ? View.GONE : View.VISIBLE);
- fieldView.setEnabled(enabled);
- hidePossible = hidePossible || couldHide;
-
- mFields.addView(fieldView);
- }
-
- // When hiding fields, place expandable
- if (hidePossible) {
- mMore.setVisibility(mHideOptional ? View.VISIBLE : View.GONE);
- mLess.setVisibility(mHideOptional ? View.GONE : View.VISIBLE);
- } else {
- mMore.setVisibility(View.GONE);
- mLess.setVisibility(View.GONE);
- }
- mMore.setEnabled(enabled);
- mLess.setEnabled(enabled);
- }
-
- /**
- * Prepare dialog for entering a custom label. The input value is trimmed: white spaces before
- * and after the input text is removed.
- * <p>
- * If the final value is empty, this change request is ignored;
- * no empty text is allowed in any custom label.
- */
- private Dialog createCustomDialog() {
- final EditText customType = new EditText(mContext);
- customType.setInputType(INPUT_TYPE_CUSTOM);
- customType.requestFocus();
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
- builder.setTitle(R.string.customLabelPickerTitle);
- builder.setView(customType);
-
- builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- final String customText = customType.getText().toString().trim();
- if (ContactsUtils.isGraphic(customText)) {
- // Now we're sure it's ok to actually change the type value.
- mType = mPendingType;
- mPendingType = null;
- mEntry.put(mKind.typeColumn, mType.rawValue);
- mEntry.put(mType.customColumn, customText);
- rebuildLabel();
- if (!mFields.hasFocus())
- mFields.requestFocus();
- }
- }
- });
-
- builder.setNegativeButton(android.R.string.cancel, null);
-
- return builder.create();
- }
-
- /**
- * Prepare dialog for picking a new {@link EditType} or entering a
- * custom label. This dialog is limited to the valid types as determined
- * by {@link EntityModifier}.
- */
- public Dialog createLabelDialog() {
- // Build list of valid types, including the current value
- final List<EditType> validTypes = EntityModifier.getValidTypes(mState, mKind, mType);
-
- // Wrap our context to inflate list items using correct theme
- final Context dialogContext = new ContextThemeWrapper(mContext,
- android.R.style.Theme_Light);
- final LayoutInflater dialogInflater = mInflater.cloneInContext(dialogContext);
-
- final ListAdapter typeAdapter = new ArrayAdapter<EditType>(mContext, RES_LABEL_ITEM,
- validTypes) {
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = dialogInflater.inflate(RES_LABEL_ITEM, parent, false);
- }
-
- final EditType type = this.getItem(position);
- final TextView textView = (TextView)convertView;
- textView.setText(type.labelRes);
- return textView;
- }
- };
-
- final DialogInterface.OnClickListener clickListener =
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
-
- final EditType selected = validTypes.get(which);
- if (selected.customColumn != null) {
- // Show custom label dialog if requested by type.
- //
- // Only when the custum value input in the next step is correct one.
- // this method also set the type value to what the user requested here.
- mPendingType = selected;
- createCustomDialog().show();
- } else {
- // User picked type, and we're sure it's ok to actually write the entry.
- mType = selected;
- mEntry.put(mKind.typeColumn, mType.rawValue);
- rebuildLabel();
- if (!mFields.hasFocus())
- mFields.requestFocus();
- }
- }
- };
-
- final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
- builder.setTitle(R.string.selectLabel);
- builder.setSingleChoiceItems(typeAdapter, 0, clickListener);
- return builder.create();
- }
-
- /** {@inheritDoc} */
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.edit_label: {
- createLabelDialog().show();
- break;
- }
- case R.id.edit_delete: {
- // Keep around in model, but mark as deleted
- mEntry.markDeleted();
-
- // Remove editor from parent view
- final ViewGroup parent = (ViewGroup)getParent();
- parent.removeView(this);
-
- if (mListener != null) {
- // Notify listener when present
- mListener.onDeleted(this);
- }
- break;
- }
- case R.id.edit_more:
- case R.id.edit_less: {
- mHideOptional = !mHideOptional;
- rebuildValues();
- break;
- }
- }
- }
-
- private static class SavedState extends BaseSavedState {
- public boolean mHideOptional;
- public int[] mVisibilities;
-
- SavedState(Parcelable superState) {
- super(superState);
- }
-
- private SavedState(Parcel in) {
- super(in);
- mVisibilities = new int[in.readInt()];
- in.readIntArray(mVisibilities);
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeInt(mVisibilities.length);
- out.writeIntArray(mVisibilities);
- }
-
- public static final Parcelable.Creator<SavedState> CREATOR
- = new Parcelable.Creator<SavedState>() {
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- /**
- * Saves the visibility of the child EditTexts, and mHideOptional.
- */
- @Override
- protected Parcelable onSaveInstanceState() {
- Parcelable superState = super.onSaveInstanceState();
- SavedState ss = new SavedState(superState);
-
- ss.mHideOptional = mHideOptional;
-
- final int numChildren = mFields.getChildCount();
- ss.mVisibilities = new int[numChildren];
- for (int i = 0; i < numChildren; i++) {
- ss.mVisibilities[i] = mFields.getChildAt(i).getVisibility();
- }
-
- return ss;
- }
-
- /**
- * Restores the visibility of the child EditTexts, and mHideOptional.
- */
- @Override
- protected void onRestoreInstanceState(Parcelable state) {
- SavedState ss = (SavedState) state;
- super.onRestoreInstanceState(ss.getSuperState());
-
- mHideOptional = ss.mHideOptional;
-
- int numChildren = Math.min(mFields.getChildCount(), ss.mVisibilities.length);
- for (int i = 0; i < numChildren; i++) {
- mFields.getChildAt(i).setVisibility(ss.mVisibilities[i]);
- }
- }
-}
diff --git a/src/com/android/contacts/ui/widget/KindSectionView.java b/src/com/android/contacts/ui/widget/KindSectionView.java
deleted file mode 100644
index 221bc16..0000000
--- a/src/com/android/contacts/ui/widget/KindSectionView.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * 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.ui.widget;
-
-import com.android.contacts.R;
-import com.android.contacts.model.Editor;
-import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.model.Editor.EditorListener;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.ui.ViewIdGenerator;
-
-import android.content.Context;
-import android.provider.ContactsContract.Data;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.View.OnClickListener;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-/**
- * Custom view for an entire section of data as segmented by
- * {@link DataKind} around a {@link Data#MIMETYPE}. This view shows a
- * section header and a trigger for adding new {@link Data} rows.
- */
-public class KindSectionView extends LinearLayout implements OnClickListener, EditorListener {
- private static final String TAG = "KindSectionView";
-
- private LayoutInflater mInflater;
-
- private ViewGroup mEditors;
- private View mAdd;
- private ImageView mAddPlusButton;
- private TextView mTitle;
-
- private DataKind mKind;
- private EntityDelta mState;
- private boolean mReadOnly;
-
- private ViewIdGenerator mViewIdGenerator;
-
- public KindSectionView(Context context) {
- super(context);
- }
-
- public KindSectionView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onFinishInflate() {
- mInflater = (LayoutInflater)getContext().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
-
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mEditors = (ViewGroup)findViewById(R.id.kind_editors);
-
- mAdd = findViewById(R.id.kind_header);
- mAdd.setOnClickListener(this);
-
- mAddPlusButton = (ImageView) findViewById(R.id.kind_plus);
-
- mTitle = (TextView)findViewById(R.id.kind_title);
- }
-
- /** {@inheritDoc} */
- public void onDeleted(Editor editor) {
- this.updateAddEnabled();
- this.updateEditorsVisible();
- }
-
- /** {@inheritDoc} */
- public void onRequest(int request) {
- // Ignore requests
- }
-
- public void setState(DataKind kind, EntityDelta state, boolean readOnly, ViewIdGenerator vig) {
- mKind = kind;
- mState = state;
- mReadOnly = readOnly;
- mViewIdGenerator = vig;
-
- setId(mViewIdGenerator.getId(state, kind, null, ViewIdGenerator.NO_VIEW_INDEX));
-
- // TODO: handle resources from remote packages
- mTitle.setText(kind.titleRes);
-
- // Only show the add button if this is a list
- mAddPlusButton.setVisibility(mKind.isList ? View.VISIBLE : View.GONE);
-
- this.rebuildFromState();
- this.updateAddEnabled();
- this.updateEditorsVisible();
- }
-
- public boolean isAnyEditorFilledOut() {
- if (mState == null) {
- return false;
- }
-
- if (!mState.hasMimeEntries(mKind.mimeType)) {
- return false;
- }
-
- int editorCount = mEditors.getChildCount();
- for (int i = 0; i < editorCount; i++) {
- GenericEditorView editorView = (GenericEditorView) mEditors.getChildAt(i);
- if (editorView.isAnyFieldFilledOut()) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Build editors for all current {@link #mState} rows.
- */
- public void rebuildFromState() {
- // Remove any existing editors
- mEditors.removeAllViews();
-
- // Check if we are displaying anything here
- boolean hasEntries = mState.hasMimeEntries(mKind.mimeType);
-
- if (!mKind.isList) {
- if (hasEntries) {
- // we might have no visible entries. check that, too
- for (ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)) {
- if (!entry.isVisible()) {
- hasEntries = false;
- break;
- }
- }
- }
-
- if (!hasEntries) {
- EntityModifier.insertChild(mState, mKind);
- hasEntries = true;
- }
- }
-
- if (hasEntries) {
- int entryIndex = 0;
- for (ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)) {
- // Skip entries that aren't visible
- if (!entry.isVisible()) continue;
-
- final GenericEditorView editor = (GenericEditorView)mInflater.inflate(
- R.layout.item_generic_editor, mEditors, false);
- editor.setValues(mKind, entry, mState, mReadOnly, mViewIdGenerator);
- // older versions of android had lists where we now have a single value
- // in these cases we should show the remove button for all but the first value
- // to ensure that nothing is removed
- editor.mDelete.setVisibility((mKind.isList || (entryIndex != 0))
- ? View.VISIBLE : View.GONE);
- editor.setEditorListener(this);
- mEditors.addView(editor);
- entryIndex++;
- }
- }
- }
-
- protected void updateEditorsVisible() {
- final boolean hasChildren = mEditors.getChildCount() > 0;
- mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE);
- }
-
- protected void updateAddEnabled() {
- // Set enabled state on the "add" view
- final boolean canInsert = EntityModifier.canInsert(mState, mKind);
- final boolean isEnabled = !mReadOnly && canInsert;
- mAdd.setEnabled(isEnabled);
- }
-
- /** {@inheritDoc} */
- public void onClick(View v) {
- // if this is not a list the plus button is not visible but the user might have clicked
- // the text.
- if (!mKind.isList)
- return;
-
- // Insert a new child and rebuild
- final ValuesDelta newValues = EntityModifier.insertChild(mState, mKind);
- this.rebuildFromState();
- this.updateAddEnabled();
- this.updateEditorsVisible();
-
- // Find the newly added EditView and set focus.
- final int newFieldId = mViewIdGenerator.getId(mState, mKind, newValues, 0);
- final View newField = findViewById(newFieldId);
- if (newField != null) {
- newField.requestFocus();
- }
- }
-}
diff --git a/src/com/android/contacts/ui/widget/PhotoEditorView.java b/src/com/android/contacts/ui/widget/PhotoEditorView.java
deleted file mode 100644
index eff39d0..0000000
--- a/src/com/android/contacts/ui/widget/PhotoEditorView.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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.ui.widget;
-
-import com.android.contacts.R;
-import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.model.Editor;
-import com.android.contacts.ui.ViewIdGenerator;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.ImageView;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-/**
- * Simple editor for {@link Photo}.
- */
-public class PhotoEditorView extends ImageView implements Editor, OnClickListener {
- private static final String TAG = "PhotoEditorView";
-
- private ValuesDelta mEntry;
- private EditorListener mListener;
-
- private boolean mHasSetPhoto = false;
- private boolean mReadOnly;
-
- public PhotoEditorView(Context context) {
- super(context);
- }
-
- public PhotoEditorView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- this.setOnClickListener(this);
- }
-
- /** {@inheritDoc} */
- public void onClick(View v) {
- if (mListener != null) {
- mListener.onRequest(EditorListener.REQUEST_PICK_PHOTO);
- }
- }
-
- /** {@inheritDoc} */
- public void onFieldChanged(String column, String value) {
- throw new UnsupportedOperationException("Photos don't support direct field changes");
- }
-
- /** {@inheritDoc} */
- public void setValues(DataKind kind, ValuesDelta values, EntityDelta state, boolean readOnly,
- ViewIdGenerator vig) {
- mEntry = values;
- mReadOnly = readOnly;
-
- setId(vig.getId(state, kind, values, 0));
-
- if (values != null) {
- // Try decoding photo if actual entry
- final byte[] photoBytes = values.getAsByteArray(Photo.PHOTO);
- if (photoBytes != null) {
- final Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0,
- photoBytes.length);
-
- setScaleType(ImageView.ScaleType.CENTER_CROP);
- setImageBitmap(photo);
- setEnabled(true);
- mHasSetPhoto = true;
- mEntry.setFromTemplate(false);
- } else {
- resetDefault();
- }
- } else {
- resetDefault();
- }
- }
-
- /**
- * Return true if a valid {@link Photo} has been set.
- */
- public boolean hasSetPhoto() {
- return mHasSetPhoto;
- }
-
- /**
- * Assign the given {@link Bitmap} as the new value, updating UI and
- * readying for persisting through {@link ValuesDelta}.
- */
- public void setPhotoBitmap(Bitmap photo) {
- if (photo == null) {
- // Clear any existing photo and return
- mEntry.put(Photo.PHOTO, (byte[])null);
- resetDefault();
- return;
- }
-
- final int size = photo.getWidth() * photo.getHeight() * 4;
- final ByteArrayOutputStream out = new ByteArrayOutputStream(size);
-
- try {
- photo.compress(Bitmap.CompressFormat.PNG, 100, out);
- out.flush();
- out.close();
-
- mEntry.put(Photo.PHOTO, out.toByteArray());
- setImageBitmap(photo);
- setEnabled(true);
- mHasSetPhoto = true;
- mEntry.setFromTemplate(false);
-
- // When the user chooses a new photo mark it as super primary
- mEntry.put(Photo.IS_SUPER_PRIMARY, 1);
- } catch (IOException e) {
- Log.w(TAG, "Unable to serialize photo: " + e.toString());
- }
- }
-
- /**
- * Set the super primary bit on the photo.
- */
- public void setSuperPrimary(boolean superPrimary) {
- mEntry.put(Photo.IS_SUPER_PRIMARY, superPrimary ? 1 : 0);
- }
-
- protected void resetDefault() {
- // Invalid photo, show default "add photo" place-holder
- setScaleType(ImageView.ScaleType.CENTER);
- if (mReadOnly) {
- setImageResource(R.drawable.ic_contact_picture);
- setEnabled(false);
- } else {
- setImageResource(R.drawable.ic_menu_add_picture);
- setEnabled(true);
- }
- mHasSetPhoto = false;
- mEntry.setFromTemplate(true);
- }
-
- /** {@inheritDoc} */
- public void setEditorListener(EditorListener listener) {
- mListener = listener;
- }
-}
diff --git a/src/com/android/contacts/ui/widget/ReadOnlyContactEditorView.java b/src/com/android/contacts/ui/widget/ReadOnlyContactEditorView.java
deleted file mode 100644
index 4635f6a..0000000
--- a/src/com/android/contacts/ui/widget/ReadOnlyContactEditorView.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * 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.ui.widget;
-
-import com.android.contacts.R;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.model.ContactsSource.EditType;
-import com.android.contacts.model.Editor.EditorListener;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.android.contacts.ui.ViewIdGenerator;
-
-import android.content.Context;
-import android.content.Entity;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-
-/**
- * Custom view that displays read-only contacts in the edit screen.
- */
-class ReadOnlyContactEditorView extends BaseContactEditorView {
-
- private View mPhotoStub;
- private TextView mName;
- private TextView mReadOnlyWarning;
- private ViewGroup mGeneral;
-
- private View mHeaderColorBar;
- private View mSideBar;
- private ImageView mHeaderIcon;
- private TextView mHeaderAccountType;
- private TextView mHeaderAccountName;
-
- private long mRawContactId = -1;
-
- public ReadOnlyContactEditorView(Context context) {
- super(context);
- }
-
- public ReadOnlyContactEditorView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- mInflater = (LayoutInflater)getContext().getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
-
- mPhoto = (PhotoEditorView)findViewById(R.id.edit_photo);
- mPhotoStub = findViewById(R.id.stub_photo);
-
- mName = (TextView) findViewById(R.id.read_only_name);
- mReadOnlyWarning = (TextView) findViewById(R.id.read_only_warning);
- mGeneral = (ViewGroup)findViewById(R.id.sect_general);
-
- mHeaderColorBar = findViewById(R.id.header_color_bar);
- mSideBar = findViewById(R.id.color_bar);
- mHeaderIcon = (ImageView) findViewById(R.id.header_icon);
- mHeaderAccountType = (TextView) findViewById(R.id.header_account_type);
- mHeaderAccountName = (TextView) findViewById(R.id.header_account_name);
- }
-
- /**
- * Set the internal state for this view, given a current
- * {@link EntityDelta} state and the {@link ContactsSource} that
- * apply to that state.
- *
- * TODO: make this more generic using data from the source
- */
- @Override
- public void setState(EntityDelta state, ContactsSource source, ViewIdGenerator vig) {
- // Remove any existing sections
- mGeneral.removeAllViews();
-
- // Bail if invalid state or source
- if (state == null || source == null) return;
-
- // Make sure we have StructuredName
- EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE);
-
- // Fill in the header info
- ValuesDelta values = state.getValues();
- String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
- CharSequence accountType = source.getDisplayLabel(mContext);
- if (TextUtils.isEmpty(accountType)) {
- accountType = mContext.getString(R.string.account_phone);
- }
- if (!TextUtils.isEmpty(accountName)) {
- mHeaderAccountName.setText(
- mContext.getString(R.string.from_account_format, accountName));
- }
- mHeaderAccountType.setText(mContext.getString(R.string.account_type_format, accountType));
- mHeaderIcon.setImageDrawable(source.getDisplayIcon(mContext));
-
- mRawContactId = values.getAsLong(RawContacts._ID);
-
- ValuesDelta primary;
-
- // Photo
- DataKind kind = source.getKindForMimetype(Photo.CONTENT_ITEM_TYPE);
- if (kind != null) {
- EntityModifier.ensureKindExists(state, source, Photo.CONTENT_ITEM_TYPE);
- mHasPhotoEditor = (source.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null);
- primary = state.getPrimaryEntry(Photo.CONTENT_ITEM_TYPE);
- mPhoto.setValues(kind, primary, state, source.readOnly, vig);
- if (!mHasPhotoEditor || !mPhoto.hasSetPhoto()) {
- mPhotoStub.setVisibility(View.GONE);
- } else {
- mPhotoStub.setVisibility(View.VISIBLE);
- }
- } else {
- mPhotoStub.setVisibility(View.VISIBLE);
- }
-
- // Name
- primary = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
- mName.setText(primary.getAsString(StructuredName.DISPLAY_NAME));
-
- // Read only warning
- mReadOnlyWarning.setText(mContext.getString(R.string.contact_read_only, accountType));
-
- // Phones
- ArrayList<ValuesDelta> phones = state.getMimeEntries(Phone.CONTENT_ITEM_TYPE);
- if (phones != null) {
- for (ValuesDelta phone : phones) {
- View field = mInflater.inflate(
- R.layout.item_read_only_field, mGeneral, false);
- TextView v;
- v = (TextView) field.findViewById(R.id.label);
- v.setText(mContext.getText(R.string.phoneLabelsGroup));
- v = (TextView) field.findViewById(R.id.data);
- v.setText(PhoneNumberUtils.formatNumber(phone.getAsString(Phone.NUMBER)));
- mGeneral.addView(field);
- }
- }
-
- // Emails
- ArrayList<ValuesDelta> emails = state.getMimeEntries(Email.CONTENT_ITEM_TYPE);
- if (emails != null) {
- for (ValuesDelta email : emails) {
- View field = mInflater.inflate(
- R.layout.item_read_only_field, mGeneral, false);
- TextView v;
- v = (TextView) field.findViewById(R.id.label);
- v.setText(mContext.getText(R.string.emailLabelsGroup));
- v = (TextView) field.findViewById(R.id.data);
- v.setText(email.getAsString(Email.DATA));
- mGeneral.addView(field);
- }
- }
-
- // Hide mGeneral if it's empty
- if (mGeneral.getChildCount() > 0) {
- mGeneral.setVisibility(View.VISIBLE);
- } else {
- mGeneral.setVisibility(View.GONE);
- }
- }
-
- /**
- * Sets the {@link EditorListener} on the name field
- */
- @Override
- public void setNameEditorListener(EditorListener listener) {
- // do nothing
- }
-
- @Override
- public long getRawContactId() {
- return mRawContactId;
- }
-}
diff --git a/src/com/android/contacts/util/AccountSelectionUtil.java b/src/com/android/contacts/util/AccountSelectionUtil.java
index cc46d2b..46d20b8 100644
--- a/src/com/android/contacts/util/AccountSelectionUtil.java
+++ b/src/com/android/contacts/util/AccountSelectionUtil.java
@@ -16,18 +16,13 @@
package com.android.contacts.util;
-import com.android.contacts.ImportVCardActivity;
-import com.android.contacts.R;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.GoogleSource;
-import com.android.contacts.model.Sources;
-
import android.accounts.Account;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.net.Uri;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
@@ -35,7 +30,10 @@
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
-import android.net.Uri;
+
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
import java.util.List;
@@ -89,8 +87,10 @@
public static Dialog getSelectAccountDialog(Context context, int resId,
DialogInterface.OnClickListener onClickListener,
DialogInterface.OnCancelListener onCancelListener) {
- final Sources sources = Sources.getInstance(context);
- final List<Account> writableAccountList = sources.getAccounts(true);
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
+ final List<Account> writableAccountList = accountTypes.getAccounts(true);
+
+ Log.i(LOG_TAG, "The number of available accounts: " + writableAccountList.size());
// Assume accountList.size() > 1
@@ -118,13 +118,11 @@
(TextView)convertView.findViewById(android.R.id.text2);
final Account account = this.getItem(position);
- final ContactsSource source =
- sources.getInflatedSource(account.type,
- ContactsSource.LEVEL_SUMMARY);
+ final AccountType accountType = accountTypes.getAccountType(account.type);
final Context context = getContext();
text1.setText(account.name);
- text2.setText(source.getDisplayLabel(context));
+ text2.setText(accountType.getDisplayLabel(context));
return convertView;
}
@@ -163,10 +161,6 @@
}
public static void doImportFromSim(Context context, Account account) {
- if (account != null) {
- GoogleSource.createMyContactsIfNotExist(account, context);
- }
-
Intent importIntent = new Intent(Intent.ACTION_VIEW);
importIntent.setType("vnd.android.cursor.item/sim-contact");
if (account != null) {
@@ -178,11 +172,8 @@
}
public static void doImportFromSdCard(Context context, Account account) {
- if (account != null) {
- GoogleSource.createMyContactsIfNotExist(account, context);
- }
-
- Intent importIntent = new Intent(context, ImportVCardActivity.class);
+ Intent importIntent = new Intent(context,
+ com.android.contacts.vcard.ImportVCardActivity.class);
if (account != null) {
importIntent.putExtra("account_name", account.name);
importIntent.putExtra("account_type", account.type);
diff --git a/src/com/android/contacts/util/AccountsListAdapter.java b/src/com/android/contacts/util/AccountsListAdapter.java
new file mode 100644
index 0000000..8dbfc8d
--- /dev/null
+++ b/src/com/android/contacts/util/AccountsListAdapter.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 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.util;
+
+import com.android.contacts.R;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountType;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+/**
+ * List-Adapter for Account selection
+ */
+public final class AccountsListAdapter extends BaseAdapter {
+ private final LayoutInflater mInflater;
+ private final List<Account> mAccounts;
+ private final AccountTypeManager mAccountTypes;
+ private final Context mContext;
+
+ public AccountsListAdapter(Context context, boolean writableOnly) {
+ mContext = context;
+ mAccountTypes = AccountTypeManager.getInstance(context);
+ mAccounts = mAccountTypes.getAccounts(writableOnly);
+ mInflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final View resultView = convertView != null ? convertView
+ : mInflater.inflate(R.layout.account_selector_list_item, parent, false);
+
+ final TextView text1 = (TextView)resultView.findViewById(android.R.id.text1);
+ final TextView text2 = (TextView)resultView.findViewById(android.R.id.text2);
+ final ImageView icon = (ImageView)resultView.findViewById(android.R.id.icon);
+
+ final Account account = mAccounts.get(position);
+ final AccountType accountType = mAccountTypes.getAccountType(account.type);
+
+ text1.setText(account.name);
+ text2.setText(accountType.getDisplayLabel(mContext));
+ icon.setImageDrawable(accountType.getDisplayIcon(mContext));
+
+ return resultView;
+ }
+
+ @Override
+ public int getCount() {
+ return mAccounts.size();
+ }
+
+ @Override
+ public Account getItem(int position) {
+ return mAccounts.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+}
+
diff --git a/src/com/android/contacts/util/Constants.java b/src/com/android/contacts/util/Constants.java
index a94608b..2457600 100644
--- a/src/com/android/contacts/util/Constants.java
+++ b/src/com/android/contacts/util/Constants.java
@@ -16,20 +16,16 @@
package com.android.contacts.util;
-import android.app.Service;
import android.provider.ContactsContract.CommonDataKinds.Phone;
-/**
- * Background {@link Service} that is used to keep our process alive long enough
- * for background threads to finish. Started and stopped directly by specific
- * background tasks when needed.
- */
public class Constants {
/**
* Specific MIME-type for {@link Phone#CONTENT_ITEM_TYPE} entries that
* distinguishes actions that should initiate a text message.
*/
- public static final String MIME_SMS_ADDRESS = "vnd.android.cursor.item/sms-address";
+ public static final String MIME_TYPE_SMS_ADDRESS = "vnd.android.cursor.item/sms-address";
+
+ public static final String MIME_TYPE_VIDEO_CHAT = "vnd.android.cursor.item/video-chat-address";
public static final String SCHEME_TEL = "tel";
public static final String SCHEME_SMSTO = "smsto";
diff --git a/src/com/android/contacts/util/ContactBadgeUtil.java b/src/com/android/contacts/util/ContactBadgeUtil.java
new file mode 100644
index 0000000..65025f8
--- /dev/null
+++ b/src/com/android/contacts/util/ContactBadgeUtil.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2010 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.util;
+
+import com.android.contacts.ContactLoader;
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+/**
+ * Provides static functions to extract summary information for aggregate contacts
+ */
+public class ContactBadgeUtil {
+ private static final String TAG = "ContactBadgeUtil";
+
+ /**
+ * Returns the social snippet attribution, including the date
+ */
+ public static CharSequence getSocialDate(ContactLoader.Result contactData,
+ Context context) {
+ if (TextUtils.isEmpty(contactData.getSocialSnippet())) {
+ return null;
+ }
+
+ final CharSequence timestampDisplayValue;
+
+ final Long statusTimestamp = contactData.getStatusTimestamp();
+ if (statusTimestamp != null) {
+ // Set the date/time field by mixing relative and absolute
+ // times.
+ int flags = DateUtils.FORMAT_ABBREV_RELATIVE;
+
+ timestampDisplayValue = DateUtils.getRelativeTimeSpanString(
+ statusTimestamp.longValue(), System.currentTimeMillis(),
+ DateUtils.MINUTE_IN_MILLIS, flags);
+ } else {
+ timestampDisplayValue = null;
+ }
+
+
+ String labelDisplayValue = null;
+
+ final Integer statusLabel = contactData.getStatusLabel();
+ final String statusResPackage = contactData.getStatusResPackage();
+ if (statusLabel != null) {
+ Resources resources;
+ if (TextUtils.isEmpty(statusResPackage)) {
+ resources = context.getResources();
+ } else {
+ PackageManager pm = context.getPackageManager();
+ try {
+ resources = pm.getResourcesForApplication(statusResPackage);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Contact status update resource package not found: "
+ + statusResPackage);
+ resources = null;
+ }
+ }
+
+ if (resources != null) {
+ try {
+ labelDisplayValue = resources.getString(statusLabel.intValue());
+ } catch (NotFoundException e) {
+ Log.w(TAG, "Contact status update resource not found: " + statusResPackage + "@"
+ + statusLabel.intValue());
+ }
+ }
+ }
+
+ final CharSequence attribution;
+ if (timestampDisplayValue != null && labelDisplayValue != null) {
+ attribution = context.getString(
+ R.string.contact_status_update_attribution_with_date,
+ timestampDisplayValue, labelDisplayValue);
+ } else if (timestampDisplayValue == null && labelDisplayValue != null) {
+ attribution = context.getString(
+ R.string.contact_status_update_attribution,
+ labelDisplayValue);
+ } else if (timestampDisplayValue != null) {
+ attribution = timestampDisplayValue;
+ } else {
+ attribution = null;
+ }
+ return attribution;
+ }
+
+ public static Bitmap loadPlaceholderPhoto(Context context) {
+ return BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_contact_picture);
+ }
+}
diff --git a/src/com/android/contacts/util/DataStatus.java b/src/com/android/contacts/util/DataStatus.java
index 88c6594..ec3c1e5 100644
--- a/src/com/android/contacts/util/DataStatus.java
+++ b/src/com/android/contacts/util/DataStatus.java
@@ -24,6 +24,8 @@
import android.text.TextUtils;
import android.text.format.DateUtils;
+import com.android.contacts.R;
+
/**
* Storage for a social status update. Holds a single update, but can use
* {@link #possibleUpdate(Cursor)} to consider updating when a better status
@@ -112,11 +114,11 @@
if (validTimestamp && validLabel) {
return context.getString(
- com.android.internal.R.string.contact_status_update_attribution_with_date,
+ R.string.contact_status_update_attribution_with_date,
timeClause, labelClause);
} else if (validLabel) {
return context.getString(
- com.android.internal.R.string.contact_status_update_attribution,
+ R.string.contact_status_update_attribution,
labelClause);
} else if (validTimestamp) {
return timeClause;
diff --git a/src/com/android/contacts/util/DateUtils.java b/src/com/android/contacts/util/DateUtils.java
new file mode 100644
index 0000000..40570f0
--- /dev/null
+++ b/src/com/android/contacts/util/DateUtils.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 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.util;
+
+import android.content.Context;
+import android.text.format.DateFormat;
+
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Utility methods for processing dates.
+ */
+public class DateUtils {
+ public static final SimpleDateFormat NO_YEAR_DATE_FORMAT = new SimpleDateFormat("--MM-dd");
+ public static final SimpleDateFormat FULL_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+ public static final SimpleDateFormat DATE_AND_TIME_FORMAT =
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+
+ // Variations of ISO 8601 date format. Do not change the order - it does affect the
+ // result in ambiguous cases.
+ private static final SimpleDateFormat[] DATE_FORMATS = {
+ FULL_DATE_FORMAT,
+ DATE_AND_TIME_FORMAT,
+ new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'"),
+ new SimpleDateFormat("yyyyMMdd"),
+ new SimpleDateFormat("yyyyMMdd'T'HHmmssSSS'Z'"),
+ new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"),
+ new SimpleDateFormat("yyyyMMdd'T'HHmm'Z'"),
+ };
+
+ static {
+ for (SimpleDateFormat format : DATE_FORMATS) {
+ format.setLenient(true);
+ }
+ }
+
+ private static final java.text.DateFormat FORMAT_WITHOUT_YEAR_MONTH_FIRST =
+ new SimpleDateFormat("MMMM dd");
+
+ private static final java.text.DateFormat FORMAT_WITHOUT_YEAR_DATE_FIRST =
+ new SimpleDateFormat("dd MMMM");
+
+ /**
+ * Parses the supplied string to see if it looks like a date. If so,
+ * returns the date. Otherwise, returns null.
+ */
+ public static Date parseDate(String string) {
+ ParsePosition parsePosition = new ParsePosition(0);
+ for (int i = 0; i < DATE_FORMATS.length; i++) {
+ SimpleDateFormat f = DATE_FORMATS[i];
+ synchronized (f) {
+ parsePosition.setIndex(0);
+ Date date = f.parse(string, parsePosition);
+ if (parsePosition.getIndex() == string.length()) {
+ return date;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Parses the supplied string to see if it looks like a date. If so,
+ * returns the same date in a cleaned-up format. Otherwise, returns
+ * the supplied string unchanged.
+ */
+ public static String formatDate(Context context, String string) {
+ if (string == null) {
+ return null;
+ }
+
+ string = string.trim();
+ if (string.length() == 0) {
+ return string;
+ }
+
+ ParsePosition parsePosition = new ParsePosition(0);
+
+ Date date;
+
+ synchronized (NO_YEAR_DATE_FORMAT) {
+ date = NO_YEAR_DATE_FORMAT.parse(string, parsePosition);
+ }
+
+ if (parsePosition.getIndex() == string.length()) {
+ java.text.DateFormat outFormat = isMonthBeforeDate(context)
+ ? FORMAT_WITHOUT_YEAR_MONTH_FIRST
+ : FORMAT_WITHOUT_YEAR_DATE_FIRST;
+ synchronized (outFormat) {
+ return outFormat.format(date);
+ }
+ }
+
+ for (int i = 0; i < DATE_FORMATS.length; i++) {
+ SimpleDateFormat f = DATE_FORMATS[i];
+ synchronized (f) {
+ parsePosition.setIndex(0);
+ date = f.parse(string, parsePosition);
+ if (parsePosition.getIndex() == string.length()) {
+ java.text.DateFormat outFormat = DateFormat.getDateFormat(context);
+ synchronized (outFormat) {
+ return outFormat.format(date);
+ }
+ }
+ }
+ }
+ return string;
+ }
+
+ private static boolean isMonthBeforeDate(Context context) {
+ char[] dateFormatOrder = DateFormat.getDateFormatOrder(context);
+ for (int i = 0; i < dateFormatOrder.length; i++) {
+ if (dateFormatOrder[i] == DateFormat.DATE) {
+ return false;
+ }
+ if (dateFormatOrder[i] == DateFormat.MONTH) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/contacts/util/DialogManager.java b/src/com/android/contacts/util/DialogManager.java
new file mode 100644
index 0000000..661c5d3
--- /dev/null
+++ b/src/com/android/contacts/util/DialogManager.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2010 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.util;
+
+import com.android.contacts.R;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnDismissListener;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * Manages creation and destruction of Dialogs that are to be shown by Views. Unlike how Dialogs
+ * are regularly used, the Dialogs are not recycled but immediately destroyed after dismissal.
+ * To be able to do that, two IDs are required which are used consecutively.
+ * How to use:<ul>
+ * <li>The owning Activity creates on instance of this class, passing itself and two Ids that are
+ * not used by other Dialogs of the Activity.</li>
+ * <li>Views owning Dialogs must implement {@link DialogManager.DialogShowingView}</li>
+ * <li>After creating the Views, configureManagingViews must be called to configure all views
+ * that implement {@link DialogManager.DialogShowingView}</li>
+ * <li>In the implementation of {@link Activity#onCreateDialog}, calls for the
+ * ViewId are forwarded to {@link DialogManager#onCreateDialog(int, Bundle)}</li>
+ * </ul>
+ * To actually show a Dialog, the View uses {@link DialogManager#showDialogInView(View, Bundle)},
+ * passing itself as a first parameter
+ */
+public class DialogManager {
+ private final Activity mActivity;
+ private boolean mUseDialogId2 = false;
+ public final static String VIEW_ID_KEY = "view_id";
+
+ public static final boolean isManagedId(int id) {
+ return (id == R.id.dialog_manager_id_1) || (id == R.id.dialog_manager_id_2);
+ }
+
+ /**
+ * Creates a new instance of this class for the given Activity.
+ * @param activity The activity this object is used for
+ */
+ public DialogManager(final Activity activity) {
+ if (activity == null) throw new IllegalArgumentException("activity must not be null");
+ mActivity = activity;
+ }
+
+ /**
+ * Called by a View to show a dialog. It has to pass itself and a Bundle with extra information.
+ * If the view can show several dialogs, it should distinguish them using an item in the Bundle.
+ * The View needs to have a valid and unique Id. This function modifies the bundle by adding a
+ * new item named {@link DialogManager#VIEW_ID_KEY}
+ */
+ public void showDialogInView(final View view, final Bundle bundle) {
+ final int viewId = view.getId();
+ if (bundle.containsKey(VIEW_ID_KEY)) {
+ throw new IllegalArgumentException("Bundle already contains a " + VIEW_ID_KEY);
+ }
+ if (viewId == View.NO_ID) {
+ throw new IllegalArgumentException("View does not have a proper ViewId");
+ }
+ bundle.putInt(VIEW_ID_KEY, viewId);
+ int dialogId = mUseDialogId2 ? R.id.dialog_manager_id_2 : R.id.dialog_manager_id_1;
+ mActivity.showDialog(dialogId, bundle);
+ }
+
+ /**
+ * Callback function called by the Activity to handle View-managed Dialogs.
+ * This function returns null if the id is not one of the two reserved Ids.
+ */
+ public Dialog onCreateDialog(final int id, final Bundle bundle) {
+ if (id == R.id.dialog_manager_id_1) {
+ mUseDialogId2 = true;
+ } else if (id == R.id.dialog_manager_id_2) {
+ mUseDialogId2 = false;
+ } else {
+ return null;
+ }
+ if (!bundle.containsKey(VIEW_ID_KEY)) {
+ throw new IllegalArgumentException("Bundle does not contain a ViewId");
+ }
+ final int viewId = bundle.getInt(VIEW_ID_KEY);
+ final View view = mActivity.findViewById(viewId);
+ if (view == null || !(view instanceof DialogShowingView)) {
+ return null;
+ }
+ final Dialog dialog = ((DialogShowingView)view).createDialog(bundle);
+ if (dialog == null) {
+ return dialog;
+ }
+
+ // As we will never re-use this dialog, we can completely kill it here
+ dialog.setOnDismissListener(new OnDismissListener() {
+ public void onDismiss(DialogInterface dialogInterface) {
+ mActivity.removeDialog(id);
+ }
+ });
+ return dialog;
+ }
+
+ /**
+ * Interface to implemented by Views that show Dialogs
+ */
+ public interface DialogShowingView {
+ /**
+ * Callback function to create a Dialog. Notice that the DialogManager overwrites the
+ * OnDismissListener on the returned Dialog, so the View should not use this Listener itself
+ */
+ Dialog createDialog(Bundle bundle);
+ }
+
+ /**
+ * Interface to implemented by Activities that host View-showing dialogs
+ */
+ public interface DialogShowingViewActivity {
+ DialogManager getDialogManager();
+ }
+}
diff --git a/src/com/android/contacts/util/LocalizedNameResolver.java b/src/com/android/contacts/util/LocalizedNameResolver.java
new file mode 100644
index 0000000..c2ee90a
--- /dev/null
+++ b/src/com/android/contacts/util/LocalizedNameResolver.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2010 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.util;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.accounts.AccountManager;
+import android.accounts.AuthenticatorDescription;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.content.res.Resources.NotFoundException;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import java.io.IOException;
+
+/**
+ * Retrieves localized names per account type. This allows customizing texts like
+ * "All Contacts" for certain account types, but e.g. "All Friends" or "All Connections" for others.
+ */
+public class LocalizedNameResolver {
+ private static final String TAG = "LocalizedNameResolver";
+
+ /**
+ * Meta-data key for the contacts configuration associated with a sync service.
+ */
+ private static final String METADATA_CONTACTS = "android.provider.CONTACTS_STRUCTURE";
+
+ private static final String CONTACTS_DATA_KIND = "ContactsDataKind";
+
+ /**
+ * Returns the name for All Contacts for the specified account type.
+ */
+ public static String getAllContactsName(Context context, String accountType) {
+ if (context == null) throw new IllegalArgumentException("Context must not be null");
+ if (accountType == null) return null;
+
+ return resolveAllContactsName(context, accountType);
+ }
+
+ /**
+ * Finds "All Contacts"-Name for the specified account type.
+ */
+ private static String resolveAllContactsName(Context context, String accountType) {
+ final AccountManager am = AccountManager.get(context);
+
+ for (AuthenticatorDescription auth : am.getAuthenticatorTypes()) {
+ if (accountType.equals(auth.type)) {
+ return resolveAllContactsNameFromMetaData(context, auth.packageName);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Finds the meta-data XML containing the contacts configuration and
+ * reads the picture priority from that file.
+ */
+ private static String resolveAllContactsNameFromMetaData(Context context, String packageName) {
+ final PackageManager pm = context.getPackageManager();
+ try {
+ PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_SERVICES
+ | PackageManager.GET_META_DATA);
+ if (pi != null && pi.services != null) {
+ for (ServiceInfo si : pi.services) {
+ final XmlResourceParser parser = si.loadXmlMetaData(pm, METADATA_CONTACTS);
+ if (parser != null) {
+ return loadAllContactsNameFromXml(context, parser, packageName);
+ }
+ }
+ }
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Problem loading \"All Contacts\"-name: " + e.toString());
+ }
+ return null;
+ }
+
+ private static String loadAllContactsNameFromXml(Context context, XmlPullParser parser,
+ String packageName) {
+ try {
+ final AttributeSet attrs = Xml.asAttributeSet(parser);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ // Drain comments and whitespace
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ throw new IllegalStateException("No start tag found");
+ }
+
+ final int depth = parser.getDepth();
+ while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+ String name = parser.getName();
+ if (type == XmlPullParser.START_TAG && CONTACTS_DATA_KIND.equals(name)) {
+ final TypedArray typedArray = context.obtainStyledAttributes(attrs,
+ android.R.styleable.ContactsDataKind);
+ try {
+ // See if a string has been hardcoded directly into the xml
+ final String nonResourceString = typedArray.getNonResourceString(
+ android.R.styleable.ContactsDataKind_allContactsName);
+ if (nonResourceString != null) {
+ return nonResourceString;
+ }
+
+ // See if a resource is referenced. We can't rely on getString
+ // to automatically resolve it as the resource lives in a different package
+ int id = typedArray.getResourceId(
+ android.R.styleable.ContactsDataKind_allContactsName, 0);
+ if (id == 0) return null;
+
+ // Resolve the resource Id
+ final PackageManager packageManager = context.getPackageManager();
+ final Resources resources;
+ try {
+ resources = packageManager.getResourcesForApplication(packageName);
+ } catch (NameNotFoundException e) {
+ return null;
+ }
+ try {
+ return resources.getString(id);
+ } catch (NotFoundException e) {
+ return null;
+ }
+ } finally {
+ typedArray.recycle();
+ }
+ }
+ }
+ return null;
+ } catch (XmlPullParserException e) {
+ throw new IllegalStateException("Problem reading XML", e);
+ } catch (IOException e) {
+ throw new IllegalStateException("Problem reading XML", e);
+ }
+ }
+}
diff --git a/src/com/android/contacts/util/PhoneCapabilityTester.java b/src/com/android/contacts/util/PhoneCapabilityTester.java
new file mode 100644
index 0000000..961e3c9
--- /dev/null
+++ b/src/com/android/contacts/util/PhoneCapabilityTester.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 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.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.net.sip.SipManager;
+import android.telephony.TelephonyManager;
+
+import java.util.List;
+
+/**
+ * Provides static functions to quickly test the capabilities of this device. The static
+ * members are not safe for threading
+ */
+public final class PhoneCapabilityTester {
+ private static boolean sIsInitialized;
+ private static boolean sIsPhone;
+ private static boolean sIsSipPhone;
+
+ /**
+ * Tests whether the Intent has a receiver registered. This can be used to show/hide
+ * functionality (like Phone, SMS)
+ */
+ public static boolean isIntentRegistered(Context context, Intent intent) {
+ final PackageManager packageManager = context.getPackageManager();
+ final List<ResolveInfo> receiverList = packageManager.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ return receiverList.size() > 0;
+ }
+
+ /**
+ * Returns true if this device can be used to make phone calls
+ */
+ public static boolean isPhone(Context context) {
+ if (!sIsInitialized) initialize(context);
+ // Is the device physically capabable of making phone calls?
+ return sIsPhone;
+ }
+
+ private static void initialize(Context context) {
+ final TelephonyManager telephonyManager = new TelephonyManager(context);
+ sIsPhone = telephonyManager.isVoiceCapable();
+ sIsSipPhone = SipManager.isVoipSupported(context);
+ sIsInitialized = true;
+ }
+
+ /**
+ * Returns true if this device can be used to make sip calls
+ */
+ public static boolean isSipPhone(Context context) {
+ if (!sIsInitialized) initialize(context);
+ return sIsSipPhone;
+ }
+
+ /**
+ * Returns true if the device has an SMS application installed.
+ */
+ public static boolean isSmsIntentRegistered(Context context) {
+ // Don't cache the result as the user might install third party apps to send SMS
+ final Intent intent = new Intent(Intent.ACTION_SENDTO,
+ Uri.fromParts(Constants.SCHEME_SMSTO, "", null));
+ return isIntentRegistered(context, intent);
+ }
+}
diff --git a/src/com/android/contacts/util/PhonebookCollatorFactory.java b/src/com/android/contacts/util/PhonebookCollatorFactory.java
new file mode 100644
index 0000000..08ce27a
--- /dev/null
+++ b/src/com/android/contacts/util/PhonebookCollatorFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2010 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.util;
+
+import java.text.Collator;
+import java.util.Locale;
+
+/**
+ * Returns the collator that can be used to sort contact list entries. This
+ * collator is the same as the one that is used in sqlite.
+ */
+public final class PhonebookCollatorFactory {
+ public static final Collator getCollator() {
+ final Locale defaultLocale = Locale.getDefault();
+ final String defaultLocaleString = defaultLocale.toString();
+ // For Japanese we use a special collator that puts japanese characters before foreign
+ // ones (this is called a dictionary collator)
+ // Warning: This function has to match the behavior in sqlite3_android.cpp (located in
+ // the framework)
+ final Locale locale;
+ if ("ja".equals(defaultLocaleString) || "ja_JP".equals(defaultLocaleString)) {
+ locale = new Locale("ja@collation=phonebook");
+ } else {
+ locale = defaultLocale;
+ }
+
+ return Collator.getInstance(locale);
+ }
+}
diff --git a/src/com/android/contacts/util/ThemeUtils.java b/src/com/android/contacts/util/ThemeUtils.java
new file mode 100644
index 0000000..d1784b6
--- /dev/null
+++ b/src/com/android/contacts/util/ThemeUtils.java
@@ -0,0 +1,32 @@
+package com.android.contacts.util;
+
+import android.content.res.Resources.Theme;
+import android.util.TypedValue;
+
+/**
+ * Provides static functions to more easily resolve attributes of the current theme
+ */
+public class ThemeUtils {
+ /**
+ * Resolves the given attribute id of the theme to a resource id
+ */
+ public static int getAttribute(Theme theme, int attrId) {
+ final TypedValue outValue = new TypedValue();
+ theme.resolveAttribute(attrId, outValue, true);
+ return outValue.resourceId;
+ }
+
+ /**
+ * Returns the resource id of the background used for buttons to show pressed and focused state
+ */
+ public static int getSelectableItemBackground(Theme theme) {
+ return getAttribute(theme, android.R.attr.selectableItemBackground);
+ }
+
+ /**
+ * Returns the resource id of the background used for list items that show activated background
+ */
+ public static int getActivatedBackground(Theme theme) {
+ return getAttribute(theme, android.R.attr.activatedBackgroundIndicator);
+ }
+}
diff --git a/src/com/android/contacts/vcard/CancelActivity.java b/src/com/android/contacts/vcard/CancelActivity.java
new file mode 100644
index 0000000..b1428bb
--- /dev/null
+++ b/src/com/android/contacts/vcard/CancelActivity.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 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.vcard;
+
+import com.android.contacts.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * The Activity for canceling vCard import/export.
+ */
+public class CancelActivity extends Activity implements ServiceConnection {
+ private final String LOG_TAG = "VCardCancel";
+
+ /* package */ final static String JOB_ID = "job_id";
+ /* package */ final static String DISPLAY_NAME = "display_name";
+
+ /**
+ * Type of the process to be canceled. Only used for choosing appropriate title/message.
+ * Must be {@link VCardService#TYPE_IMPORT} or {@link VCardService#TYPE_EXPORT}.
+ */
+ /* package */ final static String TYPE = "type";
+
+ private class RequestCancelListener implements DialogInterface.OnClickListener {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ bindService(new Intent(CancelActivity.this,
+ VCardService.class), CancelActivity.this, Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ private class CancelListener
+ implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ }
+
+ private final CancelListener mCancelListener = new CancelListener();
+ private int mJobId;
+ private String mDisplayName;
+ private int mType;
+ private Messenger mMessenger;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final Uri uri = getIntent().getData();
+ mJobId = Integer.parseInt(uri.getQueryParameter(JOB_ID));
+ mDisplayName = uri.getQueryParameter(DISPLAY_NAME);
+ mType = Integer.parseInt(uri.getQueryParameter(TYPE));
+ showDialog(R.id.dialog_cancel_confirmation);
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id, Bundle bundle) {
+ switch (id) {
+ case R.id.dialog_cancel_confirmation: {
+ final String title;
+ final String message;
+ if (mType == VCardService.TYPE_IMPORT) {
+ title = getString(R.string.cancel_import_confirmation_title);
+ message = getString(R.string.cancel_import_confirmation_message, mDisplayName);
+ } else {
+ title = getString(R.string.cancel_export_confirmation_title);
+ message = getString(R.string.cancel_export_confirmation_message, mDisplayName);
+ }
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(title)
+ .setMessage(message)
+ .setPositiveButton(android.R.string.ok, new RequestCancelListener())
+ .setOnCancelListener(mCancelListener)
+ .setNegativeButton(android.R.string.cancel, mCancelListener);
+ return builder.create();
+ }
+ case R.id.dialog_cancel_failed:
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(R.string.cancel_vcard_import_or_export_failed)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setMessage(getString(R.string.fail_reason_unknown))
+ .setOnCancelListener(mCancelListener)
+ .setPositiveButton(android.R.string.ok, mCancelListener);
+ return builder.create();
+ default:
+ Log.w(LOG_TAG, "Unknown dialog id: " + id);
+ break;
+ }
+ return super.onCreateDialog(id, bundle);
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mMessenger = new Messenger(service);
+
+ boolean callFinish = false;
+ try {
+ final CancelRequest request = new CancelRequest(mJobId, mDisplayName);
+ mMessenger.send(Message.obtain(null, VCardService.MSG_CANCEL_REQUEST, request));
+ callFinish = true;
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "RemoteException is thrown when trying to send request");
+ showDialog(R.id.dialog_cancel_failed);
+ // finish() should be called from the Dialog
+ } finally {
+ unbindService(this);
+ }
+
+ if (callFinish) {
+ finish();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mMessenger = null;
+ }
+}
diff --git a/src/com/android/contacts/vcard/CancelRequest.java b/src/com/android/contacts/vcard/CancelRequest.java
new file mode 100644
index 0000000..85893ba
--- /dev/null
+++ b/src/com/android/contacts/vcard/CancelRequest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2010 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.vcard;
+
+/**
+ * Class representing one request for canceling vCard import/export.
+ */
+public class CancelRequest {
+ public final int jobId;
+ /**
+ * Name used for showing users some useful info. Typically a file name.
+ * Must not be used to do some actual operations.
+ */
+ public final String displayName;
+ public CancelRequest(int jobId, String displayName) {
+ this.jobId = jobId;
+ this.displayName = displayName;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/vcard/ExportProcessor.java b/src/com/android/contacts/vcard/ExportProcessor.java
new file mode 100644
index 0000000..c5f293b
--- /dev/null
+++ b/src/com/android/contacts/vcard/ExportProcessor.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2010 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.vcard;
+
+import com.android.contacts.R;
+import com.android.contacts.activities.ContactBrowserActivity;
+import com.android.vcard.VCardComposer;
+import com.android.vcard.VCardConfig;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import java.io.FileNotFoundException;
+import java.io.OutputStream;
+
+/**
+ * Class for processing one export request from a user. Dropped after exporting requested Uri(s).
+ * {@link VCardService} will create another object when there is another export request.
+ */
+public class ExportProcessor extends ProcessorBase {
+ private static final String LOG_TAG = "VCardExport";
+ private static final boolean DEBUG = VCardService.DEBUG;
+
+ private final VCardService mService;
+ private final ContentResolver mResolver;
+ private final NotificationManager mNotificationManager;
+ private final ExportRequest mExportRequest;
+ private final int mJobId;
+
+ private volatile boolean mCanceled;
+ private volatile boolean mDone;
+
+ public ExportProcessor(VCardService service, ExportRequest exportRequest, int jobId) {
+ mService = service;
+ mResolver = service.getContentResolver();
+ mNotificationManager =
+ (NotificationManager)mService.getSystemService(Context.NOTIFICATION_SERVICE);
+ mExportRequest = exportRequest;
+ mJobId = jobId;
+ }
+
+ @Override
+ public final int getType() {
+ return VCardService.TYPE_EXPORT;
+ }
+
+ @Override
+ public void run() {
+ // ExecutorService ignores RuntimeException, so we need to show it here.
+ try {
+ runInternal();
+
+ if (isCancelled()) {
+ doCancelNotification();
+ }
+ } catch (OutOfMemoryError e) {
+ Log.e(LOG_TAG, "OutOfMemoryError thrown during import", e);
+ throw e;
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "RuntimeException thrown during export", e);
+ throw e;
+ } finally {
+ synchronized (this) {
+ mDone = true;
+ }
+ }
+ }
+
+ private void runInternal() {
+ if (DEBUG) Log.d(LOG_TAG, String.format("vCard export (id: %d) has started.", mJobId));
+ final ExportRequest request = mExportRequest;
+ VCardComposer composer = null;
+ boolean successful = false;
+ try {
+ if (isCancelled()) {
+ Log.i(LOG_TAG, "Export request is cancelled before handling the request");
+ return;
+ }
+ final Uri uri = request.destUri;
+ final OutputStream outputStream;
+ try {
+ outputStream = mResolver.openOutputStream(uri);
+ } catch (FileNotFoundException e) {
+ Log.w(LOG_TAG, "FileNotFoundException thrown", e);
+ // Need concise title.
+
+ final String errorReason =
+ mService.getString(R.string.fail_reason_could_not_open_file,
+ uri, e.getMessage());
+ doFinishNotification(errorReason, "");
+ return;
+ }
+
+ final String exportType = request.exportType;
+ final int vcardType;
+ if (TextUtils.isEmpty(exportType)) {
+ vcardType = VCardConfig.getVCardTypeFromString(
+ mService.getString(R.string.config_export_vcard_type));
+ } else {
+ vcardType = VCardConfig.getVCardTypeFromString(exportType);
+ }
+
+ composer = new VCardComposer(mService, vcardType, true);
+
+ // for test
+ // int vcardType = (VCardConfig.VCARD_TYPE_V21_GENERIC |
+ // VCardConfig.FLAG_USE_QP_TO_PRIMARY_PROPERTIES);
+ // composer = new VCardComposer(ExportVCardActivity.this, vcardType, true);
+
+ composer.addHandler(composer.new HandlerForOutputStream(outputStream));
+
+ if (!composer.init()) {
+ final String errorReason = composer.getErrorReason();
+ Log.e(LOG_TAG, "initialization of vCard composer failed: " + errorReason);
+ final String translatedErrorReason =
+ translateComposerError(errorReason);
+ final String title =
+ mService.getString(R.string.fail_reason_could_not_initialize_exporter,
+ translatedErrorReason);
+ doFinishNotification(title, "");
+ return;
+ }
+
+ final int total = composer.getCount();
+ if (total == 0) {
+ final String title =
+ mService.getString(R.string.fail_reason_no_exportable_contact);
+ doFinishNotification(title, "");
+ return;
+ }
+
+ int current = 1; // 1-origin
+ while (!composer.isAfterLast()) {
+ if (isCancelled()) {
+ Log.i(LOG_TAG, "Export request is cancelled during composing vCard");
+ return;
+ }
+ if (!composer.createOneEntry()) {
+ final String errorReason = composer.getErrorReason();
+ Log.e(LOG_TAG, "Failed to read a contact: " + errorReason);
+ final String translatedErrorReason =
+ translateComposerError(errorReason);
+ final String title =
+ mService.getString(R.string.fail_reason_error_occurred_during_export,
+ translatedErrorReason);
+ doFinishNotification(title, "");
+ return;
+ }
+
+ // vCard export is quite fast (compared to import), and frequent notifications
+ // bother notification bar too much.
+ if (current % 100 == 1) {
+ doProgressNotification(uri, total, current);
+ }
+ current++;
+ }
+ Log.i(LOG_TAG, "Successfully finished exporting vCard " + request.destUri);
+
+ successful = true;
+ final String filename = uri.getLastPathSegment();
+ final String title = mService.getString(R.string.exporting_vcard_finished_title,
+ filename);
+ doFinishNotification(title, "");
+ } finally {
+ if (composer != null) {
+ composer.terminate();
+ }
+
+ mService.handleFinishExportNotification(mJobId, successful);
+ }
+ }
+
+ private String translateComposerError(String errorMessage) {
+ final Resources resources = mService.getResources();
+ if (VCardComposer.FAILURE_REASON_FAILED_TO_GET_DATABASE_INFO.equals(errorMessage)) {
+ return resources.getString(R.string.composer_failed_to_get_database_infomation);
+ } else if (VCardComposer.FAILURE_REASON_NO_ENTRY.equals(errorMessage)) {
+ return resources.getString(R.string.composer_has_no_exportable_contact);
+ } else if (VCardComposer.FAILURE_REASON_NOT_INITIALIZED.equals(errorMessage)) {
+ return resources.getString(R.string.composer_not_initialized);
+ } else {
+ return errorMessage;
+ }
+ }
+
+ private void doProgressNotification(Uri uri, int totalCount, int currentCount) {
+ final String displayName = uri.getLastPathSegment();
+ final String description =
+ mService.getString(R.string.exporting_contact_list_message, displayName);
+ final String tickerText =
+ mService.getString(R.string.exporting_contact_list_title);
+ final Notification notification =
+ VCardService.constructProgressNotification(mService, VCardService.TYPE_EXPORT,
+ description, tickerText, mJobId, displayName, totalCount, currentCount);
+ mNotificationManager.notify(mJobId, notification);
+ }
+
+ private void doCancelNotification() {
+ if (DEBUG) Log.d(LOG_TAG, "send cancel notification");
+ final String description = mService.getString(R.string.exporting_vcard_canceled_title,
+ mExportRequest.destUri.getLastPathSegment());
+ final Notification notification =
+ VCardService.constructCancelNotification(mService, description);
+ mNotificationManager.notify(mJobId, notification);
+ }
+
+ private void doFinishNotification(final String title, final String description) {
+ if (DEBUG) Log.d(LOG_TAG, "send finish notification: " + title + ", " + description);
+ final Intent intent = new Intent(mService, ContactBrowserActivity.class);
+ final Notification notification =
+ VCardService.constructFinishNotification(mService, title, description, intent);
+ mNotificationManager.notify(mJobId, notification);
+ }
+
+ @Override
+ public synchronized boolean cancel(boolean mayInterruptIfRunning) {
+ if (DEBUG) Log.d(LOG_TAG, "received cancel request");
+ if (mDone || mCanceled) {
+ return false;
+ }
+ mCanceled = true;
+ return true;
+ }
+
+ @Override
+ public synchronized boolean isCancelled() {
+ return mCanceled;
+ }
+
+ @Override
+ public synchronized boolean isDone() {
+ return mDone;
+ }
+
+ public ExportRequest getRequest() {
+ return mExportRequest;
+ }
+}
diff --git a/src/com/android/contacts/vcard/ExportRequest.java b/src/com/android/contacts/vcard/ExportRequest.java
new file mode 100644
index 0000000..fae2d07
--- /dev/null
+++ b/src/com/android/contacts/vcard/ExportRequest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010 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.vcard;
+
+import android.net.Uri;
+
+public class ExportRequest {
+ public final Uri destUri;
+ /**
+ * Can be null.
+ */
+ public final String exportType;
+
+ public ExportRequest(Uri destUri) {
+ this(destUri, null);
+ }
+
+ public ExportRequest(Uri destUri, String exportType) {
+ this.destUri = destUri;
+ this.exportType = exportType;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/vcard/ExportVCardActivity.java b/src/com/android/contacts/vcard/ExportVCardActivity.java
new file mode 100644
index 0000000..8bd9899
--- /dev/null
+++ b/src/com/android/contacts/vcard/ExportVCardActivity.java
@@ -0,0 +1,309 @@
+/*
+ * 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.vcard;
+
+import com.android.contacts.R;
+import com.android.vcard.VCardComposer;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.File;
+
+/**
+ * Shows a dialog confirming the export and asks actual vCard export to {@link VCardService}
+ *
+ * This Activity first connects to VCardService and ask an available file name and shows it to
+ * a user. After the user's confirmation, it send export request with the file name, assuming the
+ * file name is not reserved yet.
+ */
+public class ExportVCardActivity extends Activity implements ServiceConnection,
+ DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
+ private static final String LOG_TAG = "VCardExport";
+ private static final boolean DEBUG = VCardService.DEBUG;
+
+ /**
+ * Handler used when some Message has come from {@link VCardService}.
+ */
+ private class IncomingHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ if (DEBUG) Log.d(LOG_TAG, "IncomingHandler received message.");
+
+ if (msg.arg1 != 0) {
+ Log.i(LOG_TAG, "Message returned from vCard server contains error code.");
+ if (msg.obj != null) {
+ mErrorReason = (String)msg.obj;
+ }
+ showDialog(msg.arg1);
+ return;
+ }
+
+ switch (msg.what) {
+ case VCardService.MSG_SET_AVAILABLE_EXPORT_DESTINATION:
+ if (msg.obj == null) {
+ Log.w(LOG_TAG, "Message returned from vCard server doesn't contain valid path");
+ mErrorReason = getString(R.string.fail_reason_unknown);
+ showDialog(R.id.dialog_fail_to_export_with_reason);
+ } else {
+ mTargetFileName = (String)msg.obj;
+ if (TextUtils.isEmpty(mTargetFileName)) {
+ Log.w(LOG_TAG, "Destination file name coming from vCard service is empty.");
+ mErrorReason = getString(R.string.fail_reason_unknown);
+ showDialog(R.id.dialog_fail_to_export_with_reason);
+ } else {
+ if (DEBUG) {
+ Log.d(LOG_TAG,
+ String.format("Target file name is set (%s). " +
+ "Show confirmation dialog", mTargetFileName));
+ }
+ showDialog(R.id.dialog_export_confirmation);
+ }
+ }
+ break;
+ default:
+ Log.w(LOG_TAG, "Unknown message type: " + msg.what);
+ super.handleMessage(msg);
+ }
+ }
+ }
+
+ /**
+ * True when this Activity is connected to {@link VCardService}.
+ *
+ * Should be touched inside synchronized block.
+ */
+ private boolean mConnected;
+
+ /**
+ * True when users need to do something and this Activity should not disconnect from
+ * VCardService. False when all necessary procedures are done (including sending export request)
+ * or there's some error occured.
+ */
+ private volatile boolean mProcessOngoing = true;
+
+ private Messenger mOutgoingMessenger;
+ private final Messenger mIncomingMessenger = new Messenger(new IncomingHandler());
+
+ // Used temporarily when asking users to confirm the file name
+ private String mTargetFileName;
+
+ // String for storing error reason temporarily.
+ private String mErrorReason;
+
+ private class ExportConfirmationListener implements DialogInterface.OnClickListener {
+ private final Uri mDestinationUri;
+
+ public ExportConfirmationListener(String path) {
+ this(Uri.parse("file://" + path));
+ }
+
+ public ExportConfirmationListener(Uri uri) {
+ mDestinationUri = uri;
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ if (DEBUG) {
+ Log.d(LOG_TAG,
+ String.format("Try sending export request (uri: %s)", mDestinationUri));
+ }
+ final ExportRequest request = new ExportRequest(mDestinationUri);
+ // The connection object will call finish().
+ if (trySend(Message.obtain(null, VCardService.MSG_EXPORT_REQUEST, request))) {
+ Log.i(LOG_TAG, "Successfully sent export request. Finish itself");
+ unbindAndFinish();
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ // Check directory is available.
+ final File targetDirectory = new File(getString(R.string.config_export_dir));
+ if (!(targetDirectory.exists() &&
+ targetDirectory.isDirectory() &&
+ targetDirectory.canRead()) &&
+ !targetDirectory.mkdirs()) {
+ showDialog(R.id.dialog_sdcard_not_found);
+ return;
+ }
+
+ if (startService(new Intent(this, VCardService.class)) == null) {
+ Log.e(LOG_TAG, "Failed to start vCard service");
+ mErrorReason = getString(R.string.fail_reason_unknown);
+ showDialog(R.id.dialog_fail_to_export_with_reason);
+ return;
+ }
+
+ if (!bindService(new Intent(this, VCardService.class), this, Context.BIND_AUTO_CREATE)) {
+ Log.e(LOG_TAG, "Failed to connect to vCard service.");
+ mErrorReason = getString(R.string.fail_reason_unknown);
+ showDialog(R.id.dialog_fail_to_export_with_reason);
+ }
+ // Continued to onServiceConnected()
+ }
+
+ @Override
+ public synchronized void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG) Log.d(LOG_TAG, "connected to service, requesting a destination file name");
+ mConnected = true;
+ mOutgoingMessenger = new Messenger(service);
+ final Message message =
+ Message.obtain(null, VCardService.MSG_REQUEST_AVAILABLE_EXPORT_DESTINATION);
+ message.replyTo = mIncomingMessenger;
+ trySend(message);
+ // Wait until MSG_SET_AVAILABLE_EXPORT_DESTINATION message is available.
+ }
+
+ // Use synchronized since we don't want to call unbindAndFinish() just after this call.
+ @Override
+ public synchronized void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) Log.d(LOG_TAG, "onServiceDisconnected()");
+ mOutgoingMessenger = null;
+ mConnected = false;
+ if (mProcessOngoing) {
+ // Unexpected disconnect event.
+ Log.w(LOG_TAG, "Disconnected from service during the process ongoing.");
+ mErrorReason = getString(R.string.fail_reason_unknown);
+ showDialog(R.id.dialog_fail_to_export_with_reason);
+ }
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id, Bundle bundle) {
+ switch (id) {
+ case R.id.dialog_export_confirmation: {
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.confirm_export_title)
+ .setMessage(getString(R.string.confirm_export_message, mTargetFileName))
+ .setPositiveButton(android.R.string.ok,
+ new ExportConfirmationListener(mTargetFileName))
+ .setNegativeButton(android.R.string.cancel, this)
+ .setOnCancelListener(this)
+ .create();
+ }
+ case R.string.fail_reason_too_many_vcard: {
+ mProcessOngoing = false;
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.exporting_contact_failed_title)
+ .setMessage(getString(R.string.exporting_contact_failed_message,
+ getString(R.string.fail_reason_too_many_vcard)))
+ .setPositiveButton(android.R.string.ok, this)
+ .create();
+ }
+ case R.id.dialog_fail_to_export_with_reason: {
+ mProcessOngoing = false;
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.exporting_contact_failed_title)
+ .setMessage(getString(R.string.exporting_contact_failed_message,
+ mErrorReason != null ? mErrorReason :
+ getString(R.string.fail_reason_unknown)))
+ .setPositiveButton(android.R.string.ok, this)
+ .setOnCancelListener(this)
+ .create();
+ }
+ case R.id.dialog_sdcard_not_found: {
+ mProcessOngoing = false;
+ return new AlertDialog.Builder(this)
+ .setTitle(R.string.no_sdcard_title)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setMessage(R.string.no_sdcard_message)
+ .setPositiveButton(android.R.string.ok, this).create();
+ }
+ }
+ return super.onCreateDialog(id, bundle);
+ }
+
+ @Override
+ protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
+ if (id == R.id.dialog_fail_to_export_with_reason) {
+ ((AlertDialog)dialog).setMessage(mErrorReason);
+ } else if (id == R.id.dialog_export_confirmation) {
+ ((AlertDialog)dialog).setMessage(
+ getString(R.string.confirm_export_message, mTargetFileName));
+ } else {
+ super.onPrepareDialog(id, dialog, args);
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ if (!isFinishing()) {
+ unbindAndFinish();
+ }
+ }
+
+ private boolean trySend(Message message) {
+ try {
+ mOutgoingMessenger.send(message);
+ return true;
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "RemoteException is thrown when trying to send request");
+ unbindService(this);
+ mErrorReason = getString(R.string.fail_reason_unknown);
+ showDialog(R.id.dialog_fail_to_export_with_reason);
+ return false;
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onClick() is called");
+ unbindAndFinish();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ if (DEBUG) Log.d(LOG_TAG, "ExportVCardActivity#onCancel() is called");
+ mProcessOngoing = false;
+ unbindAndFinish();
+ }
+
+ @Override
+ public void unbindService(ServiceConnection conn) {
+ mProcessOngoing = false;
+ super.unbindService(conn);
+ }
+
+ private synchronized void unbindAndFinish() {
+ if (mConnected) {
+ unbindService(this);
+ mConnected = false;
+ }
+ finish();
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/vcard/ImportProcessor.java b/src/com/android/contacts/vcard/ImportProcessor.java
new file mode 100644
index 0000000..1b70025
--- /dev/null
+++ b/src/com/android/contacts/vcard/ImportProcessor.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2010 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.vcard;
+
+import com.android.contacts.R;
+import com.android.vcard.VCardEntryCommitter;
+import com.android.vcard.VCardEntryConstructor;
+import com.android.vcard.VCardInterpreter;
+import com.android.vcard.VCardParser;
+import com.android.vcard.VCardParser_V21;
+import com.android.vcard.VCardParser_V30;
+import com.android.vcard.exception.VCardException;
+import com.android.vcard.exception.VCardNestedException;
+import com.android.vcard.exception.VCardNotSupportedException;
+import com.android.vcard.exception.VCardVersionException;
+
+import android.accounts.Account;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.ContactsContract.RawContacts;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class for processing one import request from a user. Dropped after importing requested Uri(s).
+ * {@link VCardService} will create another object when there is another import request.
+ */
+public class ImportProcessor extends ProcessorBase {
+ private static final String LOG_TAG = "VCardImport";
+ private static final boolean DEBUG = VCardService.DEBUG;
+
+ private final VCardService mService;
+ private final ContentResolver mResolver;
+ private final NotificationManager mNotificationManager;
+ private final ImportRequest mImportRequest;
+ private final int mJobId;
+
+ private final ImportProgressNotifier mNotifier;
+
+ // TODO: remove and show appropriate message instead.
+ private final List<Uri> mFailedUris = new ArrayList<Uri>();
+
+ private VCardParser mVCardParser;
+
+ private volatile boolean mCanceled;
+ private volatile boolean mDone;
+
+ public ImportProcessor(final VCardService service, final ImportRequest request,
+ final int jobId) {
+ mService = service;
+ mResolver = mService.getContentResolver();
+ mNotificationManager = (NotificationManager)
+ mService.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ mImportRequest = request;
+ mJobId = jobId;
+ mNotifier = new ImportProgressNotifier(service, mNotificationManager, jobId,
+ request.originalUri.getLastPathSegment());
+ }
+
+ @Override
+ public final int getType() {
+ return VCardService.TYPE_IMPORT;
+ }
+
+ @Override
+ public void run() {
+ // ExecutorService ignores RuntimeException, so we need to show it here.
+ try {
+ runInternal();
+
+ if (isCancelled()) {
+ doCancelNotification();
+ }
+ } catch (OutOfMemoryError e) {
+ Log.e(LOG_TAG, "OutOfMemoryError thrown during import", e);
+ throw e;
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "RuntimeException thrown during import", e);
+ throw e;
+ } finally {
+ synchronized (this) {
+ mDone = true;
+ }
+ }
+ }
+
+ private void runInternal() {
+ Log.i(LOG_TAG, String.format("vCard import (id: %d) has started.", mJobId));
+ final ImportRequest request = mImportRequest;
+ if (isCancelled()) {
+ Log.i(LOG_TAG, "Canceled before actually handling parameter (" + request.uri + ")");
+ return;
+ }
+ final int[] possibleVCardVersions;
+ if (request.vcardVersion == ImportVCardActivity.VCARD_VERSION_AUTO_DETECT) {
+ /**
+ * Note: this code assumes that a given Uri is able to be opened more than once,
+ * which may not be true in certain conditions.
+ */
+ possibleVCardVersions = new int[] {
+ ImportVCardActivity.VCARD_VERSION_V21,
+ ImportVCardActivity.VCARD_VERSION_V30
+ };
+ } else {
+ possibleVCardVersions = new int[] {
+ request.vcardVersion
+ };
+ }
+
+ final Uri uri = request.uri;
+ final Account account = request.account;
+ final int estimatedVCardType = request.estimatedVCardType;
+ final String estimatedCharset = request.estimatedCharset;
+ final int entryCount = request.entryCount;
+ mNotifier.addTotalCount(entryCount);
+
+ final VCardEntryConstructor constructor =
+ new VCardEntryConstructor(estimatedVCardType, account, estimatedCharset);
+ final VCardEntryCommitter committer = new VCardEntryCommitter(mResolver);
+ constructor.addEntryHandler(committer);
+ constructor.addEntryHandler(mNotifier);
+
+ final boolean successful =
+ readOneVCard(uri, estimatedVCardType, estimatedCharset,
+ constructor, possibleVCardVersions);
+
+ mService.handleFinishImportNotification(mJobId, successful);
+
+ if (successful) {
+ // TODO: successful becomes true even when cancelled. Should return more appropriate
+ // value
+ if (isCancelled()) {
+ Log.i(LOG_TAG, "vCard import has been canceled (uri: " + uri + ")");
+ // Cancel notification will be done outside this method.
+ } else {
+ Log.i(LOG_TAG, "Successfully finished importing one vCard file: " + uri);
+ List<Uri> uris = committer.getCreatedUris();
+ if (uris != null && uris.size() > 0) {
+ // TODO: construct intent showing a list of imported contact list.
+ doFinishNotification(uris.get(0));
+ } else {
+ // Not critical, but suspicious.
+ Log.w(LOG_TAG,
+ "Created Uris is null or 0 length " +
+ "though the creation itself is successful.");
+ doFinishNotification(null);
+ }
+ }
+ } else {
+ Log.w(LOG_TAG, "Failed to read one vCard file: " + uri);
+ mFailedUris.add(uri);
+ }
+ }
+
+ private void doCancelNotification() {
+ final String description = mService.getString(R.string.importing_vcard_canceled_title,
+ mImportRequest.originalUri.getLastPathSegment());
+ final Notification notification =
+ VCardService.constructCancelNotification(mService, description);
+ mNotificationManager.notify(mJobId, notification);
+ }
+
+ private void doFinishNotification(final Uri createdUri) {
+ final String description = mService.getString(R.string.importing_vcard_finished_title,
+ mImportRequest.originalUri.getLastPathSegment());
+ final Intent intent;
+ if (createdUri != null) {
+ final long rawContactId = ContentUris.parseId(createdUri);
+ final Uri contactUri = RawContacts.getContactLookupUri(
+ mResolver, ContentUris.withAppendedId(
+ RawContacts.CONTENT_URI, rawContactId));
+ intent = new Intent(Intent.ACTION_VIEW, contactUri);
+ } else {
+ intent = null;
+ }
+ final Notification notification =
+ VCardService.constructFinishNotification(mService,
+ description, description, intent);
+ mNotificationManager.notify(mJobId, notification);
+ }
+
+ private boolean readOneVCard(Uri uri, int vcardType, String charset,
+ final VCardInterpreter interpreter,
+ final int[] possibleVCardVersions) {
+ Log.i(LOG_TAG, "start importing one vCard (Uri: " + uri + ")");
+ boolean successful = false;
+ final int length = possibleVCardVersions.length;
+ for (int i = 0; i < length; i++) {
+ InputStream is = null;
+ final int vcardVersion = possibleVCardVersions[i];
+ try {
+ if (i > 0 && (interpreter instanceof VCardEntryConstructor)) {
+ // Let the object clean up internal temporary objects,
+ ((VCardEntryConstructor) interpreter).clear();
+ }
+
+ is = mResolver.openInputStream(uri);
+
+ // We need synchronized block here,
+ // since we need to handle mCanceled and mVCardParser at once.
+ // In the worst case, a user may call cancel() just before creating
+ // mVCardParser.
+ synchronized (this) {
+ mVCardParser = (vcardVersion == ImportVCardActivity.VCARD_VERSION_V30 ?
+ new VCardParser_V30(vcardType) :
+ new VCardParser_V21(vcardType));
+ if (isCancelled()) {
+ Log.i(LOG_TAG, "ImportProcessor already recieves cancel request, so " +
+ "send cancel request to vCard parser too.");
+ mVCardParser.cancel();
+ }
+ }
+ mVCardParser.parse(is, interpreter);
+
+ successful = true;
+ break;
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "IOException was emitted: " + e.getMessage());
+ } catch (VCardNestedException e) {
+ // This exception should not be thrown here. We should instead handle it
+ // in the preprocessing session in ImportVCardActivity, as we don't try
+ // to detect the type of given vCard here.
+ //
+ // TODO: Handle this case appropriately, which should mean we have to have
+ // code trying to auto-detect the type of given vCard twice (both in
+ // ImportVCardActivity and ImportVCardService).
+ Log.e(LOG_TAG, "Nested Exception is found.");
+ } catch (VCardNotSupportedException e) {
+ Log.e(LOG_TAG, e.getMessage());
+ } catch (VCardVersionException e) {
+ if (i == length - 1) {
+ Log.e(LOG_TAG, "Appropriate version for this vCard is not found.");
+ } else {
+ // We'll try the other (v30) version.
+ }
+ } catch (VCardException e) {
+ Log.e(LOG_TAG, e.getMessage());
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ return successful;
+ }
+
+ @Override
+ public synchronized boolean cancel(boolean mayInterruptIfRunning) {
+ if (DEBUG) Log.d(LOG_TAG, "ImportProcessor received cancel request");
+ if (mDone || mCanceled) {
+ return false;
+ }
+ mCanceled = true;
+ synchronized (this) {
+ if (mVCardParser != null) {
+ mVCardParser.cancel();
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public synchronized boolean isCancelled() {
+ return mCanceled;
+ }
+
+
+ @Override
+ public synchronized boolean isDone() {
+ return mDone;
+ }
+}
diff --git a/src/com/android/contacts/vcard/ImportProgressNotifier.java b/src/com/android/contacts/vcard/ImportProgressNotifier.java
new file mode 100644
index 0000000..d6d0f3f
--- /dev/null
+++ b/src/com/android/contacts/vcard/ImportProgressNotifier.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 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.vcard;
+
+import com.android.contacts.R;
+import com.android.vcard.VCardEntry;
+import com.android.vcard.VCardEntryHandler;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+
+/**
+ * {@link VCardEntryHandler} implementation letting the system update the current status of
+ * vCard import.
+ */
+public class ImportProgressNotifier implements VCardEntryHandler {
+ private static final String LOG_TAG = "VCardImport";
+
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+ private final int mJobId;
+ private final String mDisplayName;
+
+ private int mCurrentCount;
+ private int mTotalCount;
+
+ public ImportProgressNotifier(
+ Context context, NotificationManager notificationManager,
+ int jobId, String displayName) {
+ mContext = context;
+ mNotificationManager = notificationManager;
+ mJobId = jobId;
+ mDisplayName = displayName;
+ }
+
+ public void onStart() {
+ }
+
+ public void onEntryCreated(VCardEntry contactStruct) {
+ mCurrentCount++; // 1 origin.
+ if (contactStruct.isIgnorable()) {
+ return;
+ }
+
+ final String totalCountString;
+ synchronized (this) {
+ totalCountString = String.valueOf(mTotalCount);
+ }
+ final String tickerText =
+ mContext.getString(R.string.progress_notifier_message,
+ String.valueOf(mCurrentCount),
+ totalCountString,
+ contactStruct.getDisplayName());
+ final String description = mContext.getString(R.string.importing_vcard_description,
+ contactStruct.getDisplayName());
+
+ final Notification notification = VCardService.constructProgressNotification(
+ mContext.getApplicationContext(), VCardService.TYPE_IMPORT, description, tickerText,
+ mJobId, mDisplayName, mTotalCount, mCurrentCount);
+ mNotificationManager.notify(mJobId, notification);
+ }
+
+ public synchronized void addTotalCount(int additionalCount) {
+ mTotalCount += additionalCount;
+ }
+
+ public void onEnd() {
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/vcard/ImportRequest.java b/src/com/android/contacts/vcard/ImportRequest.java
new file mode 100644
index 0000000..e8b5606
--- /dev/null
+++ b/src/com/android/contacts/vcard/ImportRequest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 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.vcard;
+
+import android.accounts.Account;
+import android.net.Uri;
+
+import com.android.vcard.VCardSourceDetector;
+
+/**
+ * Class representing one request for importing vCard (given as a Uri).
+ *
+ * Mainly used when {@link ImportVCardActivity} requests {@link VCardService}
+ * to import some specific Uri.
+ *
+ * Note: This object's accepting only One Uri does NOT mean that
+ * there's only one vCard entry inside the instance, as one Uri often has multiple
+ * vCard entries inside it.
+ */
+public class ImportRequest {
+ /**
+ * Can be null (typically when there's no Account available in the system).
+ */
+ public final Account account;
+ /**
+ * Uri to be imported. May have different content than originally given from users, so
+ * when displaying user-friendly information (e.g. "importing xxx.vcf"), use
+ * {@link #originalUri} instead.
+ */
+ public final Uri uri;
+
+ /**
+ * Original uri given from users.
+ * Useful when showing user-friendly information ("importing xxx.vcf"), as
+ * {@link #uri} may have different name than the original (like "import_tmp_1.vcf").
+ *
+ * This variable must not be used for doing actual processing like re-import, as the app
+ * may not have right permission to do so.
+ */
+ public final Uri originalUri;
+ /**
+ * Can be {@link VCardSourceDetector#PARSE_TYPE_UNKNOWN}.
+ */
+ public final int estimatedVCardType;
+ /**
+ * Can be null, meaning no preferable charset is available.
+ */
+ public final String estimatedCharset;
+ /**
+ * Assumes that one Uri contains only one version, while there's a (tiny) possibility
+ * we may have two types in one vCard.
+ *
+ * e.g.
+ * BEGIN:VCARD
+ * VERSION:2.1
+ * ...
+ * END:VCARD
+ * BEGIN:VCARD
+ * VERSION:3.0
+ * ...
+ * END:VCARD
+ *
+ * We've never seen this kind of a file, but we may have to cope with it in the future.
+ */
+ public final int vcardVersion;
+
+ /**
+ * The count of vCard entries in {@link #uri}. A receiver of this object can use it
+ * when showing the progress of import. Thus a receiver must be able to torelate this
+ * variable being invalid because of vCard's limitation.
+ *
+ * vCard does not let us know this count without looking over a whole file content,
+ * which means we have to open and scan over {@link #uri} to know this value, while
+ * it may not be opened more than once (Uri does not require it to be opened multiple times
+ * and may become invalid after its close() request).
+ */
+ public final int entryCount;
+ public ImportRequest(Account account,
+ Uri uri, Uri originalUri, int estimatedType, String estimatedCharset,
+ int vcardVersion, int entryCount) {
+ this.account = account;
+ this.uri = uri;
+ this.originalUri = originalUri;
+ this.estimatedVCardType = estimatedType;
+ this.estimatedCharset = estimatedCharset;
+ this.vcardVersion = vcardVersion;
+ this.entryCount = entryCount;
+ }
+}
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
new file mode 100644
index 0000000..b3376e9
--- /dev/null
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -0,0 +1,982 @@
+/*
+ * 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.vcard;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.util.AccountSelectionUtil;
+import com.android.vcard.VCardEntryCounter;
+import com.android.vcard.VCardInterpreterCollection;
+import com.android.vcard.VCardParser;
+import com.android.vcard.VCardParser_V21;
+import com.android.vcard.VCardParser_V30;
+import com.android.vcard.VCardSourceDetector;
+import com.android.vcard.exception.VCardException;
+import com.android.vcard.exception.VCardNestedException;
+import com.android.vcard.exception.VCardVersionException;
+
+import android.accounts.Account;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.ProgressDialog;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.RelativeSizeSpan;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.WritableByteChannel;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+/**
+ * The class letting users to import vCard. This includes the UI part for letting them select
+ * an Account and posssibly a file if there's no Uri is given from its caller Activity.
+ *
+ * Note that this Activity assumes that the instance is a "one-shot Activity", which will be
+ * finished (with the method {@link Activity#finish()}) after the import and never reuse
+ * any Dialog in the instance. So this code is careless about the management around managed
+ * dialogs stuffs (like how onCreateDialog() is used).
+ */
+public class ImportVCardActivity extends ContactsActivity {
+ private static final String LOG_TAG = "VCardImport";
+
+ private static final int SELECT_ACCOUNT = 0;
+
+ /* package */ static final String VCARD_URI_ARRAY = "vcard_uri";
+ /* package */ static final String ESTIMATED_VCARD_TYPE_ARRAY = "estimated_vcard_type";
+ /* package */ static final String ESTIMATED_CHARSET_ARRAY = "estimated_charset";
+ /* package */ static final String VCARD_VERSION_ARRAY = "vcard_version";
+ /* package */ static final String ENTRY_COUNT_ARRAY = "entry_count";
+
+ /* package */ final static int VCARD_VERSION_AUTO_DETECT = 0;
+ /* package */ final static int VCARD_VERSION_V21 = 1;
+ /* package */ final static int VCARD_VERSION_V30 = 2;
+
+ private static final String SECURE_DIRECTORY_NAME = ".android_secure";
+
+ /**
+ * Notification id used when error happened before sending an import request to VCardServer.
+ */
+ private static final int DEFAULT_NOTIFICATION_ID = 1000;
+
+ final static String CACHED_URIS = "cached_uris";
+
+ private AccountSelectionUtil.AccountSelectedListener mAccountSelectionListener;
+
+ private Account mAccount;
+
+ private String mAction;
+ private Uri mUri;
+
+ private ProgressDialog mProgressDialogForScanVCard;
+ private ProgressDialog mProgressDialogForCachingVCard;
+
+ private List<VCardFile> mAllVCardFileList;
+ private VCardScanThread mVCardScanThread;
+
+ private VCardCacheThread mVCardCacheThread;
+ private ImportRequestConnection mConnection;
+
+ private String mErrorMessage;
+
+ private Handler mHandler = new Handler();
+
+ private static class VCardFile {
+ private final String mName;
+ private final String mCanonicalPath;
+ private final long mLastModified;
+
+ public VCardFile(String name, String canonicalPath, long lastModified) {
+ mName = name;
+ mCanonicalPath = canonicalPath;
+ mLastModified = lastModified;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getCanonicalPath() {
+ return mCanonicalPath;
+ }
+
+ public long getLastModified() {
+ return mLastModified;
+ }
+ }
+
+ // Runs on the UI thread.
+ private class DialogDisplayer implements Runnable {
+ private final int mResId;
+ public DialogDisplayer(int resId) {
+ mResId = resId;
+ }
+ public DialogDisplayer(String errorMessage) {
+ mResId = R.id.dialog_error_with_message;
+ mErrorMessage = errorMessage;
+ }
+ @Override
+ public void run() {
+ showDialog(mResId);
+ }
+ }
+
+ private class CancelListener
+ implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ }
+
+ private CancelListener mCancelListener = new CancelListener();
+
+ private class ImportRequestConnection implements ServiceConnection {
+ private Messenger mMessenger;
+
+ public void sendImportRequest(final ImportRequest request) {
+ Log.i(LOG_TAG, String.format("Send an import request (Uri: %s)", request.uri));
+ try {
+ mMessenger.send(Message.obtain(null,
+ VCardService.MSG_IMPORT_REQUEST,
+ request));
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "RemoteException is thrown when trying to send request");
+ runOnUiThread(new DialogDisplayer(getString(R.string.fail_reason_unknown)));
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mMessenger = new Messenger(service);
+ Log.i(LOG_TAG,
+ String.format("Connected to VCardService. Kick a vCard cache thread (uri: %s)",
+ Arrays.toString(mVCardCacheThread.getSourceUris())));
+ mVCardCacheThread.start();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.i(LOG_TAG, "Disconnected from VCardService");
+ }
+ }
+
+ /**
+ * Caches given vCard files into a local directory, and sends actual import request to
+ * {@link VCardService}.
+ *
+ * We need to cache given files into local storage. One of reasons is that some data (as Uri)
+ * may have special permissions. Callers may allow only this Activity to access that content,
+ * not what this Activity launched (like {@link VCardService}).
+ */
+ private class VCardCacheThread extends Thread
+ implements DialogInterface.OnCancelListener {
+ private boolean mCanceled;
+ private PowerManager.WakeLock mWakeLock;
+ private VCardParser mVCardParser;
+ private final Uri[] mSourceUris; // Given from a caller.
+
+ public VCardCacheThread(final Uri[] sourceUris) {
+ mSourceUris = sourceUris;
+ final Context context = ImportVCardActivity.this;
+ final PowerManager powerManager =
+ (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(
+ PowerManager.SCREEN_DIM_WAKE_LOCK |
+ PowerManager.ON_AFTER_RELEASE, LOG_TAG);
+ }
+
+ @Override
+ public void finalize() {
+ if (mWakeLock != null && mWakeLock.isHeld()) {
+ Log.w(LOG_TAG, "WakeLock is being held.");
+ mWakeLock.release();
+ }
+ }
+
+ @Override
+ public void run() {
+ Log.i(LOG_TAG, "vCard cache thread starts running.");
+ if (mConnection == null) {
+ throw new NullPointerException("vCard cache thread must be launched "
+ + "after a service connection is established");
+ }
+
+ mWakeLock.acquire();
+ try {
+ if (mCanceled == true) {
+ Log.i(LOG_TAG, "vCard cache operation is canceled.");
+ return;
+ }
+
+ final Context context = ImportVCardActivity.this;
+ // Uris given from caller applications may not be opened twice: consider when
+ // it is not from local storage (e.g. "file:///...") but from some special
+ // provider (e.g. "content://...").
+ // Thus we have to once copy the content of Uri into local storage, and read
+ // it after it.
+ //
+ // We may be able to read content of each vCard file during copying them
+ // to local storage, but currently vCard code does not allow us to do so.
+ int cache_index = 0;
+ for (Uri sourceUri : mSourceUris) {
+ String filename = null;
+ // Note: caches are removed by VCardService.
+ while (true) {
+ filename = VCardService.CACHE_FILE_PREFIX + cache_index + ".vcf";
+ final File file = context.getFileStreamPath(filename);
+ if (!file.exists()) {
+ break;
+ } else {
+ if (cache_index == Integer.MAX_VALUE) {
+ throw new RuntimeException("Exceeded cache limit");
+ }
+ cache_index++;
+ }
+ }
+ final Uri localDataUri = copyTo(sourceUri, filename);
+ if (mCanceled) {
+ Log.i(LOG_TAG, "vCard cache operation is canceled.");
+ break;
+ }
+ if (localDataUri == null) {
+ Log.w(LOG_TAG, "destUri is null");
+ break;
+ }
+ final ImportRequest parameter;
+ try {
+ parameter = constructImportRequest(localDataUri, sourceUri);
+ } catch (VCardException e) {
+ Log.e(LOG_TAG, "Maybe the file is in wrong format", e);
+ showFailureNotification(R.string.fail_reason_not_supported);
+ return;
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Unexpected IOException", e);
+ showFailureNotification(R.string.fail_reason_io_error);
+ return;
+ }
+ if (mCanceled) {
+ Log.i(LOG_TAG, "vCard cache operation is canceled.");
+ return;
+ }
+ mConnection.sendImportRequest(parameter);
+ }
+ } catch (OutOfMemoryError e) {
+ Log.e(LOG_TAG, "OutOfMemoryError occured during caching vCard");
+ System.gc();
+ runOnUiThread(new DialogDisplayer(
+ getString(R.string.fail_reason_low_memory_during_import)));
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "IOException during caching vCard", e);
+ runOnUiThread(new DialogDisplayer(
+ getString(R.string.fail_reason_io_error)));
+ } finally {
+ Log.i(LOG_TAG, "Finished caching vCard.");
+ mWakeLock.release();
+ unbindService(mConnection);
+ mProgressDialogForCachingVCard.dismiss();
+ mProgressDialogForCachingVCard = null;
+ finish();
+ }
+ }
+
+ /**
+ * Copy the content of sourceUri to the destination.
+ */
+ private Uri copyTo(final Uri sourceUri, String filename) throws IOException {
+ Log.i(LOG_TAG, String.format("Copy a Uri to app local storage (%s -> %s)",
+ sourceUri, filename));
+ final Context context = ImportVCardActivity.this;
+ final ContentResolver resolver = context.getContentResolver();
+ ReadableByteChannel inputChannel = null;
+ WritableByteChannel outputChannel = null;
+ Uri destUri = null;
+ try {
+ inputChannel = Channels.newChannel(resolver.openInputStream(sourceUri));
+ destUri = Uri.parse(context.getFileStreamPath(filename).toURI().toString());
+ outputChannel = context.openFileOutput(filename, Context.MODE_PRIVATE).getChannel();
+ final ByteBuffer buffer = ByteBuffer.allocateDirect(8192);
+ while (inputChannel.read(buffer) != -1) {
+ if (mCanceled) {
+ Log.d(LOG_TAG, "Canceled during caching " + sourceUri);
+ return null;
+ }
+ buffer.flip();
+ outputChannel.write(buffer);
+ buffer.compact();
+ }
+ buffer.flip();
+ while (buffer.hasRemaining()) {
+ outputChannel.write(buffer);
+ }
+ } finally {
+ if (inputChannel != null) {
+ try {
+ inputChannel.close();
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "Failed to close inputChannel.");
+ }
+ }
+ if (outputChannel != null) {
+ try {
+ outputChannel.close();
+ } catch(IOException e) {
+ Log.w(LOG_TAG, "Failed to close outputChannel");
+ }
+ }
+ }
+ return destUri;
+ }
+
+ /**
+ * Reads localDataUri (possibly multiple times) and constructs {@link ImportRequest} from
+ * its content.
+ *
+ * @arg localDataUri Uri actually used for the import. Should be stored in
+ * app local storage, as we cannot guarantee other types of Uris can be read
+ * multiple times. This variable populates {@link ImportRequest#uri}.
+ * @arg originalUri Uri requested to be imported. Used mainly for displaying
+ * information. This variable populates {@link ImportRequest#originalUri}.
+ */
+ private ImportRequest constructImportRequest(
+ final Uri localDataUri, final Uri originalUri)
+ throws IOException, VCardException {
+ final ContentResolver resolver = ImportVCardActivity.this.getContentResolver();
+ VCardEntryCounter counter = null;
+ VCardSourceDetector detector = null;
+ VCardInterpreterCollection interpreter = null;
+ int vcardVersion = VCARD_VERSION_V21;
+ try {
+ boolean shouldUseV30 = false;
+ InputStream is = resolver.openInputStream(localDataUri);
+ mVCardParser = new VCardParser_V21();
+ try {
+ counter = new VCardEntryCounter();
+ detector = new VCardSourceDetector();
+ interpreter = new VCardInterpreterCollection(
+ Arrays.asList(counter, detector));
+ mVCardParser.parse(is, interpreter);
+ } catch (VCardVersionException e1) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+
+ shouldUseV30 = true;
+ is = resolver.openInputStream(localDataUri);
+ mVCardParser = new VCardParser_V30();
+ try {
+ counter = new VCardEntryCounter();
+ detector = new VCardSourceDetector();
+ interpreter = new VCardInterpreterCollection(
+ Arrays.asList(counter, detector));
+ mVCardParser.parse(is, interpreter);
+ } catch (VCardVersionException e2) {
+ throw new VCardException("vCard with unspported version.");
+ }
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ vcardVersion = shouldUseV30 ? VCARD_VERSION_V30 : VCARD_VERSION_V21;
+ } catch (VCardNestedException e) {
+ Log.w(LOG_TAG, "Nested Exception is found (it may be false-positive).");
+ // Go through without throwing the Exception, as we may be able to detect the
+ // version before it
+ }
+ return new ImportRequest(mAccount,
+ localDataUri, originalUri,
+ detector.getEstimatedType(),
+ detector.getEstimatedCharset(),
+ vcardVersion, counter.getCount());
+ }
+
+ public Uri[] getSourceUris() {
+ return mSourceUris;
+ }
+
+ public void cancel() {
+ mCanceled = true;
+ if (mVCardParser != null) {
+ mVCardParser.cancel();
+ }
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ Log.i(LOG_TAG, "Cancel request has come. Abort caching vCard.");
+ cancel();
+ }
+ }
+
+ private class ImportTypeSelectedListener implements
+ DialogInterface.OnClickListener {
+ public static final int IMPORT_ONE = 0;
+ public static final int IMPORT_MULTIPLE = 1;
+ public static final int IMPORT_ALL = 2;
+ public static final int IMPORT_TYPE_SIZE = 3;
+
+ private int mCurrentIndex;
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ switch (mCurrentIndex) {
+ case IMPORT_ALL:
+ importVCardFromSDCard(mAllVCardFileList);
+ break;
+ case IMPORT_MULTIPLE:
+ showDialog(R.id.dialog_select_multiple_vcard);
+ break;
+ default:
+ showDialog(R.id.dialog_select_one_vcard);
+ break;
+ }
+ } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+ finish();
+ } else {
+ mCurrentIndex = which;
+ }
+ }
+ }
+
+ private class VCardSelectedListener implements
+ DialogInterface.OnClickListener, DialogInterface.OnMultiChoiceClickListener {
+ private int mCurrentIndex;
+ private Set<Integer> mSelectedIndexSet;
+
+ public VCardSelectedListener(boolean multipleSelect) {
+ mCurrentIndex = 0;
+ if (multipleSelect) {
+ mSelectedIndexSet = new HashSet<Integer>();
+ }
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ if (mSelectedIndexSet != null) {
+ List<VCardFile> selectedVCardFileList = new ArrayList<VCardFile>();
+ final int size = mAllVCardFileList.size();
+ // We'd like to sort the files by its index, so we do not use Set iterator.
+ for (int i = 0; i < size; i++) {
+ if (mSelectedIndexSet.contains(i)) {
+ selectedVCardFileList.add(mAllVCardFileList.get(i));
+ }
+ }
+ importVCardFromSDCard(selectedVCardFileList);
+ } else {
+ importVCardFromSDCard(mAllVCardFileList.get(mCurrentIndex));
+ }
+ } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+ finish();
+ } else {
+ // Some file is selected.
+ mCurrentIndex = which;
+ if (mSelectedIndexSet != null) {
+ if (mSelectedIndexSet.contains(which)) {
+ mSelectedIndexSet.remove(which);
+ } else {
+ mSelectedIndexSet.add(which);
+ }
+ }
+ }
+ }
+
+ public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+ if (mSelectedIndexSet == null || (mSelectedIndexSet.contains(which) == isChecked)) {
+ Log.e(LOG_TAG, String.format("Inconsist state in index %d (%s)", which,
+ mAllVCardFileList.get(which).getCanonicalPath()));
+ } else {
+ onClick(dialog, which);
+ }
+ }
+ }
+
+ /**
+ * Thread scanning VCard from SDCard. After scanning, the dialog which lets a user select
+ * a vCard file is shown. After the choice, VCardReadThread starts running.
+ */
+ private class VCardScanThread extends Thread implements OnCancelListener, OnClickListener {
+ private boolean mCanceled;
+ private boolean mGotIOException;
+ private File mRootDirectory;
+
+ // To avoid recursive link.
+ private Set<String> mCheckedPaths;
+ private PowerManager.WakeLock mWakeLock;
+
+ private class CanceledException extends Exception {
+ }
+
+ public VCardScanThread(File sdcardDirectory) {
+ mCanceled = false;
+ mGotIOException = false;
+ mRootDirectory = sdcardDirectory;
+ mCheckedPaths = new HashSet<String>();
+ PowerManager powerManager = (PowerManager)ImportVCardActivity.this.getSystemService(
+ Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(
+ PowerManager.SCREEN_DIM_WAKE_LOCK |
+ PowerManager.ON_AFTER_RELEASE, LOG_TAG);
+ }
+
+ @Override
+ public void run() {
+ mAllVCardFileList = new Vector<VCardFile>();
+ try {
+ mWakeLock.acquire();
+ getVCardFileRecursively(mRootDirectory);
+ } catch (CanceledException e) {
+ mCanceled = true;
+ } catch (IOException e) {
+ mGotIOException = true;
+ } finally {
+ mWakeLock.release();
+ }
+
+ if (mCanceled) {
+ mAllVCardFileList = null;
+ }
+
+ mProgressDialogForScanVCard.dismiss();
+ mProgressDialogForScanVCard = null;
+
+ if (mGotIOException) {
+ runOnUiThread(new DialogDisplayer(R.id.dialog_io_exception));
+ } else if (mCanceled) {
+ finish();
+ } else {
+ int size = mAllVCardFileList.size();
+ final Context context = ImportVCardActivity.this;
+ if (size == 0) {
+ runOnUiThread(new DialogDisplayer(R.id.dialog_vcard_not_found));
+ } else {
+ startVCardSelectAndImport();
+ }
+ }
+ }
+
+ private void getVCardFileRecursively(File directory)
+ throws CanceledException, IOException {
+ if (mCanceled) {
+ throw new CanceledException();
+ }
+
+ // e.g. secured directory may return null toward listFiles().
+ final File[] files = directory.listFiles();
+ if (files == null) {
+ final String currentDirectoryPath = directory.getCanonicalPath();
+ final String secureDirectoryPath =
+ mRootDirectory.getCanonicalPath().concat(SECURE_DIRECTORY_NAME);
+ if (!TextUtils.equals(currentDirectoryPath, secureDirectoryPath)) {
+ Log.w(LOG_TAG, "listFiles() returned null (directory: " + directory + ")");
+ }
+ return;
+ }
+ for (File file : directory.listFiles()) {
+ if (mCanceled) {
+ throw new CanceledException();
+ }
+ String canonicalPath = file.getCanonicalPath();
+ if (mCheckedPaths.contains(canonicalPath)) {
+ continue;
+ }
+
+ mCheckedPaths.add(canonicalPath);
+
+ if (file.isDirectory()) {
+ getVCardFileRecursively(file);
+ } else if (canonicalPath.toLowerCase().endsWith(".vcf") &&
+ file.canRead()){
+ String fileName = file.getName();
+ VCardFile vcardFile = new VCardFile(
+ fileName, canonicalPath, file.lastModified());
+ mAllVCardFileList.add(vcardFile);
+ }
+ }
+ }
+
+ public void onCancel(DialogInterface dialog) {
+ mCanceled = true;
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_NEGATIVE) {
+ mCanceled = true;
+ }
+ }
+ }
+
+ private void startVCardSelectAndImport() {
+ int size = mAllVCardFileList.size();
+ if (getResources().getBoolean(R.bool.config_import_all_vcard_from_sdcard_automatically) ||
+ size == 1) {
+ importVCardFromSDCard(mAllVCardFileList);
+ } else if (getResources().getBoolean(R.bool.config_allow_users_select_all_vcard_import)) {
+ runOnUiThread(new DialogDisplayer(R.id.dialog_select_import_type));
+ } else {
+ runOnUiThread(new DialogDisplayer(R.id.dialog_select_one_vcard));
+ }
+ }
+
+ private void importVCardFromSDCard(final List<VCardFile> selectedVCardFileList) {
+ final int size = selectedVCardFileList.size();
+ String[] uriStrings = new String[size];
+ int i = 0;
+ for (VCardFile vcardFile : selectedVCardFileList) {
+ uriStrings[i] = "file://" + vcardFile.getCanonicalPath();
+ i++;
+ }
+ importVCard(uriStrings);
+ }
+
+ private void importVCardFromSDCard(final VCardFile vcardFile) {
+ importVCard(new Uri[] {Uri.parse("file://" + vcardFile.getCanonicalPath())});
+ }
+
+ private void importVCard(final Uri uri) {
+ importVCard(new Uri[] {uri});
+ }
+
+ private void importVCard(final String[] uriStrings) {
+ final int length = uriStrings.length;
+ final Uri[] uris = new Uri[length];
+ for (int i = 0; i < length; i++) {
+ uris[i] = Uri.parse(uriStrings[i]);
+ }
+ importVCard(uris);
+ }
+
+ private void importVCard(final Uri[] uris) {
+ runOnUiThread(new Runnable() {
+ public void run() {
+ mVCardCacheThread = new VCardCacheThread(uris);
+ showDialog(R.id.dialog_cache_vcard);
+ }
+ });
+ }
+
+ private Dialog getSelectImportTypeDialog() {
+ final DialogInterface.OnClickListener listener = new ImportTypeSelectedListener();
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(R.string.select_vcard_title)
+ .setPositiveButton(android.R.string.ok, listener)
+ .setOnCancelListener(mCancelListener)
+ .setNegativeButton(android.R.string.cancel, mCancelListener);
+
+ final String[] items = new String[ImportTypeSelectedListener.IMPORT_TYPE_SIZE];
+ items[ImportTypeSelectedListener.IMPORT_ONE] =
+ getString(R.string.import_one_vcard_string);
+ items[ImportTypeSelectedListener.IMPORT_MULTIPLE] =
+ getString(R.string.import_multiple_vcard_string);
+ items[ImportTypeSelectedListener.IMPORT_ALL] =
+ getString(R.string.import_all_vcard_string);
+ builder.setSingleChoiceItems(items, ImportTypeSelectedListener.IMPORT_ONE, listener);
+ return builder.create();
+ }
+
+ private Dialog getVCardFileSelectDialog(boolean multipleSelect) {
+ final int size = mAllVCardFileList.size();
+ final VCardSelectedListener listener = new VCardSelectedListener(multipleSelect);
+ final AlertDialog.Builder builder =
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.select_vcard_title)
+ .setPositiveButton(android.R.string.ok, listener)
+ .setOnCancelListener(mCancelListener)
+ .setNegativeButton(android.R.string.cancel, mCancelListener);
+
+ CharSequence[] items = new CharSequence[size];
+ DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ for (int i = 0; i < size; i++) {
+ VCardFile vcardFile = mAllVCardFileList.get(i);
+ SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
+ stringBuilder.append(vcardFile.getName());
+ stringBuilder.append('\n');
+ int indexToBeSpanned = stringBuilder.length();
+ // Smaller date text looks better, since each file name becomes easier to read.
+ // The value set to RelativeSizeSpan is arbitrary. You can change it to any other
+ // value (but the value bigger than 1.0f would not make nice appearance :)
+ stringBuilder.append(
+ "(" + dateFormat.format(new Date(vcardFile.getLastModified())) + ")");
+ stringBuilder.setSpan(
+ new RelativeSizeSpan(0.7f), indexToBeSpanned, stringBuilder.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ items[i] = stringBuilder;
+ }
+ if (multipleSelect) {
+ builder.setMultiChoiceItems(items, (boolean[])null, listener);
+ } else {
+ builder.setSingleChoiceItems(items, 0, listener);
+ }
+ return builder.create();
+ }
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ String accountName = null;
+ String accountType = null;
+ final Intent intent = getIntent();
+ if (intent != null) {
+ accountName = intent.getStringExtra(SelectAccountActivity.ACCOUNT_NAME);
+ accountType = intent.getStringExtra(SelectAccountActivity.ACCOUNT_TYPE);
+ mAction = intent.getAction();
+ mUri = intent.getData();
+ } else {
+ Log.e(LOG_TAG, "intent does not exist");
+ }
+
+ if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
+ mAccount = new Account(accountName, accountType);
+ } else {
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this);
+ final List<Account> accountList = accountTypes.getAccounts(true);
+ if (accountList.size() == 0) {
+ mAccount = null;
+ } else if (accountList.size() == 1) {
+ mAccount = accountList.get(0);
+ } else {
+ startActivityForResult(new Intent(this, SelectAccountActivity.class),
+ SELECT_ACCOUNT);
+ return;
+ }
+ }
+
+ startImport(mAction, mUri);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ if (requestCode == SELECT_ACCOUNT) {
+ if (resultCode == RESULT_OK) {
+ mAccount = new Account(
+ intent.getStringExtra(SelectAccountActivity.ACCOUNT_NAME),
+ intent.getStringExtra(SelectAccountActivity.ACCOUNT_TYPE));
+ startImport(mAction, mUri);
+ } else {
+ if (resultCode != RESULT_CANCELED) {
+ Log.w(LOG_TAG, "Result code was not OK nor CANCELED: " + resultCode);
+ }
+ finish();
+ }
+ }
+ }
+
+ private void startImport(String action, Uri uri) {
+ if (uri != null) {
+ Log.i(LOG_TAG, "Starting vCard import using Uri " + uri);
+ importVCard(uri);
+ } else {
+ Log.i(LOG_TAG, "Start vCard without Uri. The user will select vCard manually.");
+ doScanExternalStorageAndImportVCard();
+ }
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int resId, Bundle bundle) {
+ switch (resId) {
+ case R.string.import_from_sdcard: {
+ if (mAccountSelectionListener == null) {
+ throw new NullPointerException(
+ "mAccountSelectionListener must not be null.");
+ }
+ return AccountSelectionUtil.getSelectAccountDialog(this, resId,
+ mAccountSelectionListener, mCancelListener);
+ }
+ case R.id.dialog_searching_vcard: {
+ if (mProgressDialogForScanVCard == null) {
+ String title = getString(R.string.searching_vcard_title);
+ String message = getString(R.string.searching_vcard_message);
+ mProgressDialogForScanVCard =
+ ProgressDialog.show(this, title, message, true, false);
+ mProgressDialogForScanVCard.setOnCancelListener(mVCardScanThread);
+ mVCardScanThread.start();
+ }
+ return mProgressDialogForScanVCard;
+ }
+ case R.id.dialog_sdcard_not_found: {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(R.string.no_sdcard_title)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setMessage(R.string.no_sdcard_message)
+ .setOnCancelListener(mCancelListener)
+ .setPositiveButton(android.R.string.ok, mCancelListener);
+ return builder.create();
+ }
+ case R.id.dialog_vcard_not_found: {
+ final String message = getString(R.string.import_failure_no_vcard_file);
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(R.string.scanning_sdcard_failed_title)
+ .setMessage(message)
+ .setOnCancelListener(mCancelListener)
+ .setPositiveButton(android.R.string.ok, mCancelListener);
+ return builder.create();
+ }
+ case R.id.dialog_select_import_type: {
+ return getSelectImportTypeDialog();
+ }
+ case R.id.dialog_select_multiple_vcard: {
+ return getVCardFileSelectDialog(true);
+ }
+ case R.id.dialog_select_one_vcard: {
+ return getVCardFileSelectDialog(false);
+ }
+ case R.id.dialog_cache_vcard: {
+ if (mProgressDialogForCachingVCard == null) {
+ final String title = getString(R.string.caching_vcard_title);
+ final String message = getString(R.string.caching_vcard_message);
+ mProgressDialogForCachingVCard = new ProgressDialog(this);
+ mProgressDialogForCachingVCard.setTitle(title);
+ mProgressDialogForCachingVCard.setMessage(message);
+ mProgressDialogForCachingVCard.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ mProgressDialogForCachingVCard.setOnCancelListener(mVCardCacheThread);
+ mConnection = new ImportRequestConnection();
+
+ Log.i(LOG_TAG, "Bind to VCardService.");
+ // We don't want the service finishes itself just after this connection.
+ startService(new Intent(this, VCardService.class));
+ bindService(new Intent(this, VCardService.class),
+ mConnection, Context.BIND_AUTO_CREATE);
+ }
+ return mProgressDialogForCachingVCard;
+ }
+ case R.id.dialog_io_exception: {
+ String message = (getString(R.string.scanning_sdcard_failed_message,
+ getString(R.string.fail_reason_io_error)));
+ AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(R.string.scanning_sdcard_failed_title)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setMessage(message)
+ .setOnCancelListener(mCancelListener)
+ .setPositiveButton(android.R.string.ok, mCancelListener);
+ return builder.create();
+ }
+ case R.id.dialog_error_with_message: {
+ String message = mErrorMessage;
+ if (TextUtils.isEmpty(message)) {
+ Log.e(LOG_TAG, "Error message is null while it must not.");
+ message = getString(R.string.fail_reason_unknown);
+ }
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this)
+ .setTitle(getString(R.string.reading_vcard_failed_title))
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setMessage(message)
+ .setOnCancelListener(mCancelListener)
+ .setPositiveButton(android.R.string.ok, mCancelListener);
+ return builder.create();
+ }
+ }
+
+ return super.onCreateDialog(resId, bundle);
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ if (mProgressDialogForCachingVCard != null) {
+ Log.i(LOG_TAG, "Cache thread is still running. Show progress dialog again.");
+ showDialog(R.id.dialog_cache_vcard);
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ // This Activity should finish itself on orientation change, and give the main screen back
+ // to the caller Activity.
+ finish();
+ }
+
+ /**
+ * Scans vCard in external storage (typically SDCard) and tries to import it.
+ * - When there's no SDCard available, an error dialog is shown.
+ * - When multiple vCard files are available, asks a user to select one.
+ */
+ private void doScanExternalStorageAndImportVCard() {
+ // TODO: should use getExternalStorageState().
+ final File file = Environment.getExternalStorageDirectory();
+ if (!file.exists() || !file.isDirectory() || !file.canRead()) {
+ showDialog(R.id.dialog_sdcard_not_found);
+ } else {
+ mVCardScanThread = new VCardScanThread(file);
+ showDialog(R.id.dialog_searching_vcard);
+ }
+ }
+
+ private void showFailureNotification(int reasonId) {
+ final NotificationManager notificationManager =
+ (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
+ final Notification notification =
+ VCardService.constructImportFailureNotification(
+ ImportVCardActivity.this,
+ getString(reasonId));
+ notificationManager.notify(DEFAULT_NOTIFICATION_ID, notification);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(ImportVCardActivity.this,
+ getString(R.string.vcard_import_failed), Toast.LENGTH_LONG).show();
+ }
+ });
+ }
+}
diff --git a/src/com/android/contacts/vcard/ProcessorBase.java b/src/com/android/contacts/vcard/ProcessorBase.java
new file mode 100644
index 0000000..833090d
--- /dev/null
+++ b/src/com/android/contacts/vcard/ProcessorBase.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 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.vcard;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RunnableFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A base processor class. One instance processes vCard one import/export request (imports a given
+ * vCard or exports a vCard). Expected to be used with {@link ExecutorService}.
+ *
+ * This instance starts itself with {@link #run()} method, and can be cancelled with
+ * {@link #cancel(boolean)}. Users can check the processor's status using {@link #isCancelled()}
+ * and {@link #isDone()} asynchronously.
+ *
+ * {@link #get()} and {@link #get(long, TimeUnit)}, which are form {@link Future}, aren't
+ * supported and {@link UnsupportedOperationException} will be just thrown when they are called.
+ */
+public abstract class ProcessorBase implements RunnableFuture<Object> {
+
+ /**
+ * @return the type of the processor. Must be {@link VCardService#TYPE_IMPORT} or
+ * {@link VCardService#TYPE_EXPORT}.
+ */
+ public abstract int getType();
+
+ public abstract void run();
+
+ /**
+ * Cancels this operation.
+ *
+ * @param mayInterruptIfRunning ignored. When this method is called, the instance
+ * stops processing and finish itself even if the thread is running.
+ *
+ * @see Future#cancel(boolean)
+ */
+ public abstract boolean cancel(boolean mayInterruptIfRunning);
+ public abstract boolean isCancelled();
+ public abstract boolean isDone();
+
+ /**
+ * Just throws {@link UnsupportedOperationException}.
+ */
+ @Override
+ public final Object get() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Just throws {@link UnsupportedOperationException}.
+ */
+ @Override
+ public final Object get(long timeout, TimeUnit unit) {
+ throw new UnsupportedOperationException();
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/vcard/SelectAccountActivity.java b/src/com/android/contacts/vcard/SelectAccountActivity.java
new file mode 100644
index 0000000..5f8aa28
--- /dev/null
+++ b/src/com/android/contacts/vcard/SelectAccountActivity.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2010 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.vcard;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.util.AccountSelectionUtil;
+
+import android.accounts.Account;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.List;
+
+public class SelectAccountActivity extends ContactsActivity {
+ private static final String LOG_TAG = "SelectAccountActivity";
+
+ public static final String ACCOUNT_NAME = "account_name";
+ public static final String ACCOUNT_TYPE = "account_type";
+
+ private class CancelListener
+ implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
+ }
+
+ private AccountSelectionUtil.AccountSelectedListener mAccountSelectionListener;
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ // There's three possibilities:
+ // - more than one accounts -> ask the user
+ // - just one account -> use the account without asking the user
+ // - no account -> use phone-local storage without asking the user
+ final int resId = R.string.import_from_sdcard;
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this);
+ final List<Account> accountList = accountTypes.getAccounts(true);
+ if (accountList.size() == 0) {
+ Log.w(LOG_TAG, "Account does not exist");
+ finish();
+ return;
+ } else if (accountList.size() == 1) {
+ final Account account = accountList.get(0);
+ final Intent intent = new Intent();
+ intent.putExtra(ACCOUNT_NAME, account.name);
+ intent.putExtra(ACCOUNT_TYPE, account.type);
+ setResult(RESULT_OK, intent);
+ finish();
+ return;
+ }
+
+ Log.i(LOG_TAG, "The number of available accounts: " + accountList.size());
+
+ // Multiple accounts. Let users to select one.
+ mAccountSelectionListener =
+ new AccountSelectionUtil.AccountSelectedListener(
+ this, accountList, resId) {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ final Account account = mAccountList.get(which);
+ final Intent intent = new Intent();
+ intent.putExtra(ACCOUNT_NAME, account.name);
+ intent.putExtra(ACCOUNT_TYPE, account.type);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+ };
+ showDialog(resId);
+ return;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int resId, Bundle bundle) {
+ switch (resId) {
+ case R.string.import_from_sdcard: {
+ if (mAccountSelectionListener == null) {
+ throw new NullPointerException(
+ "mAccountSelectionListener must not be null.");
+ }
+ return AccountSelectionUtil.getSelectAccountDialog(this, resId,
+ mAccountSelectionListener,
+ new CancelListener());
+ }
+ }
+ return super.onCreateDialog(resId, bundle);
+ }
+}
diff --git a/src/com/android/contacts/vcard/ThreadStarter.java b/src/com/android/contacts/vcard/ThreadStarter.java
new file mode 100644
index 0000000..d7adad6
--- /dev/null
+++ b/src/com/android/contacts/vcard/ThreadStarter.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2010 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.vcard;
+
+public interface ThreadStarter {
+ public void start();
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/vcard/VCardService.java b/src/com/android/contacts/vcard/VCardService.java
new file mode 100644
index 0000000..6422163
--- /dev/null
+++ b/src/com/android/contacts/vcard/VCardService.java
@@ -0,0 +1,591 @@
+/*
+ * Copyright (C) 2010 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.vcard;
+
+import com.android.contacts.R;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.RemoteViews;
+import android.widget.Toast;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * The class responsible for handling vCard import/export requests.
+ *
+ * This Service creates one ImportRequest/ExportRequest object (as Runnable) per request and push
+ * it to {@link ExecutorService} with single thread executor. The executor handles each request
+ * one by one, and notifies users when needed.
+ */
+// TODO: Using IntentService looks simpler than using Service + ServiceConnection though this
+// works fine enough. Investigate the feasibility.
+public class VCardService extends Service {
+ private final static String LOG_TAG = "VCardService";
+ /* package */ final static boolean DEBUG = true;
+
+ /* package */ static final int MSG_IMPORT_REQUEST = 1;
+ /* package */ static final int MSG_EXPORT_REQUEST = 2;
+ /* package */ static final int MSG_CANCEL_REQUEST = 3;
+ /* package */ static final int MSG_REQUEST_AVAILABLE_EXPORT_DESTINATION = 4;
+ /* package */ static final int MSG_SET_AVAILABLE_EXPORT_DESTINATION = 5;
+
+ /**
+ * Specifies the type of operation. Used when constructing a {@link Notification}, canceling
+ * some operation, etc.
+ */
+ /* package */ static final int TYPE_IMPORT = 1;
+ /* package */ static final int TYPE_EXPORT = 2;
+
+ /* package */ static final String CACHE_FILE_PREFIX = "import_tmp_";
+
+ private final Messenger mMessenger = new Messenger(new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_IMPORT_REQUEST: {
+ handleImportRequest((ImportRequest)msg.obj);
+ break;
+ }
+ case MSG_EXPORT_REQUEST: {
+ handleExportRequest((ExportRequest)msg.obj);
+ break;
+ }
+ case MSG_CANCEL_REQUEST: {
+ handleCancelRequest((CancelRequest)msg.obj);
+ break;
+ }
+ case MSG_REQUEST_AVAILABLE_EXPORT_DESTINATION: {
+ handleRequestAvailableExportDestination(msg);
+ break;
+ }
+ // TODO: add cancel capability for export..
+ default: {
+ Log.w(LOG_TAG, "Received unknown request, ignoring it.");
+ super.hasMessages(msg.what);
+ }
+ }
+ }
+ });
+
+ private NotificationManager mNotificationManager;
+
+ // Should be single thread, as we don't want to simultaneously handle import and export
+ // requests.
+ private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
+
+ private int mCurrentJobId;
+
+ // Stores all unfinished import/export jobs which will be executed by mExecutorService.
+ // Key is jobId.
+ private final Map<Integer, ProcessorBase> mRunningJobMap =
+ new HashMap<Integer, ProcessorBase>();
+
+ /* ** vCard exporter params ** */
+ // If true, VCardExporter is able to emits files longer than 8.3 format.
+ private static final boolean ALLOW_LONG_FILE_NAME = false;
+ private String mTargetDirectory;
+ private String mFileNamePrefix;
+ private String mFileNameSuffix;
+ private int mFileIndexMinimum;
+ private int mFileIndexMaximum;
+ private String mFileNameExtension;
+ private Set<String> mExtensionsToConsider;
+ private String mErrorReason;
+
+ // File names currently reserved by some export job.
+ private final Set<String> mReservedDestination = new HashSet<String>();
+ /* ** end of vCard exporter params ** */
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ if (DEBUG) Log.d(LOG_TAG, "vCard Service is being created.");
+ mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
+ initExporterParams();
+ }
+
+ private void initExporterParams() {
+ mTargetDirectory = getString(R.string.config_export_dir);
+ mFileNamePrefix = getString(R.string.config_export_file_prefix);
+ mFileNameSuffix = getString(R.string.config_export_file_suffix);
+ mFileNameExtension = getString(R.string.config_export_file_extension);
+
+ mExtensionsToConsider = new HashSet<String>();
+ mExtensionsToConsider.add(mFileNameExtension);
+
+ final String additionalExtensions =
+ getString(R.string.config_export_extensions_to_consider);
+ if (!TextUtils.isEmpty(additionalExtensions)) {
+ for (String extension : additionalExtensions.split(",")) {
+ String trimed = extension.trim();
+ if (trimed.length() > 0) {
+ mExtensionsToConsider.add(trimed);
+ }
+ }
+ }
+
+ final Resources resources = getResources();
+ mFileIndexMinimum = resources.getInteger(R.integer.config_export_file_min_index);
+ mFileIndexMaximum = resources.getInteger(R.integer.config_export_file_max_index);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int id) {
+ return START_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mMessenger.getBinder();
+ }
+
+ @Override
+ public void onDestroy() {
+ if (DEBUG) Log.d(LOG_TAG, "VCardService is being destroyed.");
+ cancelAllRequestsAndShutdown();
+ clearCache();
+ super.onDestroy();
+ }
+
+ private synchronized void handleImportRequest(ImportRequest request) {
+ if (DEBUG) {
+ Log.d(LOG_TAG,
+ String.format("received import request (uri: %s, originalUri: %s)",
+ request.uri, request.originalUri));
+ }
+ if (tryExecute(new ImportProcessor(this, request, mCurrentJobId))) {
+ final String displayName;
+ final String message;
+ final String lastPathSegment = request.originalUri.getLastPathSegment();
+ if ("file".equals(request.originalUri.getScheme()) &&
+ lastPathSegment != null) {
+ displayName = lastPathSegment;
+ message = getString(R.string.vcard_import_will_start_message, displayName);
+ } else {
+ displayName = getString(R.string.vcard_unknown_filename);
+ message = getString(R.string.vcard_import_will_start_message_with_default_name);
+ }
+
+ // TODO: Ideally we should detect the current status of import/export and show
+ // "started" when we can import right now and show "will start" when we cannot.
+ Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+
+ final Notification notification =
+ constructProgressNotification(
+ this, TYPE_IMPORT, message, message, mCurrentJobId,
+ displayName, -1, 0);
+ mNotificationManager.notify(mCurrentJobId, notification);
+ mCurrentJobId++;
+ } else {
+ // TODO: a little unkind to show Toast in this case, which is shown just a moment.
+ // Ideally we should show some persistent something users can notice more easily.
+ Toast.makeText(this, getString(R.string.vcard_import_request_rejected_message),
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ private synchronized void handleExportRequest(ExportRequest request) {
+ if (tryExecute(new ExportProcessor(this, request, mCurrentJobId))) {
+ final String displayName = request.destUri.getLastPathSegment();
+ final String message = getString(R.string.vcard_export_will_start_message,
+ displayName);
+
+ final String path = request.destUri.getEncodedPath();
+ if (DEBUG) Log.d(LOG_TAG, "Reserve the path " + path);
+ if (!mReservedDestination.add(path)) {
+ Log.w(LOG_TAG,
+ String.format("The path %s is already reserved. Reject export request",
+ path));
+ Toast.makeText(this, getString(R.string.vcard_export_request_rejected_message),
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+ final Notification notification =
+ constructProgressNotification(this, TYPE_EXPORT, message, message,
+ mCurrentJobId, displayName, -1, 0);
+ mNotificationManager.notify(mCurrentJobId, notification);
+ mCurrentJobId++;
+ } else {
+ Toast.makeText(this, getString(R.string.vcard_export_request_rejected_message),
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ /**
+ * Tries to call {@link ExecutorService#execute(Runnable)} toward a given processor.
+ * @return true when successful.
+ */
+ private synchronized boolean tryExecute(ProcessorBase processor) {
+ try {
+ mExecutorService.execute(processor);
+ mRunningJobMap.put(mCurrentJobId, processor);
+ return true;
+ } catch (RejectedExecutionException e) {
+ Log.w(LOG_TAG, "Failed to excetute a job.", e);
+ return false;
+ }
+ }
+
+ private synchronized void handleCancelRequest(CancelRequest request) {
+ final int jobId = request.jobId;
+ if (DEBUG) Log.d(LOG_TAG, String.format("Received cancel request. (id: %d)", jobId));
+ final ProcessorBase processor = mRunningJobMap.remove(jobId);
+
+ if (processor != null) {
+ processor.cancel(true);
+ final String description = processor.getType() == TYPE_IMPORT ?
+ getString(R.string.importing_vcard_canceled_title, request.displayName) :
+ getString(R.string.exporting_vcard_canceled_title, request.displayName);
+ final Notification notification = constructCancelNotification(this, description);
+ mNotificationManager.notify(jobId, notification);
+ if (processor.getType() == TYPE_EXPORT) {
+ final String path =
+ ((ExportProcessor)processor).getRequest().destUri.getEncodedPath();
+ Log.i(LOG_TAG,
+ String.format("Cancel reservation for the path %s if appropriate", path));
+ if (!mReservedDestination.remove(path)) {
+ Log.w(LOG_TAG, "Not reserved.");
+ }
+ }
+ } else {
+ Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
+ }
+ stopServiceWhenNoJob();
+ }
+
+ private synchronized void handleRequestAvailableExportDestination(Message msg) {
+ if (DEBUG) Log.d(LOG_TAG, "Received available export destination request.");
+ final Messenger messenger = msg.replyTo;
+ final String path = getAppropriateDestination(mTargetDirectory);
+ final Message message;
+ if (path != null) {
+ message = Message.obtain(null,
+ VCardService.MSG_SET_AVAILABLE_EXPORT_DESTINATION, 0, 0, path);
+ } else {
+ message = Message.obtain(null,
+ VCardService.MSG_SET_AVAILABLE_EXPORT_DESTINATION,
+ R.id.dialog_fail_to_export_with_reason, 0, mErrorReason);
+ }
+ try {
+ messenger.send(message);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Failed to send reply for available export destination request.", e);
+ }
+ }
+
+ /**
+ * Checks job list and call {@link #stopSelf()} when there's no job now.
+ * A new job cannot be submitted any more after this call.
+ */
+ private synchronized void stopServiceWhenNoJob() {
+ if (mRunningJobMap.size() > 0) {
+ for (final Map.Entry<Integer, ProcessorBase> entry : mRunningJobMap.entrySet()) {
+ final int jobId = entry.getKey();
+ final ProcessorBase processor = entry.getValue();
+ if (processor.isDone()) {
+ mRunningJobMap.remove(jobId);
+ } else {
+ Log.i(LOG_TAG, String.format("Found unfinished job (id: %d)", jobId));
+ return;
+ }
+ }
+ }
+
+ Log.i(LOG_TAG, "No unfinished job. Stop this service.");
+ mExecutorService.shutdown();
+ stopSelf();
+ }
+
+ /* package */ synchronized void handleFinishImportNotification(
+ int jobId, boolean successful) {
+ if (DEBUG) {
+ Log.d(LOG_TAG, String.format("Received vCard import finish notification (id: %d). "
+ + "Result: %b", jobId, (successful ? "success" : "failure")));
+ }
+ if (mRunningJobMap.remove(jobId) == null) {
+ Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
+ }
+ stopServiceWhenNoJob();
+ }
+
+ /* package */ synchronized void handleFinishExportNotification(
+ int jobId, boolean successful) {
+ if (DEBUG) {
+ Log.d(LOG_TAG, String.format("Received vCard export finish notification (id: %d). "
+ + "Result: %b", jobId, (successful ? "success" : "failure")));
+ }
+ final ProcessorBase job = mRunningJobMap.remove(jobId);
+ if (job == null) {
+ Log.w(LOG_TAG, String.format("Tried to remove unknown job (id: %d)", jobId));
+ } else if (!(job instanceof ExportProcessor)) {
+ Log.w(LOG_TAG,
+ String.format("Removed job (id: %s) isn't ExportProcessor", jobId));
+ } else {
+ final String path = ((ExportProcessor)job).getRequest().destUri.getEncodedPath();
+ if (DEBUG) Log.d(LOG_TAG, "Remove reserved path " + path);
+ mReservedDestination.remove(path);
+ }
+
+ stopServiceWhenNoJob();
+ }
+
+ /**
+ * Cancels all the import/export requests and calls {@link ExecutorService#shutdown()}, which
+ * means this Service becomes no longer ready for import/export requests.
+ *
+ * Mainly called from onDestroy().
+ */
+ private synchronized void cancelAllRequestsAndShutdown() {
+ for (final Map.Entry<Integer, ProcessorBase> entry : mRunningJobMap.entrySet()) {
+ entry.getValue().cancel(true);
+ }
+ mRunningJobMap.clear();
+ mExecutorService.shutdown();
+ }
+
+ /**
+ * Removes import caches stored locally.
+ */
+ private void clearCache() {
+ for (final String fileName : fileList()) {
+ if (fileName.startsWith(CACHE_FILE_PREFIX)) {
+ // We don't want to keep all the caches so we remove cache files old enough.
+ Log.i(LOG_TAG, "Remove a temporary file: " + fileName);
+ deleteFile(fileName);
+ }
+ }
+ }
+
+ /**
+ * Constructs a {@link Notification} showing the current status of import/export.
+ * Users can cancel the process with the Notification.
+ *
+ * @param context
+ * @param type import/export
+ * @param description Content of the Notification.
+ * @param tickerText
+ * @param jobId
+ * @param displayName Name to be shown to the Notification (e.g. "finished importing XXXX").
+ * Typycally a file name.
+ * @param totalCount The number of vCard entries to be imported. Used to show progress bar.
+ * -1 lets the system show the progress bar with "indeterminate" state.
+ * @param currentCount The index of current vCard. Used to show progress bar.
+ */
+ /* package */ static Notification constructProgressNotification(
+ Context context, int type, String description, String tickerText,
+ int jobId, String displayName, int totalCount, int currentCount) {
+ final RemoteViews remoteViews =
+ new RemoteViews(context.getPackageName(),
+ R.layout.status_bar_ongoing_event_progress_bar);
+ remoteViews.setTextViewText(R.id.status_description, description);
+ remoteViews.setProgressBar(R.id.status_progress_bar, totalCount, currentCount,
+ totalCount == -1);
+ final String percentage;
+ if (totalCount > 0) {
+ percentage = context.getString(R.string.percentage,
+ String.valueOf(currentCount * 100/totalCount));
+ } else {
+ percentage = "";
+ }
+ remoteViews.setTextViewText(R.id.status_progress_text, percentage);
+ final int icon = (type == TYPE_IMPORT ? android.R.drawable.stat_sys_download :
+ android.R.drawable.stat_sys_upload);
+ remoteViews.setImageViewResource(R.id.status_icon, icon);
+
+ final Notification notification = new Notification();
+ notification.icon = icon;
+ notification.tickerText = tickerText;
+ notification.contentView = remoteViews;
+ notification.flags |= Notification.FLAG_ONGOING_EVENT;
+
+ // Note: We cannot use extra values here (like setIntExtra()), as PendingIntent doesn't
+ // preserve them across multiple Notifications. PendingIntent preserves the first extras
+ // (when flag is not set), or update them when PendingIntent#getActivity() is called
+ // (See PendingIntent#FLAG_UPDATE_CURRENT). In either case, we cannot preserve extras as we
+ // expect (for each vCard import/export request).
+ //
+ // We use query parameter in Uri instead.
+ // Scheme and Authority is arbitorary, assuming CancelActivity never refers them.
+ final Intent intent = new Intent(context, CancelActivity.class);
+ final Uri uri = (new Uri.Builder())
+ .scheme("invalidscheme")
+ .authority("invalidauthority")
+ .appendQueryParameter(CancelActivity.JOB_ID, String.valueOf(jobId))
+ .appendQueryParameter(CancelActivity.DISPLAY_NAME, displayName)
+ .appendQueryParameter(CancelActivity.TYPE, String.valueOf(type)).build();
+ intent.setData(uri);
+
+ notification.contentIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ return notification;
+ }
+
+ /**
+ * Constructs a Notification telling users the process is canceled.
+ *
+ * @param context
+ * @param description Content of the Notification
+ */
+ /* package */ static Notification constructCancelNotification(
+ Context context, String description) {
+ return new Notification.Builder(context)
+ .setAutoCancel(true)
+ .setSmallIcon(android.R.drawable.stat_notify_error)
+ .setContentTitle(description)
+ .setContentText(description)
+ .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0))
+ .getNotification();
+ }
+
+ /**
+ * Constructs a Notification telling users the process is finished.
+ *
+ * @param context
+ * @param description Content of the Notification
+ * @param intent Intent to be launched when the Notification is clicked. Can be null.
+ */
+ /* package */ static Notification constructFinishNotification(
+ Context context, String title, String description, Intent intent) {
+ return new Notification.Builder(context)
+ .setAutoCancel(true)
+ .setSmallIcon(android.R.drawable.stat_sys_download_done)
+ .setContentTitle(title)
+ .setContentText(description)
+ .setContentIntent(PendingIntent.getActivity(context, 0,
+ (intent != null ? intent : new Intent()), 0))
+ .getNotification();
+ }
+
+ /**
+ * Constructs a Notification telling the vCard import has failed.
+ *
+ * @param context
+ * @param reason The reason why the import has failed. Shown in description field.
+ */
+ /* package */ static Notification constructImportFailureNotification(
+ Context context, String reason) {
+ return new Notification.Builder(context)
+ .setAutoCancel(true)
+ .setSmallIcon(android.R.drawable.stat_notify_error)
+ .setContentTitle(context.getString(R.string.vcard_import_failed))
+ .setContentText(reason)
+ .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0))
+ .getNotification();
+ }
+
+ /**
+ * Returns an appropriate file name for vCard export. Returns null when impossible.
+ *
+ * @return destination path for a vCard file to be exported. null on error and mErrorReason
+ * is correctly set.
+ */
+ private String getAppropriateDestination(final String destDirectory) {
+ /*
+ * Here, file names have 5 parts: directory, prefix, index, suffix, and extension.
+ * e.g. "/mnt/sdcard/prfx00001sfx.vcf" -> "/mnt/sdcard", "prfx", "00001", "sfx", and ".vcf"
+ * (In default, prefix and suffix is empty, so usually the destination would be
+ * /mnt/sdcard/00001.vcf.)
+ *
+ * This method increments "index" part from 1 to maximum, and checks whether any file name
+ * following naming rule is available. If there's no file named /mnt/sdcard/00001.vcf, the
+ * name will be returned to a caller. If there are 00001.vcf 00002.vcf, 00003.vcf is
+ * returned.
+ *
+ * There may not be any appropriate file name. If there are 99999 vCard files in the
+ * storage, for example, there's no appropriate name, so this method returns
+ * null.
+ */
+
+ // Count the number of digits of mFileIndexMaximum
+ // e.g. When mFileIndexMaximum is 99999, fileIndexDigit becomes 5, as we will count the
+ int fileIndexDigit = 0;
+ {
+ // Calling Math.Log10() is costly.
+ int tmp;
+ for (fileIndexDigit = 0, tmp = mFileIndexMaximum; tmp > 0;
+ fileIndexDigit++, tmp /= 10) {
+ }
+ }
+
+ // %s05d%s (e.g. "p00001s")
+ final String bodyFormat = "%s%0" + fileIndexDigit + "d%s";
+
+ if (!ALLOW_LONG_FILE_NAME) {
+ final String possibleBody =
+ String.format(bodyFormat, mFileNamePrefix, 1, mFileNameSuffix);
+ if (possibleBody.length() > 8 || mFileNameExtension.length() > 3) {
+ Log.e(LOG_TAG, "This code does not allow any long file name.");
+ mErrorReason = getString(R.string.fail_reason_too_long_filename,
+ String.format("%s.%s", possibleBody, mFileNameExtension));
+ Log.w(LOG_TAG, "File name becomes too long.");
+ return null;
+ }
+ }
+
+ for (int i = mFileIndexMinimum; i <= mFileIndexMaximum; i++) {
+ boolean numberIsAvailable = true;
+ String body = null;
+ for (String possibleExtension : mExtensionsToConsider) {
+ body = String.format(bodyFormat, mFileNamePrefix, i, mFileNameSuffix);
+ final String path =
+ String.format("%s/%s.%s", destDirectory, body, possibleExtension);
+ synchronized (this) {
+ if (mReservedDestination.contains(path)) {
+ if (DEBUG) {
+ Log.d(LOG_TAG, String.format("The path %s is reserved.", path));
+ }
+ numberIsAvailable = false;
+ break;
+ }
+ }
+ final File file = new File(path);
+ if (file.exists()) {
+ numberIsAvailable = false;
+ break;
+ }
+ }
+ if (numberIsAvailable) {
+ return String.format("%s/%s.%s", destDirectory, body, mFileNameExtension);
+ }
+ }
+
+ Log.w(LOG_TAG, "Reached vCard number limit. Maybe there are too many vCard in the storage");
+ mErrorReason = getString(R.string.fail_reason_too_many_vcard);
+ return null;
+ }
+}
diff --git a/src/com/android/contacts/widget/AutoScrollListView.java b/src/com/android/contacts/widget/AutoScrollListView.java
new file mode 100644
index 0000000..e9c1c42
--- /dev/null
+++ b/src/com/android/contacts/widget/AutoScrollListView.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ListView;
+
+/**
+ * A ListView that can be asked to scroll (smoothly or otherwise) to a specific
+ * position. This class takes advantage of similar functionality that exists
+ * in {@link ListView} and enhances it.
+ */
+public class AutoScrollListView extends ListView {
+
+ /**
+ * Position the element at about 1/3 of the list height
+ */
+ private static final float PREFERRED_SELECTION_OFFSET_FROM_TOP = 0.33f;
+
+ private int mRequestedScrollPosition = -1;
+ private boolean mSmoothScrollRequested;
+
+ public AutoScrollListView(Context context) {
+ super(context);
+ }
+
+ public AutoScrollListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AutoScrollListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Brings the specified position to view by optionally performing a jump-scroll maneuver:
+ * first it jumps to some position near the one requested and then does a smooth
+ * scroll to the requested position. This creates an impression of full smooth
+ * scrolling without actually traversing the entire list. If smooth scrolling is
+ * not requested, instantly positions the requested item at a preferred offset.
+ */
+ public void requestPositionToScreen(int position, boolean smoothScroll) {
+ mRequestedScrollPosition = position;
+ mSmoothScrollRequested = smoothScroll;
+ requestLayout();
+ }
+
+ @Override
+ protected void layoutChildren() {
+ super.layoutChildren();
+ if (mRequestedScrollPosition == -1) {
+ return;
+ }
+
+ final int position = mRequestedScrollPosition;
+ mRequestedScrollPosition = -1;
+
+ int firstPosition = getFirstVisiblePosition() + 1;
+ int lastPosition = getLastVisiblePosition();
+ if (position >= firstPosition && position <= lastPosition) {
+ return; // Already on screen
+ }
+
+ final int offset = (int) (getHeight() * PREFERRED_SELECTION_OFFSET_FROM_TOP);
+ if (!mSmoothScrollRequested) {
+ setSelectionFromTop(position, offset);
+
+ // Since we have changed the scrolling position, we need to redo child layout
+ // Calling "requestLayout" in the middle of a layout pass has no effect,
+ // so we call layoutChildren explicitly
+ super.layoutChildren();
+
+ } else {
+ // We will first position the list a couple of screens before or after
+ // the new selection and then scroll smoothly to it.
+ int twoScreens = (lastPosition - firstPosition) * 2;
+ int preliminaryPosition;
+ if (position < firstPosition) {
+ preliminaryPosition = position + twoScreens;
+ if (preliminaryPosition >= getCount()) {
+ preliminaryPosition = getCount() - 1;
+ }
+ if (preliminaryPosition < firstPosition) {
+ setSelection(preliminaryPosition);
+ super.layoutChildren();
+ }
+ } else {
+ preliminaryPosition = position - twoScreens;
+ if (preliminaryPosition < 0) {
+ preliminaryPosition = 0;
+ }
+ if (preliminaryPosition > lastPosition) {
+ setSelection(preliminaryPosition);
+ super.layoutChildren();
+ }
+ }
+
+
+ smoothScrollToPositionFromTop(position, offset);
+ }
+ }
+}
diff --git a/src/com/android/contacts/widget/CompositeListAdapter.java b/src/com/android/contacts/widget/CompositeListAdapter.java
new file mode 100644
index 0000000..bd0c8d6
--- /dev/null
+++ b/src/com/android/contacts/widget/CompositeListAdapter.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.database.DataSetObserver;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListAdapter;
+
+/**
+ * A general purpose adapter that is composed of multiple sub-adapters. It just
+ * appends them in the order they are added. It listens to changes from all
+ * sub-adapters and propagates them to its own listeners.
+ */
+public class CompositeListAdapter extends BaseAdapter {
+
+ private static final int INITIAL_CAPACITY = 2;
+
+ private ListAdapter[] mAdapters;
+ private int[] mCounts;
+ private int[] mViewTypeCounts;
+ private int mSize = 0;
+ private int mCount = 0;
+ private int mViewTypeCount = 0;
+ private boolean mAllItemsEnabled = true;
+ private boolean mCacheValid = true;
+
+ private DataSetObserver mDataSetObserver = new DataSetObserver() {
+
+ @Override
+ public void onChanged() {
+ invalidate();
+ notifyDataChanged();
+ }
+
+ @Override
+ public void onInvalidated() {
+ invalidate();
+ notifyDataChanged();
+ }
+ };
+
+ public CompositeListAdapter() {
+ this(INITIAL_CAPACITY);
+ }
+
+ public CompositeListAdapter(int initialCapacity) {
+ mAdapters = new ListAdapter[INITIAL_CAPACITY];
+ mCounts = new int[INITIAL_CAPACITY];
+ mViewTypeCounts = new int[INITIAL_CAPACITY];
+ }
+
+ public void addAdapter(ListAdapter adapter) {
+ if (mSize >= mAdapters.length) {
+ int newCapacity = mSize + 2;
+ ListAdapter[] newAdapters = new ListAdapter[newCapacity];
+ System.arraycopy(mAdapters, 0, newAdapters, 0, mSize);
+ mAdapters = newAdapters;
+
+ int[] newCounts = new int[newCapacity];
+ System.arraycopy(mCounts, 0, newCounts, 0, mSize);
+ mCounts = newCounts;
+
+ int[] newViewTypeCounts = new int[newCapacity];
+ System.arraycopy(mViewTypeCounts, 0, newViewTypeCounts, 0, mSize);
+ mViewTypeCounts = newViewTypeCounts;
+ }
+
+ adapter.registerDataSetObserver(mDataSetObserver);
+
+ int count = adapter.getCount();
+ int viewTypeCount = adapter.getViewTypeCount();
+
+ mAdapters[mSize] = adapter;
+ mCounts[mSize] = count;
+ mCount += count;
+ mAllItemsEnabled &= adapter.areAllItemsEnabled();
+ mViewTypeCounts[mSize] = viewTypeCount;
+ mViewTypeCount += viewTypeCount;
+ mSize++;
+
+ notifyDataChanged();
+ }
+
+ protected void notifyDataChanged() {
+ if (getCount() > 0) {
+ notifyDataSetChanged();
+ } else {
+ notifyDataSetInvalidated();
+ }
+ }
+
+ protected void invalidate() {
+ mCacheValid = false;
+ }
+
+ protected void ensureCacheValid() {
+ if (mCacheValid) {
+ return;
+ }
+
+ mCount = 0;
+ mAllItemsEnabled = true;
+ mViewTypeCount = 0;
+ for (int i = 0; i < mSize; i++) {
+ int count = mAdapters[i].getCount();
+ int viewTypeCount = mAdapters[i].getViewTypeCount();
+ mCounts[i] = count;
+ mCount += count;
+ mAllItemsEnabled &= mAdapters[i].areAllItemsEnabled();
+ mViewTypeCount += viewTypeCount;
+ }
+
+ mCacheValid = true;
+ }
+
+ public int getCount() {
+ ensureCacheValid();
+ return mCount;
+ }
+
+ public Object getItem(int position) {
+ ensureCacheValid();
+ int start = 0;
+ for (int i = 0; i < mCounts.length; i++) {
+ int end = start + mCounts[i];
+ if (position >= start && position < end) {
+ return mAdapters[i].getItem(position - start);
+ }
+ start = end;
+ }
+
+ throw new ArrayIndexOutOfBoundsException(position);
+ }
+
+ public long getItemId(int position) {
+ ensureCacheValid();
+ int start = 0;
+ for (int i = 0; i < mCounts.length; i++) {
+ int end = start + mCounts[i];
+ if (position >= start && position < end) {
+ return mAdapters[i].getItemId(position - start);
+ }
+ start = end;
+ }
+
+ throw new ArrayIndexOutOfBoundsException(position);
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ ensureCacheValid();
+ return mViewTypeCount;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ ensureCacheValid();
+ int start = 0;
+ int viewTypeOffset = 0;
+ for (int i = 0; i < mCounts.length; i++) {
+ int end = start + mCounts[i];
+ if (position >= start && position < end) {
+ return viewTypeOffset + mAdapters[i].getItemViewType(position - start);
+ }
+ viewTypeOffset += mViewTypeCounts[i];
+ start = end;
+ }
+
+ throw new ArrayIndexOutOfBoundsException(position);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ensureCacheValid();
+ int start = 0;
+ for (int i = 0; i < mCounts.length; i++) {
+ int end = start + mCounts[i];
+ if (position >= start && position < end) {
+ return mAdapters[i].getView(position - start, convertView, parent);
+ }
+ start = end;
+ }
+
+ throw new ArrayIndexOutOfBoundsException(position);
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ ensureCacheValid();
+ return mAllItemsEnabled;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ ensureCacheValid();
+ int start = 0;
+ for (int i = 0; i < mCounts.length; i++) {
+ int end = start + mCounts[i];
+ if (position >= start && position < end) {
+ return mAdapters[i].areAllItemsEnabled()
+ || mAdapters[i].isEnabled(position - start);
+ }
+ start = end;
+ }
+
+ throw new ArrayIndexOutOfBoundsException(position);
+ }
+}
diff --git a/src/com/android/contacts/widget/ContextMenuAdapter.java b/src/com/android/contacts/widget/ContextMenuAdapter.java
new file mode 100644
index 0000000..660274a
--- /dev/null
+++ b/src/com/android/contacts/widget/ContextMenuAdapter.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.view.MenuItem;
+import android.view.View;
+
+/**
+ * An adapter for the contextual menu.
+ */
+public interface ContextMenuAdapter extends View.OnCreateContextMenuListener {
+
+ /**
+ * See {@link android.app.Activity#onContextItemSelected}.
+ */
+ boolean onContextItemSelected(MenuItem item);
+}
diff --git a/src/com/android/contacts/widget/FullHeightLinearLayout.java b/src/com/android/contacts/widget/FullHeightLinearLayout.java
new file mode 100644
index 0000000..f9548a6
--- /dev/null
+++ b/src/com/android/contacts/widget/FullHeightLinearLayout.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+/**
+ * A custom layout for dialogs that need to be stretched to the full height of the screen.
+ * It overrides the height measure specification to ignore "wrap_content" and
+ * do "match_parent" instead. The "wrap_content" part is hard-coded in the framework
+ * implementation of the dialog theme.
+ */
+public final class FullHeightLinearLayout extends LinearLayout {
+
+ public FullHeightLinearLayout(Context context) {
+ super(context);
+ }
+
+ public FullHeightLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public FullHeightLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY);
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+}
diff --git a/src/com/android/contacts/widget/IndexerListAdapter.java b/src/com/android/contacts/widget/IndexerListAdapter.java
new file mode 100644
index 0000000..4cd7af0
--- /dev/null
+++ b/src/com/android/contacts/widget/IndexerListAdapter.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+
+/**
+ * A list adapter that supports section indexer and a pinned header.
+ */
+public abstract class IndexerListAdapter extends PinnedHeaderListAdapter implements SectionIndexer {
+
+ protected Context mContext;
+ private SectionIndexer mIndexer;
+ private int mIndexedPartition = 0;
+ private boolean mSectionHeaderDisplayEnabled;
+ private View mHeader;
+
+ /**
+ * An item view is displayed differently depending on whether it is placed
+ * at the beginning, middle or end of a section. It also needs to know the
+ * section header when it is at the beginning of a section. This object
+ * captures all this configuration.
+ */
+ public static final class Placement {
+ private int position = ListView.INVALID_POSITION;
+ public boolean firstInSection;
+ public boolean lastInSection;
+ public String sectionHeader;
+
+ public void invalidate() {
+ position = ListView.INVALID_POSITION;
+ }
+ }
+
+ private Placement mPlacementCache = new Placement();
+
+ /**
+ * Constructor.
+ */
+ public IndexerListAdapter(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ /**
+ * Creates a section header view that will be pinned at the top of the list
+ * as the user scrolls.
+ */
+ protected abstract View createPinnedSectionHeaderView(Context context, ViewGroup parent);
+
+ /**
+ * Sets the title in the pinned header as the user scrolls.
+ */
+ protected abstract void setPinnedSectionTitle(View pinnedHeaderView, String title);
+
+ public boolean isSectionHeaderDisplayEnabled() {
+ return mSectionHeaderDisplayEnabled;
+ }
+
+ public void setSectionHeaderDisplayEnabled(boolean flag) {
+ this.mSectionHeaderDisplayEnabled = flag;
+ }
+
+ public int getIndexedPartition() {
+ return mIndexedPartition;
+ }
+
+ public void setIndexedPartition(int partition) {
+ this.mIndexedPartition = partition;
+ }
+
+ public SectionIndexer getIndexer() {
+ return mIndexer;
+ }
+
+ public void setIndexer(SectionIndexer indexer) {
+ mIndexer = indexer;
+ mPlacementCache.invalidate();
+ }
+
+ public Object[] getSections() {
+ if (mIndexer == null) {
+ return new String[] { " " };
+ } else {
+ return mIndexer.getSections();
+ }
+ }
+
+ /**
+ * @return relative position of the section in the indexed partition
+ */
+ public int getPositionForSection(int sectionIndex) {
+ if (mIndexer == null) {
+ return -1;
+ }
+
+ return mIndexer.getPositionForSection(sectionIndex);
+ }
+
+ /**
+ * @param position relative position in the indexed partition
+ */
+ public int getSectionForPosition(int position) {
+ if (mIndexer == null) {
+ return -1;
+ }
+
+ return mIndexer.getSectionForPosition(position);
+ }
+
+ @Override
+ public int getPinnedHeaderCount() {
+ if (isSectionHeaderDisplayEnabled()) {
+ return super.getPinnedHeaderCount() + 1;
+ } else {
+ return super.getPinnedHeaderCount();
+ }
+ }
+
+ @Override
+ public View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent) {
+ if (isSectionHeaderDisplayEnabled() && viewIndex == getPinnedHeaderCount() - 1) {
+ if (mHeader == null) {
+ mHeader = createPinnedSectionHeaderView(mContext, parent);
+ }
+ return mHeader;
+ } else {
+ return super.getPinnedHeaderView(viewIndex, convertView, parent);
+ }
+ }
+
+ @Override
+ public void configurePinnedHeaders(PinnedHeaderListView listView) {
+ super.configurePinnedHeaders(listView);
+
+ if (!isSectionHeaderDisplayEnabled()) {
+ return;
+ }
+
+ int index = getPinnedHeaderCount() - 1;
+ if (mIndexer == null || getCount() == 0) {
+ listView.setHeaderInvisible(index, false);
+ } else {
+ int listPosition = listView.getPositionAt(listView.getTotalTopPinnedHeaderHeight());
+ int position = listPosition - listView.getHeaderViewsCount();
+
+ int section = -1;
+ int partition = getPartitionForPosition(position);
+ if (partition == mIndexedPartition) {
+ int offset = getOffsetInPartition(position);
+ if (offset != -1) {
+ section = getSectionForPosition(offset);
+ }
+ }
+
+ if (section == -1) {
+ listView.setHeaderInvisible(index, false);
+ } else {
+ setPinnedSectionTitle(mHeader, (String)mIndexer.getSections()[section]);
+
+ // Compute the item position where the current partition begins
+ int partitionStart = getPositionForPartition(mIndexedPartition);
+ if (hasHeader(mIndexedPartition)) {
+ partitionStart++;
+ }
+
+ // Compute the item position where the next section begins
+ int nextSectionPosition = partitionStart + getPositionForSection(section + 1);
+ boolean isLastInSection = position == nextSectionPosition - 1;
+ listView.setFadingHeader(index, listPosition, isLastInSection);
+ }
+ }
+ }
+
+ /**
+ * Computes the item's placement within its section and populates the {@code placement}
+ * object accordingly. Please note that the returned object is volatile and should be
+ * copied if the result needs to be used later.
+ */
+ public Placement getItemPlacementInSection(int position) {
+ if (mPlacementCache.position == position) {
+ return mPlacementCache;
+ }
+
+ mPlacementCache.position = position;
+ if (isSectionHeaderDisplayEnabled()) {
+ int section = getSectionForPosition(position);
+ if (section != -1 && getPositionForSection(section) == position) {
+ mPlacementCache.firstInSection = true;
+ mPlacementCache.sectionHeader = (String)getSections()[section];
+ } else {
+ mPlacementCache.firstInSection = false;
+ mPlacementCache.sectionHeader = null;
+ }
+
+ mPlacementCache.lastInSection = (getPositionForSection(section + 1) - 1 == position);
+ } else {
+ mPlacementCache.firstInSection = false;
+ mPlacementCache.lastInSection = false;
+ mPlacementCache.sectionHeader = null;
+ }
+ return mPlacementCache;
+ }
+}
diff --git a/src/com/android/contacts/widget/InterpolatingLayout.java b/src/com/android/contacts/widget/InterpolatingLayout.java
new file mode 100644
index 0000000..fc67ef5
--- /dev/null
+++ b/src/com/android/contacts/widget/InterpolatingLayout.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+/**
+ * Layout similar to LinearLayout that allows a child to specify examples of
+ * desired size depending on the parent size. For example if the widget wants to
+ * be 100dip when parent is 200dip and 110dip when parent is 400dip, the layout
+ * will ensure these requirements and interpolate for other parent sizes.
+ * You can also specify minWidth for each child. You can have at most one
+ * child with layout_width="match_parent" - it will take the entire remaining
+ * space.
+ */
+public class InterpolatingLayout extends ViewGroup {
+
+ private Rect mInRect = new Rect();
+ private Rect mOutRect = new Rect();
+
+ public InterpolatingLayout(Context context) {
+ super(context);
+ }
+
+ public InterpolatingLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public InterpolatingLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public final static class LayoutParams extends LinearLayout.LayoutParams {
+
+ public int narrowParentWidth;
+ public int narrowWidth;
+ public int narrowMarginLeft;
+ public int narrowPaddingLeft;
+ public int narrowMarginRight;
+ public int narrowPaddingRight;
+ public int wideParentWidth;
+ public int wideWidth;
+ public int wideMarginLeft;
+ public int widePaddingLeft;
+ public int wideMarginRight;
+ public int widePaddingRight;
+ private float widthMultiplier;
+ private int widthConstant;
+ private float leftMarginMultiplier;
+ private int leftMarginConstant;
+ private float leftPaddingMultiplier;
+ private int leftPaddingConstant;
+ private float rightMarginMultiplier;
+ private int rightMarginConstant;
+ private float rightPaddingMultiplier;
+ private int rightPaddingConstant;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.InterpolatingLayout_Layout);
+
+ narrowParentWidth = a.getDimensionPixelSize(
+ R.styleable.InterpolatingLayout_Layout_layout_narrowParentWidth, -1);
+ narrowWidth = a.getDimensionPixelSize(
+ R.styleable.InterpolatingLayout_Layout_layout_narrowWidth, -1);
+ narrowMarginLeft = a.getDimensionPixelSize(
+ R.styleable.InterpolatingLayout_Layout_layout_narrowMarginLeft, -1);
+ narrowPaddingLeft = a.getDimensionPixelSize(
+ R.styleable.InterpolatingLayout_Layout_layout_narrowPaddingLeft, -1);
+ narrowMarginRight = a.getDimensionPixelSize(
+ R.styleable.InterpolatingLayout_Layout_layout_narrowMarginRight, -1);
+ narrowPaddingRight = a.getDimensionPixelSize(
+ R.styleable.InterpolatingLayout_Layout_layout_narrowPaddingRight, -1);
+ wideParentWidth = a.getDimensionPixelSize(
+ R.styleable.InterpolatingLayout_Layout_layout_wideParentWidth, -1);
+ wideWidth = a.getDimensionPixelSize(
+ R.styleable.InterpolatingLayout_Layout_layout_wideWidth, -1);
+ wideMarginLeft = a.getDimensionPixelSize(
+ R.styleable.InterpolatingLayout_Layout_layout_wideMarginLeft, -1);
+ widePaddingLeft = a.getDimensionPixelSize(
+ R.styleable.InterpolatingLayout_Layout_layout_widePaddingLeft, -1);
+ wideMarginRight = a.getDimensionPixelSize(
+ R.styleable.InterpolatingLayout_Layout_layout_wideMarginRight, -1);
+ widePaddingRight = a.getDimensionPixelSize(
+ R.styleable.InterpolatingLayout_Layout_layout_widePaddingRight, -1);
+
+ a.recycle();
+
+ if (narrowWidth != -1) {
+ widthMultiplier = (float) (wideWidth - narrowWidth)
+ / (wideParentWidth - narrowParentWidth);
+ widthConstant = (int) (narrowWidth - narrowParentWidth * widthMultiplier);
+ }
+
+ if (narrowMarginLeft != -1) {
+ leftMarginMultiplier = (float) (wideMarginLeft - narrowMarginLeft)
+ / (wideParentWidth - narrowParentWidth);
+ leftMarginConstant = (int) (narrowMarginLeft - narrowParentWidth
+ * leftMarginMultiplier);
+ }
+
+ if (narrowPaddingLeft != -1) {
+ leftPaddingMultiplier = (float) (widePaddingLeft - narrowPaddingLeft)
+ / (wideParentWidth - narrowParentWidth);
+ leftPaddingConstant = (int) (narrowPaddingLeft - narrowParentWidth
+ * leftPaddingMultiplier);
+ }
+
+ if (narrowMarginRight != -1) {
+ rightMarginMultiplier = (float) (wideMarginRight - narrowMarginRight)
+ / (wideParentWidth - narrowParentWidth);
+ rightMarginConstant = (int) (narrowMarginRight - narrowParentWidth
+ * rightMarginMultiplier);
+ }
+
+ if (narrowPaddingRight != -1) {
+ rightPaddingMultiplier = (float) (widePaddingRight - narrowPaddingRight)
+ / (wideParentWidth - narrowParentWidth);
+ rightPaddingConstant = (int) (narrowPaddingRight - narrowParentWidth
+ * rightPaddingMultiplier);
+ }
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(MarginLayoutParams source) {
+ super(source);
+ }
+
+ public int resolveWidth(int parentSize) {
+ if (narrowWidth == -1) {
+ return width;
+ } else {
+ int w = (int) (parentSize * widthMultiplier) + widthConstant;
+ return w <= 0 ? WRAP_CONTENT : w;
+ }
+ }
+
+ public int resolveLeftMargin(int parentSize) {
+ if (narrowMarginLeft == -1) {
+ return leftMargin;
+ } else {
+ int w = (int) (parentSize * leftMarginMultiplier) + leftMarginConstant;
+ return w < 0 ? 0 : w;
+ }
+ }
+
+ public int resolveLeftPadding(int parentSize) {
+ int w = (int) (parentSize * leftPaddingMultiplier) + leftPaddingConstant;
+ return w < 0 ? 0 : w;
+ }
+
+ public int resolveRightMargin(int parentSize) {
+ if (narrowMarginRight == -1) {
+ return rightMargin;
+ } else {
+ int w = (int) (parentSize * rightMarginMultiplier) + rightMarginConstant;
+ return w < 0 ? 0 : w;
+ }
+ }
+
+ public int resolveRightPadding(int parentSize) {
+ int w = (int) (parentSize * rightPaddingMultiplier) + rightPaddingConstant;
+ return w < 0 ? 0 : w;
+ }
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
+
+ int width = 0;
+ int height = 0;
+
+ View fillChild = null;
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() == View.GONE) {
+ continue;
+ }
+
+ LayoutParams params = (LayoutParams) child.getLayoutParams();
+ if (params.width == LayoutParams.MATCH_PARENT) {
+ if (fillChild != null) {
+ throw new RuntimeException(
+ "Interpolating layout allows at most one child"
+ + " with layout_width='match_parent'");
+ }
+ fillChild = child;
+ } else {
+ int childWidth = params.resolveWidth(parentWidth);
+ int childWidthMeasureSpec;
+ switch (childWidth) {
+ case LayoutParams.WRAP_CONTENT:
+ childWidthMeasureSpec = MeasureSpec.UNSPECIFIED;
+ break;
+ default:
+ childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ childWidth, MeasureSpec.EXACTLY);
+ break;
+ }
+
+ int childHeightMeasureSpec;
+ switch (params.height) {
+ case LayoutParams.WRAP_CONTENT:
+ childHeightMeasureSpec = MeasureSpec.UNSPECIFIED;
+ break;
+ case LayoutParams.MATCH_PARENT:
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ parentHeight - params.topMargin - params.bottomMargin,
+ MeasureSpec.EXACTLY);
+ break;
+ default:
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ params.height, MeasureSpec.EXACTLY);
+ break;
+ }
+
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ width += child.getMeasuredWidth();
+ height = Math.max(child.getMeasuredHeight(), height);
+ }
+
+ width += params.resolveLeftMargin(parentWidth) + params.resolveRightMargin(parentWidth);
+ }
+
+ if (fillChild != null) {
+ int remainder = parentWidth - width;
+ int childMeasureSpec = remainder > 0
+ ? MeasureSpec.makeMeasureSpec(remainder, MeasureSpec.EXACTLY)
+ : MeasureSpec.UNSPECIFIED;
+ fillChild.measure(childMeasureSpec, heightMeasureSpec);
+ width += fillChild.getMeasuredWidth();
+ height = Math.max(fillChild.getMeasuredHeight(), height);
+ }
+
+ setMeasuredDimension(
+ resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ int offset = 0;
+ int width = right - left;
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+
+ LayoutParams params = (LayoutParams) child.getLayoutParams();
+ int gravity = params.gravity;
+ if (gravity == -1) {
+ gravity = Gravity.LEFT | Gravity.TOP;
+ }
+
+ if (params.narrowPaddingLeft != -1 || params.narrowPaddingRight != -1) {
+ int leftPadding = params.narrowPaddingLeft == -1 ? child.getPaddingLeft()
+ : params.resolveLeftPadding(width);
+ int rightPadding = params.narrowPaddingRight == -1 ? child.getPaddingRight()
+ : params.resolveRightPadding(width);
+ child.setPadding(
+ leftPadding, child.getPaddingTop(), rightPadding, child.getPaddingBottom());
+ }
+
+ int leftMargin = params.resolveLeftMargin(width);
+ int rightMargin = params.resolveRightMargin(width);
+
+ mInRect.set(offset + leftMargin, params.topMargin,
+ right - rightMargin, bottom - params.bottomMargin);
+
+ Gravity.apply(gravity, child.getMeasuredWidth(), child.getMeasuredHeight(),
+ mInRect, mOutRect);
+ child.layout(mOutRect.left, mOutRect.top, mOutRect.right, mOutRect.bottom);
+
+ offset = mOutRect.right + rightMargin;
+ }
+ }
+}
diff --git a/src/com/android/contacts/widget/NotifyingSpinner.java b/src/com/android/contacts/widget/NotifyingSpinner.java
new file mode 100644
index 0000000..972cb35
--- /dev/null
+++ b/src/com/android/contacts/widget/NotifyingSpinner.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Spinner;
+
+/**
+ * Spinner that notifies a listener when the user taps on an item, whether or not this results
+ * in a change of selection.
+ */
+public class NotifyingSpinner extends Spinner {
+
+ public interface SelectionListener {
+ void onSetSelection(NotifyingSpinner view, int position);
+ }
+
+ private SelectionListener mListener;
+
+ public NotifyingSpinner(Context context) {
+ super(context);
+ }
+
+ public NotifyingSpinner(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setSetSelectionListener(SelectionListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void setSelection(int position) {
+ super.setSelection(position);
+
+ if (mListener != null) {
+ mListener.onSetSelection(this, position);
+ }
+ }
+}
diff --git a/src/com/android/contacts/widget/PinnedHeaderListAdapter.java b/src/com/android/contacts/widget/PinnedHeaderListAdapter.java
new file mode 100644
index 0000000..a4d375e
--- /dev/null
+++ b/src/com/android/contacts/widget/PinnedHeaderListAdapter.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import com.android.common.widget.CompositeCursorAdapter;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A subclass of {@link CompositeCursorAdapter} that manages pinned partition headers.
+ */
+public abstract class PinnedHeaderListAdapter extends CompositeCursorAdapter
+ implements PinnedHeaderListView.PinnedHeaderAdapter {
+
+ public static final int PARTITION_HEADER_TYPE = 0;
+
+ private boolean mPinnedPartitionHeadersEnabled;
+ private boolean mHeaderVisibility[];
+
+ public PinnedHeaderListAdapter(Context context) {
+ super(context);
+ }
+
+ public PinnedHeaderListAdapter(Context context, int initialCapacity) {
+ super(context, initialCapacity);
+ }
+
+ public boolean getPinnedPartitionHeadersEnabled() {
+ return mPinnedPartitionHeadersEnabled;
+ }
+
+ public void setPinnedPartitionHeadersEnabled(boolean flag) {
+ this.mPinnedPartitionHeadersEnabled = flag;
+ }
+
+ public int getPinnedHeaderCount() {
+ if (mPinnedPartitionHeadersEnabled) {
+ return getPartitionCount();
+ } else {
+ return 0;
+ }
+ }
+
+ protected boolean isPinnedPartitionHeaderVisible(int partition) {
+ return mPinnedPartitionHeadersEnabled && hasHeader(partition)
+ && !isPartitionEmpty(partition);
+ }
+
+ /**
+ * The default implementation creates the same type of view as a normal
+ * partition header.
+ */
+ public View getPinnedHeaderView(int partition, View convertView, ViewGroup parent) {
+ if (hasHeader(partition)) {
+ View view = null;
+ if (convertView != null) {
+ Integer headerType = (Integer)convertView.getTag();
+ if (headerType != null && headerType == PARTITION_HEADER_TYPE) {
+ view = convertView;
+ }
+ }
+ if (view == null) {
+ view = newHeaderView(getContext(), partition, null, parent);
+ view.setTag(PARTITION_HEADER_TYPE);
+ view.setFocusable(false);
+ view.setEnabled(false);
+ }
+ bindHeaderView(view, partition, getCursor(partition));
+ return view;
+ } else {
+ return null;
+ }
+ }
+
+ public void configurePinnedHeaders(PinnedHeaderListView listView) {
+ if (!mPinnedPartitionHeadersEnabled) {
+ return;
+ }
+
+ int size = getPartitionCount();
+
+ // Cache visibility bits, because we will need them several times later on
+ if (mHeaderVisibility == null || mHeaderVisibility.length != size) {
+ mHeaderVisibility = new boolean[size];
+ }
+ for (int i = 0; i < size; i++) {
+ boolean visible = isPinnedPartitionHeaderVisible(i);
+ mHeaderVisibility[i] = visible;
+ if (!visible) {
+ listView.setHeaderInvisible(i, true);
+ }
+ }
+
+ int headerViewsCount = listView.getHeaderViewsCount();
+
+ // Starting at the top, find and pin headers for partitions preceding the visible one(s)
+ int maxTopHeader = -1;
+ int topHeaderHeight = 0;
+ for (int i = 0; i < size; i++) {
+ if (mHeaderVisibility[i]) {
+ int position = listView.getPositionAt(topHeaderHeight) - headerViewsCount;
+ int partition = getPartitionForPosition(position);
+ if (i > partition) {
+ break;
+ }
+
+ listView.setHeaderPinnedAtTop(i, topHeaderHeight, false);
+ topHeaderHeight += listView.getPinnedHeaderHeight(i);
+ maxTopHeader = i;
+ }
+ }
+
+ // Starting at the bottom, find and pin headers for partitions following the visible one(s)
+ int maxBottomHeader = size;
+ int bottomHeaderHeight = 0;
+ int listHeight = listView.getHeight();
+ for (int i = size; --i > maxTopHeader;) {
+ if (mHeaderVisibility[i]) {
+ int position = listView.getPositionAt(listHeight - bottomHeaderHeight)
+ - headerViewsCount;
+ if (position < 0) {
+ break;
+ }
+
+ int partition = getPartitionForPosition(position - 1);
+ if (partition == -1 || i <= partition) {
+ break;
+ }
+
+ int height = listView.getPinnedHeaderHeight(i);
+ bottomHeaderHeight += height;
+ // Animate the header only if the partition is completely invisible below
+ // the bottom of the view
+ int firstPositionForPartition = getPositionForPartition(i);
+ boolean animate = position < firstPositionForPartition;
+ listView.setHeaderPinnedAtBottom(i, listHeight - bottomHeaderHeight, animate);
+ maxBottomHeader = i;
+ }
+ }
+
+ // Headers in between the top-pinned and bottom-pinned should be hidden
+ for (int i = maxTopHeader + 1; i < maxBottomHeader; i++) {
+ if (mHeaderVisibility[i]) {
+ listView.setHeaderInvisible(i, isPartitionEmpty(i));
+ }
+ }
+ }
+
+ public int getScrollPositionForHeader(int viewIndex) {
+ return getPositionForPartition(viewIndex);
+ }
+}
diff --git a/src/com/android/contacts/widget/PinnedHeaderListDemoActivity.java b/src/com/android/contacts/widget/PinnedHeaderListDemoActivity.java
new file mode 100644
index 0000000..b553e3f
--- /dev/null
+++ b/src/com/android/contacts/widget/PinnedHeaderListDemoActivity.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import com.android.contacts.R;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * An activity that demonstrates various use cases for the {@link PinnedHeaderListView}.
+ * If we decide to move PinnedHeaderListView to the framework, this class could go
+ * to API demos.
+ */
+public class PinnedHeaderListDemoActivity extends ListActivity {
+
+ public final static class TestPinnedHeaderListAdapter extends PinnedHeaderListAdapter {
+
+ public TestPinnedHeaderListAdapter(Context context) {
+ super(context);
+ setPinnedPartitionHeadersEnabled(true);
+ }
+
+ private String[] mHeaders;
+ private int mPinnedHeaderCount;
+
+ public void setHeaders(String[] headers) {
+ this.mHeaders = headers;
+ }
+
+ @Override
+ protected View newHeaderView(Context context, int partition, Cursor cursor,
+ ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ return inflater.inflate(R.layout.list_section, null);
+ }
+
+ @Override
+ protected void bindHeaderView(View view, int parition, Cursor cursor) {
+ TextView headerText = (TextView)view.findViewById(R.id.header_text);
+ headerText.setText(mHeaders[parition]);
+ }
+
+ @Override
+ protected View newView(Context context, int partition, Cursor cursor, int position,
+ ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ return inflater.inflate(android.R.layout.simple_list_item_1, null);
+ }
+
+ @Override
+ protected void bindView(View v, int partition, Cursor cursor, int position) {
+ TextView text = (TextView)v.findViewById(android.R.id.text1);
+ text.setText(cursor.getString(1));
+ }
+
+ @Override
+ public View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ View view = inflater.inflate(R.layout.list_section, parent, false);
+ view.setFocusable(false);
+ view.setEnabled(false);
+ bindHeaderView(view, viewIndex, null);
+ return view;
+ }
+
+ @Override
+ public int getPinnedHeaderCount() {
+ return mPinnedHeaderCount;
+ }
+ }
+
+ private Handler mHandler = new Handler();
+
+ @Override
+ protected void onCreate(Bundle bundle) {
+ super.onCreate(bundle);
+
+ setContentView(R.layout.pinned_header_list_demo);
+
+ final TestPinnedHeaderListAdapter adapter = new TestPinnedHeaderListAdapter(this);
+
+ Bundle extras = getIntent().getExtras();
+ int[] counts = extras.getIntArray("counts");
+ String[] names = extras.getStringArray("names");
+ boolean[] showIfEmpty = extras.getBooleanArray("showIfEmpty");
+ boolean[] hasHeader = extras.getBooleanArray("headers");
+ int[] delays = extras.getIntArray("delays");
+
+ if (counts == null || names == null || showIfEmpty == null || delays == null) {
+ throw new IllegalArgumentException("Missing required extras");
+ }
+
+ adapter.setHeaders(names);
+ for (int i = 0; i < counts.length; i++) {
+ adapter.addPartition(showIfEmpty[i], names[i] != null);
+ adapter.mPinnedHeaderCount = names.length;
+ }
+ setListAdapter(adapter);
+ for (int i = 0; i < counts.length; i++) {
+ final int sectionId = i;
+ final Cursor cursor = makeCursor(names[i], counts[i]);
+ mHandler.postDelayed(new Runnable() {
+
+ public void run() {
+ adapter.changeCursor(sectionId, cursor);
+
+ }
+ }, delays[i]);
+ }
+ }
+
+ private Cursor makeCursor(String name, int count) {
+ MatrixCursor cursor = new MatrixCursor(new String[]{"_id", name});
+ for (int i = 0; i < count; i++) {
+ cursor.addRow(new Object[]{i, name + "[" + i + "]"});
+ }
+ return cursor;
+ }
+}
diff --git a/src/com/android/contacts/widget/PinnedHeaderListView.java b/src/com/android/contacts/widget/PinnedHeaderListView.java
new file mode 100644
index 0000000..cef7203
--- /dev/null
+++ b/src/com/android/contacts/widget/PinnedHeaderListView.java
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ListAdapter;
+
+/**
+ * A ListView that maintains a header pinned at the top of the list. The
+ * pinned header can be pushed up and dissolved as needed.
+ */
+public class PinnedHeaderListView extends AutoScrollListView
+ implements OnScrollListener, OnItemSelectedListener {
+
+ /**
+ * Adapter interface. The list adapter must implement this interface.
+ */
+ public interface PinnedHeaderAdapter {
+
+ /**
+ * Returns the overall number of pinned headers, visible or not.
+ */
+ int getPinnedHeaderCount();
+
+ /**
+ * Creates or updates the pinned header view.
+ */
+ View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent);
+
+ /**
+ * Configures the pinned headers to match the visible list items. The
+ * adapter should call {@link PinnedHeaderListView#setHeaderPinnedAtTop},
+ * {@link PinnedHeaderListView#setHeaderPinnedAtBottom},
+ * {@link PinnedHeaderListView#setFadingHeader} or
+ * {@link PinnedHeaderListView#setHeaderInvisible}, for each header that
+ * needs to change its position or visibility.
+ */
+ void configurePinnedHeaders(PinnedHeaderListView listView);
+
+ /**
+ * Returns the list position to scroll to if the pinned header is touched.
+ * Return -1 if the list does not need to be scrolled.
+ */
+ int getScrollPositionForHeader(int viewIndex);
+ }
+
+ private static final int MAX_ALPHA = 255;
+ private static final int TOP = 0;
+ private static final int BOTTOM = 1;
+ private static final int FADING = 2;
+
+ private static final int DEFAULT_ANIMATION_DURATION = 100;
+
+ private static final class PinnedHeader {
+ View view;
+ boolean visible;
+ int y;
+ int height;
+ int alpha;
+ int state;
+
+ boolean animating;
+ boolean targetVisible;
+ int sourceY;
+ int targetY;
+ long targetTime;
+ }
+
+ private PinnedHeaderAdapter mAdapter;
+ private int mSize;
+ private PinnedHeader[] mHeaders;
+ private RectF mBounds = new RectF();
+ private Rect mClipRect = new Rect();
+ private OnScrollListener mOnScrollListener;
+ private OnItemSelectedListener mOnItemSelectedListener;
+ private int mScrollState;
+
+ private int mAnimationDuration = DEFAULT_ANIMATION_DURATION;
+ private boolean mAnimating;
+ private long mAnimationTargetTime;
+ private int mHeaderPaddingLeft;
+ private int mHeaderWidth;
+
+ public PinnedHeaderListView(Context context) {
+ this(context, null, com.android.internal.R.attr.listViewStyle);
+ }
+
+ public PinnedHeaderListView(Context context, AttributeSet attrs) {
+ this(context, attrs, com.android.internal.R.attr.listViewStyle);
+ }
+
+ public PinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ super.setOnScrollListener(this);
+ super.setOnItemSelectedListener(this);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ mHeaderPaddingLeft = getPaddingLeft();
+ mHeaderWidth = r - l - mHeaderPaddingLeft - getPaddingRight();
+ }
+
+ public void setPinnedHeaderAnimationDuration(int duration) {
+ mAnimationDuration = duration;
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ mAdapter = (PinnedHeaderAdapter)adapter;
+ super.setAdapter(adapter);
+ }
+
+ @Override
+ public void setOnScrollListener(OnScrollListener onScrollListener) {
+ mOnScrollListener = onScrollListener;
+ super.setOnScrollListener(this);
+ }
+
+ @Override
+ public void setOnItemSelectedListener(OnItemSelectedListener listener) {
+ mOnItemSelectedListener = listener;
+ super.setOnItemSelectedListener(this);
+ }
+
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ if (mAdapter != null) {
+ int count = mAdapter.getPinnedHeaderCount();
+ if (count != mSize) {
+ mSize = count;
+ if (mHeaders == null) {
+ mHeaders = new PinnedHeader[mSize];
+ } else if (mHeaders.length < mSize) {
+ PinnedHeader[] headers = mHeaders;
+ mHeaders = new PinnedHeader[mSize];
+ System.arraycopy(headers, 0, mHeaders, 0, headers.length);
+ }
+ }
+
+ for (int i = 0; i < mSize; i++) {
+ if (mHeaders[i] == null) {
+ mHeaders[i] = new PinnedHeader();
+ }
+ mHeaders[i].view = mAdapter.getPinnedHeaderView(i, mHeaders[i].view, this);
+ }
+
+ mAnimationTargetTime = System.currentTimeMillis() + mAnimationDuration;
+ mAdapter.configurePinnedHeaders(this);
+ invalidateIfAnimating();
+
+ }
+ if (mOnScrollListener != null) {
+ mOnScrollListener.onScroll(this, firstVisibleItem, visibleItemCount, totalItemCount);
+ }
+ }
+
+ @Override
+ protected float getTopFadingEdgeStrength() {
+ // Disable vertical fading at the top when the pinned header is present
+ return mSize > 0 ? 0 : super.getTopFadingEdgeStrength();
+ }
+
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ mScrollState = scrollState;
+ if (mOnScrollListener != null) {
+ mOnScrollListener.onScrollStateChanged(this, scrollState);
+ }
+ }
+
+ /**
+ * Ensures that the selected item is positioned below the top-pinned headers
+ * and above the bottom-pinned ones.
+ */
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ int height = getHeight();
+
+ int windowTop = 0;
+ int windowBottom = height;
+
+ int prevHeaderBottom = 0;
+ for (int i = 0; i < mSize; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible) {
+ if (header.state == TOP) {
+ windowTop = header.y + header.height;
+ } else if (header.state == BOTTOM) {
+ windowBottom = header.y;
+ break;
+ }
+ }
+ }
+
+ View selectedView = getSelectedView();
+ if (selectedView != null) {
+ if (selectedView.getTop() < windowTop) {
+ setSelectionFromTop(position, windowTop);
+ } else if (selectedView.getBottom() > windowBottom) {
+ setSelectionFromTop(position, windowBottom - selectedView.getHeight());
+ }
+ }
+
+ if (mOnItemSelectedListener != null) {
+ mOnItemSelectedListener.onItemSelected(parent, view, position, id);
+ }
+ }
+
+ public void onNothingSelected(AdapterView<?> parent) {
+ if (mOnItemSelectedListener != null) {
+ mOnItemSelectedListener.onNothingSelected(parent);
+ }
+ }
+
+ public int getPinnedHeaderHeight(int viewIndex) {
+ ensurePinnedHeaderLayout(viewIndex);
+ return mHeaders[viewIndex].view.getHeight();
+ }
+
+ /**
+ * Set header to be pinned at the top.
+ *
+ * @param viewIndex index of the header view
+ * @param y is position of the header in pixels.
+ * @param animate true if the transition to the new coordinate should be animated
+ */
+ public void setHeaderPinnedAtTop(int viewIndex, int y, boolean animate) {
+ ensurePinnedHeaderLayout(viewIndex);
+ PinnedHeader header = mHeaders[viewIndex];
+ header.visible = true;
+ header.y = y;
+ header.state = TOP;
+
+ // TODO perhaps we should animate at the top as well
+ header.animating = false;
+ }
+
+ /**
+ * Set header to be pinned at the bottom.
+ *
+ * @param viewIndex index of the header view
+ * @param y is position of the header in pixels.
+ * @param animate true if the transition to the new coordinate should be animated
+ */
+ public void setHeaderPinnedAtBottom(int viewIndex, int y, boolean animate) {
+ ensurePinnedHeaderLayout(viewIndex);
+ PinnedHeader header = mHeaders[viewIndex];
+ header.state = BOTTOM;
+ if (header.animating) {
+ header.targetTime = mAnimationTargetTime;
+ header.sourceY = header.y;
+ header.targetY = y;
+ } else if (animate && (header.y != y || !header.visible)) {
+ if (header.visible) {
+ header.sourceY = header.y;
+ } else {
+ header.visible = true;
+ header.sourceY = y + header.height;
+ }
+ header.animating = true;
+ header.targetVisible = true;
+ header.targetTime = mAnimationTargetTime;
+ header.targetY = y;
+ } else {
+ header.visible = true;
+ header.y = y;
+ }
+ }
+
+ /**
+ * Set header to be pinned at the top of the first visible item.
+ *
+ * @param viewIndex index of the header view
+ * @param position is position of the header in pixels.
+ */
+ public void setFadingHeader(int viewIndex, int position, boolean fade) {
+ ensurePinnedHeaderLayout(viewIndex);
+
+ View child = getChildAt(position - getFirstVisiblePosition());
+ if (child == null) return;
+
+ PinnedHeader header = mHeaders[viewIndex];
+ header.visible = true;
+ header.state = FADING;
+ header.alpha = MAX_ALPHA;
+ header.animating = false;
+
+ int top = getTotalTopPinnedHeaderHeight();
+ header.y = top;
+ if (fade) {
+ int bottom = child.getBottom() - top;
+ int headerHeight = header.height;
+ if (bottom < headerHeight) {
+ int portion = bottom - headerHeight;
+ header.alpha = MAX_ALPHA * (headerHeight + portion) / headerHeight;
+ header.y = top + portion;
+ }
+ }
+ }
+
+ /**
+ * Makes header invisible.
+ *
+ * @param viewIndex index of the header view
+ * @param animate true if the transition to the new coordinate should be animated
+ */
+ public void setHeaderInvisible(int viewIndex, boolean animate) {
+ PinnedHeader header = mHeaders[viewIndex];
+ if (header.visible && (animate || header.animating) && header.state == BOTTOM) {
+ header.sourceY = header.y;
+ if (!header.animating) {
+ header.visible = true;
+ header.targetY = getBottom() + header.height;
+ }
+ header.animating = true;
+ header.targetTime = mAnimationTargetTime;
+ header.targetVisible = false;
+ } else {
+ header.visible = false;
+ }
+ }
+
+ private void ensurePinnedHeaderLayout(int viewIndex) {
+ View view = mHeaders[viewIndex].view;
+ if (view.isLayoutRequested()) {
+ int widthSpec = MeasureSpec.makeMeasureSpec(mHeaderWidth, MeasureSpec.EXACTLY);
+ int heightSpec;
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ if (layoutParams != null && layoutParams.height > 0) {
+ heightSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY);
+ } else {
+ heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ }
+ view.measure(widthSpec, heightSpec);
+ int height = view.getMeasuredHeight();
+ mHeaders[viewIndex].height = height;
+ view.layout(0, 0, mHeaderWidth, height);
+ }
+ }
+
+ /**
+ * Returns the sum of heights of headers pinned to the top.
+ */
+ public int getTotalTopPinnedHeaderHeight() {
+ for (int i = mSize; --i >= 0;) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && header.state == TOP) {
+ return header.y + header.height;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the list item position at the specified y coordinate.
+ */
+ public int getPositionAt(int y) {
+ do {
+ int position = pointToPosition(getPaddingLeft() + 1, y);
+ if (position != -1) {
+ return position;
+ }
+ // If position == -1, we must have hit a separator. Let's examine
+ // a nearby pixel
+ y--;
+ } while (y > 0);
+ return 0;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (mScrollState == SCROLL_STATE_IDLE) {
+ final int y = (int)ev.getY();
+ for (int i = mSize; --i >= 0;) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && header.y <= y && header.y + header.height > y) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ return smoothScrollToPartition(i);
+ } else {
+ return true;
+ }
+ }
+ }
+ }
+
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ private boolean smoothScrollToPartition(int partition) {
+ final int position = mAdapter.getScrollPositionForHeader(partition);
+ if (position == -1) {
+ return false;
+ }
+
+ int offset = 0;
+ for (int i = 0; i < partition; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible) {
+ offset += header.height;
+ }
+ }
+
+ smoothScrollToPositionFromTop(position + getHeaderViewsCount(), offset);
+ return true;
+ }
+
+ private void invalidateIfAnimating() {
+ mAnimating = false;
+ for (int i = 0; i < mSize; i++) {
+ if (mHeaders[i].animating) {
+ mAnimating = true;
+ invalidate();
+ return;
+ }
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ long currentTime = mAnimating ? System.currentTimeMillis() : 0;
+
+ int top = 0;
+ int bottom = getBottom();
+ boolean hasVisibleHeaders = false;
+ for (int i = 0; i < mSize; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible) {
+ hasVisibleHeaders = true;
+ if (header.state == BOTTOM && header.y < bottom) {
+ bottom = header.y;
+ } else if (header.state == TOP || header.state == FADING) {
+ int newTop = header.y + header.height;
+ if (newTop > top) {
+ top = newTop;
+ }
+ }
+ }
+ }
+
+ if (hasVisibleHeaders) {
+ canvas.save();
+ mClipRect.set(0, top, getWidth(), bottom);
+ canvas.clipRect(mClipRect);
+ }
+
+ super.dispatchDraw(canvas);
+
+ if (hasVisibleHeaders) {
+ canvas.restore();
+
+ // First draw top headers, then the bottom ones to handle the Z axis correctly
+ for (int i = mSize; --i >= 0;) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && (header.state == TOP || header.state == FADING)) {
+ drawHeader(canvas, header, currentTime);
+ }
+ }
+
+ for (int i = 0; i < mSize; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && header.state == BOTTOM) {
+ drawHeader(canvas, header, currentTime);
+ }
+ }
+ }
+
+ invalidateIfAnimating();
+ }
+
+ private void drawHeader(Canvas canvas, PinnedHeader header, long currentTime) {
+ if (header.animating) {
+ int timeLeft = (int)(header.targetTime - currentTime);
+ if (timeLeft <= 0) {
+ header.y = header.targetY;
+ header.visible = header.targetVisible;
+ header.animating = false;
+ } else {
+ header.y = header.targetY + (header.sourceY - header.targetY) * timeLeft
+ / mAnimationDuration;
+ }
+ }
+ if (header.visible) {
+ View view = header.view;
+ int saveCount = canvas.save();
+ canvas.translate(mHeaderPaddingLeft, header.y);
+ if (header.state == FADING) {
+ mBounds.set(0, 0, mHeaderWidth, view.getHeight());
+ canvas.saveLayerAlpha(mBounds, header.alpha, Canvas.ALL_SAVE_FLAG);
+ }
+ view.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+ }
+}
diff --git a/src/com/android/contacts/widget/SearchEditText.java b/src/com/android/contacts/widget/SearchEditText.java
new file mode 100644
index 0000000..d8ea2be
--- /dev/null
+++ b/src/com/android/contacts/widget/SearchEditText.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+
+/**
+ * A custom text editor that helps automatically dismiss the activity along with the soft
+ * keyboard.
+ */
+public class SearchEditText extends EditText implements OnEditorActionListener, TextWatcher {
+
+ private boolean mMaginfyingGlassEnabled = true;
+ private Drawable mMagnifyingGlass;
+ private OnFilterTextListener mListener;
+
+ private boolean mMagnifyingGlassShown;
+
+ public interface OnFilterTextListener {
+ void onFilterChange(String queryString);
+ void onCancelSearch();
+ }
+
+ public SearchEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ addTextChangedListener(this);
+ setOnEditorActionListener(this);
+ mMagnifyingGlass = getCompoundDrawables()[2];
+ setCompoundDrawables(null, null, null, null);
+ }
+
+ public boolean isMaginfyingGlassEnabled() {
+ return mMaginfyingGlassEnabled;
+ }
+
+ public void setMaginfyingGlassEnabled(boolean flag) {
+ this.mMaginfyingGlassEnabled = flag;
+ }
+
+ public void setOnFilterTextListener(OnFilterTextListener listener) {
+ this.mListener = listener;
+ }
+
+ /**
+ * Conditionally shows a magnifying glass icon on the right side of the text field
+ * when the text it empty.
+ */
+ @Override
+ public boolean onPreDraw() {
+ boolean emptyText = TextUtils.isEmpty(getText());
+ if (mMagnifyingGlassShown != emptyText) {
+ mMagnifyingGlassShown = emptyText;
+ if (mMagnifyingGlassShown && mMaginfyingGlassEnabled) {
+ setCompoundDrawables(null, null, mMagnifyingGlass, null);
+ } else {
+ setCompoundDrawables(null, null, null, null);
+ }
+ return false;
+ }
+ return super.onPreDraw();
+ }
+
+ /**
+ * Dismisses the search UI along with the keyboard if the filter text is empty.
+ */
+ @Override
+ public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK && TextUtils.isEmpty(getText()) && mListener != null) {
+ mListener.onCancelSearch();
+ return true;
+ }
+ return false;
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ /**
+ * Event handler for search UI.
+ */
+ public void afterTextChanged(Editable s) {
+ if (mListener != null) {
+ mListener.onFilterChange(trim(s));
+ }
+ }
+
+ private String trim(Editable s) {
+ return s.toString().trim();
+ }
+
+ /**
+ * Event handler for search UI.
+ */
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ hideSoftKeyboard();
+ if (TextUtils.isEmpty(trim(getText())) && mListener != null) {
+ mListener.onCancelSearch();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void hideSoftKeyboard() {
+ // Hide soft keyboard, if visible
+ InputMethodManager inputMethodManager = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+ }
+
+}
diff --git a/src/com/android/contacts/widget/SingleItemAdapter.java b/src/com/android/contacts/widget/SingleItemAdapter.java
new file mode 100644
index 0000000..3532bfc
--- /dev/null
+++ b/src/com/android/contacts/widget/SingleItemAdapter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+/**
+ * A general purpose adapter that contains exactly one item.
+ */
+public abstract class SingleItemAdapter extends BaseAdapter {
+
+ public int getCount() {
+ return 1;
+ }
+
+ public Object getItem(int position) {
+ return null;
+ }
+
+ public long getItemId(int position) {
+ return 0;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return getView(convertView, parent);
+ }
+
+ /**
+ * Creates the view.
+ */
+ protected abstract View getView(View convertView, ViewGroup parent);
+}
diff --git a/src/com/android/contacts/widget/TextHighlightingAnimation.java b/src/com/android/contacts/widget/TextHighlightingAnimation.java
new file mode 100644
index 0000000..21bbc63
--- /dev/null
+++ b/src/com/android/contacts/widget/TextHighlightingAnimation.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import com.android.internal.R;
+
+import android.database.CharArrayBuffer;
+import android.graphics.Color;
+import android.os.Handler;
+import android.text.TextPaint;
+import android.text.style.CharacterStyle;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+
+/**
+ * An animation that alternately dims and brightens the non-highlighted portion of text.
+ */
+public abstract class TextHighlightingAnimation implements Runnable, TextWithHighlightingFactory {
+
+ private static final int MAX_ALPHA = 255;
+ private static final int MIN_ALPHA = 50;
+
+ private AccelerateInterpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
+ private DecelerateInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
+
+ private final static DimmingSpan[] sEmptySpans = new DimmingSpan[0];
+
+ /**
+ * Frame rate expressed a number of millis between frames.
+ */
+ private static final long FRAME_RATE = 50;
+
+ private DimmingSpan mDimmingSpan;
+ private Handler mHandler;
+ private boolean mAnimating;
+ private boolean mDimming;
+ private long mTargetTime;
+ private final int mDuration;
+
+ /**
+ * A Spanned that highlights a part of text by dimming another part of that text.
+ */
+ public class TextWithHighlightingImpl implements TextWithHighlighting {
+
+ private final DimmingSpan[] mSpans;
+ private boolean mDimmingEnabled;
+ private CharArrayBuffer mText;
+ private int mDimmingSpanStart;
+ private int mDimmingSpanEnd;
+ private String mString;
+
+ public TextWithHighlightingImpl() {
+ mSpans = new DimmingSpan[] { mDimmingSpan };
+ }
+
+ public void setText(CharArrayBuffer baseText, CharArrayBuffer highlightedText) {
+ mText = baseText;
+
+ // TODO figure out a way to avoid string allocation
+ mString = new String(mText.data, 0, mText.sizeCopied);
+
+ int index = indexOf(baseText, highlightedText);
+
+ if (index == 0 || index == -1) {
+ mDimmingEnabled = false;
+ } else {
+ mDimmingEnabled = true;
+ mDimmingSpanStart = 0;
+ mDimmingSpanEnd = index;
+ }
+ }
+
+ /**
+ * An implementation of indexOf on CharArrayBuffers that finds the first match of
+ * the start of buffer2 in buffer1. For example, indexOf("abcd", "cdef") == 2
+ */
+ private int indexOf(CharArrayBuffer buffer1, CharArrayBuffer buffer2) {
+ char[] string1 = buffer1.data;
+ char[] string2 = buffer2.data;
+ int count1 = buffer1.sizeCopied;
+ int count2 = buffer2.sizeCopied;
+
+ // Ignore matching tails of the two buffers
+ while (count1 > 0 && count2 > 0 && string1[count1 - 1] == string2[count2 - 1]) {
+ count1--;
+ count2--;
+ }
+
+ int size = count2;
+ for (int i = 0; i < count1; i++) {
+ if (i + size > count1) {
+ size = count1 - i;
+ }
+ int j;
+ for (j = 0; j < size; j++) {
+ if (string1[i+j] != string2[j]) {
+ break;
+ }
+ }
+ if (j == size) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public <T> T[] getSpans(int start, int end, Class<T> type) {
+ if (mDimmingEnabled) {
+ return (T[])mSpans;
+ } else {
+ return (T[])sEmptySpans;
+ }
+ }
+
+ public int getSpanStart(Object tag) {
+ // We only have one span - no need to check the tag parameter
+ return mDimmingSpanStart;
+ }
+
+ public int getSpanEnd(Object tag) {
+ // We only have one span - no need to check the tag parameter
+ return mDimmingSpanEnd;
+ }
+
+ public int getSpanFlags(Object tag) {
+ // String is immutable - flags not needed
+ return 0;
+ }
+
+ public int nextSpanTransition(int start, int limit, Class type) {
+ // Never called since we only have one span
+ return 0;
+ }
+
+ public char charAt(int index) {
+ return mText.data[index];
+ }
+
+ public int length() {
+ return mText.sizeCopied;
+ }
+
+ public CharSequence subSequence(int start, int end) {
+ // Never called - implementing for completeness
+ return new String(mText.data, start, end);
+ }
+
+ @Override
+ public String toString() {
+ return mString;
+ }
+ }
+
+ /**
+ * A Span that modifies alpha of the default foreground color.
+ */
+ private static class DimmingSpan extends CharacterStyle {
+ private int mAlpha;
+
+ public void setAlpha(int alpha) {
+ mAlpha = alpha;
+ }
+
+ @Override
+ public void updateDrawState(TextPaint ds) {
+
+ // Only dim the text in the basic state; not selected, focused or pressed
+ int[] states = ds.drawableState;
+ if (states != null) {
+ int count = states.length;
+ for (int i = 0; i < count; i++) {
+ switch (states[i]) {
+ case R.attr.state_pressed:
+ case R.attr.state_selected:
+ case R.attr.state_focused:
+ // We can simply return, because the supplied text
+ // paint is already configured with defaults.
+ return;
+ }
+ }
+ }
+
+ int color = ds.getColor();
+ color = Color.argb(mAlpha, Color.red(color), Color.green(color), Color.blue(color));
+ ds.setColor(color);
+ }
+ }
+
+ /**
+ * Constructor.
+ */
+ public TextHighlightingAnimation(int duration) {
+ mDuration = duration;
+ mHandler = new Handler();
+ mDimmingSpan = new DimmingSpan();
+ mDimmingSpan.setAlpha(MAX_ALPHA);
+ }
+
+ /**
+ * Returns a Spanned that can be used by a text view to show text with highlighting.
+ */
+ public TextWithHighlightingImpl createTextWithHighlighting() {
+ return new TextWithHighlightingImpl();
+ }
+
+ /**
+ * Override and invalidate (redraw) TextViews showing {@link TextWithHighlightingImpl}.
+ */
+ protected abstract void invalidate();
+
+ /**
+ * Starts the highlighting animation, which will dim portions of text.
+ */
+ public void startHighlighting() {
+ startAnimation(true);
+ }
+
+ /**
+ * Starts un-highlighting animation, which will brighten the dimmed portions of text
+ * to the brightness level of the rest of text.
+ */
+ public void stopHighlighting() {
+ startAnimation(false);
+ }
+
+ /**
+ * Called when the animation starts.
+ */
+ protected void onAnimationStarted() {
+ }
+
+ /**
+ * Called when the animation has stopped.
+ */
+ protected void onAnimationEnded() {
+ }
+
+ private void startAnimation(boolean dim) {
+ if (mDimming != dim) {
+ mDimming = dim;
+ long now = System.currentTimeMillis();
+ if (!mAnimating) {
+ mAnimating = true;
+ mTargetTime = now + mDuration;
+ onAnimationStarted();
+ mHandler.post(this);
+ } else {
+
+ // If we have started dimming, reverse the direction and adjust the target
+ // time accordingly.
+ mTargetTime = (now + mDuration) - (mTargetTime - now);
+ }
+ }
+ }
+
+ /**
+ * Animation step.
+ */
+ public void run() {
+ long now = System.currentTimeMillis();
+ long timeLeft = mTargetTime - now;
+ if (timeLeft < 0) {
+ mDimmingSpan.setAlpha(mDimming ? MIN_ALPHA : MAX_ALPHA);
+ mAnimating = false;
+ onAnimationEnded();
+ return;
+ }
+
+ // Start=1, end=0
+ float virtualTime = (float)timeLeft / mDuration;
+ if (mDimming) {
+ float interpolatedTime = DECELERATE_INTERPOLATOR.getInterpolation(virtualTime);
+ mDimmingSpan.setAlpha((int)(MIN_ALPHA + (MAX_ALPHA-MIN_ALPHA) * interpolatedTime));
+ } else {
+ float interpolatedTime = ACCELERATE_INTERPOLATOR.getInterpolation(virtualTime);
+ mDimmingSpan.setAlpha((int)(MIN_ALPHA + (MAX_ALPHA-MIN_ALPHA) * (1-interpolatedTime)));
+ }
+
+ invalidate();
+
+ // Repeat
+ mHandler.postDelayed(this, FRAME_RATE);
+ }
+}
diff --git a/src/com/android/contacts/widget/TextWithHighlighting.java b/src/com/android/contacts/widget/TextWithHighlighting.java
new file mode 100644
index 0000000..3a32b02
--- /dev/null
+++ b/src/com/android/contacts/widget/TextWithHighlighting.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import android.database.CharArrayBuffer;
+import android.text.Spanned;
+
+/**
+ * A Spanned that highlights a part of text by dimming another part of that text.
+ */
+public interface TextWithHighlighting extends Spanned {
+ void setText(CharArrayBuffer baseText, CharArrayBuffer highlightedText);
+}
diff --git a/src/com/android/contacts/widget/TextWithHighlightingFactory.java b/src/com/android/contacts/widget/TextWithHighlightingFactory.java
new file mode 100644
index 0000000..ee5744d
--- /dev/null
+++ b/src/com/android/contacts/widget/TextWithHighlightingFactory.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+/**
+ * A factory for text fields with animated highlighting.
+ */
+public interface TextWithHighlightingFactory {
+ TextWithHighlighting createTextWithHighlighting();
+}
diff --git a/src/com/android/contacts/widget/TransitionAnimationView.java b/src/com/android/contacts/widget/TransitionAnimationView.java
new file mode 100644
index 0000000..a58a6c9
--- /dev/null
+++ b/src/com/android/contacts/widget/TransitionAnimationView.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import com.android.contacts.R;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorInflater;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewParent;
+import android.widget.FrameLayout;
+
+/**
+ * A container for a view that needs to have exit/enter animations when rebinding data.
+ * This layout should have a single child. Just before rebinding data that child
+ * should make this call:
+ * <pre>
+ * TransitionAnimationView.startAnimation(this);
+ * </pre>
+ */
+public class TransitionAnimationView extends FrameLayout implements AnimatorListener {
+
+ private View mPreviousStateView;
+ private Bitmap mPreviousStateBitmap;
+ private int mEnterAnimationId;
+ private int mExitAnimationId;
+ private int mAnimationDuration;
+ private Rect mClipMargins = new Rect();
+ private Rect mClipRect = new Rect();
+ private Animator mEnterAnimation;
+ private Animator mExitAnimation;
+
+ public TransitionAnimationView(Context context) {
+ this(context, null, 0);
+ }
+
+ public TransitionAnimationView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TransitionAnimationView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = getContext().obtainStyledAttributes(
+ attrs, R.styleable.TransitionAnimationView);
+
+ mEnterAnimationId = a.getResourceId(R.styleable.TransitionAnimationView_enterAnimation,
+ android.R.animator.fade_in);
+ mExitAnimationId = a.getResourceId(R.styleable.TransitionAnimationView_exitAnimation,
+ android.R.animator.fade_out);
+ mClipMargins.left = a.getDimensionPixelOffset(
+ R.styleable.TransitionAnimationView_clipMarginLeft, 0);
+ mClipMargins.top = a.getDimensionPixelOffset(
+ R.styleable.TransitionAnimationView_clipMarginTop, 0);
+ mClipMargins.right = a.getDimensionPixelOffset(
+ R.styleable.TransitionAnimationView_clipMarginRight, 0);
+ mClipMargins.bottom = a.getDimensionPixelOffset(
+ R.styleable.TransitionAnimationView_clipMarginBottom, 0);
+ mAnimationDuration = a.getInt(
+ R.styleable.TransitionAnimationView_animationDuration, 100);
+
+ a.recycle();
+
+ mPreviousStateView = new View(context);
+ mPreviousStateView.setVisibility(View.INVISIBLE);
+ addView(mPreviousStateView);
+
+ mEnterAnimation = AnimatorInflater.loadAnimator(getContext(), mEnterAnimationId);
+ if (mEnterAnimation == null) {
+ throw new IllegalArgumentException("Invalid enter animation: " + mEnterAnimationId);
+ }
+ mEnterAnimation.addListener(this);
+ mEnterAnimation.setDuration(mAnimationDuration);
+
+ mExitAnimation = AnimatorInflater.loadAnimator(getContext(), mExitAnimationId);
+ if (mExitAnimation == null) {
+ throw new IllegalArgumentException("Invalid exit animation: " + mExitAnimationId);
+ }
+
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (changed || mPreviousStateBitmap == null) {
+ if (mPreviousStateBitmap != null) {
+ mPreviousStateBitmap.recycle();
+ mPreviousStateBitmap = null;
+ }
+ int width = right - left;
+ int height = bottom - top;
+ if (width > 0 && height > 0) {
+ mPreviousStateBitmap = Bitmap.createBitmap(
+ width, height, Bitmap.Config.ARGB_8888);
+ mPreviousStateView.setBackgroundDrawable(
+ new BitmapDrawable(getContext().getResources(), mPreviousStateBitmap));
+ mClipRect.set(mClipMargins.left, mClipMargins.top,
+ width - mClipMargins.right, height - mClipMargins.bottom);
+ } else {
+ mPreviousStateBitmap = null;
+ mPreviousStateView.setBackgroundDrawable(null);
+ }
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mPreviousStateView.setBackgroundDrawable(null);
+ if (mPreviousStateBitmap != null) {
+ mPreviousStateBitmap.recycle();
+ mPreviousStateBitmap = null;
+ }
+ }
+
+ public static void startAnimation(View view, boolean closing) {
+ TransitionAnimationView container = null;
+ ViewParent parent = view.getParent();
+ while (parent instanceof View) {
+ if (parent instanceof TransitionAnimationView) {
+ container = (TransitionAnimationView) parent;
+ break;
+ }
+ parent = parent.getParent();
+ }
+
+ if (container != null) {
+ container.start(view, closing);
+ }
+ }
+
+ private void start(View view, boolean closing) {
+ if (mEnterAnimation.isRunning()) {
+ mEnterAnimation.end();
+ }
+ if (mExitAnimation.isRunning()) {
+ mExitAnimation.end();
+ }
+ if (view.getVisibility() != View.VISIBLE) {
+ if (!closing) {
+ mEnterAnimation.setTarget(view);
+ mEnterAnimation.start();
+ }
+ } else if (closing) {
+ mExitAnimation.setTarget(view);
+ mExitAnimation.start();
+ } else {
+ if (mPreviousStateBitmap == null) {
+ return;
+ }
+
+ Canvas canvas = new Canvas(mPreviousStateBitmap);
+ Paint paint = new Paint();
+ paint.setColor(Color.TRANSPARENT);
+ canvas.drawRect(0, 0, mPreviousStateBitmap.getWidth(), mPreviousStateBitmap.getHeight(),
+ paint);
+ canvas.clipRect(mClipRect);
+ view.draw(canvas);
+ mPreviousStateView.setVisibility(View.VISIBLE);
+
+ mEnterAnimation.setTarget(view);
+ mEnterAnimation.start();
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mPreviousStateView.setVisibility(View.INVISIBLE);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 7af1a54..e714c25 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -4,9 +4,9 @@
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.
@@ -17,25 +17,66 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.contacts.tests">
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+
<application>
<uses-library android:name="android.test.runner" />
<meta-data android:name="com.android.contacts.iconset" android:resource="@xml/iconset" />
+
+ <activity android:name=".allintents.AllIntentsActivity"
+ android:label="@string/contactsIntents"
+ >
+ <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>
+ </activity>
+
+ <activity android:name=".allintents.ResultActivity"
+ android:label="@string/result"
+ >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name=".widget.PinnedHeaderUseCaseActivity"
+ android:label="@string/pinnedHeaderList"
+ >
+ <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>
+ </activity>
+
+ <activity android:name=".quickcontact.QuickContactTestsActivity"
+ android:label="@string/quickContactTests"
+ >
+ <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>
+ </activity>
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.android.contacts"
android:label="Contacts app tests">
</instrumentation>
-
+
<instrumentation android:name="com.android.contacts.ContactsLaunchPerformance"
android:targetPackage="com.android.contacts"
android:label="Contacts launch performance">
</instrumentation>
-
<instrumentation android:name="com.android.contacts.DialerLaunchPerformance"
android:targetPackage="com.android.contacts"
android:label="Dialer launch performance">
</instrumentation>
-</manifest>
+</manifest>
diff --git a/tests/assets/v21_simple.vcf b/tests/assets/v21_simple.vcf
new file mode 100644
index 0000000..86f4d33
--- /dev/null
+++ b/tests/assets/v21_simple.vcf
@@ -0,0 +1,3 @@
+BEGIN:VCARD
+N:test
+END:VCARD
\ No newline at end of file
diff --git a/tests/assets/v30_simple.vcf b/tests/assets/v30_simple.vcf
new file mode 100644
index 0000000..418661f
--- /dev/null
+++ b/tests/assets/v30_simple.vcf
@@ -0,0 +1,13 @@
+BEGIN:VCARD
+VERSION:3.0
+FN:And Roid
+N:And;Roid;;;
+ORG:Open;Handset; Alliance
+SORT-STRING:android
+TEL;TYPE=PREF;TYPE=VOICE:0300000000
+CLASS:PUBLIC
+X-GNO:0
+X-GN:group0
+X-REDUCTION:0
+REV:20081031T065854Z
+END:VCARD
diff --git a/res/drawable-mdpi-finger/ic_contact_picture.png b/tests/res/drawable/ic_contact_picture.png
similarity index 100%
rename from res/drawable-mdpi-finger/ic_contact_picture.png
rename to tests/res/drawable/ic_contact_picture.png
Binary files differ
diff --git a/tests/res/layout/intent_list_item.xml b/tests/res/layout/intent_list_item.xml
new file mode 100644
index 0000000..4749224
--- /dev/null
+++ b/tests/res/layout/intent_list_item.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:gravity="center_vertical"
+ android:paddingLeft="6dip"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+/>
diff --git a/tests/res/layout/quick_contact_tests.xml b/tests/res/layout/quick_contact_tests.xml
new file mode 100644
index 0000000..fb2d518
--- /dev/null
+++ b/tests/res/layout/quick_contact_tests.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginTop="16dip">
+ <QuickContactBadge
+ android:id="@+id/small_badge1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/quickContactBadgeStyleWindowSmall"
+ android:layout_marginLeft="32dip" />
+ <QuickContactBadge
+ android:id="@+id/medium_badge1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/quickContactBadgeStyleWindowMedium"
+ android:layout_marginLeft="64dip" />
+ <QuickContactBadge
+ android:id="@+id/large_badge1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/quickContactBadgeStyleWindowLarge"
+ android:layout_marginLeft="64dip" />
+ </LinearLayout>
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Button
+ android:id="@+id/pick_contact"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="32dip"
+ android:text="@string/pickContact" />
+ <TextView
+ android:id="@+id/uri"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="32dip" />
+ </LinearLayout>
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_marginBottom="16dip">
+ <QuickContactBadge
+ android:id="@+id/small_badge2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/quickContactBadgeStyleSmallWindowSmall"
+ android:layout_marginLeft="32dip" />
+ <QuickContactBadge
+ android:id="@+id/medium_badge2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/quickContactBadgeStyleSmallWindowMedium"
+ android:layout_marginLeft="64dip" />
+ <QuickContactBadge
+ android:id="@+id/large_badge2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/quickContactBadgeStyleSmallWindowLarge"
+ android:layout_marginLeft="64dip" />
+ </LinearLayout>
+</LinearLayout>
+
diff --git a/tests/res/layout/result.xml b/tests/res/layout/result.xml
new file mode 100644
index 0000000..0ab32c6
--- /dev/null
+++ b/tests/res/layout/result.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+>
+
+ <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/table"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:shrinkColumns="1"
+ android:stretchColumns="*">
+ </TableLayout>
+</ScrollView>
+
diff --git a/tests/res/values/donottranslate_strings.xml b/tests/res/values/donottranslate_strings.xml
new file mode 100644
index 0000000..8557fd6
--- /dev/null
+++ b/tests/res/values/donottranslate_strings.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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">
+
+ <string name="contactsIntents">Contacts Intents</string>
+ <string name="result">Result returned by activity</string>
+
+ <string-array name="allIntents">
+ <!-- List modes -->
+ <item>LIST_DEFAULT</item>
+ <item>LIST_ALL_CONTACTS_ACTION</item>
+ <item>LIST_CONTACTS_WITH_PHONES_ACTION</item>
+ <item>LIST_STARRED_ACTION</item>
+ <item>LIST_FREQUENT_ACTION</item>
+ <item>LIST_STREQUENT_ACTION</item>
+ <item>ACTION_PICK: contact</item>
+ <item>ACTION_PICK: contact (legacy)</item>
+ <item>ACTION_PICK: phone</item>
+ <item>ACTION_PICK: phone (legacy)</item>
+ <item>ACTION_PICK: postal</item>
+ <item>ACTION_PICK: postal (legacy)</item>
+ <item>ACTION_CREATE_SHORTCUT: contact</item>
+ <item>ACTION_CREATE_SHORTCUT: dial</item>
+ <item>ACTION_CREATE_SHORTCUT: message</item>
+ <item>ACTION_GET_CONTENT: contact</item>
+ <item>ACTION_GET_CONTENT: contact (legacy)</item>
+ <item>ACTION_GET_CONTENT: phone</item>
+ <item>ACTION_GET_CONTENT: phone (legacy)</item>
+ <item>ACTION_GET_CONTENT: postal</item>
+ <item>ACTION_GET_CONTENT: postal (legacy)</item>
+ <item>ACTION_INSERT_OR_EDIT</item>
+ <item>ACTION_SEARCH (call button)</item>
+ <item>ACTION_SEARCH: contact</item>
+ <item>ACTION_SEARCH: email</item>
+ <item>ACTION_SEARCH: phone</item>
+ <item>SEARCH_SUGGESTION_CLICKED (call button)</item>
+ <item>SEARCH_SUGGESTION_CLICKED: contact</item>
+ <item>SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED</item>
+ <item>SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED</item>
+ <item>TODO: JOIN_CONTACT</item>
+
+ <!-- Edit Contact -->
+ <item>EDIT (content uri with only id)</item>
+ <item>EDIT (lookup uri without id)</item>
+ <item>EDIT (lookup uri)</item>
+ <item>EDIT (called for raw contact)</item>
+ <item>EDIT (legacy style uri)</item>
+ <item>EDIT (create new contact)</item>
+ <item>EDIT (create new contact with data)</item>
+ <item>EDIT (create new contact for account)</item>
+ <item>EDIT (create new contact for account with data)</item>
+ <item>EDIT (create new raw contact)</item>
+ <item>EDIT (create new legacy)</item>
+
+ <!-- View Contact -->
+ <item>VIEW (content uri with only id)</item>
+ <item>VIEW (lookup uri without id)</item>
+ <item>VIEW (lookup uri)</item>
+ <item>VIEW (called for raw contact)</item>
+ <item>VIEW (legacy style uri)</item>
+
+ <!-- Various ways to start Contacts -->
+ <item>DIAL</item>
+ <item>DIAL phone (deprecated)</item>
+ <item>DIAL person (deprecated)</item>
+ <item>DIAL voicemail</item>
+ <item>CALL BUTTON</item>
+ <item>DIAL tel</item>
+ <item>VIEW tel</item>
+ <item>VIEW calls (call-log after a phone call)</item>
+ </string-array>
+
+ <string name="pinnedHeaderList">Pinned Headers</string>
+ <string name="quickContactTests">Quick Contact modes</string>
+ <string name="pickContact">Pick contact</string>
+
+ <string-array name="pinnedHeaderUseCases">
+ <item>One short section - no headers</item>
+ <item>Two short sections with headers</item>
+ <item>Five short sections with headers</item>
+ </string-array>
+</resources>
diff --git a/tests/src/com/android/contacts/ContactDetailTest.java b/tests/src/com/android/contacts/ContactDetailTest.java
new file mode 100644
index 0000000..0b850b7
--- /dev/null
+++ b/tests/src/com/android/contacts/ContactDetailTest.java
@@ -0,0 +1,41 @@
+package com.android.contacts;
+
+import com.android.contacts.activities.ContactDetailActivity;
+import com.android.contacts.tests.mocks.ContactsMockContext;
+import com.android.contacts.tests.mocks.MockContentProvider;
+
+import android.test.ActivityUnitTestCase;
+
+public class ContactDetailTest extends ActivityUnitTestCase<ContactDetailActivity> {
+ private ContactsMockContext mContext;
+ private MockContentProvider mContactsProvider;
+
+ public ContactDetailTest() {
+ super(ContactDetailActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContext = new ContactsMockContext(getInstrumentation().getTargetContext());
+ mContactsProvider = mContext.getContactsProvider();
+ setActivityContext(mContext);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+// public void testFoo() {
+// // Use lookup-style Uris that also contain the Contact-ID
+// //long rawContactId1 = mCreator.createRawContact("JohnDoe", "John", "Doe");
+// //long contactId1 = mCreator.getContactIdByRawContactId(rawContactId1);
+// //Uri contactUri1 = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId1);
+// Intent intent = new Intent(Intent.ACTION_VIEW,
+// ContentUris.withAppendedId(Contacts.CONTENT_URI, 123));
+// startActivity(intent, null, null);
+// ContactDetailActivity activity = getActivity();
+// mContactsProvider.verify();
+// }
+}
diff --git a/tests/src/com/android/contacts/ContactLoaderTest.java b/tests/src/com/android/contacts/ContactLoaderTest.java
new file mode 100644
index 0000000..503fc64
--- /dev/null
+++ b/tests/src/com/android/contacts/ContactLoaderTest.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2010 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.contacts.tests.mocks.ContactsMockContext;
+import com.android.contacts.tests.mocks.MockContentProvider;
+
+import android.content.ContentUris;
+import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.DisplayNameSources;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.StatusUpdates;
+import android.test.LoaderTestCase;
+
+/**
+ * Runs ContactLoader tests for the the contact-detail and editor view.
+ */
+public class ContactLoaderTest extends LoaderTestCase {
+ ContactsMockContext mMockContext;
+ MockContentProvider mContactsProvider;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mMockContext = new ContactsMockContext(getContext());
+ mContactsProvider = mMockContext.getContactsProvider();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private ContactLoader.Result assertLoadContact(Uri uri) {
+ final ContactLoader loader = new ContactLoader(mMockContext, uri);
+ return getLoaderResultSynchronously(loader);
+ }
+
+ public void testNullUri() {
+ ContactLoader.Result result = assertLoadContact(null);
+ assertEquals(ContactLoader.Result.ERROR, result);
+ }
+
+ public void testEmptyUri() {
+ ContactLoader.Result result = assertLoadContact(Uri.EMPTY);
+ assertEquals(ContactLoader.Result.ERROR, result);
+ }
+
+ public void testInvalidUri() {
+ ContactLoader.Result result = assertLoadContact(Uri.parse("content://wtf"));
+ assertEquals(ContactLoader.Result.ERROR, result);
+ }
+
+ public void testLoadContactWithContactIdUri() {
+ // Use content Uris that only contain the ID
+ final long contactId = 1;
+ final long rawContactId = 11;
+ final long dataId = 21;
+
+ final String lookupKey = "aa%12%@!";
+ final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ final Uri entityUri = Uri.withAppendedPath(baseUri, Contacts.Entity.CONTENT_DIRECTORY);
+ final Uri lookupUri = ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
+ contactId);
+
+ ContactQueries queries = new ContactQueries();
+ mContactsProvider.expectTypeQuery(baseUri, Contacts.CONTENT_ITEM_TYPE);
+ queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
+
+ ContactLoader.Result contact = assertLoadContact(baseUri);
+
+ assertEquals(contactId, contact.getId());
+ assertEquals(rawContactId, contact.getNameRawContactId());
+ assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+ assertEquals(lookupKey, contact.getLookupKey());
+ assertEquals(lookupUri, contact.getLookupUri());
+ assertEquals(1, contact.getEntities().size());
+ assertEquals(1, contact.getStatuses().size());
+ mContactsProvider.verify();
+ }
+
+ public void testLoadContactWithOldStyleUri() {
+ // Use content Uris that only contain the ID but use the format used in Donut
+ final long contactId = 1;
+ final long rawContactId = 11;
+ final long dataId = 21;
+
+ final String lookupKey = "aa%12%@!";
+ final Uri legacyUri = ContentUris.withAppendedId(
+ Uri.parse("content://contacts"), rawContactId);
+ final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
+ final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ final Uri lookupUri = ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
+ contactId);
+ final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY);
+
+ ContactQueries queries = new ContactQueries();
+ queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, contactId, lookupKey);
+ queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
+
+ ContactLoader.Result contact = assertLoadContact(legacyUri);
+
+ assertEquals(contactId, contact.getId());
+ assertEquals(rawContactId, contact.getNameRawContactId());
+ assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+ assertEquals(lookupKey, contact.getLookupKey());
+ assertEquals(lookupUri, contact.getLookupUri());
+ assertEquals(1, contact.getEntities().size());
+ assertEquals(1, contact.getStatuses().size());
+ mContactsProvider.verify();
+ }
+
+ public void testLoadContactWithRawContactIdUri() {
+ // Use content Uris that only contain the ID but use the format used in Donut
+ final long contactId = 1;
+ final long rawContactId = 11;
+ final long dataId = 21;
+
+ final String lookupKey = "aa%12%@!";
+ final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
+ final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ final Uri lookupUri = ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
+ contactId);
+ final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY);
+
+ ContactQueries queries = new ContactQueries();
+ mContactsProvider.expectTypeQuery(rawContactUri, RawContacts.CONTENT_ITEM_TYPE);
+ queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, contactId, lookupKey);
+ queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
+
+ ContactLoader.Result contact = assertLoadContact(rawContactUri);
+
+ assertEquals(contactId, contact.getId());
+ assertEquals(rawContactId, contact.getNameRawContactId());
+ assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+ assertEquals(lookupKey, contact.getLookupKey());
+ assertEquals(lookupUri, contact.getLookupUri());
+ assertEquals(1, contact.getEntities().size());
+ assertEquals(1, contact.getStatuses().size());
+ mContactsProvider.verify();
+ }
+
+ public void testLoadContactWithContactLookupUri() {
+ // Use lookup-style Uris that do not contain the Contact-ID
+
+ final long contactId = 1;
+ final long rawContactId = 11;
+ final long dataId = 21;
+
+ final String lookupKey = "aa%12%@!";
+ final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ final Uri lookupNoIdUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
+ final Uri lookupUri = ContentUris.withAppendedId(lookupNoIdUri, contactId);
+ final Uri entityUri = Uri.withAppendedPath(lookupNoIdUri, Contacts.Entity.CONTENT_DIRECTORY);
+
+ ContactQueries queries = new ContactQueries();
+ mContactsProvider.expectTypeQuery(lookupNoIdUri, Contacts.CONTENT_ITEM_TYPE);
+ queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
+
+ ContactLoader.Result contact = assertLoadContact(lookupNoIdUri);
+
+ assertEquals(contactId, contact.getId());
+ assertEquals(rawContactId, contact.getNameRawContactId());
+ assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+ assertEquals(lookupKey, contact.getLookupKey());
+ assertEquals(lookupUri, contact.getLookupUri());
+ assertEquals(1, contact.getEntities().size());
+ assertEquals(1, contact.getStatuses().size());
+ mContactsProvider.verify();
+ }
+
+ public void testLoadContactWithContactLookupAndIdUri() {
+ // Use lookup-style Uris that also contain the Contact-ID
+ final long contactId = 1;
+ final long rawContactId = 11;
+ final long dataId = 21;
+
+ final String lookupKey = "aa%12%@!";
+ final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ final Uri lookupUri = ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
+ contactId);
+ final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY);
+
+ ContactQueries queries = new ContactQueries();
+ mContactsProvider.expectTypeQuery(lookupUri, Contacts.CONTENT_ITEM_TYPE);
+ queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
+
+ ContactLoader.Result contact = assertLoadContact(lookupUri);
+
+ assertEquals(contactId, contact.getId());
+ assertEquals(rawContactId, contact.getNameRawContactId());
+ assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+ assertEquals(lookupKey, contact.getLookupKey());
+ assertEquals(lookupUri, contact.getLookupUri());
+ assertEquals(1, contact.getEntities().size());
+ assertEquals(1, contact.getStatuses().size());
+ mContactsProvider.verify();
+ }
+
+ public void testLoadContactWithContactLookupWithIncorrectIdUri() {
+ // Use lookup-style Uris that contain incorrect Contact-ID
+ // (we want to ensure that still the correct contact is chosen)
+
+ final long contactId = 1;
+ final long wrongContactId = 2;
+ final long rawContactId = 11;
+ final long wrongRawContactId = 12;
+ final long dataId = 21;
+
+ final String lookupKey = "aa%12%@!";
+ final String wrongLookupKey = "ab%12%@!";
+ final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ final Uri wrongBaseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, wrongContactId);
+ final Uri lookupUri = ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
+ contactId);
+ final Uri lookupWithWrongIdUri = ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
+ wrongContactId);
+ final Uri entityUri = Uri.withAppendedPath(lookupWithWrongIdUri,
+ Contacts.Entity.CONTENT_DIRECTORY);
+
+ ContactQueries queries = new ContactQueries();
+ mContactsProvider.expectTypeQuery(lookupWithWrongIdUri, Contacts.CONTENT_ITEM_TYPE);
+ queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
+
+ ContactLoader.Result contact = assertLoadContact(lookupWithWrongIdUri);
+
+ assertEquals(contactId, contact.getId());
+ assertEquals(rawContactId, contact.getNameRawContactId());
+ assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+ assertEquals(lookupKey, contact.getLookupKey());
+ assertEquals(lookupUri, contact.getLookupUri());
+ assertEquals(1, contact.getEntities().size());
+ assertEquals(1, contact.getStatuses().size());
+
+ mContactsProvider.verify();
+ }
+
+ class ContactQueries {
+ public void fetchAllData(
+ Uri baseUri, long contactId, long rawContactId, long dataId, String encodedLookup) {
+ mContactsProvider.expectQuery(baseUri)
+ .withProjection(new String[] {
+ Contacts.NAME_RAW_CONTACT_ID, Contacts.DISPLAY_NAME_SOURCE,
+ Contacts.LOOKUP_KEY, Contacts.DISPLAY_NAME, Contacts.PHONETIC_NAME,
+ Contacts.PHOTO_ID, Contacts.STARRED, Contacts.CONTACT_PRESENCE,
+ Contacts.CONTACT_STATUS, Contacts.CONTACT_STATUS_TIMESTAMP,
+ Contacts.CONTACT_STATUS_RES_PACKAGE, Contacts.CONTACT_STATUS_LABEL,
+
+ Contacts.Entity.CONTACT_ID,
+ Contacts.Entity.RAW_CONTACT_ID,
+
+ RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE, RawContacts.DIRTY,
+ RawContacts.VERSION, RawContacts.SOURCE_ID, RawContacts.SYNC1,
+ RawContacts.SYNC2, RawContacts.SYNC3, RawContacts.SYNC4,
+ RawContacts.DELETED, RawContacts.IS_RESTRICTED,
+ RawContacts.NAME_VERIFIED,
+
+ Contacts.Entity.DATA_ID,
+
+ Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, Data.DATA5,
+ Data.DATA6, Data.DATA7, Data.DATA8, Data.DATA9, Data.DATA10,
+ Data.DATA11, Data.DATA12, Data.DATA13, Data.DATA14, Data.DATA15,
+ Data.SYNC1, Data.SYNC2, Data.SYNC3, Data.SYNC4,
+ Data.DATA_VERSION, Data.IS_PRIMARY,
+ Data.IS_SUPER_PRIMARY, Data.MIMETYPE, Data.RES_PACKAGE,
+
+ GroupMembership.GROUP_SOURCE_ID,
+
+ Data.PRESENCE, Data.CHAT_CAPABILITY,
+ Data.STATUS, Data.STATUS_RES_PACKAGE, Data.STATUS_ICON,
+ Data.STATUS_LABEL, Data.STATUS_TIMESTAMP,
+
+ Contacts.PHOTO_URI,
+ })
+ .withSortOrder(Contacts.Entity.RAW_CONTACT_ID)
+ .returnRow(
+ rawContactId, 40,
+ "aa%12%@!", "John Doe", "jdo",
+ 0, 0, StatusUpdates.AVAILABLE,
+ "Having lunch", 0,
+ "mockPkg1", 10,
+
+ contactId,
+ rawContactId,
+
+ "mockAccountName", "mockAccountType", 0,
+ 1, 0, "sync1",
+ "sync2", "sync3", "sync4",
+ 0, 0, 0,
+
+ dataId,
+
+ "dat1", "dat2", "dat3", "dat4", "dat5",
+ "dat6", "dat7", "dat8", "dat9", "dat10",
+ "dat11", "dat12", "dat13", "dat14", "dat15",
+ "syn1", "syn2", "syn3", "syn4",
+
+ 0, 0,
+ 0, StructuredName.CONTENT_ITEM_TYPE, "mockPkg2",
+
+ "groupId",
+
+ StatusUpdates.INVISIBLE, null,
+ "Having dinner", "mockPkg3", 0,
+ 20, 0,
+
+ "content:some.photo.uri"
+ );
+ }
+
+ void fetchLookupAndId(final Uri sourceUri, final long expectedContactId,
+ final String expectedEncodedLookup) {
+ mContactsProvider.expectQuery(sourceUri)
+ .withProjection(Contacts.LOOKUP_KEY, Contacts._ID)
+ .returnRow(expectedEncodedLookup, expectedContactId);
+ }
+
+ void fetchContactIdAndLookupFromRawContactUri(final Uri rawContactUri,
+ final long expectedContactId, final String expectedEncodedLookup) {
+ // TODO: use a lighter query by joining rawcontacts with contacts in provider
+ // (See ContactContracts.java)
+ final Uri dataUri = Uri.withAppendedPath(rawContactUri,
+ RawContacts.Data.CONTENT_DIRECTORY);
+ mContactsProvider.expectQuery(dataUri)
+ .withProjection(RawContacts.CONTACT_ID, Contacts.LOOKUP_KEY)
+ .returnRow(expectedContactId, expectedEncodedLookup);
+ }
+ }
+}
diff --git a/tests/src/com/android/contacts/ContactsUtilsTests.java b/tests/src/com/android/contacts/ContactsUtilsTests.java
index 8e18044..e8deb11 100644
--- a/tests/src/com/android/contacts/ContactsUtilsTests.java
+++ b/tests/src/com/android/contacts/ContactsUtilsTests.java
@@ -16,73 +16,16 @@
package com.android.contacts;
-import android.content.ContentValues;
import android.content.Intent;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
/**
* Tests for {@link ContactsUtils}.
*/
-@LargeTest
+@SmallTest
public class ContactsUtilsTests extends AndroidTestCase {
- private static final String TEST_ADDRESS = "user@example.org";
- private static final String TEST_PROTOCOL = "prot%col";
-
- public void testImIntent() throws Exception {
- // Normal IM is appended as path
- final ContentValues values = new ContentValues();
- values.put(Im.MIMETYPE, Im.CONTENT_ITEM_TYPE);
- values.put(Im.TYPE, Im.TYPE_HOME);
- values.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
- values.put(Im.DATA, TEST_ADDRESS);
-
- final Intent intent = ContactsUtils.buildImIntent(values);
- assertEquals(Intent.ACTION_SENDTO, intent.getAction());
-
- final Uri data = intent.getData();
- assertEquals("imto", data.getScheme());
- assertEquals("gtalk", data.getAuthority());
- assertEquals(TEST_ADDRESS, data.getPathSegments().get(0));
- }
-
- public void testImIntentCustom() throws Exception {
- // Custom IM types have encoded authority
- final ContentValues values = new ContentValues();
- values.put(Im.MIMETYPE, Im.CONTENT_ITEM_TYPE);
- values.put(Im.TYPE, Im.TYPE_HOME);
- values.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM);
- values.put(Im.CUSTOM_PROTOCOL, TEST_PROTOCOL);
- values.put(Im.DATA, TEST_ADDRESS);
-
- final Intent intent = ContactsUtils.buildImIntent(values);
- assertEquals(Intent.ACTION_SENDTO, intent.getAction());
-
- final Uri data = intent.getData();
- assertEquals("imto", data.getScheme());
- assertEquals(TEST_PROTOCOL, data.getAuthority());
- assertEquals(TEST_ADDRESS, data.getPathSegments().get(0));
- }
-
- public void testImEmailIntent() throws Exception {
- // Email addresses are treated as Google Talk entries
- final ContentValues values = new ContentValues();
- values.put(Email.MIMETYPE, Email.CONTENT_ITEM_TYPE);
- values.put(Email.TYPE, Email.TYPE_HOME);
- values.put(Email.DATA, TEST_ADDRESS);
-
- final Intent intent = ContactsUtils.buildImIntent(values);
- assertEquals(Intent.ACTION_SENDTO, intent.getAction());
-
- final Uri data = intent.getData();
- assertEquals("imto", data.getScheme());
- assertEquals("gtalk", data.getAuthority());
- assertEquals(TEST_ADDRESS, data.getPathSegments().get(0));
- }
public void testIsGraphicNull() throws Exception {
assertFalse(ContactsUtils.isGraphic(null));
diff --git a/tests/src/com/android/contacts/EntityDeltaListTests.java b/tests/src/com/android/contacts/EntityDeltaListTests.java
new file mode 100644
index 0000000..ee27dd9
--- /dev/null
+++ b/tests/src/com/android/contacts/EntityDeltaListTests.java
@@ -0,0 +1,592 @@
+/*
+ * 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 static android.content.ContentProviderOperation.TYPE_ASSERT;
+import static android.content.ContentProviderOperation.TYPE_DELETE;
+import static android.content.ContentProviderOperation.TYPE_INSERT;
+import static android.content.ContentProviderOperation.TYPE_UPDATE;
+
+import com.android.contacts.EntityModifierTests.MockContactsSource;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.EntityDelta;
+import com.android.contacts.model.EntityModifier;
+import com.android.contacts.model.EntityDeltaList;
+import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.google.android.collect.Lists;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.content.Entity;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.provider.ContactsContract.AggregationExceptions;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+
+/**
+ * Tests for {@link EntityDeltaList} which focus on "diff" operations that should
+ * create {@link AggregationExceptions} in certain cases.
+ */
+@LargeTest
+public class EntityDeltaListTests extends AndroidTestCase {
+ public static final String TAG = "EntityDeltaListTests";
+
+ private static final long CONTACT_FIRST = 1;
+ private static final long CONTACT_SECOND = 2;
+
+ public static final long CONTACT_BOB = 10;
+ public static final long CONTACT_MARY = 11;
+
+ public static final long PHONE_RED = 20;
+ public static final long PHONE_GREEN = 21;
+ public static final long PHONE_BLUE = 22;
+
+ public static final long EMAIL_YELLOW = 25;
+
+ public static final long VER_FIRST = 100;
+ public static final long VER_SECOND = 200;
+
+ public static final String TEST_PHONE = "555-1212";
+ public static final String TEST_ACCOUNT = "org.example.test";
+
+ public EntityDeltaListTests() {
+ super();
+ }
+
+ @Override
+ public void setUp() {
+ mContext = getContext();
+ }
+
+ /**
+ * Build a {@link AccountType} that has various odd constraints for
+ * testing purposes.
+ */
+ protected AccountType getAccountType() {
+ return new MockContactsSource();
+ }
+
+ static ContentValues getValues(ContentProviderOperation operation)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Field field = ContentProviderOperation.class.getDeclaredField("mValues");
+ field.setAccessible(true);
+ return (ContentValues) field.get(operation);
+ }
+
+ static EntityDelta getUpdate(long rawContactId) {
+ final Entity before = EntityDeltaTests.getEntity(rawContactId,
+ EntityDeltaTests.TEST_PHONE_ID);
+ return EntityDelta.fromBefore(before);
+ }
+
+ static EntityDelta getInsert() {
+ final ContentValues after = new ContentValues();
+ after.put(RawContacts.ACCOUNT_NAME, EntityDeltaTests.TEST_ACCOUNT_NAME);
+ after.put(RawContacts.SEND_TO_VOICEMAIL, 1);
+
+ final ValuesDelta values = ValuesDelta.fromAfter(after);
+ return new EntityDelta(values);
+ }
+
+ static EntityDeltaList buildSet(EntityDelta... deltas) {
+ final EntityDeltaList set = EntityDeltaList.fromSingle(deltas[0]);
+ for (int i = 1; i < deltas.length; i++) {
+ set.add(deltas[i]);
+ }
+ return set;
+ }
+
+ static EntityDelta buildBeforeEntity(long rawContactId, long version,
+ ContentValues... entries) {
+ // Build an existing contact read from database
+ final ContentValues contact = new ContentValues();
+ contact.put(RawContacts.VERSION, version);
+ contact.put(RawContacts._ID, rawContactId);
+ final Entity before = new Entity(contact);
+ for (ContentValues entry : entries) {
+ before.addSubValue(Data.CONTENT_URI, entry);
+ }
+ return EntityDelta.fromBefore(before);
+ }
+
+ static EntityDelta buildAfterEntity(ContentValues... entries) {
+ // Build an existing contact read from database
+ final ContentValues contact = new ContentValues();
+ contact.put(RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT);
+ final EntityDelta after = new EntityDelta(ValuesDelta.fromAfter(contact));
+ for (ContentValues entry : entries) {
+ after.addEntry(ValuesDelta.fromAfter(entry));
+ }
+ return after;
+ }
+
+ static ContentValues buildPhone(long phoneId) {
+ return buildPhone(phoneId, Long.toString(phoneId));
+ }
+
+ static ContentValues buildPhone(long phoneId, String value) {
+ final ContentValues values = new ContentValues();
+ values.put(Data._ID, phoneId);
+ values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+ values.put(Phone.NUMBER, value);
+ values.put(Phone.TYPE, Phone.TYPE_HOME);
+ return values;
+ }
+
+ static ContentValues buildEmail(long emailId) {
+ final ContentValues values = new ContentValues();
+ values.put(Data._ID, emailId);
+ values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ values.put(Email.DATA, Long.toString(emailId));
+ values.put(Email.TYPE, Email.TYPE_HOME);
+ return values;
+ }
+
+ static void insertPhone(EntityDeltaList set, long rawContactId, ContentValues values) {
+ final EntityDelta match = set.getByRawContactId(rawContactId);
+ match.addEntry(ValuesDelta.fromAfter(values));
+ }
+
+ static ValuesDelta getPhone(EntityDeltaList set, long rawContactId, long dataId) {
+ final EntityDelta match = set.getByRawContactId(rawContactId);
+ return match.getEntry(dataId);
+ }
+
+ static void assertDiffPattern(EntityDelta delta, ContentProviderOperation... pattern) {
+ final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
+ delta.buildAssert(diff);
+ delta.buildDiff(diff);
+ assertDiffPattern(diff, pattern);
+ }
+
+ static void assertDiffPattern(EntityDeltaList set, ContentProviderOperation... pattern) {
+ assertDiffPattern(set.buildDiff(), pattern);
+ }
+
+ static void assertDiffPattern(ArrayList<ContentProviderOperation> diff,
+ ContentProviderOperation... pattern) {
+ assertEquals("Unexpected operations", pattern.length, diff.size());
+ for (int i = 0; i < pattern.length; i++) {
+ final ContentProviderOperation expected = pattern[i];
+ final ContentProviderOperation found = diff.get(i);
+
+ assertEquals("Unexpected uri", expected.getUri(), found.getUri());
+
+ final String expectedType = getStringForType(expected.getType());
+ final String foundType = getStringForType(found.getType());
+ assertEquals("Unexpected type", expectedType, foundType);
+
+ if (expected.getType() == TYPE_DELETE) continue;
+
+ try {
+ final ContentValues expectedValues = getValues(expected);
+ final ContentValues foundValues = getValues(found);
+
+ expectedValues.remove(BaseColumns._ID);
+ foundValues.remove(BaseColumns._ID);
+
+ assertEquals("Unexpected values", expectedValues, foundValues);
+ } catch (NoSuchFieldException e) {
+ fail(e.toString());
+ } catch (IllegalAccessException e) {
+ fail(e.toString());
+ }
+ }
+ }
+
+ static String getStringForType(int type) {
+ switch (type) {
+ case TYPE_ASSERT: return "TYPE_ASSERT";
+ case TYPE_INSERT: return "TYPE_INSERT";
+ case TYPE_UPDATE: return "TYPE_UPDATE";
+ case TYPE_DELETE: return "TYPE_DELETE";
+ default: return Integer.toString(type);
+ }
+ }
+
+ static ContentProviderOperation buildAssertVersion(long version) {
+ final ContentValues values = new ContentValues();
+ values.put(RawContacts.VERSION, version);
+ return buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT, values);
+ }
+
+ static ContentProviderOperation buildAggregationModeUpdate(int mode) {
+ final ContentValues values = new ContentValues();
+ values.put(RawContacts.AGGREGATION_MODE, mode);
+ return buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE, values);
+ }
+
+ static ContentProviderOperation buildUpdateAggregationSuspended() {
+ return buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_SUSPENDED);
+ }
+
+ static ContentProviderOperation buildUpdateAggregationDefault() {
+ return buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT);
+ }
+
+ static ContentProviderOperation buildUpdateAggregationKeepTogether(long rawContactId) {
+ final ContentValues values = new ContentValues();
+ values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId);
+ values.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
+ return buildOper(AggregationExceptions.CONTENT_URI, TYPE_UPDATE, values);
+ }
+
+ static ContentValues buildDataInsert(ValuesDelta values, long rawContactId) {
+ final ContentValues insertValues = values.getCompleteValues();
+ insertValues.put(Data.RAW_CONTACT_ID, rawContactId);
+ return insertValues;
+ }
+
+ static ContentProviderOperation buildDelete(Uri uri) {
+ return buildOper(uri, TYPE_DELETE, (ContentValues)null);
+ }
+
+ static ContentProviderOperation buildOper(Uri uri, int type, ValuesDelta values) {
+ return buildOper(uri, type, values.getCompleteValues());
+ }
+
+ static ContentProviderOperation buildOper(Uri uri, int type, ContentValues values) {
+ switch (type) {
+ case TYPE_ASSERT:
+ return ContentProviderOperation.newAssertQuery(uri).withValues(values).build();
+ case TYPE_INSERT:
+ return ContentProviderOperation.newInsert(uri).withValues(values).build();
+ case TYPE_UPDATE:
+ return ContentProviderOperation.newUpdate(uri).withValues(values).build();
+ case TYPE_DELETE:
+ return ContentProviderOperation.newDelete(uri).build();
+ }
+ return null;
+ }
+
+ static Long getVersion(EntityDeltaList set, Long rawContactId) {
+ return set.getByRawContactId(rawContactId).getValues().getAsLong(RawContacts.VERSION);
+ }
+
+ /**
+ * Count number of {@link AggregationExceptions} updates contained in the
+ * given list of {@link ContentProviderOperation}.
+ */
+ static int countExceptionUpdates(ArrayList<ContentProviderOperation> diff) {
+ int updateCount = 0;
+ for (ContentProviderOperation oper : diff) {
+ if (AggregationExceptions.CONTENT_URI.equals(oper.getUri())
+ && oper.getType() == ContentProviderOperation.TYPE_UPDATE) {
+ updateCount++;
+ }
+ }
+ return updateCount;
+ }
+
+ public void testInsert() {
+ final EntityDelta insert = getInsert();
+ final EntityDeltaList set = buildSet(insert);
+
+ // Inserting single shouldn't create rules
+ final ArrayList<ContentProviderOperation> diff = set.buildDiff();
+ final int exceptionCount = countExceptionUpdates(diff);
+ assertEquals("Unexpected exception updates", 0, exceptionCount);
+ }
+
+ public void testUpdateUpdate() {
+ final EntityDelta updateFirst = getUpdate(CONTACT_FIRST);
+ final EntityDelta updateSecond = getUpdate(CONTACT_SECOND);
+ final EntityDeltaList set = buildSet(updateFirst, updateSecond);
+
+ // Updating two existing shouldn't create rules
+ final ArrayList<ContentProviderOperation> diff = set.buildDiff();
+ final int exceptionCount = countExceptionUpdates(diff);
+ assertEquals("Unexpected exception updates", 0, exceptionCount);
+ }
+
+ public void testUpdateInsert() {
+ final EntityDelta update = getUpdate(CONTACT_FIRST);
+ final EntityDelta insert = getInsert();
+ final EntityDeltaList set = buildSet(update, insert);
+
+ // New insert should only create one rule
+ final ArrayList<ContentProviderOperation> diff = set.buildDiff();
+ final int exceptionCount = countExceptionUpdates(diff);
+ assertEquals("Unexpected exception updates", 1, exceptionCount);
+ }
+
+ public void testInsertUpdateInsert() {
+ final EntityDelta insertFirst = getInsert();
+ final EntityDelta update = getUpdate(CONTACT_FIRST);
+ final EntityDelta insertSecond = getInsert();
+ final EntityDeltaList set = buildSet(insertFirst, update, insertSecond);
+
+ // Two inserts should create two rules to bind against single existing
+ final ArrayList<ContentProviderOperation> diff = set.buildDiff();
+ final int exceptionCount = countExceptionUpdates(diff);
+ assertEquals("Unexpected exception updates", 2, exceptionCount);
+ }
+
+ public void testInsertInsertInsert() {
+ final EntityDelta insertFirst = getInsert();
+ final EntityDelta insertSecond = getInsert();
+ final EntityDelta insertThird = getInsert();
+ final EntityDeltaList set = buildSet(insertFirst, insertSecond, insertThird);
+
+ // Three new inserts should create only two binding rules
+ final ArrayList<ContentProviderOperation> diff = set.buildDiff();
+ final int exceptionCount = countExceptionUpdates(diff);
+ assertEquals("Unexpected exception updates", 2, exceptionCount);
+ }
+
+ public void testMergeDataRemoteInsert() {
+ final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+ buildPhone(PHONE_RED)));
+ final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+ buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
+
+ // Merge in second version, verify they match
+ final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
+ assertEquals("Unexpected change when merging", second, merged);
+ }
+
+ public void testMergeDataLocalUpdateRemoteInsert() {
+ final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+ buildPhone(PHONE_RED)));
+ final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+ buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
+
+ // Change the local number to trigger update
+ final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED);
+ phone.put(Phone.NUMBER, TEST_PHONE);
+
+ assertDiffPattern(first,
+ buildAssertVersion(VER_FIRST),
+ buildUpdateAggregationSuspended(),
+ buildOper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
+ buildUpdateAggregationDefault());
+
+ // Merge in the second version, verify diff matches
+ final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
+ assertDiffPattern(merged,
+ buildAssertVersion(VER_SECOND),
+ buildUpdateAggregationSuspended(),
+ buildOper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
+ buildUpdateAggregationDefault());
+ }
+
+ public void testMergeDataLocalUpdateRemoteDelete() {
+ final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+ buildPhone(PHONE_RED)));
+ final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+ buildPhone(PHONE_GREEN)));
+
+ // Change the local number to trigger update
+ final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED);
+ phone.put(Phone.NUMBER, TEST_PHONE);
+
+ assertDiffPattern(first,
+ buildAssertVersion(VER_FIRST),
+ buildUpdateAggregationSuspended(),
+ buildOper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
+ buildUpdateAggregationDefault());
+
+ // Merge in the second version, verify that our update changed to
+ // insert, since RED was deleted on remote side
+ final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
+ assertDiffPattern(merged,
+ buildAssertVersion(VER_SECOND),
+ buildUpdateAggregationSuspended(),
+ buildOper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(phone, CONTACT_BOB)),
+ buildUpdateAggregationDefault());
+ }
+
+ public void testMergeDataLocalDeleteRemoteUpdate() {
+ final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+ buildPhone(PHONE_RED)));
+ final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+ buildPhone(PHONE_RED, TEST_PHONE)));
+
+ // Delete phone locally
+ final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED);
+ phone.markDeleted();
+
+ assertDiffPattern(first,
+ buildAssertVersion(VER_FIRST),
+ buildUpdateAggregationSuspended(),
+ buildDelete(Data.CONTENT_URI),
+ buildUpdateAggregationDefault());
+
+ // Merge in the second version, verify that our delete remains
+ final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
+ assertDiffPattern(merged,
+ buildAssertVersion(VER_SECOND),
+ buildUpdateAggregationSuspended(),
+ buildDelete(Data.CONTENT_URI),
+ buildUpdateAggregationDefault());
+ }
+
+ public void testMergeDataLocalInsertRemoteInsert() {
+ final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+ buildPhone(PHONE_RED)));
+ final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+ buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
+
+ // Insert new phone locally
+ final ValuesDelta bluePhone = ValuesDelta.fromAfter(buildPhone(PHONE_BLUE));
+ first.getByRawContactId(CONTACT_BOB).addEntry(bluePhone);
+ assertDiffPattern(first,
+ buildAssertVersion(VER_FIRST),
+ buildUpdateAggregationSuspended(),
+ buildOper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bluePhone, CONTACT_BOB)),
+ buildUpdateAggregationDefault());
+
+ // Merge in the second version, verify that our insert remains
+ final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
+ assertDiffPattern(merged,
+ buildAssertVersion(VER_SECOND),
+ buildUpdateAggregationSuspended(),
+ buildOper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bluePhone, CONTACT_BOB)),
+ buildUpdateAggregationDefault());
+ }
+
+ public void testMergeRawContactLocalInsertRemoteInsert() {
+ final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+ buildPhone(PHONE_RED)));
+ final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+ buildPhone(PHONE_RED)), buildBeforeEntity(CONTACT_MARY, VER_SECOND,
+ buildPhone(PHONE_RED)));
+
+ // Add new contact locally, should remain insert
+ final ContentValues joePhoneInsert = buildPhone(PHONE_BLUE);
+ final EntityDelta joeContact = buildAfterEntity(joePhoneInsert);
+ final ContentValues joeContactInsert = joeContact.getValues().getCompleteValues();
+ joeContactInsert.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
+ first.add(joeContact);
+ assertDiffPattern(first,
+ buildAssertVersion(VER_FIRST),
+ buildOper(RawContacts.CONTENT_URI, TYPE_INSERT, joeContactInsert),
+ buildOper(Data.CONTENT_URI, TYPE_INSERT, joePhoneInsert),
+ buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT),
+ buildUpdateAggregationKeepTogether(CONTACT_BOB));
+
+ // Merge in the second version, verify that our insert remains
+ final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
+ assertDiffPattern(merged,
+ buildAssertVersion(VER_SECOND),
+ buildAssertVersion(VER_SECOND),
+ buildOper(RawContacts.CONTENT_URI, TYPE_INSERT, joeContactInsert),
+ buildOper(Data.CONTENT_URI, TYPE_INSERT, joePhoneInsert),
+ buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT),
+ buildUpdateAggregationKeepTogether(CONTACT_BOB));
+ }
+
+ public void testMergeRawContactLocalDeleteRemoteDelete() {
+ final EntityDeltaList first = buildSet(
+ buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)),
+ buildBeforeEntity(CONTACT_MARY, VER_FIRST, buildPhone(PHONE_RED)));
+ final EntityDeltaList second = buildSet(
+ buildBeforeEntity(CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
+
+ // Remove contact locally
+ first.getByRawContactId(CONTACT_MARY).markDeleted();
+ assertDiffPattern(first,
+ buildAssertVersion(VER_FIRST),
+ buildAssertVersion(VER_FIRST),
+ buildDelete(RawContacts.CONTENT_URI));
+
+ // Merge in the second version, verify that our delete isn't needed
+ final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
+ assertDiffPattern(merged);
+ }
+
+ public void testMergeRawContactLocalUpdateRemoteDelete() {
+ final EntityDeltaList first = buildSet(
+ buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)),
+ buildBeforeEntity(CONTACT_MARY, VER_FIRST, buildPhone(PHONE_RED)));
+ final EntityDeltaList second = buildSet(
+ buildBeforeEntity(CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
+
+ // Perform local update
+ final ValuesDelta phone = getPhone(first, CONTACT_MARY, PHONE_RED);
+ phone.put(Phone.NUMBER, TEST_PHONE);
+ assertDiffPattern(first,
+ buildAssertVersion(VER_FIRST),
+ buildAssertVersion(VER_FIRST),
+ buildUpdateAggregationSuspended(),
+ buildOper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
+ buildUpdateAggregationDefault());
+
+ final ContentValues phoneInsert = phone.getCompleteValues();
+ final ContentValues contactInsert = first.getByRawContactId(CONTACT_MARY).getValues()
+ .getCompleteValues();
+ contactInsert.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
+
+ // Merge and verify that update turned into insert
+ final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
+ assertDiffPattern(merged,
+ buildAssertVersion(VER_SECOND),
+ buildOper(RawContacts.CONTENT_URI, TYPE_INSERT, contactInsert),
+ buildOper(Data.CONTENT_URI, TYPE_INSERT, phoneInsert),
+ buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT),
+ buildUpdateAggregationKeepTogether(CONTACT_BOB));
+ }
+
+ public void testMergeUsesNewVersion() {
+ final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+ buildPhone(PHONE_RED)));
+ final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+ buildPhone(PHONE_RED)));
+
+ assertEquals((Long)VER_FIRST, getVersion(first, CONTACT_BOB));
+ assertEquals((Long)VER_SECOND, getVersion(second, CONTACT_BOB));
+
+ final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
+ assertEquals((Long)VER_SECOND, getVersion(merged, CONTACT_BOB));
+ }
+
+ public void testMergeAfterEnsureAndTrim() {
+ final EntityDeltaList first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
+ buildEmail(EMAIL_YELLOW)));
+ final EntityDeltaList second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+ buildEmail(EMAIL_YELLOW)));
+
+ // Ensure we have at least one phone
+ final AccountType source = getAccountType();
+ final EntityDelta bobContact = first.getByRawContactId(CONTACT_BOB);
+ EntityModifier.ensureKindExists(bobContact, source, Phone.CONTENT_ITEM_TYPE);
+ final ValuesDelta bobPhone = bobContact.getSuperPrimaryEntry(Phone.CONTENT_ITEM_TYPE, true);
+
+ // Make sure the update would insert a row
+ assertDiffPattern(first,
+ buildAssertVersion(VER_FIRST),
+ buildUpdateAggregationSuspended(),
+ buildOper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bobPhone, CONTACT_BOB)),
+ buildUpdateAggregationDefault());
+
+ // Trim values and ensure that we don't insert things
+ EntityModifier.trimEmpty(bobContact, source);
+ assertDiffPattern(first);
+
+ // Now re-parent the change, which should remain no-op
+ final EntityDeltaList merged = EntityDeltaList.mergeAfter(second, first);
+ assertDiffPattern(merged);
+ }
+}
diff --git a/tests/src/com/android/contacts/EntityModifierTests.java b/tests/src/com/android/contacts/EntityModifierTests.java
index 18877a3..4acaa92 100644
--- a/tests/src/com/android/contacts/EntityModifierTests.java
+++ b/tests/src/com/android/contacts/EntityModifierTests.java
@@ -20,14 +20,15 @@
import static android.content.ContentProviderOperation.TYPE_INSERT;
import static android.content.ContentProviderOperation.TYPE_UPDATE;
-import com.android.contacts.model.ContactsSource;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.AccountType.EditType;
+import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.EntitySet;
-import com.android.contacts.model.Sources;
-import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.model.ContactsSource.EditType;
import com.android.contacts.model.EntityDelta.ValuesDelta;
+import com.android.contacts.model.EntityDeltaList;
+import com.android.contacts.model.EntityModifier;
+import com.android.contacts.tests.mocks.MockAccountTypeManager;
import com.google.android.collect.Lists;
import android.content.ContentProviderOperation;
@@ -35,15 +36,15 @@
import android.content.Context;
import android.content.Entity;
import android.os.Bundle;
-import android.provider.ContactsContract.Intents.Insert;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Intents.Insert;
+import android.provider.ContactsContract.RawContacts;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
@@ -51,7 +52,7 @@
import java.util.List;
/**
- * Tests for {@link EntityModifier} to verify that {@link ContactsSource}
+ * Tests for {@link EntityModifier} to verify that {@link AccountType}
* constraints are being enforced correctly.
*/
@LargeTest
@@ -70,20 +71,15 @@
private static final String TEST_ACCOUNT_NAME = "unittest@example.com";
private static final String TEST_ACCOUNT_TYPE = "com.example.unittest";
- public EntityModifierTests() {
- super();
- }
-
@Override
public void setUp() {
mContext = getContext();
}
- public static class MockContactsSource extends ContactsSource {
- @Override
- protected void inflate(Context context, int inflateLevel) {
+ public static class MockContactsSource extends AccountType {
+
+ MockContactsSource() {
this.accountType = TEST_ACCOUNT_TYPE;
- this.setInflatedLevel(ContactsSource.LEVEL_CONSTRAINTS);
// Phone allows maximum 2 home, 1 work, and unlimited other, with
// constraint of 5 numbers maximum.
@@ -138,20 +134,18 @@
}
/**
- * Build a {@link ContactsSource} that has various odd constraints for
+ * Build a {@link AccountType} that has various odd constraints for
* testing purposes.
*/
- protected ContactsSource getSource() {
- final ContactsSource source = new MockContactsSource();
- source.ensureInflated(getContext(), ContactsSource.LEVEL_CONSTRAINTS);
- return source;
+ protected AccountType getAccountType() {
+ return new MockContactsSource();
}
/**
- * Build {@link Sources} instance.
+ * Build {@link AccountTypeManager} instance.
*/
- protected Sources getSources(ContactsSource... sources) {
- return new Sources(sources);
+ protected AccountTypeManager getAccountTypes(AccountType... types) {
+ return new MockAccountTypeManager(types, null);
}
/**
@@ -192,7 +186,7 @@
*/
public void testValidTypes() {
// Build a source and pull specific types
- final ContactsSource source = getSource();
+ final AccountType source = getAccountType();
final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
final EditType typeWork = EntityModifier.getType(kindPhone, Phone.TYPE_WORK);
@@ -237,7 +231,7 @@
*/
public void testCanInsert() {
// Build a source and pull specific types
- final ContactsSource source = getSource();
+ final AccountType source = getAccountType();
final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
final EditType typeWork = EntityModifier.getType(kindPhone, Phone.TYPE_WORK);
@@ -266,7 +260,7 @@
*/
public void testBestValidType() {
// Build a source and pull specific types
- final ContactsSource source = getSource();
+ final AccountType source = getAccountType();
final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
final EditType typeWork = EntityModifier.getType(kindPhone, Phone.TYPE_WORK);
@@ -302,7 +296,7 @@
}
public void testIsEmptyEmpty() {
- final ContactsSource source = getSource();
+ final AccountType source = getAccountType();
final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
// Test entirely empty row
@@ -313,7 +307,7 @@
}
public void testIsEmptyDirectFields() {
- final ContactsSource source = getSource();
+ final AccountType source = getAccountType();
final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
@@ -330,7 +324,7 @@
}
public void testTrimEmptySingle() {
- final ContactsSource source = getSource();
+ final AccountType source = getAccountType();
final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
@@ -371,60 +365,60 @@
}
public void testTrimEmptySpaces() {
- final ContactsSource source = getSource();
+ final AccountType source = getAccountType();
final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
// Test row that has type values, but values are spaces
- final EntityDelta state = EntitySetTests.buildBeforeEntity(TEST_ID, VER_FIRST);
+ final EntityDelta state = EntityDeltaListTests.buildBeforeEntity(TEST_ID, VER_FIRST);
final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome);
values.put(Phone.NUMBER, " ");
// Build diff, expecting insert for data row and update enforcement
- EntitySetTests.assertDiffPattern(state,
- EntitySetTests.buildAssertVersion(VER_FIRST),
- EntitySetTests.buildUpdateAggregationSuspended(),
- EntitySetTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
- EntitySetTests.buildDataInsert(values, TEST_ID)),
- EntitySetTests.buildUpdateAggregationDefault());
+ EntityDeltaListTests.assertDiffPattern(state,
+ EntityDeltaListTests.buildAssertVersion(VER_FIRST),
+ EntityDeltaListTests.buildUpdateAggregationSuspended(),
+ EntityDeltaListTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
+ EntityDeltaListTests.buildDataInsert(values, TEST_ID)),
+ EntityDeltaListTests.buildUpdateAggregationDefault());
// Trim empty rows and try again, expecting delete of overall contact
EntityModifier.trimEmpty(state, source);
- EntitySetTests.assertDiffPattern(state,
- EntitySetTests.buildAssertVersion(VER_FIRST),
- EntitySetTests.buildDelete(RawContacts.CONTENT_URI));
+ EntityDeltaListTests.assertDiffPattern(state,
+ EntityDeltaListTests.buildAssertVersion(VER_FIRST),
+ EntityDeltaListTests.buildDelete(RawContacts.CONTENT_URI));
}
public void testTrimLeaveValid() {
- final ContactsSource source = getSource();
+ final AccountType source = getAccountType();
final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
// Test row that has type values with valid number
- final EntityDelta state = EntitySetTests.buildBeforeEntity(TEST_ID, VER_FIRST);
+ final EntityDelta state = EntityDeltaListTests.buildBeforeEntity(TEST_ID, VER_FIRST);
final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome);
values.put(Phone.NUMBER, TEST_PHONE);
// Build diff, expecting insert for data row and update enforcement
- EntitySetTests.assertDiffPattern(state,
- EntitySetTests.buildAssertVersion(VER_FIRST),
- EntitySetTests.buildUpdateAggregationSuspended(),
- EntitySetTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
- EntitySetTests.buildDataInsert(values, TEST_ID)),
- EntitySetTests.buildUpdateAggregationDefault());
+ EntityDeltaListTests.assertDiffPattern(state,
+ EntityDeltaListTests.buildAssertVersion(VER_FIRST),
+ EntityDeltaListTests.buildUpdateAggregationSuspended(),
+ EntityDeltaListTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
+ EntityDeltaListTests.buildDataInsert(values, TEST_ID)),
+ EntityDeltaListTests.buildUpdateAggregationDefault());
// Trim empty rows and try again, expecting no differences
EntityModifier.trimEmpty(state, source);
- EntitySetTests.assertDiffPattern(state,
- EntitySetTests.buildAssertVersion(VER_FIRST),
- EntitySetTests.buildUpdateAggregationSuspended(),
- EntitySetTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
- EntitySetTests.buildDataInsert(values, TEST_ID)),
- EntitySetTests.buildUpdateAggregationDefault());
+ EntityDeltaListTests.assertDiffPattern(state,
+ EntityDeltaListTests.buildAssertVersion(VER_FIRST),
+ EntityDeltaListTests.buildUpdateAggregationSuspended(),
+ EntityDeltaListTests.buildOper(Data.CONTENT_URI, TYPE_INSERT,
+ EntityDeltaListTests.buildDataInsert(values, TEST_ID)),
+ EntityDeltaListTests.buildUpdateAggregationDefault());
}
public void testTrimEmptyUntouched() {
- final ContactsSource source = getSource();
+ final AccountType source = getAccountType();
final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
@@ -448,7 +442,7 @@
}
public void testTrimEmptyAfterUpdate() {
- final ContactsSource source = getSource();
+ final AccountType source = getAccountType();
final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
@@ -500,14 +494,14 @@
}
public void testTrimInsertEmpty() {
- final ContactsSource source = getSource();
- final Sources sources = getSources(source);
- final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
+ final AccountType accountType = getAccountType();
+ final AccountTypeManager accountTypes = getAccountTypes(accountType);
+ final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
// Try creating a contact without any child entries
final EntityDelta state = getEntity(null);
- final EntitySet set = EntitySet.fromSingle(state);
+ final EntityDeltaList set = EntityDeltaList.fromSingle(state);
// Build diff, expecting single insert
final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
@@ -520,22 +514,22 @@
}
// Trim empty rows and try again, expecting no insert
- EntityModifier.trimEmpty(set, sources);
+ EntityModifier.trimEmpty(set, accountTypes);
diff.clear();
state.buildDiff(diff);
assertEquals("Unexpected operations", 0, diff.size());
}
public void testTrimInsertInsert() {
- final ContactsSource source = getSource();
- final Sources sources = getSources(source);
- final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
+ final AccountType accountType = getAccountType();
+ final AccountTypeManager accountTypes = getAccountTypes(accountType);
+ final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
// Try creating a contact with single empty entry
final EntityDelta state = getEntity(null);
final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome);
- final EntitySet set = EntitySet.fromSingle(state);
+ final EntityDeltaList set = EntityDeltaList.fromSingle(state);
// Build diff, expecting two insert operations
final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
@@ -553,16 +547,16 @@
}
// Trim empty rows and try again, expecting silence
- EntityModifier.trimEmpty(set, sources);
+ EntityModifier.trimEmpty(set, accountTypes);
diff.clear();
state.buildDiff(diff);
assertEquals("Unexpected operations", 0, diff.size());
}
public void testTrimUpdateRemain() {
- final ContactsSource source = getSource();
- final Sources sources = getSources(source);
- final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
+ final AccountType accountType = getAccountType();
+ final AccountTypeManager accountTypes = getAccountTypes(accountType);
+ final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
// Build "before" with two phone numbers
@@ -579,7 +573,7 @@
second.put(Phone.NUMBER, TEST_PHONE);
final EntityDelta state = getEntity(TEST_ID, first, second);
- final EntitySet set = EntitySet.fromSingle(state);
+ final EntityDeltaList set = EntityDeltaList.fromSingle(state);
// Build diff, expecting no changes
final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
@@ -609,7 +603,7 @@
}
// Now run trim, which should turn that update into delete
- EntityModifier.trimEmpty(set, sources);
+ EntityModifier.trimEmpty(set, accountTypes);
diff.clear();
state.buildDiff(diff);
assertEquals("Unexpected operations", 3, diff.size());
@@ -631,9 +625,9 @@
}
public void testTrimUpdateUpdate() {
- final ContactsSource source = getSource();
- final Sources sources = getSources(source);
- final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
+ final AccountType accountType = getAccountType();
+ final AccountTypeManager accountTypes = getAccountTypes(accountType);
+ final DataKind kindPhone = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
// Build "before" with two phone numbers
@@ -644,7 +638,7 @@
first.put(Phone.NUMBER, TEST_PHONE);
final EntityDelta state = getEntity(TEST_ID, first);
- final EntitySet set = EntitySet.fromSingle(state);
+ final EntityDeltaList set = EntityDeltaList.fromSingle(state);
// Build diff, expecting no changes
final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
@@ -674,7 +668,7 @@
}
// Now run trim, which should turn into deleting the whole contact
- EntityModifier.trimEmpty(set, sources);
+ EntityModifier.trimEmpty(set, accountTypes);
diff.clear();
state.buildDiff(diff);
assertEquals("Unexpected operations", 1, diff.size());
@@ -686,8 +680,7 @@
}
public void testParseExtrasExistingName() {
- final ContactsSource source = getSource();
- final DataKind kindName = source.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
+ final AccountType accountType = getAccountType();
// Build "before" name
final ContentValues first = new ContentValues();
@@ -699,15 +692,14 @@
final EntityDelta state = getEntity(TEST_ID, first);
final Bundle extras = new Bundle();
extras.putString(Insert.NAME, TEST_NAME2);
- EntityModifier.parseExtras(mContext, source, state, extras);
+ EntityModifier.parseExtras(mContext, accountType, state, extras);
final int nameCount = state.getMimeEntriesCount(StructuredName.CONTENT_ITEM_TYPE, true);
assertEquals("Unexpected names", 1, nameCount);
}
public void testParseExtrasIgnoreLimit() {
- final ContactsSource source = getSource();
- final DataKind kindIm = source.getKindForMimetype(Im.CONTENT_ITEM_TYPE);
+ final AccountType accountType = getAccountType();
// Build "before" IM
final ContentValues first = new ContentValues();
@@ -718,37 +710,38 @@
final EntityDelta state = getEntity(TEST_ID, first);
final int beforeCount = state.getMimeEntries(Im.CONTENT_ITEM_TYPE).size();
- // We should ignore data that doesn't fit source rules, since source
+ // We should ignore data that doesn't fit account type rules, since account type
// only allows single Im
final Bundle extras = new Bundle();
extras.putInt(Insert.IM_PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
extras.putString(Insert.IM_HANDLE, TEST_IM);
- EntityModifier.parseExtras(mContext, source, state, extras);
+ EntityModifier.parseExtras(mContext, accountType, state, extras);
final int afterCount = state.getMimeEntries(Im.CONTENT_ITEM_TYPE).size();
- assertEquals("Broke source rules", beforeCount, afterCount);
+ assertEquals("Broke account type rules", beforeCount, afterCount);
}
public void testParseExtrasIgnoreUnhandled() {
- final ContactsSource source = getSource();
+ final AccountType accountType = getAccountType();
final EntityDelta state = getEntity(TEST_ID);
- // We should silently ignore types unsupported by source
+ // We should silently ignore types unsupported by account type
final Bundle extras = new Bundle();
extras.putString(Insert.POSTAL, TEST_POSTAL);
- EntityModifier.parseExtras(mContext, source, state, extras);
+ EntityModifier.parseExtras(mContext, accountType, state, extras);
- assertNull("Broke source rules", state.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE));
+ assertNull("Broke accoun type rules",
+ state.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE));
}
public void testParseExtrasJobTitle() {
- final ContactsSource source = getSource();
+ final AccountType accountType = getAccountType();
final EntityDelta state = getEntity(TEST_ID);
// Make sure that we create partial Organizations
final Bundle extras = new Bundle();
extras.putString(Insert.JOB_TITLE, TEST_NAME);
- EntityModifier.parseExtras(mContext, source, state, extras);
+ EntityModifier.parseExtras(mContext, accountType, state, extras);
final int count = state.getMimeEntries(Organization.CONTENT_ITEM_TYPE).size();
assertEquals("Expected to create organization", 1, count);
diff --git a/tests/src/com/android/contacts/EntitySetTests.java b/tests/src/com/android/contacts/EntitySetTests.java
deleted file mode 100644
index 037c927..0000000
--- a/tests/src/com/android/contacts/EntitySetTests.java
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * 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 static android.content.ContentProviderOperation.TYPE_ASSERT;
-import static android.content.ContentProviderOperation.TYPE_DELETE;
-import static android.content.ContentProviderOperation.TYPE_INSERT;
-import static android.content.ContentProviderOperation.TYPE_UPDATE;
-
-import com.android.contacts.EntityModifierTests.MockContactsSource;
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.EntityModifier;
-import com.android.contacts.model.EntitySet;
-import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.google.android.collect.Lists;
-
-import android.content.ContentProviderOperation;
-import android.content.ContentValues;
-import android.content.Entity;
-import android.net.Uri;
-import android.provider.BaseColumns;
-import android.provider.ContactsContract.AggregationExceptions;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-
-/**
- * Tests for {@link EntitySet} which focus on "diff" operations that should
- * create {@link AggregationExceptions} in certain cases.
- */
-@LargeTest
-public class EntitySetTests extends AndroidTestCase {
- public static final String TAG = "EntitySetTests";
-
- private static final long CONTACT_FIRST = 1;
- private static final long CONTACT_SECOND = 2;
-
- public static final long CONTACT_BOB = 10;
- public static final long CONTACT_MARY = 11;
-
- public static final long PHONE_RED = 20;
- public static final long PHONE_GREEN = 21;
- public static final long PHONE_BLUE = 22;
-
- public static final long EMAIL_YELLOW = 25;
-
- public static final long VER_FIRST = 100;
- public static final long VER_SECOND = 200;
-
- public static final String TEST_PHONE = "555-1212";
- public static final String TEST_ACCOUNT = "org.example.test";
-
- public EntitySetTests() {
- super();
- }
-
- @Override
- public void setUp() {
- mContext = getContext();
- }
-
- /**
- * Build a {@link ContactsSource} that has various odd constraints for
- * testing purposes.
- */
- protected ContactsSource getSource() {
- final ContactsSource source = new MockContactsSource();
- source.ensureInflated(getContext(), ContactsSource.LEVEL_CONSTRAINTS);
- return source;
- }
-
- static ContentValues getValues(ContentProviderOperation operation)
- throws NoSuchFieldException, IllegalAccessException {
- final Field field = ContentProviderOperation.class.getDeclaredField("mValues");
- field.setAccessible(true);
- return (ContentValues) field.get(operation);
- }
-
- static EntityDelta getUpdate(long rawContactId) {
- final Entity before = EntityDeltaTests.getEntity(rawContactId,
- EntityDeltaTests.TEST_PHONE_ID);
- return EntityDelta.fromBefore(before);
- }
-
- static EntityDelta getInsert() {
- final ContentValues after = new ContentValues();
- after.put(RawContacts.ACCOUNT_NAME, EntityDeltaTests.TEST_ACCOUNT_NAME);
- after.put(RawContacts.SEND_TO_VOICEMAIL, 1);
-
- final ValuesDelta values = ValuesDelta.fromAfter(after);
- return new EntityDelta(values);
- }
-
- static EntitySet buildSet(EntityDelta... deltas) {
- final EntitySet set = EntitySet.fromSingle(deltas[0]);
- for (int i = 1; i < deltas.length; i++) {
- set.add(deltas[i]);
- }
- return set;
- }
-
- static EntityDelta buildBeforeEntity(long rawContactId, long version,
- ContentValues... entries) {
- // Build an existing contact read from database
- final ContentValues contact = new ContentValues();
- contact.put(RawContacts.VERSION, version);
- contact.put(RawContacts._ID, rawContactId);
- final Entity before = new Entity(contact);
- for (ContentValues entry : entries) {
- before.addSubValue(Data.CONTENT_URI, entry);
- }
- return EntityDelta.fromBefore(before);
- }
-
- static EntityDelta buildAfterEntity(ContentValues... entries) {
- // Build an existing contact read from database
- final ContentValues contact = new ContentValues();
- contact.put(RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT);
- final EntityDelta after = new EntityDelta(ValuesDelta.fromAfter(contact));
- for (ContentValues entry : entries) {
- after.addEntry(ValuesDelta.fromAfter(entry));
- }
- return after;
- }
-
- static ContentValues buildPhone(long phoneId) {
- return buildPhone(phoneId, Long.toString(phoneId));
- }
-
- static ContentValues buildPhone(long phoneId, String value) {
- final ContentValues values = new ContentValues();
- values.put(Data._ID, phoneId);
- values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
- values.put(Phone.NUMBER, value);
- values.put(Phone.TYPE, Phone.TYPE_HOME);
- return values;
- }
-
- static ContentValues buildEmail(long emailId) {
- final ContentValues values = new ContentValues();
- values.put(Data._ID, emailId);
- values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
- values.put(Email.DATA, Long.toString(emailId));
- values.put(Email.TYPE, Email.TYPE_HOME);
- return values;
- }
-
- static void insertPhone(EntitySet set, long rawContactId, ContentValues values) {
- final EntityDelta match = set.getByRawContactId(rawContactId);
- match.addEntry(ValuesDelta.fromAfter(values));
- }
-
- static ValuesDelta getPhone(EntitySet set, long rawContactId, long dataId) {
- final EntityDelta match = set.getByRawContactId(rawContactId);
- return match.getEntry(dataId);
- }
-
- static void assertDiffPattern(EntityDelta delta, ContentProviderOperation... pattern) {
- final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
- delta.buildAssert(diff);
- delta.buildDiff(diff);
- assertDiffPattern(diff, pattern);
- }
-
- static void assertDiffPattern(EntitySet set, ContentProviderOperation... pattern) {
- assertDiffPattern(set.buildDiff(), pattern);
- }
-
- static void assertDiffPattern(ArrayList<ContentProviderOperation> diff,
- ContentProviderOperation... pattern) {
- assertEquals("Unexpected operations", pattern.length, diff.size());
- for (int i = 0; i < pattern.length; i++) {
- final ContentProviderOperation expected = pattern[i];
- final ContentProviderOperation found = diff.get(i);
-
- assertEquals("Unexpected uri", expected.getUri(), found.getUri());
-
- final String expectedType = getStringForType(expected.getType());
- final String foundType = getStringForType(found.getType());
- assertEquals("Unexpected type", expectedType, foundType);
-
- if (expected.getType() == TYPE_DELETE) continue;
-
- try {
- final ContentValues expectedValues = getValues(expected);
- final ContentValues foundValues = getValues(found);
-
- expectedValues.remove(BaseColumns._ID);
- foundValues.remove(BaseColumns._ID);
-
- assertEquals("Unexpected values", expectedValues, foundValues);
- } catch (NoSuchFieldException e) {
- fail(e.toString());
- } catch (IllegalAccessException e) {
- fail(e.toString());
- }
- }
- }
-
- static String getStringForType(int type) {
- switch (type) {
- case TYPE_ASSERT: return "TYPE_ASSERT";
- case TYPE_INSERT: return "TYPE_INSERT";
- case TYPE_UPDATE: return "TYPE_UPDATE";
- case TYPE_DELETE: return "TYPE_DELETE";
- default: return Integer.toString(type);
- }
- }
-
- static ContentProviderOperation buildAssertVersion(long version) {
- final ContentValues values = new ContentValues();
- values.put(RawContacts.VERSION, version);
- return buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT, values);
- }
-
- static ContentProviderOperation buildAggregationModeUpdate(int mode) {
- final ContentValues values = new ContentValues();
- values.put(RawContacts.AGGREGATION_MODE, mode);
- return buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE, values);
- }
-
- static ContentProviderOperation buildUpdateAggregationSuspended() {
- return buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_SUSPENDED);
- }
-
- static ContentProviderOperation buildUpdateAggregationDefault() {
- return buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT);
- }
-
- static ContentProviderOperation buildUpdateAggregationKeepTogether(long rawContactId) {
- final ContentValues values = new ContentValues();
- values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId);
- values.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
- return buildOper(AggregationExceptions.CONTENT_URI, TYPE_UPDATE, values);
- }
-
- static ContentValues buildDataInsert(ValuesDelta values, long rawContactId) {
- final ContentValues insertValues = values.getCompleteValues();
- insertValues.put(Data.RAW_CONTACT_ID, rawContactId);
- return insertValues;
- }
-
- static ContentProviderOperation buildDelete(Uri uri) {
- return buildOper(uri, TYPE_DELETE, (ContentValues)null);
- }
-
- static ContentProviderOperation buildOper(Uri uri, int type, ValuesDelta values) {
- return buildOper(uri, type, values.getCompleteValues());
- }
-
- static ContentProviderOperation buildOper(Uri uri, int type, ContentValues values) {
- switch (type) {
- case TYPE_ASSERT:
- return ContentProviderOperation.newAssertQuery(uri).withValues(values).build();
- case TYPE_INSERT:
- return ContentProviderOperation.newInsert(uri).withValues(values).build();
- case TYPE_UPDATE:
- return ContentProviderOperation.newUpdate(uri).withValues(values).build();
- case TYPE_DELETE:
- return ContentProviderOperation.newDelete(uri).build();
- }
- return null;
- }
-
- static Long getVersion(EntitySet set, Long rawContactId) {
- return set.getByRawContactId(rawContactId).getValues().getAsLong(RawContacts.VERSION);
- }
-
- /**
- * Count number of {@link AggregationExceptions} updates contained in the
- * given list of {@link ContentProviderOperation}.
- */
- static int countExceptionUpdates(ArrayList<ContentProviderOperation> diff) {
- int updateCount = 0;
- for (ContentProviderOperation oper : diff) {
- if (AggregationExceptions.CONTENT_URI.equals(oper.getUri())
- && oper.getType() == ContentProviderOperation.TYPE_UPDATE) {
- updateCount++;
- }
- }
- return updateCount;
- }
-
- public void testInsert() {
- final EntityDelta insert = getInsert();
- final EntitySet set = buildSet(insert);
-
- // Inserting single shouldn't create rules
- final ArrayList<ContentProviderOperation> diff = set.buildDiff();
- final int exceptionCount = countExceptionUpdates(diff);
- assertEquals("Unexpected exception updates", 0, exceptionCount);
- }
-
- public void testUpdateUpdate() {
- final EntityDelta updateFirst = getUpdate(CONTACT_FIRST);
- final EntityDelta updateSecond = getUpdate(CONTACT_SECOND);
- final EntitySet set = buildSet(updateFirst, updateSecond);
-
- // Updating two existing shouldn't create rules
- final ArrayList<ContentProviderOperation> diff = set.buildDiff();
- final int exceptionCount = countExceptionUpdates(diff);
- assertEquals("Unexpected exception updates", 0, exceptionCount);
- }
-
- public void testUpdateInsert() {
- final EntityDelta update = getUpdate(CONTACT_FIRST);
- final EntityDelta insert = getInsert();
- final EntitySet set = buildSet(update, insert);
-
- // New insert should only create one rule
- final ArrayList<ContentProviderOperation> diff = set.buildDiff();
- final int exceptionCount = countExceptionUpdates(diff);
- assertEquals("Unexpected exception updates", 1, exceptionCount);
- }
-
- public void testInsertUpdateInsert() {
- final EntityDelta insertFirst = getInsert();
- final EntityDelta update = getUpdate(CONTACT_FIRST);
- final EntityDelta insertSecond = getInsert();
- final EntitySet set = buildSet(insertFirst, update, insertSecond);
-
- // Two inserts should create two rules to bind against single existing
- final ArrayList<ContentProviderOperation> diff = set.buildDiff();
- final int exceptionCount = countExceptionUpdates(diff);
- assertEquals("Unexpected exception updates", 2, exceptionCount);
- }
-
- public void testInsertInsertInsert() {
- final EntityDelta insertFirst = getInsert();
- final EntityDelta insertSecond = getInsert();
- final EntityDelta insertThird = getInsert();
- final EntitySet set = buildSet(insertFirst, insertSecond, insertThird);
-
- // Three new inserts should create only two binding rules
- final ArrayList<ContentProviderOperation> diff = set.buildDiff();
- final int exceptionCount = countExceptionUpdates(diff);
- assertEquals("Unexpected exception updates", 2, exceptionCount);
- }
-
- public void testInsertInsertSeparate() {
- // This assumes getInsert() will return back an "empty" raw
- // contact meaning it will contain no actual information
- final EntityDelta insertFirst = getInsert();
- final EntityDelta insertSecond = getInsert();
- final EntitySet set = buildSet(insertFirst, insertSecond);
-
- // This would normally build a TYPE_KEEP_SEPARATE aggregation exception,
- // but since the raw contacts won't be added because they are empty,
- // we should get 0 exceptions back
- set.splitRawContacts();
-
- final ArrayList<ContentProviderOperation> diff = set.buildDiff();
- final int exceptionCount = countExceptionUpdates(diff);
- assertEquals("Unexpected exception updates", 0, exceptionCount);
- }
-
- public void testUpdateInsertSeparate() {
- // This assumes getInsert() will return back an "empty" raw
- // contact meaning it will contain no actual information
- final EntityDelta update = getUpdate(CONTACT_FIRST);
- final EntityDelta insert = getInsert();
- final EntitySet set = buildSet(update, insert);
-
- // This would normally build a KEEP_SEPARATE aggregation exception,
- // but since the insert won't be added because it is empty,
- // we should get 0 exceptions back
- set.splitRawContacts();
-
- final ArrayList<ContentProviderOperation> diff = set.buildDiff();
- final int exceptionCount = countExceptionUpdates(diff);
- assertEquals("Unexpected exception updates", 0, exceptionCount);
- }
-
- public void testUpdateInsertInsertSeparate() {
- // This assumes getInsert() will return back an "empty" raw
- // contact meaning it will contain no actual information
- final EntityDelta update = getUpdate(CONTACT_FIRST);
- final EntityDelta insertFirst = getInsert();
- final EntityDelta insertSecond = getInsert();
- final EntitySet set = buildSet(update, insertFirst, insertSecond);
-
- // This would normally build a KEEP_SEPARATE aggregation exception,
- // but since the inserts won't be added because they are empty,
- // we should get 0 exceptions back
- set.splitRawContacts();
-
- final ArrayList<ContentProviderOperation> diff = set.buildDiff();
- final int exceptionCount = countExceptionUpdates(diff);
- assertEquals("Unexpected exception updates", 0, exceptionCount);
- }
-
- public void testMergeDataRemoteInsert() {
- final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
- buildPhone(PHONE_RED)));
- final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
- buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
-
- // Merge in second version, verify they match
- final EntitySet merged = EntitySet.mergeAfter(second, first);
- assertEquals("Unexpected change when merging", second, merged);
- }
-
- public void testMergeDataLocalUpdateRemoteInsert() {
- final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
- buildPhone(PHONE_RED)));
- final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
- buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
-
- // Change the local number to trigger update
- final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED);
- phone.put(Phone.NUMBER, TEST_PHONE);
-
- assertDiffPattern(first,
- buildAssertVersion(VER_FIRST),
- buildUpdateAggregationSuspended(),
- buildOper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
- buildUpdateAggregationDefault());
-
- // Merge in the second version, verify diff matches
- final EntitySet merged = EntitySet.mergeAfter(second, first);
- assertDiffPattern(merged,
- buildAssertVersion(VER_SECOND),
- buildUpdateAggregationSuspended(),
- buildOper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
- buildUpdateAggregationDefault());
- }
-
- public void testMergeDataLocalUpdateRemoteDelete() {
- final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
- buildPhone(PHONE_RED)));
- final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
- buildPhone(PHONE_GREEN)));
-
- // Change the local number to trigger update
- final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED);
- phone.put(Phone.NUMBER, TEST_PHONE);
-
- assertDiffPattern(first,
- buildAssertVersion(VER_FIRST),
- buildUpdateAggregationSuspended(),
- buildOper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
- buildUpdateAggregationDefault());
-
- // Merge in the second version, verify that our update changed to
- // insert, since RED was deleted on remote side
- final EntitySet merged = EntitySet.mergeAfter(second, first);
- assertDiffPattern(merged,
- buildAssertVersion(VER_SECOND),
- buildUpdateAggregationSuspended(),
- buildOper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(phone, CONTACT_BOB)),
- buildUpdateAggregationDefault());
- }
-
- public void testMergeDataLocalDeleteRemoteUpdate() {
- final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
- buildPhone(PHONE_RED)));
- final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
- buildPhone(PHONE_RED, TEST_PHONE)));
-
- // Delete phone locally
- final ValuesDelta phone = getPhone(first, CONTACT_BOB, PHONE_RED);
- phone.markDeleted();
-
- assertDiffPattern(first,
- buildAssertVersion(VER_FIRST),
- buildUpdateAggregationSuspended(),
- buildDelete(Data.CONTENT_URI),
- buildUpdateAggregationDefault());
-
- // Merge in the second version, verify that our delete remains
- final EntitySet merged = EntitySet.mergeAfter(second, first);
- assertDiffPattern(merged,
- buildAssertVersion(VER_SECOND),
- buildUpdateAggregationSuspended(),
- buildDelete(Data.CONTENT_URI),
- buildUpdateAggregationDefault());
- }
-
- public void testMergeDataLocalInsertRemoteInsert() {
- final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
- buildPhone(PHONE_RED)));
- final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
- buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
-
- // Insert new phone locally
- final ValuesDelta bluePhone = ValuesDelta.fromAfter(buildPhone(PHONE_BLUE));
- first.getByRawContactId(CONTACT_BOB).addEntry(bluePhone);
- assertDiffPattern(first,
- buildAssertVersion(VER_FIRST),
- buildUpdateAggregationSuspended(),
- buildOper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bluePhone, CONTACT_BOB)),
- buildUpdateAggregationDefault());
-
- // Merge in the second version, verify that our insert remains
- final EntitySet merged = EntitySet.mergeAfter(second, first);
- assertDiffPattern(merged,
- buildAssertVersion(VER_SECOND),
- buildUpdateAggregationSuspended(),
- buildOper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bluePhone, CONTACT_BOB)),
- buildUpdateAggregationDefault());
- }
-
- public void testMergeRawContactLocalInsertRemoteInsert() {
- final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
- buildPhone(PHONE_RED)));
- final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
- buildPhone(PHONE_RED)), buildBeforeEntity(CONTACT_MARY, VER_SECOND,
- buildPhone(PHONE_RED)));
-
- // Add new contact locally, should remain insert
- final ContentValues joePhoneInsert = buildPhone(PHONE_BLUE);
- final EntityDelta joeContact = buildAfterEntity(joePhoneInsert);
- final ContentValues joeContactInsert = joeContact.getValues().getCompleteValues();
- joeContactInsert.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
- first.add(joeContact);
- assertDiffPattern(first,
- buildAssertVersion(VER_FIRST),
- buildOper(RawContacts.CONTENT_URI, TYPE_INSERT, joeContactInsert),
- buildOper(Data.CONTENT_URI, TYPE_INSERT, joePhoneInsert),
- buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT),
- buildUpdateAggregationKeepTogether(CONTACT_BOB));
-
- // Merge in the second version, verify that our insert remains
- final EntitySet merged = EntitySet.mergeAfter(second, first);
- assertDiffPattern(merged,
- buildAssertVersion(VER_SECOND),
- buildAssertVersion(VER_SECOND),
- buildOper(RawContacts.CONTENT_URI, TYPE_INSERT, joeContactInsert),
- buildOper(Data.CONTENT_URI, TYPE_INSERT, joePhoneInsert),
- buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT),
- buildUpdateAggregationKeepTogether(CONTACT_BOB));
- }
-
- public void testMergeRawContactLocalDeleteRemoteDelete() {
- final EntitySet first = buildSet(
- buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)),
- buildBeforeEntity(CONTACT_MARY, VER_FIRST, buildPhone(PHONE_RED)));
- final EntitySet second = buildSet(
- buildBeforeEntity(CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
-
- // Remove contact locally
- first.getByRawContactId(CONTACT_MARY).markDeleted();
- assertDiffPattern(first,
- buildAssertVersion(VER_FIRST),
- buildAssertVersion(VER_FIRST),
- buildDelete(RawContacts.CONTENT_URI));
-
- // Merge in the second version, verify that our delete isn't needed
- final EntitySet merged = EntitySet.mergeAfter(second, first);
- assertDiffPattern(merged);
- }
-
- public void testMergeRawContactLocalUpdateRemoteDelete() {
- final EntitySet first = buildSet(
- buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)),
- buildBeforeEntity(CONTACT_MARY, VER_FIRST, buildPhone(PHONE_RED)));
- final EntitySet second = buildSet(
- buildBeforeEntity(CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
-
- // Perform local update
- final ValuesDelta phone = getPhone(first, CONTACT_MARY, PHONE_RED);
- phone.put(Phone.NUMBER, TEST_PHONE);
- assertDiffPattern(first,
- buildAssertVersion(VER_FIRST),
- buildAssertVersion(VER_FIRST),
- buildUpdateAggregationSuspended(),
- buildOper(Data.CONTENT_URI, TYPE_UPDATE, phone.getAfter()),
- buildUpdateAggregationDefault());
-
- final ContentValues phoneInsert = phone.getCompleteValues();
- final ContentValues contactInsert = first.getByRawContactId(CONTACT_MARY).getValues()
- .getCompleteValues();
- contactInsert.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_SUSPENDED);
-
- // Merge and verify that update turned into insert
- final EntitySet merged = EntitySet.mergeAfter(second, first);
- assertDiffPattern(merged,
- buildAssertVersion(VER_SECOND),
- buildOper(RawContacts.CONTENT_URI, TYPE_INSERT, contactInsert),
- buildOper(Data.CONTENT_URI, TYPE_INSERT, phoneInsert),
- buildAggregationModeUpdate(RawContacts.AGGREGATION_MODE_DEFAULT),
- buildUpdateAggregationKeepTogether(CONTACT_BOB));
- }
-
- public void testMergeUsesNewVersion() {
- final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
- buildPhone(PHONE_RED)));
- final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
- buildPhone(PHONE_RED)));
-
- assertEquals((Long)VER_FIRST, getVersion(first, CONTACT_BOB));
- assertEquals((Long)VER_SECOND, getVersion(second, CONTACT_BOB));
-
- final EntitySet merged = EntitySet.mergeAfter(second, first);
- assertEquals((Long)VER_SECOND, getVersion(merged, CONTACT_BOB));
- }
-
- public void testMergeAfterEnsureAndTrim() {
- final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST,
- buildEmail(EMAIL_YELLOW)));
- final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
- buildEmail(EMAIL_YELLOW)));
-
- // Ensure we have at least one phone
- final ContactsSource source = getSource();
- final EntityDelta bobContact = first.getByRawContactId(CONTACT_BOB);
- EntityModifier.ensureKindExists(bobContact, source, Phone.CONTENT_ITEM_TYPE);
- final ValuesDelta bobPhone = bobContact.getSuperPrimaryEntry(Phone.CONTENT_ITEM_TYPE, true);
-
- // Make sure the update would insert a row
- assertDiffPattern(first,
- buildAssertVersion(VER_FIRST),
- buildUpdateAggregationSuspended(),
- buildOper(Data.CONTENT_URI, TYPE_INSERT, buildDataInsert(bobPhone, CONTACT_BOB)),
- buildUpdateAggregationDefault());
-
- // Trim values and ensure that we don't insert things
- EntityModifier.trimEmpty(bobContact, source);
- assertDiffPattern(first);
-
- // Now re-parent the change, which should remain no-op
- final EntitySet merged = EntitySet.mergeAfter(second, first);
- assertDiffPattern(merged);
- }
-}
diff --git a/tests/src/com/android/contacts/GroupingListAdapterTests.java b/tests/src/com/android/contacts/GroupingListAdapterTests.java
deleted file mode 100644
index 1877fac..0000000
--- a/tests/src/com/android/contacts/GroupingListAdapterTests.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2010 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.database.CharArrayBuffer;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.provider.CallLog.Calls;
-import android.test.AndroidTestCase;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.ViewGroup;
-
-import static com.android.contacts.GroupingListAdapter.ITEM_TYPE_STANDALONE;
-import static com.android.contacts.GroupingListAdapter.ITEM_TYPE_IN_GROUP;
-import static com.android.contacts.GroupingListAdapter.ITEM_TYPE_GROUP_HEADER;
-
-/**
- * Tests for the contact call list adapter.
- *
- * Running all tests:
- *
- * runtest contacts
- * or
- * adb shell am instrument \
- * -w com.android.contacts.tests/android.test.InstrumentationTestRunner
- */
-public class GroupingListAdapterTests extends AndroidTestCase {
-
- static private final String[] CALL_LOG_PROJECTION = new String[] {
- Calls._ID,
- Calls.NUMBER,
- Calls.DATE,
- };
-
- private static final int CALLS_NUMBER_COLUMN_INDEX = 1;
-
- private MatrixCursor mCursor;
- private long mNextCall;
-
- private GroupingListAdapter mAdapter = new GroupingListAdapter(null) {
-
- @Override
- protected void addGroups(Cursor cursor) {
- int count = cursor.getCount();
- int groupItemCount = 1;
- cursor.moveToFirst();
- String currentValue = cursor.getString(CALLS_NUMBER_COLUMN_INDEX);
- for (int i = 1; i < count; i++) {
- cursor.moveToNext();
- String value = cursor.getString(CALLS_NUMBER_COLUMN_INDEX);
- if (TextUtils.equals(value, currentValue)) {
- groupItemCount++;
- } else {
- if (groupItemCount > 1) {
- addGroup(i - groupItemCount, groupItemCount, false);
- }
-
- groupItemCount = 1;
- currentValue = value;
- }
- }
- if (groupItemCount > 1) {
- addGroup(count - groupItemCount, groupItemCount, false);
- }
- }
-
- @Override
- protected void bindChildView(View view, Context context, Cursor cursor) {
- }
-
- @Override
- protected void bindGroupView(View view, Context context, Cursor cursor, int groupSize,
- boolean expanded) {
- }
-
- @Override
- protected void bindStandAloneView(View view, Context context, Cursor cursor) {
- }
-
- @Override
- protected View newChildView(Context context, ViewGroup parent) {
- return null;
- }
-
- @Override
- protected View newGroupView(Context context, ViewGroup parent) {
- return null;
- }
-
- @Override
- protected View newStandAloneView(Context context, ViewGroup parent) {
- return null;
- }
- };
-
- private void buildCursor(String... numbers) {
- mCursor = new MatrixCursor(CALL_LOG_PROJECTION);
- mNextCall = 1;
- for (String number : numbers) {
- mCursor.addRow(new Object[]{mNextCall, number, 1000 - mNextCall});
- mNextCall++;
- }
- }
-
- public void testGroupingWithoutGroups() {
- buildCursor("1", "2", "3");
- mAdapter.changeCursor(mCursor);
-
- assertEquals(3, mAdapter.getCount());
- assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 1);
- assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 2);
- }
-
- public void testGroupingWithCollapsedGroupAtTheBeginning() {
- buildCursor("1", "1", "2");
- mAdapter.changeCursor(mCursor);
-
- assertEquals(2, mAdapter.getCount());
- assertPositionMetadata(0, ITEM_TYPE_GROUP_HEADER, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 2);
- }
-
- public void testGroupingWithExpandedGroupAtTheBeginning() {
- buildCursor("1", "1", "2");
- mAdapter.changeCursor(mCursor);
- mAdapter.toggleGroup(0);
-
- assertEquals(4, mAdapter.getCount());
- assertPositionMetadata(0, ITEM_TYPE_GROUP_HEADER, true, 0);
- assertPositionMetadata(1, ITEM_TYPE_IN_GROUP, false, 0);
- assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1);
- assertPositionMetadata(3, ITEM_TYPE_STANDALONE, false, 2);
- }
-
- public void testGroupingWithExpandCollapseCycleAtTheBeginning() {
- buildCursor("1", "1", "2");
- mAdapter.changeCursor(mCursor);
- mAdapter.toggleGroup(0);
- mAdapter.toggleGroup(0);
-
- assertEquals(2, mAdapter.getCount());
- assertPositionMetadata(0, ITEM_TYPE_GROUP_HEADER, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 2);
- }
-
- public void testGroupingWithCollapsedGroupInTheMiddle() {
- buildCursor("1", "2", "2", "2", "3");
- mAdapter.changeCursor(mCursor);
-
- assertEquals(3, mAdapter.getCount());
- assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, false, 1);
- assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 4);
- }
-
- public void testGroupingWithExpandedGroupInTheMiddle() {
- buildCursor("1", "2", "2", "2", "3");
- mAdapter.changeCursor(mCursor);
- mAdapter.toggleGroup(1);
-
- assertEquals(6, mAdapter.getCount());
- assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, true, 1);
- assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1);
- assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2);
- assertPositionMetadata(4, ITEM_TYPE_IN_GROUP, false, 3);
- assertPositionMetadata(5, ITEM_TYPE_STANDALONE, false, 4);
- }
-
- public void testGroupingWithCollapsedGroupAtTheEnd() {
- buildCursor("1", "2", "3", "3", "3");
- mAdapter.changeCursor(mCursor);
-
- assertEquals(3, mAdapter.getCount());
- assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 1);
- assertPositionMetadata(2, ITEM_TYPE_GROUP_HEADER, false, 2);
- }
-
- public void testGroupingWithExpandedGroupAtTheEnd() {
- buildCursor("1", "2", "3", "3", "3");
- mAdapter.changeCursor(mCursor);
- mAdapter.toggleGroup(2);
-
- assertEquals(6, mAdapter.getCount());
- assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_STANDALONE, false, 1);
- assertPositionMetadata(2, ITEM_TYPE_GROUP_HEADER, true, 2);
- assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2);
- assertPositionMetadata(4, ITEM_TYPE_IN_GROUP, false, 3);
- assertPositionMetadata(5, ITEM_TYPE_IN_GROUP, false, 4);
- }
-
- public void testGroupingWithMultipleCollapsedGroups() {
- buildCursor("1", "2", "2", "3", "4", "4", "5", "5", "6");
- mAdapter.changeCursor(mCursor);
-
- assertEquals(6, mAdapter.getCount());
- assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, false, 1);
- assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 3);
- assertPositionMetadata(3, ITEM_TYPE_GROUP_HEADER, false, 4);
- assertPositionMetadata(4, ITEM_TYPE_GROUP_HEADER, false, 6);
- assertPositionMetadata(5, ITEM_TYPE_STANDALONE, false, 8);
- }
-
- public void testGroupingWithMultipleExpandedGroups() {
- buildCursor("1", "2", "2", "3", "4", "4", "5", "5", "6");
- mAdapter.changeCursor(mCursor);
- mAdapter.toggleGroup(1);
-
- // Note that expanding the group of 2's shifted the group of 5's down from the
- // 4th to the 6th position
- mAdapter.toggleGroup(6);
-
- assertEquals(10, mAdapter.getCount());
- assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, true, 1);
- assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1);
- assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2);
- assertPositionMetadata(4, ITEM_TYPE_STANDALONE, false, 3);
- assertPositionMetadata(5, ITEM_TYPE_GROUP_HEADER, false, 4);
- assertPositionMetadata(6, ITEM_TYPE_GROUP_HEADER, true, 6);
- assertPositionMetadata(7, ITEM_TYPE_IN_GROUP, false, 6);
- assertPositionMetadata(8, ITEM_TYPE_IN_GROUP, false, 7);
- assertPositionMetadata(9, ITEM_TYPE_STANDALONE, false, 8);
- }
-
- public void testPositionCache() {
- buildCursor("1", "2", "2", "3", "4", "4", "5", "5", "6");
- mAdapter.changeCursor(mCursor);
-
- // First pass - building up cache
- assertEquals(6, mAdapter.getCount());
- assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, false, 1);
- assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 3);
- assertPositionMetadata(3, ITEM_TYPE_GROUP_HEADER, false, 4);
- assertPositionMetadata(4, ITEM_TYPE_GROUP_HEADER, false, 6);
- assertPositionMetadata(5, ITEM_TYPE_STANDALONE, false, 8);
-
- // Second pass - using cache
- assertEquals(6, mAdapter.getCount());
- assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, false, 1);
- assertPositionMetadata(2, ITEM_TYPE_STANDALONE, false, 3);
- assertPositionMetadata(3, ITEM_TYPE_GROUP_HEADER, false, 4);
- assertPositionMetadata(4, ITEM_TYPE_GROUP_HEADER, false, 6);
- assertPositionMetadata(5, ITEM_TYPE_STANDALONE, false, 8);
-
- // Invalidate cache by expanding a group
- mAdapter.toggleGroup(1);
-
- // First pass - building up cache
- assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, true, 1);
- assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1);
- assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2);
- assertPositionMetadata(4, ITEM_TYPE_STANDALONE, false, 3);
- assertPositionMetadata(5, ITEM_TYPE_GROUP_HEADER, false, 4);
- assertPositionMetadata(6, ITEM_TYPE_GROUP_HEADER, false, 6);
- assertPositionMetadata(7, ITEM_TYPE_STANDALONE, false, 8);
-
- // Second pass - using cache
- assertPositionMetadata(0, ITEM_TYPE_STANDALONE, false, 0);
- assertPositionMetadata(1, ITEM_TYPE_GROUP_HEADER, true, 1);
- assertPositionMetadata(2, ITEM_TYPE_IN_GROUP, false, 1);
- assertPositionMetadata(3, ITEM_TYPE_IN_GROUP, false, 2);
- assertPositionMetadata(4, ITEM_TYPE_STANDALONE, false, 3);
- assertPositionMetadata(5, ITEM_TYPE_GROUP_HEADER, false, 4);
- assertPositionMetadata(6, ITEM_TYPE_GROUP_HEADER, false, 6);
- assertPositionMetadata(7, ITEM_TYPE_STANDALONE, false, 8);
- }
-
- public void testGroupDescriptorArrayGrowth() {
- String[] numbers = new String[500];
- for (int i = 0; i < numbers.length; i++) {
-
- // Make groups of 2
- numbers[i] = String.valueOf((i / 2) * 2);
- }
-
- buildCursor(numbers);
- mAdapter.changeCursor(mCursor);
-
- assertEquals(250, mAdapter.getCount());
- }
-
- private void assertPositionMetadata(int position, int itemType, boolean isExpanded,
- int cursorPosition) {
- GroupingListAdapter.PositionMetadata metadata = new GroupingListAdapter.PositionMetadata();
- mAdapter.obtainPositionMetadata(metadata, position);
- assertEquals(itemType, metadata.itemType);
- if (metadata.itemType == ITEM_TYPE_GROUP_HEADER) {
- assertEquals(isExpanded, metadata.isExpanded);
- }
- assertEquals(cursorPosition, metadata.cursorPosition);
- }
-}
diff --git a/tests/src/com/android/contacts/RecentCallsListActivityTests.java b/tests/src/com/android/contacts/RecentCallsListActivityTests.java
index d00854f..3c2a9fe 100644
--- a/tests/src/com/android/contacts/RecentCallsListActivityTests.java
+++ b/tests/src/com/android/contacts/RecentCallsListActivityTests.java
@@ -58,7 +58,8 @@
Calls.TYPE,
Calls.CACHED_NAME,
Calls.CACHED_NUMBER_TYPE,
- Calls.CACHED_NUMBER_LABEL
+ Calls.CACHED_NUMBER_LABEL,
+ Calls.COUNTRY_ISO,
};
static private final int RAND_DURATION = -1;
static private final long NOW = -1L;
@@ -283,6 +284,7 @@
row.add(""); // cached name
row.add(0); // cached number type
row.add(""); // cached number label
+ row.add("US"); // country ISO
}
/**
diff --git a/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java b/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java
new file mode 100644
index 0000000..912950f
--- /dev/null
+++ b/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2010 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.activities;
+
+import com.android.contacts.ContactsApplication;
+import com.android.contacts.R;
+import com.android.contacts.detail.ContactDetailFragment;
+import com.android.contacts.list.ContactBrowseListFragment;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.BaseAccountType;
+import com.android.contacts.test.InjectedServices;
+import com.android.contacts.tests.mocks.ContactsMockContext;
+import com.android.contacts.tests.mocks.MockAccountTypeManager;
+import com.android.contacts.tests.mocks.MockContentProvider;
+import com.android.contacts.tests.mocks.MockContentProvider.Query;
+import com.android.contacts.tests.mocks.MockSharedPreferences;
+
+import android.accounts.Account;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.Loader;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.ContactCounts;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.Groups;
+import android.provider.ContactsContract.ProviderStatus;
+import android.provider.Settings;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.Smoke;
+import android.widget.TextView;
+
+/**
+ * Tests for {@link ContactBrowserActivity}.
+ *
+ * Running all tests:
+ *
+ * runtest contacts
+ * or
+ * adb shell am instrument \
+ * -w com.android.contacts.tests/android.test.InstrumentationTestRunner
+ */
+@Smoke
+public class ContactBrowserActivityTest
+ extends ActivityInstrumentationTestCase2<ContactBrowserActivity>
+{
+ static {
+ // AsyncTask class needs to be initialized on the main thread.
+ AsyncTask.init();
+ }
+
+ private static final String TEST_ACCOUNT = "testAccount";
+ private static final String TEST_ACCOUNT_TYPE = "testAccountType";
+
+ private ContactsMockContext mContext;
+ private MockContentProvider mContactsProvider;
+ private MockContentProvider mSettingsProvider;
+
+ public ContactBrowserActivityTest() {
+ super(ContactBrowserActivity.class);
+ }
+
+ @Override
+ public void setUp() {
+ mContext = new ContactsMockContext(getInstrumentation().getTargetContext());
+ mContactsProvider = mContext.getContactsProvider();
+ mSettingsProvider = mContext.getSettingsProvider();
+ InjectedServices services = new InjectedServices();
+ services.setContentResolver(mContext.getContentResolver());
+ services.setSharedPreferences(new MockSharedPreferences());
+
+ AccountType accountType = new BaseAccountType();
+ accountType.accountType = TEST_ACCOUNT_TYPE;
+
+ Account account = new Account(TEST_ACCOUNT, TEST_ACCOUNT_TYPE);
+
+ services.setSystemService(AccountTypeManager.ACCOUNT_TYPE_SERVICE,
+ new MockAccountTypeManager(
+ new AccountType[] { accountType }, new Account[] { account }));
+ ContactsApplication.injectServices(services);
+ }
+
+ public void testSingleAccountNoGroups() {
+ expectSettingsQueriesAndReturnDefault();
+ expectProviderStatusQueryAndReturnNormal();
+ expectGroupsQueryAndReturnEmpty();
+ expectContactListQuery(100);
+ expectContactLookupQuery("lu1", 1, "lu1", 1);
+ expectContactEntityQuery("lu1", 1);
+
+ setActivityIntent(new Intent(Intent.ACTION_DEFAULT));
+
+ ContactBrowserActivity activity = getActivity();
+
+ getInstrumentation().waitForIdleSync();
+
+ ContactBrowseListFragment listFragment = activity.getListFragment();
+ ContactDetailFragment detailFragment = activity.getDetailFragment();
+
+ Loader<?> filterLoader =
+ activity.getLoaderManager().getLoader(R.id.contact_list_filter_loader);
+ Loader<?> listLoader =
+ listFragment.getLoaderManager().getLoader(0);
+
+ // TODO: wait for detail loader
+ // TODO: wait for lookup key loading
+ mContext.waitForLoaders(filterLoader, listLoader);
+
+ getInstrumentation().waitForIdleSync();
+
+ mContext.verify();
+
+ TextView nameText = (TextView) detailFragment.getView().findViewById(R.id.name);
+ assertEquals("Contact 1", nameText.getText());
+ }
+
+ private void expectSettingsQueriesAndReturnDefault() {
+ mSettingsProvider
+ .expectQuery(Settings.System.CONTENT_URI)
+ .withProjection(Settings.System.VALUE)
+ .withSelection(Settings.System.NAME + "=?",
+ ContactsContract.Preferences.DISPLAY_ORDER)
+ .returnRow(ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY)
+ .anyNumberOfTimes();
+ mSettingsProvider
+ .expectQuery(Settings.System.CONTENT_URI)
+ .withProjection(Settings.System.VALUE)
+ .withSelection(Settings.System.NAME + "=?",
+ ContactsContract.Preferences.SORT_ORDER)
+ .returnRow(ContactsContract.Preferences.SORT_ORDER_PRIMARY)
+ .anyNumberOfTimes();
+ }
+
+ private void expectProviderStatusQueryAndReturnNormal() {
+ mContactsProvider
+ .expectQuery(ProviderStatus.CONTENT_URI)
+ .withProjection(ProviderStatus.STATUS, ProviderStatus.DATA1)
+ .returnRow(ProviderStatus.STATUS_NORMAL, null)
+ .anyNumberOfTimes();
+ }
+
+ private void expectGroupsQueryAndReturnEmpty() {
+ mContactsProvider
+ .expectQuery(Groups.CONTENT_URI)
+ .withAnyProjection()
+ .withAnySelection()
+ .returnEmptyCursor()
+ .anyNumberOfTimes();
+ }
+
+ private void expectContactListQuery(int count) {
+ Uri uri = Contacts.CONTENT_URI.buildUpon()
+ .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true")
+ .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(Directory.DEFAULT))
+ .build();
+
+ Query query = mContactsProvider
+ .expectQuery(uri)
+ .withAnyProjection()
+ .withSortOrder(Contacts.SORT_KEY_PRIMARY);
+ for (int i = 1; i <= count; i++) {
+ ContentValues values = new ContentValues();
+ values.put(Contacts._ID, i);
+ values.put(Contacts.DISPLAY_NAME, "Contact " + i);
+ values.put(Contacts.SORT_KEY_PRIMARY, "contact " + i);
+ values.put(Contacts.LOOKUP_KEY, "lu" + i);
+ query.returnRow(values);
+ }
+ }
+
+ private void expectContactLookupQuery(
+ String lookupKey, long id, String returnLookupKey, long returnId) {
+ Uri uri = Contacts.getLookupUri(id, lookupKey);
+ mContactsProvider.expectTypeQuery(uri, Contacts.CONTENT_ITEM_TYPE);
+ mContactsProvider
+ .expectQuery(uri)
+ .withProjection(Contacts._ID, Contacts.LOOKUP_KEY)
+ .returnRow(returnId, returnLookupKey);
+ }
+
+ private void expectContactEntityQuery(String lookupKey, int contactId) {
+ Uri uri = Uri.withAppendedPath(
+ Contacts.getLookupUri(contactId, lookupKey), Contacts.Entity.CONTENT_DIRECTORY);
+ ContentValues row1 = new ContentValues();
+ row1.put(Contacts.Entity.DATA_ID, 1);
+ row1.put(Contacts.Entity.LOOKUP_KEY, lookupKey);
+ row1.put(Contacts.Entity.CONTACT_ID, contactId);
+ row1.put(Contacts.Entity.DISPLAY_NAME, "Contact " + contactId);
+ row1.put(Contacts.Entity.ACCOUNT_NAME, TEST_ACCOUNT);
+ row1.put(Contacts.Entity.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE);
+ mContactsProvider
+ .expectQuery(uri)
+ .withAnyProjection()
+ .withAnySortOrder()
+ .returnRow(row1)
+ .anyNumberOfTimes();
+ }
+}
diff --git a/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java b/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
new file mode 100644
index 0000000..4cee32c
--- /dev/null
+++ b/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
@@ -0,0 +1,131 @@
+/*
+ * 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.detail;
+
+import com.android.contacts.detail.ContactDetailFragment.ViewEntry;
+
+import android.content.ContentValues;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Tests for {@link ContactDetailFragment}.
+ */
+@SmallTest
+public class ContactDetailFragmentTests extends AndroidTestCase {
+ private static final String TEST_ADDRESS = "user@example.org";
+ private static final String TEST_PROTOCOL = "prot%col";
+
+ public void testImIntent() throws Exception {
+ // Test GTalk XMPP URI. No chat capabilities provided
+ final ContentValues values = new ContentValues();
+ values.put(Im.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+ values.put(Im.TYPE, Im.TYPE_HOME);
+ values.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
+ values.put(Im.DATA, TEST_ADDRESS);
+
+ ViewEntry entry = new ContactDetailFragment.ViewEntry();
+ ContactDetailFragment.buildImActions(entry, values);
+ assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
+ assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
+
+ assertNull(entry.secondaryIntent);
+ }
+
+ public void testImIntentWithAudio() throws Exception {
+ // Test GTalk XMPP URI. Audio chat capabilities provided
+ final ContentValues values = new ContentValues();
+ values.put(Im.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+ values.put(Im.TYPE, Im.TYPE_HOME);
+ values.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
+ values.put(Im.DATA, TEST_ADDRESS);
+ values.put(Im.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO);
+
+ ViewEntry entry = new ContactDetailFragment.ViewEntry();
+ ContactDetailFragment.buildImActions(entry, values);
+ assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
+ assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
+
+ assertEquals(Intent.ACTION_SENDTO, entry.secondaryIntent.getAction());
+ assertEquals("xmpp:" + TEST_ADDRESS + "?call", entry.secondaryIntent.getData().toString());
+ }
+
+ public void testImIntentWithVideo() throws Exception {
+ // Test GTalk XMPP URI. Video chat capabilities provided
+ final ContentValues values = new ContentValues();
+ values.put(Im.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+ values.put(Im.TYPE, Im.TYPE_HOME);
+ values.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
+ values.put(Im.DATA, TEST_ADDRESS);
+ values.put(Im.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO |
+ Im.CAPABILITY_HAS_VOICE);
+
+ ViewEntry entry = new ContactDetailFragment.ViewEntry();
+ ContactDetailFragment.buildImActions(entry, values);
+ assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
+ assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
+
+ assertEquals(Intent.ACTION_SENDTO, entry.secondaryIntent.getAction());
+ assertEquals("xmpp:" + TEST_ADDRESS + "?call", entry.secondaryIntent.getData().toString());
+ }
+
+ public void testImIntentCustom() throws Exception {
+ // Custom IM types have encoded authority. We send the imto Intent here, because
+ // legacy third party apps might not accept xmpp yet
+ final ContentValues values = new ContentValues();
+ values.put(Im.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+ values.put(Im.TYPE, Im.TYPE_HOME);
+ values.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM);
+ values.put(Im.CUSTOM_PROTOCOL, TEST_PROTOCOL);
+ values.put(Im.DATA, TEST_ADDRESS);
+
+ ViewEntry entry = new ContactDetailFragment.ViewEntry();
+ ContactDetailFragment.buildImActions(entry, values);
+ assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
+
+ final Uri data = entry.intent.getData();
+ assertEquals("imto", data.getScheme());
+ assertEquals(TEST_PROTOCOL, data.getAuthority());
+ assertEquals(TEST_ADDRESS, data.getPathSegments().get(0));
+
+ assertNull(entry.secondaryIntent);
+ }
+
+ public void testImEmailIntent() throws Exception {
+ // Email addresses are treated as Google Talk entries
+ // This test only tests the VIDEO+CAMERA case. The other cases have been addressed by the
+ // Im tests
+ final ContentValues values = new ContentValues();
+ values.put(Email.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ values.put(Email.TYPE, Email.TYPE_HOME);
+ values.put(Email.DATA, TEST_ADDRESS);
+ values.put(Email.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO |
+ Im.CAPABILITY_HAS_VOICE);
+
+ ViewEntry entry = new ContactDetailFragment.ViewEntry();
+ ContactDetailFragment.buildImActions(entry, values);
+ assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
+ assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
+
+ assertEquals(Intent.ACTION_SENDTO, entry.secondaryIntent.getAction());
+ assertEquals("xmpp:" + TEST_ADDRESS + "?call", entry.secondaryIntent.getData().toString());
+ }
+}
diff --git a/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java b/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java
new file mode 100644
index 0000000..4514e1e
--- /dev/null
+++ b/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 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.interactions;
+
+import com.android.contacts.ContactsApplication;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.BaseAccountType;
+import com.android.contacts.test.FragmentTestActivity;
+import com.android.contacts.test.InjectedServices;
+import com.android.contacts.tests.mocks.ContactsMockContext;
+import com.android.contacts.tests.mocks.MockAccountTypeManager;
+import com.android.contacts.tests.mocks.MockContentProvider;
+import com.android.contacts.tests.mocks.MockContentProvider.Query;
+
+import android.content.ContentUris;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Contacts.Entity;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.Smoke;
+
+/**
+ * Tests for {@link ContactDeletionInteraction}.
+ *
+ * Running all tests:
+ *
+ * runtest contacts
+ * or
+ * adb shell am instrument \
+ * -w com.android.contacts.tests/android.test.InstrumentationTestRunner
+ */
+@Smoke
+public class ContactDeletionInteractionTest
+ extends ActivityInstrumentationTestCase2<FragmentTestActivity> {
+
+ static {
+ // AsyncTask class needs to be initialized on the main thread.
+ AsyncTask.init();
+ }
+
+ private static final Uri CONTACT_URI = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
+ private static final Uri ENTITY_URI = Uri.withAppendedPath(
+ CONTACT_URI, Entity.CONTENT_DIRECTORY);
+
+ public static final String WRITABLE_ACCOUNT_TYPE = "writable";
+ public static final String READONLY_ACCOUNT_TYPE = "readonly";
+
+ private ContactsMockContext mContext;
+ private MockContentProvider mContactsProvider;
+ private ContactDeletionInteraction mFragment;
+
+ public ContactDeletionInteractionTest() {
+ super(FragmentTestActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContext = new ContactsMockContext(getInstrumentation().getTargetContext());
+ InjectedServices services = new InjectedServices();
+ services.setContentResolver(mContext.getContentResolver());
+
+ AccountType readOnlyAccountType = new BaseAccountType();
+ readOnlyAccountType.accountType = READONLY_ACCOUNT_TYPE;
+ readOnlyAccountType.readOnly = true;
+
+ AccountType writableAccountType = new BaseAccountType();
+ writableAccountType.accountType = WRITABLE_ACCOUNT_TYPE;
+
+ services.setSystemService(AccountTypeManager.ACCOUNT_TYPE_SERVICE,
+ new MockAccountTypeManager(
+ new AccountType[] { writableAccountType, readOnlyAccountType }, null));
+ ContactsApplication.injectServices(services);
+ mContactsProvider = mContext.getContactsProvider();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ ContactsApplication.injectServices(null);
+ super.tearDown();
+ }
+
+ public void testSingleWritableRawContact() {
+ expectQuery().returnRow(1, WRITABLE_ACCOUNT_TYPE, 13, "foo");
+ assertWithMessageId(R.string.deleteConfirmation);
+ }
+
+ public void testReadOnlyRawContacts() {
+ expectQuery().returnRow(1, READONLY_ACCOUNT_TYPE, 13, "foo");
+ assertWithMessageId(R.string.readOnlyContactWarning);
+ }
+
+ public void testMixOfWritableAndReadOnlyRawContacts() {
+ expectQuery()
+ .returnRow(1, WRITABLE_ACCOUNT_TYPE, 13, "foo")
+ .returnRow(2, READONLY_ACCOUNT_TYPE, 13, "foo");
+ assertWithMessageId(R.string.readOnlyContactDeleteConfirmation);
+ }
+
+ public void testMultipleWritableRawContacts() {
+ expectQuery()
+ .returnRow(1, WRITABLE_ACCOUNT_TYPE, 13, "foo")
+ .returnRow(2, WRITABLE_ACCOUNT_TYPE, 13, "foo");
+ assertWithMessageId(R.string.multipleContactDeleteConfirmation);
+ }
+
+ private Query expectQuery() {
+ return mContactsProvider.expectQuery(ENTITY_URI).withProjection(
+ Entity.RAW_CONTACT_ID, Entity.ACCOUNT_TYPE, Entity.CONTACT_ID, Entity.LOOKUP_KEY);
+ }
+
+ private void assertWithMessageId(int messageId) {
+ final FragmentTestActivity activity = getActivity();
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mFragment = ContactDeletionInteraction.start(activity, CONTACT_URI);
+ }
+ });
+
+ getInstrumentation().waitForIdleSync();
+
+ mContext.waitForLoaders(mFragment.getLoaderManager(), R.id.dialog_delete_contact_loader_id);
+
+ getInstrumentation().waitForIdleSync();
+
+ mContext.verify();
+
+ assertEquals(messageId, mFragment.mMessageId);
+ }
+}
diff --git a/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java b/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
new file mode 100644
index 0000000..43e844e
--- /dev/null
+++ b/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2010 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.interactions;
+
+import com.android.contacts.R;
+import com.android.contacts.interactions.PhoneNumberInteraction.PhoneItem;
+import com.android.contacts.tests.mocks.ContactsMockContext;
+import com.android.contacts.tests.mocks.MockContentProvider;
+import com.android.contacts.tests.mocks.MockContentProvider.Query;
+
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.Smoke;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for {@link PhoneNumberInteraction}.
+ *
+ * Running all tests:
+ *
+ * runtest contacts
+ * or
+ * adb shell am instrument \
+ * -w com.android.contacts.tests/android.test.InstrumentationTestRunner
+ */
+@Smoke
+public class PhoneNumberInteractionTest extends InstrumentationTestCase {
+
+ static {
+ // AsyncTask class needs to be initialized on the main thread.
+ AsyncTask.init();
+ }
+
+ private final static class TestPhoneNumberInteraction extends PhoneNumberInteraction {
+ Intent startedIntent;
+ int dialogId;
+ Bundle dialogArgs;
+
+ public TestPhoneNumberInteraction(
+ Context context, boolean sendTextMessage, OnDismissListener dismissListener) {
+ super(context, sendTextMessage, dismissListener);
+ }
+
+ @Override
+ void startActivity(Intent intent) {
+ this.startedIntent = intent;
+ }
+
+ @Override
+ void showDialog(int dialogId, Bundle bundle) {
+ this.dialogId = dialogId;
+ this.dialogArgs = bundle;
+ }
+ }
+
+ private ContactsMockContext mContext;
+ private MockContentProvider mContactsProvider;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mContext = new ContactsMockContext(getInstrumentation().getTargetContext());
+ mContactsProvider = mContext.getContactsProvider();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mContactsProvider.verify();
+ super.tearDown();
+ }
+
+ public void testSendSmsWhenOnlyOneNumberAvailable() {
+ Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
+ expectQuery(contactUri)
+ .returnRow(1, "123", 0, null, Phone.TYPE_HOME, null);
+
+ TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
+ mContext, true, null);
+
+ interaction.startInteraction(contactUri);
+ interaction.getLoader().waitForLoader();
+
+ assertEquals(Intent.ACTION_SENDTO, interaction.startedIntent.getAction());
+ assertEquals("sms:123", interaction.startedIntent.getDataString());
+ }
+
+ public void testSendSmsWhenThereIsPrimaryNumber() {
+ Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
+ expectQuery(contactUri)
+ .returnRow(1, "123", 0, null, Phone.TYPE_HOME, null)
+ .returnRow(2, "456", 1, null, Phone.TYPE_HOME, null);
+
+ TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
+ mContext, true, null);
+
+ interaction.startInteraction(contactUri);
+ interaction.getLoader().waitForLoader();
+
+ assertEquals(Intent.ACTION_SENDTO, interaction.startedIntent.getAction());
+ assertEquals("sms:456", interaction.startedIntent.getDataString());
+ }
+
+ public void testCallNumberWhenThereAreDuplicates() {
+ Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
+ expectQuery(contactUri)
+ .returnRow(1, "123", 0, null, Phone.TYPE_HOME, null)
+ .returnRow(2, "123", 0, null, Phone.TYPE_WORK, null);
+
+ TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
+ mContext, false, null);
+
+ interaction.startInteraction(contactUri);
+ interaction.getLoader().waitForLoader();
+
+ assertEquals(Intent.ACTION_CALL_PRIVILEGED, interaction.startedIntent.getAction());
+ assertEquals("tel:123", interaction.startedIntent.getDataString());
+ }
+
+ public void testShowDisambigDialogForCalling() {
+ Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
+ expectQuery(contactUri)
+ .returnRow(1, "123", 0, "account", Phone.TYPE_HOME, "label")
+ .returnRow(2, "456", 0, null, Phone.TYPE_WORK, null);
+
+ TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
+ mContext, false, null);
+
+ interaction.startInteraction(contactUri);
+ interaction.getLoader().waitForLoader();
+
+ assertEquals(R.id.dialog_phone_number_call_disambiguation, interaction.dialogId);
+
+ ArrayList<PhoneItem> items = interaction.dialogArgs.getParcelableArrayList(
+ PhoneNumberInteraction.EXTRA_KEY_ITEMS);
+ assertEquals(2, items.size());
+
+ PhoneItem item = items.get(0);
+ assertEquals(1, item.id);
+ assertEquals("123", item.phoneNumber);
+ assertEquals("account", item.accountType);
+ assertEquals(Phone.TYPE_HOME, item.type);
+ assertEquals("label", item.label);
+ }
+
+ private Query expectQuery(Uri contactUri) {
+ Uri dataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY);
+ return mContactsProvider
+ .expectQuery(dataUri)
+ .withProjection(
+ Phone._ID,
+ Phone.NUMBER,
+ Phone.IS_SUPER_PRIMARY,
+ RawContacts.ACCOUNT_TYPE,
+ Phone.TYPE,
+ Phone.LABEL)
+ .withSelection("mimetype='vnd.android.cursor.item/phone_v2' AND data1 NOT NULL");
+ }
+}
diff --git a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
new file mode 100644
index 0000000..50a1f1f
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
@@ -0,0 +1,604 @@
+/*
+ * Copyright (C) 2010 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.allintents;
+
+import com.android.contacts.tests.R;
+import com.google.android.collect.Lists;
+
+import android.accounts.Account;
+import android.app.ListActivity;
+import android.app.SearchManager;
+import android.content.ComponentName;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Contacts.ContactMethods;
+import android.provider.Contacts.People;
+import android.provider.Contacts.Phones;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Intents;
+import android.provider.ContactsContract.Intents.Insert;
+import android.provider.ContactsContract.Intents.UI;
+import android.provider.ContactsContract.RawContacts;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.Toast;
+
+/**
+ * An activity that provides access to various modes of the contacts application.
+ * Useful for manual and scripted tests.
+ */
+@SuppressWarnings("deprecation")
+public class AllIntentsActivity extends ListActivity
+ implements SelectAccountDialogFragment.Listener {
+
+ private static final String ANDROID_CONTACTS_PACKAGE = "com.android.contacts";
+
+ private static final String CONTACT_LIST_ACTIVITY_CLASS_NAME =
+ "com.android.contacts.activities.ContactBrowserActivity";
+
+ public enum ContactsIntent {
+ LIST_DEFAULT,
+ LIST_ALL_CONTACTS_ACTION,
+ LIST_CONTACTS_WITH_PHONES_ACTION,
+ LIST_STARRED_ACTION,
+ LIST_FREQUENT_ACTION,
+ LIST_STREQUENT_ACTION,
+ ACTION_PICK_CONTACT,
+ ACTION_PICK_CONTACT_LEGACY,
+ ACTION_PICK_PHONE,
+ ACTION_PICK_PHONE_LEGACY,
+ ACTION_PICK_POSTAL,
+ ACTION_PICK_POSTAL_LEGACY,
+ ACTION_CREATE_SHORTCUT_CONTACT,
+ ACTION_CREATE_SHORTCUT_DIAL,
+ ACTION_CREATE_SHORTCUT_MESSAGE,
+ ACTION_GET_CONTENT_CONTACT,
+ ACTION_GET_CONTENT_CONTACT_LEGACY,
+ ACTION_GET_CONTENT_PHONE,
+ ACTION_GET_CONTENT_PHONE_LEGACY,
+ ACTION_GET_CONTENT_POSTAL,
+ ACTION_GET_CONTENT_POSTAL_LEGACY,
+ ACTION_INSERT_OR_EDIT,
+ ACTION_SEARCH_CALL,
+ ACTION_SEARCH_CONTACT,
+ ACTION_SEARCH_EMAIL,
+ ACTION_SEARCH_PHONE,
+ SEARCH_SUGGESTION_CLICKED_CALL_BUTTON,
+ SEARCH_SUGGESTION_CLICKED_CONTACT,
+ SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED,
+ SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED,
+ JOIN_CONTACT,
+ EDIT_CONTACT,
+ EDIT_CONTACT_LOOKUP,
+ EDIT_CONTACT_LOOKUP_ID,
+ EDIT_RAW_CONTACT,
+ EDIT_LEGACY,
+ EDIT_NEW_CONTACT,
+ EDIT_NEW_CONTACT_WITH_DATA,
+ EDIT_NEW_CONTACT_FOR_ACCOUNT,
+ EDIT_NEW_CONTACT_FOR_ACCOUNT_WITH_DATA,
+ EDIT_NEW_RAW_CONTACT,
+ EDIT_NEW_LEGACY,
+ VIEW_CONTACT,
+ VIEW_CONTACT_LOOKUP,
+ VIEW_CONTACT_LOOKUP_ID,
+ VIEW_RAW_CONTACT,
+ VIEW_LEGACY,
+ DIAL,
+ DIAL_phone,
+ DIAL_person,
+ DIAL_voicemail,
+ CALL_BUTTON,
+ DIAL_tel,
+ VIEW_tel,
+ VIEW_calllog;
+
+ public static ContactsIntent get(int ordinal) {
+ return values()[ordinal];
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setListAdapter(new ArrayAdapter<String>(this, R.layout.intent_list_item,
+ getResources().getStringArray(R.array.allIntents)));
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+
+ switch (ContactsIntent.get(position)) {
+ case LIST_DEFAULT: {
+ startContactListActivity(
+ new Intent(Intent.ACTION_VIEW, Contacts.CONTENT_URI));
+ break;
+ }
+ case LIST_ALL_CONTACTS_ACTION: {
+ startContactListActivity(
+ new Intent(UI.LIST_ALL_CONTACTS_ACTION, Contacts.CONTENT_URI));
+ break;
+ }
+ case LIST_CONTACTS_WITH_PHONES_ACTION: {
+ startContactListActivity(
+ new Intent(UI.LIST_CONTACTS_WITH_PHONES_ACTION, Contacts.CONTENT_URI));
+ break;
+ }
+ case LIST_STARRED_ACTION: {
+ startContactListActivity(
+ new Intent(UI.LIST_STARRED_ACTION, Contacts.CONTENT_URI));
+ break;
+ }
+ case LIST_FREQUENT_ACTION: {
+ startContactListActivity(
+ new Intent(UI.LIST_FREQUENT_ACTION, Contacts.CONTENT_URI));
+ break;
+ }
+ case LIST_STREQUENT_ACTION: {
+ startContactListActivity(
+ new Intent(UI.LIST_STREQUENT_ACTION, Contacts.CONTENT_URI));
+ break;
+ }
+ case ACTION_PICK_CONTACT: {
+ startContactSelectionActivityForResult(
+ new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI));
+ break;
+ }
+ case ACTION_PICK_CONTACT_LEGACY: {
+ startContactSelectionActivityForResult(
+ new Intent(Intent.ACTION_PICK, People.CONTENT_URI));
+ break;
+ }
+ case ACTION_PICK_PHONE: {
+ startContactSelectionActivityForResult(
+ new Intent(Intent.ACTION_PICK, Phone.CONTENT_URI));
+ break;
+ }
+ case ACTION_PICK_PHONE_LEGACY: {
+ startContactSelectionActivityForResult(
+ new Intent(Intent.ACTION_PICK, Phones.CONTENT_URI));
+ break;
+ }
+ case ACTION_PICK_POSTAL: {
+ startContactSelectionActivityForResult(
+ new Intent(Intent.ACTION_PICK, StructuredPostal.CONTENT_URI));
+ break;
+ }
+ case ACTION_PICK_POSTAL_LEGACY: {
+ Intent intent = new Intent(Intent.ACTION_PICK);
+ intent.setType(ContactMethods.CONTENT_POSTAL_TYPE);
+ startContactSelectionActivityForResult(intent);
+ break;
+ }
+ case ACTION_CREATE_SHORTCUT_CONTACT: {
+ Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+ startContactSelectionActivityForResult(intent);
+ break;
+ }
+ case ACTION_CREATE_SHORTCUT_DIAL: {
+ Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+ intent.setComponent(
+ new ComponentName(ANDROID_CONTACTS_PACKAGE, "alias.DialShortcut"));
+ startActivityForResult(intent, 0);
+ break;
+ }
+ case ACTION_CREATE_SHORTCUT_MESSAGE: {
+ Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
+ intent.setComponent(
+ new ComponentName(ANDROID_CONTACTS_PACKAGE, "alias.MessageShortcut"));
+ startActivityForResult(intent, 0);
+ break;
+ }
+ case ACTION_GET_CONTENT_CONTACT: {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType(Contacts.CONTENT_ITEM_TYPE);
+ startContactSelectionActivityForResult(intent);
+ break;
+ }
+ case ACTION_GET_CONTENT_CONTACT_LEGACY: {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType(People.CONTENT_ITEM_TYPE);
+ startContactSelectionActivityForResult(intent);
+ break;
+ }
+ case ACTION_GET_CONTENT_PHONE: {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType(Phone.CONTENT_ITEM_TYPE);
+ startContactSelectionActivityForResult(intent);
+ break;
+ }
+ case ACTION_GET_CONTENT_PHONE_LEGACY: {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType(Phones.CONTENT_ITEM_TYPE);
+ startContactSelectionActivityForResult(intent);
+ break;
+ }
+ case ACTION_GET_CONTENT_POSTAL: {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType(StructuredPostal.CONTENT_ITEM_TYPE);
+ startContactSelectionActivityForResult(intent);
+ break;
+ }
+ case ACTION_GET_CONTENT_POSTAL_LEGACY: {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType(ContactMethods.CONTENT_POSTAL_ITEM_TYPE);
+ startContactSelectionActivityForResult(intent);
+ break;
+ }
+ case ACTION_INSERT_OR_EDIT: {
+ Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ intent.setType(Contacts.CONTENT_ITEM_TYPE);
+ putDataExtra(intent);
+ startActivity(intent);
+ break;
+ }
+ case ACTION_SEARCH_CALL: {
+ Intent intent = new Intent(Intent.ACTION_SEARCH);
+ intent.putExtra(SearchManager.ACTION_MSG, "call");
+ intent.putExtra(SearchManager.QUERY, "800-4664-411");
+ startSearchResultActivity(intent);
+ break;
+ }
+ case ACTION_SEARCH_CONTACT: {
+ Intent intent = new Intent(Intent.ACTION_SEARCH);
+ intent.putExtra(SearchManager.QUERY, "a");
+ intent.setType(Contacts.CONTENT_TYPE);
+ startSearchResultActivity(intent);
+ break;
+ }
+ case ACTION_SEARCH_EMAIL: {
+ Toast.makeText(this, "Unsupported", Toast.LENGTH_SHORT).show();
+ break;
+ }
+ case ACTION_SEARCH_PHONE: {
+ Toast.makeText(this, "Unsupported", Toast.LENGTH_SHORT).show();
+ break;
+ }
+ case SEARCH_SUGGESTION_CLICKED_CALL_BUTTON: {
+ long contactId = findArbitraryContactWithPhoneNumber();
+ if (contactId != -1) {
+ Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ Intent intent = new Intent(Intents.SEARCH_SUGGESTION_CLICKED);
+ intent.setData(contactUri);
+ intent.putExtra(SearchManager.ACTION_MSG, "call");
+ startContactListActivity(intent);
+ }
+ break;
+ }
+ case SEARCH_SUGGESTION_CLICKED_CONTACT: {
+ long contactId = findArbitraryContactWithPhoneNumber();
+ if (contactId != -1) {
+ Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ Intent intent = new Intent(Intents.SEARCH_SUGGESTION_CLICKED);
+ intent.setData(contactUri);
+ startContactListActivity(intent);
+ }
+ break;
+ }
+ case SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED: {
+ Intent intent = new Intent(Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED);
+ intent.setData(Uri.parse("tel:800-4664411"));
+ startContactListActivity(intent);
+ break;
+ }
+ case SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED: {
+ Intent intent = new Intent(Intents.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED);
+ intent.setData(Uri.parse("tel:800-4664411"));
+ startContactListActivity(intent);
+ break;
+ }
+ case JOIN_CONTACT: {
+ // TODO
+ break;
+ }
+ case EDIT_CONTACT: {
+ final long contactId = findArbitraryContactWithPhoneNumber();
+ final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ final Intent intent = new Intent(Intent.ACTION_EDIT, uri);
+ startActivity(intent);
+ break;
+ }
+ case EDIT_CONTACT_LOOKUP: {
+ final long contactId = findArbitraryContactWithPhoneNumber();
+ final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ final Uri lookupUri = Contacts.getLookupUri(getContentResolver(), uri);
+ final String lookupKey = lookupUri.getPathSegments().get(2);
+ final Uri lookupWithoutIdUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
+ lookupKey);
+ final Intent intent = new Intent(Intent.ACTION_EDIT, lookupWithoutIdUri);
+ startActivity(intent);
+ break;
+ }
+ case EDIT_CONTACT_LOOKUP_ID: {
+ final long contactId = findArbitraryContactWithPhoneNumber();
+ final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ final Uri lookupUri = Contacts.getLookupUri(getContentResolver(), uri);
+ final Intent intent = new Intent(Intent.ACTION_EDIT, lookupUri);
+ startActivity(intent);
+ break;
+ }
+ case EDIT_RAW_CONTACT: {
+ final long contactId = findArbitraryContactWithPhoneNumber();
+ final long rawContactId = findArbitraryRawContactOfContact(contactId);
+ final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
+ final Intent intent = new Intent(Intent.ACTION_EDIT, uri);
+ startActivity(intent);
+ break;
+ }
+ case EDIT_LEGACY: {
+ final Uri legacyContentUri = Uri.parse("content://contacts/people");
+ final long contactId = findArbitraryContactWithPhoneNumber();
+ final long rawContactId = findArbitraryRawContactOfContact(contactId);
+ final Uri uri = ContentUris.withAppendedId(legacyContentUri, rawContactId);
+ final Intent intent = new Intent(Intent.ACTION_EDIT, uri);
+ startActivity(intent);
+ break;
+ }
+ case EDIT_NEW_CONTACT: {
+ startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
+ break;
+ }
+ case EDIT_NEW_CONTACT_WITH_DATA: {
+ Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+
+ putDataExtra(intent);
+
+ startActivity(intent);
+ break;
+ }
+ case EDIT_NEW_CONTACT_FOR_ACCOUNT:
+ case EDIT_NEW_CONTACT_FOR_ACCOUNT_WITH_DATA: {
+ final SelectAccountDialogFragment dialog = new SelectAccountDialogFragment();
+ dialog.setArguments(SelectAccountDialogFragment.createBundle(position));
+ dialog.show(getFragmentManager(), SelectAccountDialogFragment.TAG);
+ break;
+ }
+ case EDIT_NEW_RAW_CONTACT: {
+ startActivity(new Intent(Intent.ACTION_INSERT, RawContacts.CONTENT_URI));
+ break;
+ }
+ case EDIT_NEW_LEGACY: {
+ final Uri legacyContentUri = Uri.parse("content://contacts/people");
+ startActivity(new Intent(Intent.ACTION_INSERT, legacyContentUri));
+ break;
+ }
+ case VIEW_CONTACT: {
+ final long contactId = findArbitraryContactWithPhoneNumber();
+ final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ startActivity(intent);
+ break;
+ }
+ case VIEW_CONTACT_LOOKUP: {
+ final long contactId = findArbitraryContactWithPhoneNumber();
+ final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ final Uri lookupUri = Contacts.getLookupUri(getContentResolver(), uri);
+ final String lookupKey = lookupUri.getPathSegments().get(2);
+ final Uri lookupWithoutIdUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
+ lookupKey);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, lookupWithoutIdUri);
+ startActivity(intent);
+ break;
+ }
+ case VIEW_CONTACT_LOOKUP_ID: {
+ final long contactId = findArbitraryContactWithPhoneNumber();
+ final Uri uri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ final Uri lookupUri = Contacts.getLookupUri(getContentResolver(), uri);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, lookupUri);
+ startActivity(intent);
+ break;
+ }
+ case VIEW_RAW_CONTACT: {
+ final long contactId = findArbitraryContactWithPhoneNumber();
+ final long rawContactId = findArbitraryRawContactOfContact(contactId);
+ final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ startActivity(intent);
+ break;
+ }
+ case VIEW_LEGACY: {
+ final Uri legacyContentUri = Uri.parse("content://contacts/people");
+ final long contactId = findArbitraryContactWithPhoneNumber();
+ final long rawContactId = findArbitraryRawContactOfContact(contactId);
+ final Uri uri = ContentUris.withAppendedId(legacyContentUri, rawContactId);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ startActivity(intent);
+ break;
+ }
+ case DIAL: {
+ startActivity(new Intent(Intent.ACTION_DIAL));
+ break;
+ }
+ case DIAL_phone: {
+ // This is the legacy URI (there is no >2.0 way to call a phone data item)
+ final long dataId = findArbitraryPhoneDataId();
+ if (dataId != -1) {
+ final Uri legacyContentUri = Uri.parse("content://contacts/phones");
+ final Uri uri = ContentUris.withAppendedId(legacyContentUri, dataId);
+ startActivity(new Intent(Intent.ACTION_DIAL, uri));
+ }
+ break;
+ }
+ case DIAL_person: {
+ // This is the legacy URI (there is no >2.0 way to call a person)
+ final long contactId = findArbitraryContactWithPhoneNumber();
+ if (contactId != -1) {
+ final Uri legacyContentUri = Uri.parse("content://contacts/people");
+ final long rawContactId = findArbitraryRawContactOfContact(contactId);
+ final Uri uri = ContentUris.withAppendedId(legacyContentUri, rawContactId);
+ startActivity(new Intent(Intent.ACTION_DIAL, uri));
+ }
+ break;
+ }
+ case DIAL_voicemail: {
+ startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse("voicemail:")));
+ break;
+ }
+ case CALL_BUTTON: {
+ startActivity(new Intent(Intent.ACTION_CALL_BUTTON));
+ break;
+ }
+ case DIAL_tel: {
+ startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse("tel:555-123-4567")));
+ break;
+ }
+ case VIEW_tel: {
+ startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("tel:555-123-4567")));
+ break;
+ }
+ case VIEW_calllog: {
+ final Intent intent = new Intent(Intent.ACTION_VIEW, null);
+ intent.setType("vnd.android.cursor.dir/calls");
+ startActivity(intent);
+ break;
+ }
+ default: {
+ Toast.makeText(this, "Sorry, we forgot to write this...", Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
+ private Intent buildFilterIntent(int actionCode, boolean legacy) {
+ Intent intent = new Intent(UI.FILTER_CONTACTS_ACTION);
+ intent.putExtra(UI.FILTER_TEXT_EXTRA_KEY, "A");
+// ContactsRequest request = new ContactsRequest();
+// request.setActionCode(actionCode);
+// intent.putExtra("originalRequest", request);
+ return intent;
+ }
+
+ private void startContactListActivity(Intent intent) {
+ intent.setComponent(
+ new ComponentName(ANDROID_CONTACTS_PACKAGE, CONTACT_LIST_ACTIVITY_CLASS_NAME));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+
+ private void startContactSelectionActivityForResult(Intent intent) {
+ startActivityForResult(intent, 12);
+ }
+
+ private void startSearchResultActivity(Intent intent) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Intent intent = new Intent(this, ResultActivity.class);
+ intent.putExtra("resultCode", resultCode);
+ intent.putExtra("data", data);
+ startActivity(intent);
+ }
+
+ private long findArbitraryContactWithPhoneNumber() {
+ final Cursor cursor = getContentResolver().query(Contacts.CONTENT_URI,
+ new String[] { Contacts._ID },
+ Contacts.HAS_PHONE_NUMBER + "!=0 AND " + Contacts.STARRED + "!=0" ,
+ null, "RANDOM() LIMIT 1");
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getLong(0);
+ }
+ } finally {
+ cursor.close();
+ }
+
+ return -1;
+ }
+
+ private long findArbitraryPhoneDataId() {
+ final Cursor cursor = getContentResolver().query(Data.CONTENT_URI,
+ new String[] { Data._ID },
+ Data.MIMETYPE + "=" + Phone.MIMETYPE,
+ null, "RANDOM() LIMIT 1");
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getLong(0);
+ }
+ } finally {
+ cursor.close();
+ }
+
+ return -1;
+ }
+
+ private long findArbitraryRawContactOfContact(long contactId) {
+ final Cursor cursor = getContentResolver().query(RawContacts.CONTENT_URI,
+ new String[] { RawContacts._ID },
+ RawContacts.CONTACT_ID + "=?",
+ new String[] { String.valueOf(contactId) },
+ RawContacts._ID + " LIMIT 1");
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getLong(0);
+ }
+ } finally {
+ cursor.close();
+ }
+
+ return -1;
+ }
+
+ @Override
+ public void onAccountChosen(Account account, int tag) {
+ switch (ContactsIntent.get(tag)) {
+ case EDIT_NEW_CONTACT_FOR_ACCOUNT: {
+ final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+ intent.putExtra(Insert.ACCOUNT, account);
+ startActivity(intent);
+ break;
+ }
+ case EDIT_NEW_CONTACT_FOR_ACCOUNT_WITH_DATA: {
+ final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+
+ intent.putExtra(Insert.ACCOUNT, account);
+ putDataExtra(intent);
+
+ startActivity(intent);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ public void putDataExtra(final Intent intent) {
+ ContentValues row1 = new ContentValues();
+ row1.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
+ row1.put(Organization.COMPANY, "Android");
+
+ ContentValues row2 = new ContentValues();
+ row2.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ row2.put(Email.TYPE, Email.TYPE_CUSTOM);
+ row2.put(Email.LABEL, "Green Bot");
+ row2.put(Email.ADDRESS, "android@android.com");
+
+ intent.putParcelableArrayListExtra(Insert.DATA, Lists.newArrayList(row1, row2));
+ }
+}
diff --git a/tests/src/com/android/contacts/tests/allintents/ResultActivity.java b/tests/src/com/android/contacts/tests/allintents/ResultActivity.java
new file mode 100644
index 0000000..562f2ba
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/allintents/ResultActivity.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2010 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.allintents;
+
+import com.android.contacts.tests.R;
+
+import android.app.Activity;
+import android.content.ContentUris;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+import android.widget.ImageView.ScaleType;
+
+import java.util.Arrays;
+
+/**
+ * An activity that shows the result of a contacts activity invocation.
+ */
+public class ResultActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.result);
+
+ Intent intent = getIntent();
+ addRowsForIntent((Intent)intent.getExtras().get("data"));
+ }
+
+ private void addRowsForIntent(Intent intent) {
+ if (intent == null) {
+ addRow("", "No data intent returned");
+ } else {
+ addRow("INTENT", intent.toString());
+ addSeparator(3);
+
+ Bundle extras = intent.getExtras();
+ if (extras != null && !extras.isEmpty()) {
+ for (String key : extras.keySet()) {
+ Object value = extras.get(key);
+ addRow("EXTRA", key);
+ addRowForValue("", value);
+ }
+
+ addSeparator(3);
+ }
+
+ String dataUri = intent.getDataString();
+ if (dataUri != null) {
+ addRowsForQuery(Uri.parse(dataUri));
+ }
+ }
+ }
+
+ private void addRowForValue(String label, Object value) {
+ if (value == null) {
+ addRow(label, "null");
+ } else if (value instanceof Bitmap) {
+ addRowWithBitmap(label, (Bitmap)value);
+ } else if (value instanceof Intent) {
+ addRow(label, "INTENT");
+ addRowsForIntent((Intent)value);
+ } else if (value instanceof Uri) {
+ addRow(label, "DATA");
+ addRowsForQuery((Uri)value);
+ } else if (value.getClass().isArray()) {
+ addRow(label, "ARRAY");
+ Parcelable[] array = (Parcelable[])value;
+ for (int i = 0; i < array.length; i++) {
+ addRowForValue("[" + i + "]", String.valueOf(array[i]));
+ }
+ } else {
+ addRow(label, String.valueOf(value));
+ }
+ }
+
+ private void addRowsForQuery(Uri dataUri) {
+ Cursor cursor = getContentResolver().query(dataUri, null, null, null, null);
+ if (cursor == null) {
+ addRow("", "No data for this URI");
+ } else {
+ try {
+ while (cursor.moveToNext()) {
+ addRow("", "DATA");
+ String[] columnNames = cursor.getColumnNames();
+ String[] names = new String[columnNames.length];
+ System.arraycopy(columnNames, 0, names, 0, columnNames.length);
+ Arrays.sort(names);
+ for (int i = 0; i < names.length; i++) {
+ int index = cursor.getColumnIndex(names[i]);
+ String value = cursor.getString(index);
+ addRow(names[i], value);
+
+ if (names[i].equals(Contacts.PHOTO_ID) && !TextUtils.isEmpty(value)) {
+ addRowWithPhoto(Long.parseLong(value));
+ }
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+
+ private void addRow(String column0, String column1) {
+ TextView label = new TextView(this);
+ label.setPadding(4, 4, 4, 4);
+ label.setText(column0);
+ TextView value = new TextView(this);
+ value.setPadding(4, 4, 4, 4);
+ value.setText(column1);
+ addRow(label, value);
+ }
+
+ private void addRowWithPhoto(long photoId) {
+ byte[] data = null;
+ Cursor cursor = getContentResolver().query(
+ ContentUris.withAppendedId(Data.CONTENT_URI, photoId),
+ new String[]{Photo.PHOTO}, null, null, null);
+ try {
+ if (cursor.moveToNext()) {
+ data = cursor.getBlob(0);
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ if (data == null) {
+ return;
+ }
+
+ addRowWithBitmap("Photo", BitmapFactory.decodeByteArray(data, 0, data.length));
+ }
+
+ private void addRowWithBitmap(String label, Bitmap bitmap) {
+ TextView labelView = new TextView(this);
+ labelView.setPadding(4, 4, 4, 4);
+ labelView.setText(label);
+
+ ImageView imageView = new ImageView(this);
+ imageView.setImageBitmap(bitmap);
+ imageView.setPadding(4, 4, 4, 4);
+ imageView.setScaleType(ScaleType.FIT_START);
+ addRow(labelView, imageView);
+ }
+
+ private void addRow(View column0, View column1) {
+ TableLayout table = (TableLayout)findViewById(R.id.table);
+ TableRow row = new TableRow(this);
+ row.addView(column0);
+ row.addView(column1);
+ table.addView(row);
+
+ addSeparator(1);
+ }
+
+ private void addSeparator(int height) {
+ TableLayout table = (TableLayout)findViewById(R.id.table);
+ View separator = new View(this);
+ TableLayout.LayoutParams params = new TableLayout.LayoutParams();
+ params.height = height;
+ separator.setLayoutParams(params);
+ separator.setBackgroundColor(Color.rgb(33, 66, 33));
+ table.addView(separator);
+ }
+}
diff --git a/tests/src/com/android/contacts/tests/allintents/SelectAccountDialogFragment.java b/tests/src/com/android/contacts/tests/allintents/SelectAccountDialogFragment.java
new file mode 100644
index 0000000..e074a0b
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/allintents/SelectAccountDialogFragment.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 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.allintents;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+/**
+ * Shows a dialog asking the user which account to chose.
+ * The result is passed back to the owning Activity
+ * Does not perform any action by itself.
+ */
+public class SelectAccountDialogFragment extends DialogFragment {
+ public static final String TAG = "SelectAccountDialogFragment";
+
+ private static final String EXTRA_TAG = "tag";
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Bundle parameters = getArguments();
+
+ final Account[] accounts = AccountManager.get(getActivity()).getAccounts();
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ final LayoutInflater inflater = LayoutInflater.from(builder.getContext());
+
+ final ArrayAdapter<Account> accountAdapter = new ArrayAdapter<Account>(builder.getContext(),
+ android.R.layout.simple_list_item_2, accounts) {
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final View resultView = convertView == null
+ ? inflater.inflate(android.R.layout.simple_list_item_2, parent, false)
+ : convertView;
+
+ final TextView text1 = (TextView)resultView.findViewById(android.R.id.text1);
+ final TextView text2 = (TextView)resultView.findViewById(android.R.id.text2);
+
+ final Account account = getItem(position);
+
+ text1.setText("Name: " + account.name);
+ text2.setText("Type: " + account.type);
+
+ return resultView;
+ }
+ };
+
+ final DialogInterface.OnClickListener clickListener =
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+
+ ((Listener) getActivity()).onAccountChosen(accountAdapter.getItem(which),
+ parameters.getInt(EXTRA_TAG));
+ }
+ };
+
+ builder.setTitle("Choose account to send to editor");
+ builder.setSingleChoiceItems(accountAdapter, 0, clickListener);
+ final AlertDialog result = builder.create();
+ return result;
+ }
+
+ public static Bundle createBundle(int tag) {
+ final Bundle result = new Bundle();
+ result.putInt(EXTRA_TAG, tag);
+ return result;
+ }
+
+ public interface Listener {
+ void onAccountChosen(Account account, int tag);
+ }
+}
diff --git a/tests/src/com/android/contacts/tests/mocks/ContactsMockContext.java b/tests/src/com/android/contacts/tests/mocks/ContactsMockContext.java
new file mode 100644
index 0000000..9af1350
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/mocks/ContactsMockContext.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2010 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.mocks;
+
+//import com.android.providers.contacts.ContactsMockPackageManager;
+
+import android.app.LoaderManager;
+import android.content.AsyncTaskLoader;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Loader;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.provider.ContactsContract;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+/**
+ * A mock context for contacts unit tests. Forwards everything to
+ * a supplied context, except content resolver operations, which are sent
+ * to mock content providers.
+ */
+public class ContactsMockContext extends ContextWrapper {
+
+ private static final String TAG = "ContactsMockContext";
+
+ private ContactsMockPackageManager mPackageManager;
+ private MockContentResolver mContentResolver;
+ private MockContentProvider mContactsProvider;
+ private MockContentProvider mSettingsProvider;
+
+ public ContactsMockContext(Context base) {
+ super(base);
+ mPackageManager = new ContactsMockPackageManager();
+ mContentResolver = new MockContentResolver();
+ mContactsProvider = new MockContentProvider();
+ mContentResolver.addProvider(ContactsContract.AUTHORITY, mContactsProvider);
+ mContactsProvider.attachInfo(this, new ProviderInfo());
+ mSettingsProvider = new MockContentProvider();
+ mSettingsProvider.attachInfo(this, new ProviderInfo());
+ mContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+
+ public MockContentProvider getContactsProvider() {
+ return mContactsProvider;
+ }
+
+ public MockContentProvider getSettingsProvider() {
+ return mSettingsProvider;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ return mPackageManager;
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ return this;
+ }
+
+ public void verify() {
+ mContactsProvider.verify();
+ mSettingsProvider.verify();
+ }
+
+ /**
+ * Waits for the specified loaders to complete loading.
+ */
+ public void waitForLoaders(LoaderManager loaderManager, int... loaderIds) {
+ Loader<?>[] loaders = new Loader<?>[loaderIds.length];
+ for (int i = 0; i < loaderIds.length; i++) {
+ final int loaderId = loaderIds[i];
+ final AsyncTaskLoader<?> loader =
+ (AsyncTaskLoader<?>) loaderManager.getLoader(loaderId);
+ if (loader == null) {
+ Assert.fail("Loader does not exist: " + loaderId);
+ return;
+ }
+
+ loaders[i] = loader;
+ }
+
+ waitForLoaders(loaders);
+ }
+
+ /**
+ * Waits for the specified loaders to complete loading.
+ */
+ public void waitForLoaders(Loader<?>... loaders) {
+ // We want to wait for each loader using a separate thread, so that we can
+ // simulate race conditions.
+ Thread[] waitThreads = new Thread[loaders.length];
+ for (int i = 0; i < loaders.length; i++) {
+ final AsyncTaskLoader<?> loader = (AsyncTaskLoader<?>) loaders[i];
+ waitThreads[i] = new Thread("LoaderWaitingThread" + i) {
+ @Override
+ public void run() {
+ try {
+ loader.waitForLoader();
+ } catch (Throwable e) {
+ Log.e(TAG, "Exception while waiting for loader: " + loader.getId(), e);
+ Assert.fail("Exception while waiting for loader: " + loader.getId());
+ }
+ }
+ };
+ waitThreads[i].start();
+ }
+
+ // Now we wait for all these threads to finish
+ for (Thread thread : waitThreads) {
+ try {
+ thread.join();
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+
+}
diff --git a/tests/src/com/android/contacts/tests/mocks/ContactsMockPackageManager.java b/tests/src/com/android/contacts/tests/mocks/ContactsMockPackageManager.java
new file mode 100644
index 0000000..0f893fe
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/mocks/ContactsMockPackageManager.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 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.mocks;
+
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.test.mock.MockPackageManager;
+
+/**
+ */
+public class ContactsMockPackageManager extends MockPackageManager {
+ public ContactsMockPackageManager() {
+ }
+
+ @Override
+ public Drawable getActivityLogo(ComponentName activityName) throws NameNotFoundException {
+ return new ColorDrawable();
+ }
+
+ @Override
+ public Drawable getActivityIcon(ComponentName activityName) {
+ return new ColorDrawable();
+ }
+
+ @Override
+ public Drawable getDrawable(String packageName, int resid, ApplicationInfo appInfo) {
+ // TODO: make programmable
+ return new ColorDrawable();
+ }
+}
diff --git a/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java b/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java
new file mode 100644
index 0000000..2635a09
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 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.mocks;
+
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+
+import android.accounts.Account;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A mock {@link AccountTypeManager} class.
+ */
+public class MockAccountTypeManager extends AccountTypeManager {
+
+ private final AccountType[] mTypes;
+ private Account[] mAccounts;
+
+ public MockAccountTypeManager(AccountType[] types, Account[] accounts) {
+ this.mTypes = types;
+ this.mAccounts = accounts;
+ }
+
+ @Override
+ public AccountType getAccountType(String accountType) {
+ for (AccountType type : mTypes) {
+ if (accountType.equals(type.accountType)) {
+ return type;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public ArrayList<Account> getAccounts(boolean writableOnly) {
+ return new ArrayList<Account>(Arrays.asList(mAccounts));
+ }
+}
diff --git a/tests/src/com/android/contacts/tests/mocks/MockContentProvider.java b/tests/src/com/android/contacts/tests/mocks/MockContentProvider.java
new file mode 100644
index 0000000..40d5969
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/mocks/MockContentProvider.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2010 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.mocks;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import junit.framework.Assert;
+
+/**
+ * A programmable mock content provider.
+ */
+public class MockContentProvider extends ContentProvider {
+ private static final String TAG = "MockContentProvider";
+
+ public static class Query {
+
+ private final Uri mUri;
+ private String[] mProjection;
+ private String[] mDefaultProjection;
+ private String mSelection;
+ private String[] mSelectionArgs;
+ private String mSortOrder;
+ private ArrayList<Object> mRows = new ArrayList<Object>();
+ private boolean mAnyProjection;
+ private boolean mAnySelection;
+ private boolean mAnySortOrder;
+ private boolean mAnyNumberOfTimes;
+
+ private boolean mExecuted;
+
+ public Query(Uri uri) {
+ mUri = uri;
+ }
+
+ @Override
+ public String toString() {
+ return queryToString(mUri, mProjection, mSelection, mSelectionArgs, mSortOrder);
+ }
+
+ public Query withProjection(String... projection) {
+ mProjection = projection;
+ return this;
+ }
+
+ public Query withDefaultProjection(String... projection) {
+ mDefaultProjection = projection;
+ return this;
+ }
+
+ public Query withAnyProjection() {
+ mAnyProjection = true;
+ return this;
+ }
+
+ public Query withSelection(String selection, String... selectionArgs) {
+ mSelection = selection;
+ mSelectionArgs = selectionArgs;
+ return this;
+ }
+
+ public Query withAnySelection() {
+ mAnySelection = true;
+ return this;
+ }
+
+ public Query withSortOrder(String sortOrder) {
+ mSortOrder = sortOrder;
+ return this;
+ }
+
+ public Query withAnySortOrder() {
+ mAnySortOrder = true;
+ return this;
+ }
+
+ public Query returnRow(ContentValues values) {
+ mRows.add(values);
+ return this;
+ }
+
+ public Query returnRow(Object... row) {
+ mRows.add(row);
+ return this;
+ }
+
+ public Query returnEmptyCursor() {
+ mRows.clear();
+ return this;
+ }
+
+ public Query anyNumberOfTimes() {
+ mAnyNumberOfTimes = true;
+ return this;
+ }
+
+ public boolean equals(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ if (!uri.equals(mUri)) {
+ return false;
+ }
+
+ if (!mAnyProjection && !equals(projection, mProjection)) {
+ return false;
+ }
+
+ if (!mAnySelection && !equals(selection, mSelection)) {
+ return false;
+ }
+
+ if (!mAnySelection && !equals(selectionArgs, mSelectionArgs)) {
+ return false;
+ }
+
+ if (!mAnySortOrder && !equals(sortOrder, mSortOrder)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean equals(String string1, String string2) {
+ if (TextUtils.isEmpty(string1)) {
+ string1 = null;
+ }
+ if (TextUtils.isEmpty(string2)) {
+ string2 = null;
+ }
+ return TextUtils.equals(string1, string2);
+ }
+
+ private static boolean equals(String[] array1, String[] array2) {
+ boolean empty1 = array1 == null || array1.length == 0;
+ boolean empty2 = array2 == null || array2.length == 0;
+ if (empty1 && empty2) {
+ return true;
+ }
+ if (empty1 != empty2 && (empty1 || empty2)) {
+ return false;
+ }
+
+ if (array1.length != array2.length) return false;
+
+ for (int i = 0; i < array1.length; i++) {
+ if (!array1[i].equals(array2[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public Cursor getResult(String[] projection) {
+ String[] columnNames;
+ if (mAnyProjection) {
+ columnNames = projection;
+ } else {
+ columnNames = mProjection != null ? mProjection : mDefaultProjection;
+ }
+
+ MatrixCursor cursor = new MatrixCursor(columnNames);
+ for (Object row : mRows) {
+ if (row instanceof Object[]) {
+ cursor.addRow((Object[]) row);
+ } else {
+ ContentValues values = (ContentValues) row;
+ Object[] columns = new Object[projection.length];
+ for (int i = 0; i < projection.length; i++) {
+ columns[i] = values.get(projection[i]);
+ }
+ cursor.addRow(columns);
+ }
+ }
+ return cursor;
+ }
+ }
+
+ public static class TypeQuery {
+ private final Uri mUri;
+ private final String mType;
+
+ public TypeQuery(Uri uri, String type) {
+ mUri = uri;
+ mType = type;
+ }
+
+ public Uri getUri() {
+ return mUri;
+ }
+
+ public String getType() {
+ return mType;
+ }
+
+ @Override
+ public String toString() {
+ return mUri + " --> " + mType;
+ }
+
+ public boolean equals(Uri uri) {
+ return getUri().equals(uri);
+ }
+ }
+
+ private ArrayList<Query> mExpectedQueries = new ArrayList<Query>();
+ private HashMap<Uri, String> mExpectedTypeQueries = Maps.newHashMap();
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ public Query expectQuery(Uri contentUri) {
+ Query query = new Query(contentUri);
+ mExpectedQueries.add(query);
+ return query;
+ }
+
+ public void expectTypeQuery(Uri uri, String type) {
+ mExpectedTypeQueries.put(uri, type);
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+
+ for (Iterator<Query> iterator = mExpectedQueries.iterator(); iterator.hasNext();) {
+ Query query = iterator.next();
+ if (query.equals(uri, projection, selection, selectionArgs, sortOrder)) {
+ query.mExecuted = true;
+ if (!query.mAnyNumberOfTimes) {
+ iterator.remove();
+ }
+ return query.getResult(projection);
+ }
+ }
+
+ if (mExpectedQueries.isEmpty()) {
+ Assert.fail("Unexpected query: "
+ + queryToString(uri, projection, selection, selectionArgs, sortOrder));
+ } else {
+ StringBuilder sb = new StringBuilder();
+ sb.append(mExpectedQueries.get(0));
+ for (int i = 1; i < mExpectedQueries.size(); i++) {
+ sb.append("\n ").append(mExpectedQueries.get(i));
+ }
+ Assert.fail("Incorrect query.\n Expected: " + sb + "\n Actual: " +
+ queryToString(uri, projection, selection, selectionArgs, sortOrder));
+ }
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ if (mExpectedTypeQueries.isEmpty()) {
+ Assert.fail("Unexpected getType query: " + uri);
+ }
+
+ String mimeType = mExpectedTypeQueries.get(uri);
+ if (mimeType != null) {
+ return mimeType;
+ }
+
+ Assert.fail("Unknown mime type for: " + uri);
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ private static String queryToString(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(uri).append(" ");
+ if (projection != null) {
+ sb.append(Arrays.toString(projection));
+ } else {
+ sb.append("[]");
+ }
+ if (selection != null) {
+ sb.append(" selection: '").append(selection).append("'");
+ if (selectionArgs != null) {
+ sb.append(Arrays.toString(selectionArgs));
+ } else {
+ sb.append("[]");
+ }
+ }
+ if (sortOrder != null) {
+ sb.append(" sort: '").append(sortOrder).append("'");
+ }
+ return sb.toString();
+ }
+
+ public void verify() {
+ ArrayList<Query> mMissedQueries = Lists.newArrayList();
+ for (Query query : mExpectedQueries) {
+ if (!query.mExecuted) {
+ mMissedQueries.add(query);
+ }
+ }
+ Assert.assertTrue("Not all expected queries have been called: " +
+ mMissedQueries, mMissedQueries.isEmpty());
+ }
+}
diff --git a/tests/src/com/android/contacts/tests/mocks/MockSharedPreferences.java b/tests/src/com/android/contacts/tests/mocks/MockSharedPreferences.java
new file mode 100644
index 0000000..40fd934
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/mocks/MockSharedPreferences.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 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.mocks;
+
+import com.google.android.collect.Maps;
+
+import android.content.SharedPreferences;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * A programmable mock content provider.
+ */
+public class MockSharedPreferences implements SharedPreferences, SharedPreferences.Editor {
+
+ private HashMap<String, Object> mValues = Maps.newHashMap();
+ private HashMap<String, Object> mTempValues = Maps.newHashMap();
+
+ public Editor edit() {
+ return this;
+ }
+
+ public boolean contains(String key) {
+ return mValues.containsKey(key);
+ }
+
+ public Map<String, ?> getAll() {
+ return new HashMap<String, Object>(mValues);
+ }
+
+ public boolean getBoolean(String key, boolean defValue) {
+ if (mValues.containsKey(key)) {
+ return ((Boolean)mValues.get(key)).booleanValue();
+ }
+ return defValue;
+ }
+
+ public float getFloat(String key, float defValue) {
+ if (mValues.containsKey(key)) {
+ return ((Float)mValues.get(key)).floatValue();
+ }
+ return defValue;
+ }
+
+ public int getInt(String key, int defValue) {
+ if (mValues.containsKey(key)) {
+ return ((Integer)mValues.get(key)).intValue();
+ }
+ return defValue;
+ }
+
+ public long getLong(String key, long defValue) {
+ if (mValues.containsKey(key)) {
+ return ((Long)mValues.get(key)).longValue();
+ }
+ return defValue;
+ }
+
+ public String getString(String key, String defValue) {
+ if (mValues.containsKey(key))
+ return (String)mValues.get(key);
+ return defValue;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Set<String> getStringSet(String key, Set<String> defValues) {
+ if (mValues.containsKey(key)) {
+ return (Set<String>) mValues.get(key);
+ }
+ return defValues;
+ }
+
+ public void registerOnSharedPreferenceChangeListener(
+ OnSharedPreferenceChangeListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void unregisterOnSharedPreferenceChangeListener(
+ OnSharedPreferenceChangeListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Editor putBoolean(String key, boolean value) {
+ mTempValues.put(key, Boolean.valueOf(value));
+ return this;
+ }
+
+ public Editor putFloat(String key, float value) {
+ mTempValues.put(key, value);
+ return this;
+ }
+
+ public Editor putInt(String key, int value) {
+ mTempValues.put(key, value);
+ return this;
+ }
+
+ public Editor putLong(String key, long value) {
+ mTempValues.put(key, value);
+ return this;
+ }
+
+ public Editor putString(String key, String value) {
+ mTempValues.put(key, value);
+ return this;
+ }
+
+ public Editor putStringSet(String key, Set<String> values) {
+ mTempValues.put(key, values);
+ return this;
+ }
+
+ public Editor remove(String key) {
+ mTempValues.remove(key);
+ return this;
+ }
+
+ public Editor clear() {
+ mTempValues.clear();
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean commit() {
+ mValues = (HashMap<String, Object>)mTempValues.clone();
+ return true;
+ }
+
+ public void apply() {
+ commit();
+ }
+}
diff --git a/tests/src/com/android/contacts/tests/quickcontact/QuickContactTestsActivity.java b/tests/src/com/android/contacts/tests/quickcontact/QuickContactTestsActivity.java
new file mode 100644
index 0000000..527d7a3
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/quickcontact/QuickContactTestsActivity.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 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.quickcontact;
+
+import com.android.contacts.tests.R;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.QuickContactBadge;
+import android.widget.TextView;
+
+public class QuickContactTestsActivity extends Activity {
+ private static final int REQUEST_CODE_PICK = 1;
+ private static final String PREF_NAME = "quick_contact_prefs";
+ private static final String PREF_SETTING_URI = "uri";
+
+ private Button mPickContact;
+ private TextView mUriTextView;
+ private QuickContactBadge mSmallBadge1;
+ private QuickContactBadge mSmallBadge2;
+ private QuickContactBadge mMediumBadge1;
+ private QuickContactBadge mMediumBadge2;
+ private QuickContactBadge mLargeBadge1;
+ private QuickContactBadge mLargeBadge2;
+
+ private Uri mContactUri;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.quick_contact_tests);
+
+ mPickContact = (Button) findViewById(R.id.pick_contact);
+ mUriTextView = (TextView) findViewById(R.id.uri);
+ mSmallBadge1 = (QuickContactBadge) findViewById(R.id.small_badge1);
+ mSmallBadge2 = (QuickContactBadge) findViewById(R.id.small_badge2);
+ mMediumBadge1 = (QuickContactBadge) findViewById(R.id.medium_badge1);
+ mMediumBadge2 = (QuickContactBadge) findViewById(R.id.medium_badge2);
+ mLargeBadge1 = (QuickContactBadge) findViewById(R.id.large_badge1);
+ mLargeBadge2 = (QuickContactBadge) findViewById(R.id.large_badge2);
+
+ mPickContact.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
+ startActivityForResult(intent , REQUEST_CODE_PICK);
+ }
+ });
+
+ // Load Uri if known
+ final SharedPreferences sharedPreferences = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
+ final String uriString = sharedPreferences.getString(PREF_SETTING_URI, null);
+ if (uriString != null) {
+ mContactUri = Uri.parse(uriString);
+ assignUri();
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == Activity.RESULT_CANCELED) return;
+ switch (requestCode) {
+ case REQUEST_CODE_PICK: {
+ mContactUri = data.getData();
+ assignUri();
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ final SharedPreferences sharedPreferences = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
+ final Editor editor = sharedPreferences.edit();
+ editor.putString(PREF_SETTING_URI, mContactUri == null ? null : mContactUri.toString());
+ editor.apply();
+ }
+
+ private void assignUri() {
+ mUriTextView.setText(mContactUri.toString());
+ mSmallBadge1.assignContactUri(mContactUri);
+ mSmallBadge2.assignContactUri(mContactUri);
+ mMediumBadge1.assignContactUri(mContactUri);
+ mMediumBadge2.assignContactUri(mContactUri);
+ mLargeBadge1.assignContactUri(mContactUri);
+ mLargeBadge2.assignContactUri(mContactUri);
+ }
+}
diff --git a/tests/src/com/android/contacts/tests/widget/PinnedHeaderUseCaseActivity.java b/tests/src/com/android/contacts/tests/widget/PinnedHeaderUseCaseActivity.java
new file mode 100644
index 0000000..b01963f
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/widget/PinnedHeaderUseCaseActivity.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import com.android.contacts.tests.R;
+import com.android.contacts.widget.PinnedHeaderListView;
+
+import android.app.ListActivity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+ * An activity that demonstrates various use cases for the {@link PinnedHeaderListView}.
+ */
+public class PinnedHeaderUseCaseActivity extends ListActivity {
+
+ private static final int SINGLE_SHORT_SECTION_NO_HEADERS = 0;
+ private static final int TWO_SHORT_SECTIONS_WITH_HEADERS = 1;
+ private static final int FIVE_SHORT_SECTIONS_WITH_HEADERS = 2;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setListAdapter(new ArrayAdapter<String>(this, R.layout.intent_list_item,
+ getResources().getStringArray(R.array.pinnedHeaderUseCases)));
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ switch (position) {
+ case SINGLE_SHORT_SECTION_NO_HEADERS:
+ startActivity(
+ new int[]{5},
+ new String[]{"Line"},
+ new boolean[]{false},
+ new boolean[]{false},
+ new int[]{0});
+ break;
+ case TWO_SHORT_SECTIONS_WITH_HEADERS:
+ startActivity(
+ new int[]{2, 30},
+ new String[]{"First", "Second"},
+ new boolean[]{true, true},
+ new boolean[]{false, false},
+ new int[]{0, 2000});
+ break;
+ case FIVE_SHORT_SECTIONS_WITH_HEADERS:
+ startActivity(
+ new int[]{1, 5, 5, 5, 5},
+ new String[]{"First", "Second", "Third", "Fourth", "Fifth"},
+ new boolean[]{true, true, true, true, true},
+ new boolean[]{false, false, false, false, false},
+ new int[]{0, 2000, 3000, 4000, 5000});
+ break;
+ }
+ }
+
+ private void startActivity(int[] counts, String[] names, boolean[] headers,
+ boolean[] showIfEmpty, int[] delays) {
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName("com.android.contacts",
+ "com.android.contacts.widget.PinnedHeaderListDemoActivity"));
+ intent.putExtra("counts", counts);
+ intent.putExtra("names", names);
+ intent.putExtra("headers", headers);
+ intent.putExtra("showIfEmpty", showIfEmpty);
+ intent.putExtra("delays", delays);
+
+ startActivity(intent);
+ }
+}
diff --git a/tests/src/com/android/contacts/widget/CompositeListAdapterTest.java b/tests/src/com/android/contacts/widget/CompositeListAdapterTest.java
new file mode 100644
index 0000000..87d268b
--- /dev/null
+++ b/tests/src/com/android/contacts/widget/CompositeListAdapterTest.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2010 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.widget;
+
+import com.google.android.collect.Lists;
+
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.test.AndroidTestCase;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Tests for {@link CompositeListAdapter}.
+ */
+public class CompositeListAdapterTest extends AndroidTestCase {
+
+ private final class MockAdapter extends ArrayAdapter<String> {
+ boolean allItemsEnabled = true;
+ HashSet<Integer> enabledItems = new HashSet<Integer>();
+ int viewTypeCount = 1;
+ HashMap<Integer, Integer> viewTypes = new HashMap<Integer, Integer>();
+
+ private MockAdapter(Context context, List<String> objects) {
+ super(context, android.R.layout.simple_list_item_1, objects);
+ for (int i = 0; i < objects.size(); i++) {
+ viewTypes.put(i, 0);
+ }
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return new MockView(getContext(), position);
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return allItemsEnabled;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return enabledItems.contains(position);
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return viewTypeCount;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return viewTypes.get(position);
+ }
+ }
+
+ private final class MockView extends View {
+ public MockView(Context context, int position) {
+ super(context);
+ setTag(position);
+ }
+ }
+
+ private final class TestDataSetObserver extends DataSetObserver {
+
+ public int changeCount;
+ public int invalidationCount;
+
+ @Override
+ public void onChanged() {
+ changeCount++;
+ }
+
+ @Override
+ public void onInvalidated() {
+ invalidationCount++;
+ }
+ }
+
+ private MockAdapter mAdapter1;
+ private MockAdapter mAdapter2;
+ private MockAdapter mAdapter3;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mAdapter1 = new MockAdapter(getContext(), Lists.newArrayList("A", "B"));
+ mAdapter2 = new MockAdapter(getContext(), new ArrayList<String>());
+ mAdapter3 = new MockAdapter(getContext(), Lists.newArrayList("C", "D", "E"));
+ }
+
+ public void testGetCount() {
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+ adapter.addAdapter(mAdapter2);
+ adapter.addAdapter(mAdapter3);
+
+ assertEquals(5, adapter.getCount());
+ }
+
+ public void testGetCountWithInvalidation() {
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ assertEquals(0, adapter.getCount());
+
+ adapter.addAdapter(mAdapter1);
+ assertEquals(2, adapter.getCount());
+
+ adapter.addAdapter(mAdapter2);
+ assertEquals(2, adapter.getCount());
+
+ adapter.addAdapter(mAdapter3);
+ assertEquals(5, adapter.getCount());
+ }
+
+ public void testGetItem() {
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+ adapter.addAdapter(mAdapter2);
+ adapter.addAdapter(mAdapter3);
+
+ assertEquals("A", adapter.getItem(0));
+ assertEquals("B", adapter.getItem(1));
+ assertEquals("C", adapter.getItem(2));
+ assertEquals("D", adapter.getItem(3));
+ assertEquals("E", adapter.getItem(4));
+ }
+
+ public void testGetItemId() {
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+ adapter.addAdapter(mAdapter2);
+ adapter.addAdapter(mAdapter3);
+
+ assertEquals(0, adapter.getItemId(0));
+ assertEquals(1, adapter.getItemId(1));
+ assertEquals(0, adapter.getItemId(2));
+ assertEquals(1, adapter.getItemId(3));
+ assertEquals(2, adapter.getItemId(4));
+ }
+
+ public void testGetView() {
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+ adapter.addAdapter(mAdapter2);
+ adapter.addAdapter(mAdapter3);
+
+ assertEquals(0, adapter.getView(0, null, null).getTag());
+ assertEquals(1, adapter.getView(1, null, null).getTag());
+ assertEquals(0, adapter.getView(2, null, null).getTag());
+ assertEquals(1, adapter.getView(3, null, null).getTag());
+ assertEquals(2, adapter.getView(4, null, null).getTag());
+ }
+
+ public void testGetViewTypeCount() {
+ mAdapter1.viewTypeCount = 2;
+ mAdapter2.viewTypeCount = 3;
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+ adapter.addAdapter(mAdapter2);
+ adapter.addAdapter(mAdapter3);
+
+ // Note that mAdapter2 adds an implicit +1
+ assertEquals(6, adapter.getViewTypeCount());
+ }
+
+ public void testGetItemViewType() {
+ mAdapter1.viewTypeCount = 2;
+ mAdapter1.viewTypes.put(0, 1);
+ mAdapter1.viewTypes.put(1, 0);
+
+ mAdapter3.viewTypeCount = 3;
+ mAdapter3.viewTypes.put(0, 1);
+ mAdapter3.viewTypes.put(1, 2);
+ mAdapter3.viewTypes.put(2, 0);
+
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+ adapter.addAdapter(mAdapter2);
+ adapter.addAdapter(mAdapter3);
+
+ assertEquals(1, adapter.getItemViewType(0));
+ assertEquals(0, adapter.getItemViewType(1));
+
+ // Note: mAdapter2 throws in a +1
+
+ assertEquals(4, adapter.getItemViewType(2));
+ assertEquals(5, adapter.getItemViewType(3));
+ assertEquals(3, adapter.getItemViewType(4));
+ }
+
+ public void testNotifyDataSetChangedPropagated() {
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+ adapter.addAdapter(mAdapter2);
+
+ TestDataSetObserver observer = new TestDataSetObserver();
+ adapter.registerDataSetObserver(observer);
+ mAdapter1.add("X");
+
+ assertEquals(1, observer.changeCount);
+ assertEquals(0, observer.invalidationCount);
+ assertEquals(3, adapter.getCount());
+ assertEquals("A", adapter.getItem(0));
+ assertEquals("B", adapter.getItem(1));
+ assertEquals("X", adapter.getItem(2));
+
+ mAdapter2.add("Y");
+ assertEquals(2, observer.changeCount);
+ assertEquals(0, observer.invalidationCount);
+ assertEquals(4, adapter.getCount());
+ assertEquals("A", adapter.getItem(0));
+ assertEquals("B", adapter.getItem(1));
+ assertEquals("X", adapter.getItem(2));
+ assertEquals("Y", adapter.getItem(3));
+
+ }
+
+ public void testNotifyDataSetChangedOnAddingAdapter() {
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+
+ TestDataSetObserver observer = new TestDataSetObserver();
+ adapter.registerDataSetObserver(observer);
+ adapter.addAdapter(mAdapter3);
+
+ assertEquals(1, observer.changeCount);
+ assertEquals(0, observer.invalidationCount);
+ assertEquals(5, adapter.getCount());
+ assertEquals("A", adapter.getItem(0));
+ assertEquals("B", adapter.getItem(1));
+ assertEquals("C", adapter.getItem(2));
+ assertEquals("D", adapter.getItem(3));
+ assertEquals("E", adapter.getItem(4));
+ }
+
+ public void testNotifyDataSetInvalidated() {
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+
+ TestDataSetObserver observer = new TestDataSetObserver();
+ adapter.registerDataSetObserver(observer);
+
+ mAdapter1.remove("A");
+ assertEquals(1, observer.changeCount);
+ assertEquals(0, observer.invalidationCount);
+ assertEquals(1, adapter.getCount());
+
+ mAdapter1.remove("B");
+ assertEquals(1, observer.changeCount);
+ assertEquals(1, observer.invalidationCount);
+ assertEquals(0, adapter.getCount());
+ }
+
+ public void testAreAllItemsEnabled() {
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+ adapter.addAdapter(mAdapter3);
+
+ assertTrue(adapter.areAllItemsEnabled());
+ }
+
+ public void testAreAllItemsEnabledWithInvalidation() {
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+ assertTrue(adapter.areAllItemsEnabled());
+
+ mAdapter3.allItemsEnabled = false;
+ adapter.addAdapter(mAdapter3);
+
+ assertFalse(adapter.areAllItemsEnabled());
+ }
+
+ public void testIsEnabled() {
+ mAdapter1.allItemsEnabled = false;
+ mAdapter1.enabledItems.add(1);
+
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+ adapter.addAdapter(mAdapter2);
+ adapter.addAdapter(mAdapter3);
+
+ assertFalse(adapter.isEnabled(0));
+ assertTrue(adapter.isEnabled(1));
+ assertTrue(adapter.isEnabled(2));
+ assertTrue(adapter.isEnabled(3));
+ assertTrue(adapter.isEnabled(4));
+ }
+
+ public void testIsEnabledWhenAllEnabledAtLeastOneAdapter() {
+ mAdapter1.allItemsEnabled = false;
+ mAdapter1.enabledItems.add(1);
+ mAdapter3.allItemsEnabled = false;
+ mAdapter3.enabledItems.add(1);
+
+ CompositeListAdapter adapter = new CompositeListAdapter();
+ adapter.addAdapter(mAdapter1);
+ adapter.addAdapter(mAdapter3);
+
+ assertFalse(adapter.isEnabled(0));
+ assertTrue(adapter.isEnabled(1));
+ assertFalse(adapter.isEnabled(2));
+ assertTrue(adapter.isEnabled(3));
+ assertFalse(adapter.isEnabled(4));
+ }
+}