am c05d3191: (-s ours) am c6defdf4: am 9dca896a: (-s ours) Import revised translations. DO NOT MERGE
* commit 'c05d31919ceba83d4c731c93a0fab764a6729c25':
Import revised translations. DO NOT MERGE
diff --git a/Android.mk b/Android.mk
index ff1b7e2..1c9dcc4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -5,7 +5,11 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := com.android.phone.common com.android.vcard android-common
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ com.android.phone.common \
+ com.android.vcard \
+ android-common \
+ guava
LOCAL_PACKAGE_NAME := Contacts
LOCAL_CERTIFICATE := shared
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 52562e0..83331da 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -46,7 +46,7 @@
>
<!-- A virtual 12 key dialer -->
- <activity android:name="TwelveKeyDialer"
+ <activity android:name=".activities.DialpadActivity"
android:launchMode="singleTop"
>
<intent-filter>
@@ -57,7 +57,7 @@
</activity>
<!-- A list of recent calls -->
- <activity android:name="RecentCallsListActivity"
+ <activity android:name=".activities.CallLogActivity"
android:label="@string/recentCallsIconLabel"
>
<intent-filter>
@@ -119,7 +119,7 @@
</activity>
<!-- Tab container for all tabs -->
- <activity android:name="DialtactsActivity"
+ <activity android:name=".activities.DialtactsActivity"
android:label="@string/launcherDialer"
android:theme="@style/DialtactsTheme"
android:launchMode="singleTask"
@@ -185,16 +185,6 @@
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
-
- <intent-filter>
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.DEFAULT" />
- <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>
<!-- The actual list of contacts -->
@@ -291,6 +281,7 @@
<data android:mimeType="vnd.android.cursor.dir/phone" />
<data android:mimeType="vnd.android.cursor.dir/postal-address_v2" />
<data android:mimeType="vnd.android.cursor.dir/postal-address" />
+ <data android:mimeType="vnd.android.cursor.dir/email_v2" />
</intent-filter>
<intent-filter>
@@ -335,7 +326,7 @@
<activity
android:name=".activities.ShowOrCreateActivity"
- android:theme="@style/FullyTranslucent">
+ android:theme="@android:style/Theme.Translucent.NoTitleBar">
<intent-filter>
<action android:name="com.android.contacts.action.SHOW_OR_CREATE_CONTACT" />
@@ -348,13 +339,12 @@
<!-- Used to show QuickContact window over a translucent activity, which is a
temporary hack until we add better framework support. -->
<activity
- android:name=".quickcontact.QuickContactActivity"
- android:theme="@style/FullyTranslucent.QuickContact"
+ android:name=".quickcontact.QuickContactWindow"
+ android:theme="@style/Theme.QuickContact"
android:launchMode="singleTop"
android:excludeFromRecents="true"
android:taskAffinity="android.task.quickcontact"
- android:windowSoftInputMode="stateUnchanged"
- >
+ android:windowSoftInputMode="stateUnchanged">
<intent-filter>
<action android:name="com.android.contacts.action.QUICK_CONTACT" />
@@ -442,6 +432,11 @@
<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.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>
@@ -550,7 +545,7 @@
<!-- vCard related -->
<activity android:name=".vcard.ImportVCardActivity"
- android:configChanges="orientation"
+ android:configChanges="orientation|screenSize|keyboardHidden"
android:theme="@style/BackgroundOnly">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
diff --git a/res/drawable-hdpi/divider_horizontal_light.png b/res/drawable-hdpi/divider_horizontal_light.png
new file mode 100644
index 0000000..20d7a01
--- /dev/null
+++ b/res/drawable-hdpi/divider_horizontal_light.png
Binary files differ
diff --git a/res/drawable-xlarge-hdpi/panel_message.9.png b/res/drawable-hdpi/panel_message.9.png
similarity index 100%
rename from res/drawable-xlarge-hdpi/panel_message.9.png
rename to res/drawable-hdpi/panel_message.9.png
Binary files differ
diff --git a/res/drawable-mdpi/divider_horizontal_light.png b/res/drawable-mdpi/divider_horizontal_light.png
new file mode 100644
index 0000000..20d7a01
--- /dev/null
+++ b/res/drawable-mdpi/divider_horizontal_light.png
Binary files differ
diff --git a/res/drawable-xlarge-mdpi/panel_message.9.png b/res/drawable-mdpi/panel_message.9.png
similarity index 100%
rename from res/drawable-xlarge-mdpi/panel_message.9.png
rename to res/drawable-mdpi/panel_message.9.png
Binary files differ
diff --git a/res/drawable/quickactions_arrow_left_holo_light.xml b/res/drawable/quickactions_arrow_left_holo_light.xml
new file mode 100644
index 0000000..c1e18bd
--- /dev/null
+++ b/res/drawable/quickactions_arrow_left_holo_light.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_first="true" android:drawable="@drawable/quickactions_arrowdown_left_holo_light" />
+ <item android:state_last="true" android:drawable="@drawable/quickactions_arrowup_left_holo_light" />
+ <!-- TODO: provide callout-less state -->
+ <item android:drawable="@drawable/quickactions_arrowup_left_holo_light" />
+</selector>
diff --git a/res/drawable/quickactions_arrow_middle_holo_light.xml b/res/drawable/quickactions_arrow_middle_holo_light.xml
new file mode 100644
index 0000000..f88b513
--- /dev/null
+++ b/res/drawable/quickactions_arrow_middle_holo_light.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_first="true" android:drawable="@drawable/quickactions_arrowdown_middle_holo_light" />
+ <item android:state_last="true" android:drawable="@drawable/quickactions_arrowup_middle_holo_light" />
+ <!-- TODO: provide callout-less state -->
+ <item android:drawable="@drawable/quickactions_arrowup_middle_holo_light" />
+</selector>
diff --git a/res/drawable/quickactions_arrow_right_holo_light.xml b/res/drawable/quickactions_arrow_right_holo_light.xml
new file mode 100644
index 0000000..3e309fe
--- /dev/null
+++ b/res/drawable/quickactions_arrow_right_holo_light.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_first="true" android:drawable="@drawable/quickactions_arrowdown_right_holo_light" />
+ <item android:state_last="true" android:drawable="@drawable/quickactions_arrowup_right_holo_light" />
+ <!-- TODO: provide callout-less state -->
+ <item android:drawable="@drawable/quickactions_arrowup_right_holo_light" />
+</selector>
diff --git a/res/layout-land/twelve_key_dialer.xml b/res/layout-land/dialpad_activity.xml
similarity index 100%
rename from res/layout-land/twelve_key_dialer.xml
rename to res/layout-land/dialpad_activity.xml
diff --git a/res/layout-long-land/twelve_key_dialer.xml b/res/layout-long-land/dialpad_activity.xml
similarity index 100%
rename from res/layout-long-land/twelve_key_dialer.xml
rename to res/layout-long-land/dialpad_activity.xml
diff --git a/res/layout-long/twelve_key_dialer.xml b/res/layout-long/dialpad_activity.xml
similarity index 90%
rename from res/layout-long/twelve_key_dialer.xml
rename to res/layout-long/dialpad_activity.xml
index bd90df4..2580625 100644
--- a/res/layout-long/twelve_key_dialer.xml
+++ b/res/layout-long/dialpad_activity.xml
@@ -14,11 +14,14 @@
limitations under the License.
-->
+<!-- TODO (stopship) We don't want to specify a background color here. For now we just
+keep it because otherwise the dialer needs some imagination to use (white on white) -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/top"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
+ android:background="@android:color/black"
>
<!-- Text field above the keypad where the digits are displayed.
diff --git a/res/layout-xlarge-land/contact_detail_list_item.xml b/res/layout-xlarge-land/contact_detail_list_item.xml
index 529059a..9ad0e1b 100644
--- a/res/layout-xlarge-land/contact_detail_list_item.xml
+++ b/res/layout-xlarge-land/contact_detail_list_item.xml
@@ -23,42 +23,33 @@
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"
+
+ <FrameLayout
+ android:layout_width="@dimen/detail_item_type_width"
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" />
+ android:paddingLeft="@dimen/detail_item_side_margin">
+
+ <TextView
+ android:id="@+id/kind"
+ style="@style/ContactDetailItemType" />
+
+ <TextView
+ android:id="@+id/type"
+ style="@style/ContactDetailItemType" />
+
+ </FrameLayout>
<LinearLayout
android:layout_width="wrap_content"
@@ -67,26 +58,21 @@
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"
@@ -96,14 +82,6 @@
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"
@@ -114,21 +92,28 @@
android:layout_marginRight="14dip"
android:background="?android:attr/dividerVertical" />
- <ImageView
- android:id="@+id/secondary_action_button"
- android:layout_width="32dip"
+ <FrameLayout
+ android:id="@+id/secondary_action_button_container"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:layout_centerVertical="true"
- android:gravity="center"
- android:scaleType="center"
- android:background="?android:attr/selectableItemBackground" />
+ android:layout_marginTop="10dip"
+ android:paddingLeft="@dimen/detail_item_icon_margin"
+ android:paddingRight="@dimen/detail_item_icon_margin"
+ android:duplicateParentState="false"
+ android:background="?android:attr/selectableItemBackground">
+
+ <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:duplicateParentState="false" />
+
+ </FrameLayout>
</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_item.xml b/res/layout-xlarge/aggregation_suggestions_item.xml
index f12c608..5ea8347 100644
--- a/res/layout-xlarge/aggregation_suggestions_item.xml
+++ b/res/layout-xlarge/aggregation_suggestions_item.xml
@@ -24,6 +24,8 @@
android:orientation="horizontal"
android:paddingLeft="5dip"
android:paddingRight="15dip"
+ android:background="?android:attr/selectableItemBackground"
+ android:focusable="true"
>
<ImageView
android:id="@+id/aggregation_suggestion_photo"
diff --git a/res/layout-xlarge/contact_detail_fragment.xml b/res/layout-xlarge/contact_detail_fragment.xml
index 60d3cd5..4d6a900 100644
--- a/res/layout-xlarge/contact_detail_fragment.xml
+++ b/res/layout-xlarge/contact_detail_fragment.xml
@@ -23,25 +23,6 @@
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"
@@ -78,7 +59,7 @@
ex:layout_narrowMarginLeft="32dip"
ex:layout_narrowPaddingRight="16dip"
android:cacheColorHint="#00000000"
- android:divider="@android:color/transparent"
+ android:divider="@null"
/>
</com.android.contacts.widget.InterpolatingLayout>
diff --git a/res/layout-xlarge/edit_spinner.xml b/res/layout-xlarge/edit_spinner.xml
new file mode 100644
index 0000000..f1909fe
--- /dev/null
+++ b/res/layout-xlarge/edit_spinner.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<!-- Spinner for a field in the contact editor. -->
+
+<Spinner
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/spinner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/editor_field_left_padding"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:paddingLeft="5dip"
+ android:paddingTop="5dip"/>
\ No newline at end of file
diff --git a/res/layout-xlarge/event_field_editor_view.xml b/res/layout-xlarge/event_field_editor_view.xml
new file mode 100644
index 0000000..c9eef08
--- /dev/null
+++ b/res/layout-xlarge/event_field_editor_view.xml
@@ -0,0 +1,56 @@
+<?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.
+-->
+
+<!-- Editor for a single event entry in the contact editor -->
+
+<com.android.contacts.editor.EventFieldEditorView
+ 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">
+
+ <include
+ android:id="@+id/title"
+ layout="@layout/edit_field_title" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="bottom"
+ android:focusable="true"
+ android:clickable="true">
+
+ <include
+ android:id="@+id/date_view"
+ layout="@layout/edit_date_picker" />
+
+ <include
+ android:id="@+id/spinner"
+ layout="@layout/edit_spinner"
+ android:paddingTop="15dip"
+ android:visibility="gone" />
+
+ <include
+ android:id="@+id/delete_button_container"
+ layout="@layout/edit_delete_button"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+</com.android.contacts.editor.EventFieldEditorView>
diff --git a/res/layout-xlarge/item_kind_section.xml b/res/layout-xlarge/item_kind_section.xml
index 57de26d..f0ae1dc 100644
--- a/res/layout-xlarge/item_kind_section.xml
+++ b/res/layout-xlarge/item_kind_section.xml
@@ -20,41 +20,22 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:paddingBottom="@dimen/editor_field_bottom_padding"
+ android:orientation="vertical">
- <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" />
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/divider_horizontal_light" />
<LinearLayout
android:id="@+id/kind_editors"
- android:layout_width="0dip"
+ android:layout_width="match_parent"
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>
+ <include
+ android:id="@+id/add_field_footer"
+ layout="@layout/edit_add_field" />
+
</com.android.contacts.editor.KindSectionView>
diff --git a/res/layout-xlarge/list_section.xml b/res/layout-xlarge/list_section.xml
deleted file mode 100644
index c684a0f..0000000
--- a/res/layout-xlarge/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="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/phonetic_name_editor_view.xml b/res/layout-xlarge/phonetic_name_editor_view.xml
new file mode 100644
index 0000000..c3fa6a3
--- /dev/null
+++ b/res/layout-xlarge/phonetic_name_editor_view.xml
@@ -0,0 +1,59 @@
+<?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.
+-->
+
+<com.android.contacts.editor.PhoneticNameEditorView
+ 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"
+ android:paddingRight="?android:attr/scrollbarSize"
+ android:layout_marginLeft="52dip"
+ android:layout_marginRight="48dip"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="4dip">
+
+ <include
+ android:id="@+id/spinner"
+ layout="@layout/edit_spinner"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:focusable="true"
+ android:clickable="true">
+
+ <include
+ android:id="@+id/editors"
+ layout="@layout/edit_field_list" />
+
+ <include
+ android:id="@+id/expansion_view_container"
+ layout="@layout/edit_expansion_view"
+ android:visibility="gone" />
+
+ <include
+ android:id="@+id/delete_button_container"
+ layout="@layout/edit_delete_button"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+</com.android.contacts.editor.PhoneticNameEditorView>
diff --git a/res/layout-xlarge/raw_contact_editor_header.xml b/res/layout-xlarge/raw_contact_editor_header.xml
index a973464..498998d 100644
--- a/res/layout-xlarge/raw_contact_editor_header.xml
+++ b/res/layout-xlarge/raw_contact_editor_header.xml
@@ -16,52 +16,53 @@
<!-- Account info header -->
-<RelativeLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="64dip"
- android:layout_width="match_parent"
- android:background="?android:attr/selectableItemBackground">
+ android:layout_width="match_parent">
- <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"
+ <RelativeLayout
+ android:id="@+id/account"
android:layout_width="match_parent"
- android:layout_height="1px"
- android:layout_alignParentBottom="true"
- android:background="?android:attr/listDivider"
- android:visibility="gone" />
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground">
-</RelativeLayout>
+ <ImageView
+ android:id="@+id/account_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/account_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toLeftOf="@+id/account_icon"
+ android:layout_alignTop="@id/account_icon"
+ android:layout_marginTop="-4dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true" />
+
+ <TextView
+ android:id="@+id/account_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toLeftOf="@+id/account_icon"
+ android:layout_alignBottom="@+id/account_icon"
+ android:layout_marginBottom="2dip"
+
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimary"
+ android:singleLine="true" />
+
+ <include
+ android:id="@+id/divider"
+ android:layout_alignParentBottom="true"
+ layout="@layout/edit_divider" />
+ </RelativeLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout-xlarge/raw_contact_editor_view.xml b/res/layout-xlarge/raw_contact_editor_view.xml
index 3c3b9d0..0e3d227 100644
--- a/res/layout-xlarge/raw_contact_editor_view.xml
+++ b/res/layout-xlarge/raw_contact_editor_view.xml
@@ -21,9 +21,14 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <include
- layout="@layout/raw_contact_editor_header"
- android:id="@+id/header" />
+ <FrameLayout
+ android:id="@+id/anchor_for_account_switcher"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <include
+ layout="@layout/raw_contact_editor_header"
+ android:id="@+id/header" />
+ </FrameLayout>
<LinearLayout
android:id="@+id/body"
@@ -67,25 +72,13 @@
android:layout_height="1px"
android:background="@color/contact_detail_header_divider_color" />
- <com.android.contacts.editor.StructuredNameEditorView
+ <include
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" />
+ layout="@layout/structured_name_editor_view" />
- <com.android.contacts.editor.PhoneticNameEditorView
+ <include
android:id="@+id/edit_phonetic_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" />
+ layout="@layout/phonetic_name_editor_view" />
<FrameLayout
android:layout_width="match_parent"
diff --git a/res/layout-xlarge/structured_name_editor_view.xml b/res/layout-xlarge/structured_name_editor_view.xml
new file mode 100644
index 0000000..37b5536
--- /dev/null
+++ b/res/layout-xlarge/structured_name_editor_view.xml
@@ -0,0 +1,58 @@
+<?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.
+-->
+
+<com.android.contacts.editor.StructuredNameEditorView
+ 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"
+ android:paddingRight="?android:attr/scrollbarSize"
+ android:layout_marginLeft="52dip"
+ android:layout_marginRight="48dip"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="4dip">
+
+ <include
+ android:id="@+id/spinner"
+ layout="@layout/edit_spinner"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:focusable="true"
+ android:clickable="true">
+
+ <include
+ android:id="@+id/editors"
+ layout="@layout/edit_field_list" />
+
+ <include
+ android:id="@+id/expansion_view_container"
+ layout="@layout/edit_expansion_view"
+ android:visibility="gone" />
+
+ <include
+ android:id="@+id/delete_button_container"
+ layout="@layout/edit_delete_button"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+</com.android.contacts.editor.StructuredNameEditorView>
diff --git a/res/layout-xlarge/text_fields_editor_view.xml b/res/layout-xlarge/text_fields_editor_view.xml
new file mode 100644
index 0000000..8be354b
--- /dev/null
+++ b/res/layout-xlarge/text_fields_editor_view.xml
@@ -0,0 +1,59 @@
+<?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.
+-->
+
+<com.android.contacts.editor.TextFieldsEditorView
+ 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">
+
+ <include
+ android:id="@+id/title"
+ layout="@layout/edit_field_title" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="top"
+ android:focusable="true"
+ android:clickable="true">
+
+ <include
+ android:id="@+id/editors"
+ layout="@layout/edit_field_list" />
+
+ <include
+ android:id="@+id/spinner"
+ layout="@layout/edit_spinner"
+ android:paddingTop="15dip"
+ android:visibility="gone" />
+
+ <include
+ android:id="@+id/expansion_view_container"
+ layout="@layout/edit_expansion_view"
+ android:visibility="gone" />
+
+ <include
+ android:id="@+id/delete_button_container"
+ layout="@layout/edit_delete_button"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+</com.android.contacts.editor.TextFieldsEditorView>
diff --git a/res/layout/aggregation_suggestions_item.xml b/res/layout/aggregation_suggestions_item.xml
index f2477cb..9ed1bf3 100644
--- a/res/layout/aggregation_suggestions_item.xml
+++ b/res/layout/aggregation_suggestions_item.xml
@@ -24,6 +24,8 @@
android:orientation="horizontal"
android:paddingLeft="5dip"
android:paddingRight="15dip"
+ android:background="?android:attr/selectableItemBackground"
+ android:focusable="true"
>
<ImageView
diff --git a/res/layout/recent_calls.xml b/res/layout/call_log_activity.xml
similarity index 63%
copy from res/layout/recent_calls.xml
copy to res/layout/call_log_activity.xml
index f054b70..b391795 100644
--- a/res/layout/recent_calls.xml
+++ b/res/layout/call_log_activity.xml
@@ -18,17 +18,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
>
- <ListView android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scrollbarStyle="outsideOverlay"
- />
-
- <TextView android:id="@android:id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="@string/recentCalls_empty"
- android:gravity="center"
- android:textAppearance="?android:attr/textAppearanceLarge"
- />
+ <fragment class="com.android.contacts.calllog.CallLogFragment"
+ android:id="@+id/call_log_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
</FrameLayout>
diff --git a/res/layout/recent_calls.xml b/res/layout/call_log_fragment.xml
similarity index 91%
rename from res/layout/recent_calls.xml
rename to res/layout/call_log_fragment.xml
index f054b70..2a27fcd 100644
--- a/res/layout/recent_calls.xml
+++ b/res/layout/call_log_fragment.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
+<!-- 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.
@@ -19,11 +19,11 @@
android:layout_height="match_parent"
>
<ListView android:id="@android:id/list"
- android:layout_width="match_parent"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarStyle="outsideOverlay"
/>
-
+
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/res/layout/recent_calls_list_child_item.xml b/res/layout/call_log_list_child_item.xml
similarity index 95%
rename from res/layout/recent_calls_list_child_item.xml
rename to res/layout/call_log_list_child_item.xml
index 527e259..bb48fcf 100644
--- a/res/layout/recent_calls_list_child_item.xml
+++ b/res/layout/call_log_list_child_item.xml
@@ -33,6 +33,6 @@
android:background="@drawable/call_background_secondary"
/>
- <include layout="@layout/recent_calls_list_item_layout"/>
+ <include layout="@layout/call_log_list_item_layout"/>
</RelativeLayout>
diff --git a/res/layout/recent_calls_list_group_item.xml b/res/layout/call_log_list_group_item.xml
similarity index 100%
rename from res/layout/recent_calls_list_group_item.xml
rename to res/layout/call_log_list_group_item.xml
diff --git a/res/layout/recent_calls_list_item.xml b/res/layout/call_log_list_item.xml
similarity index 95%
rename from res/layout/recent_calls_list_item.xml
rename to res/layout/call_log_list_item.xml
index 2c519d6..4ea0563 100644
--- a/res/layout/recent_calls_list_item.xml
+++ b/res/layout/call_log_list_item.xml
@@ -32,6 +32,6 @@
android:background="@drawable/call_background"
/>
- <include layout="@layout/recent_calls_list_item_layout"/>
+ <include layout="@layout/call_log_list_item_layout"/>
</RelativeLayout>
diff --git a/res/layout/recent_calls_list_item_layout.xml b/res/layout/call_log_list_item_layout.xml
similarity index 100%
rename from res/layout/recent_calls_list_item_layout.xml
rename to res/layout/call_log_list_item_layout.xml
diff --git a/res/layout/contact_browser.xml b/res/layout/contact_browser.xml
index 0b4bb63..8affd46 100644
--- a/res/layout/contact_browser.xml
+++ b/res/layout/contact_browser.xml
@@ -18,4 +18,25 @@
android:id="@+id/list_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <FrameLayout
+ android:id="@+id/main_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <fragment
+ android:id="@+id/list_fragment"
+ class="com.android.contacts.list.DefaultContactBrowseListFragment"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ />
+ </FrameLayout>
+ <FrameLayout
+ 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" />
+ </FrameLayout>
</FrameLayout>
diff --git a/res/layout/contact_detail_fragment.xml b/res/layout/contact_detail_fragment.xml
index 90a075a..70a9a28 100644
--- a/res/layout/contact_detail_fragment.xml
+++ b/res/layout/contact_detail_fragment.xml
@@ -20,16 +20,12 @@
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"
+ android:background="@color/background_primary"
+ android:divider="@null"
/>
<ScrollView android:id="@android:id/empty"
diff --git a/res/layout/contact_detail_header_view.xml b/res/layout/contact_detail_header_view.xml
index 3248920..80dcacc 100644
--- a/res/layout/contact_detail_header_view.xml
+++ b/res/layout/contact_detail_header_view.xml
@@ -14,46 +14,54 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout 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">
+ android:layout_height="150dip">
<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"
- />
+ android:scaleType="centerCrop"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ />
+
+ <!-- Transparent view to overlay on the contact's photo
+ (to allow white text to appear over a white photo). -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:background="#000000"
+ android:alpha=".25"
+ />
<LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:layout_gravity="center_vertical" >
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentLeft="true"
+ android:layout_marginLeft="10dip"
+ android:orientation="vertical" >
<TextView android:id="@+id/name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_height="0dip"
+ android:layout_weight="1"
android:singleLine="true"
android:ellipsize="end"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="bottom"
+ android:textSize="@dimen/detail_header_name_text_size"
+ android:textColor="@color/detail_header_view_text_color"
android:textStyle="bold"
- android:shadowColor="#BB000000"
- android:shadowRadius="2.75"
/>
<TextView android:id="@+id/phonetic_name"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/detail_header_view_text_color"
android:singleLine="true"
android:ellipsize="end"
android:layout_marginTop="-2dip"
@@ -61,9 +69,10 @@
/>
<TextView android:id="@+id/organization"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/detail_header_view_text_color"
android:singleLine="true"
android:ellipsize="end"
android:layout_marginTop="-2dip"
@@ -71,7 +80,7 @@
/>
<TextView android:id="@+id/attribution"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
@@ -82,9 +91,10 @@
/>
<TextView android:id="@+id/status"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/detail_header_view_text_color"
android:singleLine="true"
android:ellipsize="end"
android:layout_marginTop="-2dip"
@@ -92,9 +102,8 @@
/>
<TextView android:id="@+id/status_date"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="12sp"
android:layout_marginTop="-2dip"
@@ -106,9 +115,13 @@
android:id="@+id/star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
android:layout_gravity="center_vertical"
android:contentDescription="@string/description_star"
android:visibility="invisible"
style="?android:attr/starStyle"
/>
-</LinearLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/contact_detail_header_view_list_item.xml b/res/layout/contact_detail_header_view_list_item.xml
new file mode 100644
index 0000000..cf149df
--- /dev/null
+++ b/res/layout/contact_detail_header_view_list_item.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<com.android.contacts.detail.ContactDetailHeaderView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/contact_header_widget"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
\ No newline at end of file
diff --git a/res/layout/contact_detail_list_item.xml b/res/layout/contact_detail_list_item.xml
index 13b07bb..2e4a3e5 100644
--- a/res/layout/contact_detail_list_item.xml
+++ b/res/layout/contact_detail_list_item.xml
@@ -21,114 +21,71 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:paddingLeft="@dimen/detail_item_side_margin"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
- <!-- Seperator that shows the kind -->
<LinearLayout
- android:id="@+id/kind_divider"
- android:layout_width="match_parent"
+ android:layout_width="0dip"
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" />
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:gravity="center_vertical">
+
<TextView
android:id="@+id/kind"
- android:gravity="center_vertical"
- android:layout_width="match_parent"
- android:layout_height="31dip"
- android:paddingLeft="16dip"
+ style="@style/ContactDetailItemType" />
+
+ <TextView
+ android:id="@+id/type"
+ style="@style/ContactDetailItemType" />
+
+ <TextView
+ android:id="@+id/data"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingBottom="5dip"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <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:textColor="?android:attr/textColorTertiary" />
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="1px"
- android:background="@drawable/list_item_divider_holo" />
+ android:visibility="gone" />
</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:id="@+id/presence_icon"
+ android:layout_width="32dip"
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">
+ android:layout_marginLeft="5dip"
+ android:gravity="center"
+ android:scaleType="centerInside" />
- <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" />
+ <View
+ android:id="@+id/divider"
+ android:layout_width="1px"
+ android:layout_height="match_parent"
+ android:layout_marginTop="15dip"
+ android:layout_marginBottom="10dip"
+ android:background="?android:attr/dividerVertical" />
- <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" />
-
+ <FrameLayout
+ android:id="@+id/secondary_action_button_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_marginTop="5dip"
+ android:paddingLeft="@dimen/detail_item_icon_margin"
+ android:paddingRight="@dimen/detail_item_icon_margin"
+ android:duplicateParentState="false"
+ android:background="?android:attr/selectableItemBackground">
<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>
+ android:duplicateParentState="false" />
+ </FrameLayout>
</LinearLayout>
diff --git a/res/layout/contact_detail_separator_list_item.xml b/res/layout/contact_detail_separator_list_item.xml
new file mode 100644
index 0000000..87f0597
--- /dev/null
+++ b/res/layout/contact_detail_separator_list_item.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.
+-->
+
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dip"
+ android:background="@drawable/divider_horizontal_light"
+ />
\ No newline at end of file
diff --git a/res/layout/contact_editor_activity.xml b/res/layout/contact_editor_activity.xml
index 630e82c..8c13629 100644
--- a/res/layout/contact_editor_activity.xml
+++ b/res/layout/contact_editor_activity.xml
@@ -22,21 +22,4 @@
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
index 602f7f9..f3989e1 100644
--- a/res/layout/contact_editor_fragment.xml
+++ b/res/layout/contact_editor_fragment.xml
@@ -17,6 +17,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="@color/background_primary"
>
<ScrollView
diff --git a/res/layout/twelve_key_dialer.xml b/res/layout/dialpad_activity.xml
similarity index 91%
rename from res/layout/twelve_key_dialer.xml
rename to res/layout/dialpad_activity.xml
index d4c9d8f..2cae9ae 100644
--- a/res/layout/twelve_key_dialer.xml
+++ b/res/layout/dialpad_activity.xml
@@ -14,11 +14,14 @@
limitations under the License.
-->
+<!-- TODO (stopship) We don't want to specify a background color here. For now we just
+keep it because otherwise the dialer needs some imagination to use (white on white) -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/top"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
+ android:background="@android:color/black"
>
<!-- Text field above the keypad where the digits are displayed.
diff --git a/res/layout/dialer_activity.xml b/res/layout/dialtacts_activity.xml
similarity index 100%
rename from res/layout/dialer_activity.xml
rename to res/layout/dialtacts_activity.xml
diff --git a/res/layout/edit_add_field.xml b/res/layout/edit_add_field.xml
new file mode 100644
index 0000000..1c8c740
--- /dev/null
+++ b/res/layout/edit_add_field.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+<!-- Layout of "add field" row in contact editor -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="13dip"
+ android:background="?android:attr/selectableItemBackground">
+ <TextView
+ android:id="@+id/add_text"
+ android:layout_gravity="center_vertical"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary" />
+ <ImageView
+ android:id="@+id/add_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ 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:contentDescription="@string/description_plus_button" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/edit_date_picker.xml b/res/layout/edit_date_picker.xml
new file mode 100644
index 0000000..ca5e281
--- /dev/null
+++ b/res/layout/edit_date_picker.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<!-- Button to select a date in the contact editor. -->
+
+<Button
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/date_view"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/editor_field_left_padding"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ style="@android:style/Widget.Holo.Light.Spinner" />
\ No newline at end of file
diff --git a/res/layout/edit_delete_button.xml b/res/layout/edit_delete_button.xml
new file mode 100644
index 0000000..f05b0e4
--- /dev/null
+++ b/res/layout/edit_delete_button.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+
+<!-- "Delete field" button in the contact editor. -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top">
+ <ImageView
+ android:id="@+id/delete_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:duplicateParentState="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:src="@drawable/ic_menu_remove_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_minus_button" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/edit_divider.xml b/res/layout/edit_divider.xml
index eb2a49a..786bfca 100644
--- a/res/layout/edit_divider.xml
+++ b/res/layout/edit_divider.xml
@@ -16,6 +16,6 @@
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="1dip"
android:background="?android:attr/listDivider"
/>
diff --git a/res/layout/edit_expansion_view.xml b/res/layout/edit_expansion_view.xml
new file mode 100644
index 0000000..96fe8de
--- /dev/null
+++ b/res/layout/edit_expansion_view.xml
@@ -0,0 +1,34 @@
+<?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.
+-->
+
+<!-- "More" or "less" expansion button in the contact editor. -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top">
+ <ImageView
+ android:id="@+id/expansion_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:duplicateParentState="true"
+ android:background="?android:attr/selectableItemBackground"
+ 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" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/edit_field_list.xml b/res/layout/edit_field_list.xml
new file mode 100644
index 0000000..ba715c7
--- /dev/null
+++ b/res/layout/edit_field_list.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.
+-->
+
+<!-- Layout to contain a list of fields in the contact editor. -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/editors"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/editor_field_left_padding"
+ android:orientation="vertical" />
\ No newline at end of file
diff --git a/res/layout/edit_field_title.xml b/res/layout/edit_field_title.xml
new file mode 100644
index 0000000..4918c4d
--- /dev/null
+++ b/res/layout/edit_field_title.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<!-- Title of a field in the contact editor. -->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/editor_field_top_padding"
+ android:paddingLeft="@dimen/editor_field_left_padding"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
\ No newline at end of file
diff --git a/res/layout/edit_spinner.xml b/res/layout/edit_spinner.xml
new file mode 100644
index 0000000..9e22ee7
--- /dev/null
+++ b/res/layout/edit_spinner.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.
+-->
+
+<!-- Spinner for a field in the contact editor. -->
+
+<Spinner
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/spinner"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/editor_field_left_padding"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:paddingLeft="5dip"/>
\ No newline at end of file
diff --git a/res/layout/event_field_editor_view.xml b/res/layout/event_field_editor_view.xml
new file mode 100644
index 0000000..6625279
--- /dev/null
+++ b/res/layout/event_field_editor_view.xml
@@ -0,0 +1,54 @@
+<?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.
+-->
+
+<!-- Editor for a single event entry in the contact editor -->
+
+<com.android.contacts.editor.EventFieldEditorView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include
+ android:id="@+id/title"
+ layout="@layout/edit_field_title" />
+
+ <include
+ android:id="@+id/spinner"
+ layout="@layout/edit_spinner"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:focusable="true"
+ android:clickable="true">
+
+ <include
+ android:id="@+id/date_view"
+ layout="@layout/edit_date_picker" />
+
+ <include
+ android:id="@+id/delete_button_container"
+ layout="@layout/edit_delete_button"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+</com.android.contacts.editor.EventFieldEditorView>
diff --git a/res/layout/external_raw_contact_editor_view.xml b/res/layout/external_raw_contact_editor_view.xml
index a964ce4..c524a74 100644
--- a/res/layout/external_raw_contact_editor_view.xml
+++ b/res/layout/external_raw_contact_editor_view.xml
@@ -51,7 +51,7 @@
android:background="@color/edit_divider"
/>
- <ImageView android:id="@+id/header_icon"
+ <ImageView android:id="@+id/account_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="7dip"
@@ -60,11 +60,11 @@
android:layout_below="@id/header_color_bar"
/>
- <TextView android:id="@+id/header_account_type"
+ <TextView android:id="@+id/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_toRightOf="@+id/account_icon"
+ android:layout_alignTop="@id/account_icon"
android:layout_marginTop="-4dip"
android:textSize="24sp"
@@ -72,11 +72,11 @@
android:singleLine="true"
/>
- <TextView android:id="@+id/header_account_name"
+ <TextView android:id="@+id/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_toRightOf="@+id/account_icon"
+ android:layout_alignBottom="@+id/account_icon"
android:layout_marginBottom="2dip"
android:textAppearance="?android:attr/textAppearanceSmall"
diff --git a/res/layout/item_group_membership.xml b/res/layout/item_group_membership.xml
index 65730d4..cdf4e8a 100644
--- a/res/layout/item_group_membership.xml
+++ b/res/layout/item_group_membership.xml
@@ -21,11 +21,6 @@
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"
diff --git a/res/layout/item_kind_section.xml b/res/layout/item_kind_section.xml
index fdb55c9..6c6f960 100644
--- a/res/layout/item_kind_section.xml
+++ b/res/layout/item_kind_section.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.
@@ -20,59 +20,22 @@
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:paddingBottom="@dimen/editor_field_bottom_padding"
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" />
-
- <ImageButton
- android:id="@+id/kind_plus"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:duplicateParentState="true"
- 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>
+ android:background="@drawable/divider_horizontal_light" />
<LinearLayout
android:id="@+id/kind_editors"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="6dip"
android:orientation="vertical" />
-</com.android.contacts.editor.KindSectionView>
+ <include
+ android:id="@+id/add_field_footer"
+ layout="@layout/edit_add_field" />
+
+</com.android.contacts.editor.KindSectionView>
\ No newline at end of file
diff --git a/res/layout/item_photo_editor.xml b/res/layout/item_photo_editor.xml
index 642908e..3590963 100644
--- a/res/layout/item_photo_editor.xml
+++ b/res/layout/item_photo_editor.xml
@@ -27,7 +27,7 @@
android:src="@drawable/ic_contact_picture"
android:cropToPadding="true"
android:scaleType="centerCrop"
- android:gravity="center"
+ android:gravity="left"
/>
<View
android:id="@+id/frame"
diff --git a/res/layout/list_section.xml b/res/layout/list_section.xml
index e920673..5265f88 100644
--- a/res/layout/list_section.xml
+++ b/res/layout/list_section.xml
@@ -18,8 +18,7 @@
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="25dip"
- android:background="@drawable/section_header"
+ android:layout_height="@dimen/list_section_height"
>
<TextView
android:id="@+id/header_text"
diff --git a/res/layout/phonetic_name_editor_view.xml b/res/layout/phonetic_name_editor_view.xml
new file mode 100644
index 0000000..832ca2c
--- /dev/null
+++ b/res/layout/phonetic_name_editor_view.xml
@@ -0,0 +1,54 @@
+<?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.
+-->
+
+<com.android.contacts.editor.PhoneticNameEditorView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/editor_field_bottom_padding"
+ android:orientation="vertical">
+
+ <include
+ android:id="@+id/spinner"
+ layout="@layout/edit_spinner"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:focusable="true"
+ android:clickable="true">
+
+ <include
+ android:id="@+id/editors"
+ layout="@layout/edit_field_list" />
+
+ <include
+ android:id="@+id/expansion_view_container"
+ layout="@layout/edit_expansion_view"
+ android:visibility="gone" />
+
+ <include
+ android:id="@+id/delete_button_container"
+ layout="@layout/edit_delete_button"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+</com.android.contacts.editor.PhoneticNameEditorView>
diff --git a/res/layout/quickcontact.xml b/res/layout/quickcontact.xml
index a74424c..e2b291c 100644
--- a/res/layout/quickcontact.xml
+++ b/res/layout/quickcontact.xml
@@ -1,3 +1,4 @@
+<?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");
@@ -12,13 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<view
+<LinearLayout
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:id="@android:id/content"
+ android:layout_width="@dimen/quick_contact_width"
android:layout_height="wrap_content"
+ android:visibility="invisible"
android:orientation="vertical">
<FrameLayout
@@ -133,4 +133,4 @@
android:text="@string/quickcontact_clear_defaults_button" />
</LinearLayout>
</FrameLayout>
-</view>
+</LinearLayout>
diff --git a/res/layout/quickcontact_activity.xml b/res/layout/quickcontact_activity.xml
new file mode 100644
index 0000000..aced4a8
--- /dev/null
+++ b/res/layout/quickcontact_activity.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+<view
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.contacts.quickcontact.FloatingChildLayout"
+ android:id="@+id/floating_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:descendantFocusability="afterDescendants">
+
+ <include layout="@layout/quickcontact" />
+
+</view>
diff --git a/res/layout/quickcontact_default_item.xml b/res/layout/quickcontact_default_item.xml
index 25b6910..3a918f0 100755
--- a/res/layout/quickcontact_default_item.xml
+++ b/res/layout/quickcontact_default_item.xml
@@ -28,13 +28,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
- android:textAppearance="?android:attr/textAppearanceMediumInverse" />
+ android:textAppearance="?android:attr/textAppearanceMedium" />
<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" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
diff --git a/res/layout/quickcontact_resolve_item.xml b/res/layout/quickcontact_resolve_item.xml
index 55de80e..2805722 100755
--- a/res/layout/quickcontact_resolve_item.xml
+++ b/res/layout/quickcontact_resolve_item.xml
@@ -28,13 +28,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
- android:textAppearance="?android:attr/textAppearanceMediumInverse" />
+ android:textAppearance="?android:attr/textAppearanceMedium" />
<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" />
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
diff --git a/res/layout/raw_contact_editor_view.xml b/res/layout/raw_contact_editor_view.xml
index ac30a6b..7c027eb 100644
--- a/res/layout/raw_contact_editor_view.xml
+++ b/res/layout/raw_contact_editor_view.xml
@@ -21,96 +21,77 @@
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"
+ <LinearLayout
+ android:id="@+id/anchor_for_account_switcher"
android:layout_height="wrap_content"
- android:paddingLeft="12dip"
- android:paddingTop="10dip">
+ android:layout_width="match_parent"
+ android:layout_marginRight="4dip"
+ android:background="@color/account_selection_background"
+ android:orientation="horizontal"
+ android:gravity="left|center_vertical">
- <include
- android:id="@+id/edit_photo"
- layout="@layout/item_photo_editor" />
+ <FrameLayout
+ android:id="@+id/stub_photo"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
- </FrameLayout>
+ <include
+ android:id="@+id/edit_photo"
+ layout="@layout/item_photo_editor" />
- <com.android.contacts.editor.StructuredNameEditorView
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/account"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:background="@color/account_selection_background"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/account_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="20sp"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ android:singleLine="true" />
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/account_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ android:singleLine="true" />
+
+ <ImageView
+ android:id="@+id/account_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+
+
+ <include
android:id="@+id/edit_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingRight="?android:attr/scrollbarSize"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="4dip" />
+ layout="@layout/structured_name_editor_view" />
- <com.android.contacts.editor.PhoneticNameEditorView
+ <include
android:id="@+id/edit_phonetic_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingRight="?android:attr/scrollbarSize"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="4dip" />
+ layout="@layout/phonetic_name_editor_view" />
<ViewStub android:id="@+id/aggregation_suggestion_stub"
android:inflatedId="@+id/aggregation_suggestion"
diff --git a/res/layout/structured_name_editor_view.xml b/res/layout/structured_name_editor_view.xml
new file mode 100644
index 0000000..83efa6f
--- /dev/null
+++ b/res/layout/structured_name_editor_view.xml
@@ -0,0 +1,56 @@
+<?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.
+-->
+
+<com.android.contacts.editor.StructuredNameEditorView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include
+ android:id="@+id/divider"
+ layout="@layout/edit_divider" />
+
+ <include
+ android:id="@+id/spinner"
+ layout="@layout/edit_spinner"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:focusable="true"
+ android:clickable="true">
+
+ <include
+ android:id="@+id/editors"
+ layout="@layout/edit_field_list" />
+
+ <include
+ android:id="@+id/expansion_view_container"
+ layout="@layout/edit_expansion_view"
+ android:visibility="gone" />
+
+ <include
+ android:id="@+id/delete_button_container"
+ layout="@layout/edit_delete_button"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+</com.android.contacts.editor.StructuredNameEditorView>
diff --git a/res/layout/text_fields_editor_view.xml b/res/layout/text_fields_editor_view.xml
new file mode 100644
index 0000000..d33e804
--- /dev/null
+++ b/res/layout/text_fields_editor_view.xml
@@ -0,0 +1,57 @@
+<?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.
+-->
+
+<com.android.contacts.editor.TextFieldsEditorView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include
+ android:id="@+id/title"
+ layout="@layout/edit_field_title" />
+
+ <include
+ android:id="@+id/spinner"
+ layout="@layout/edit_spinner"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:focusable="true"
+ android:clickable="true">
+
+ <include
+ android:id="@+id/editors"
+ layout="@layout/edit_field_list" />
+
+ <include
+ android:id="@+id/expansion_view_container"
+ layout="@layout/edit_expansion_view"
+ android:visibility="gone" />
+
+ <include
+ android:id="@+id/delete_button_container"
+ layout="@layout/edit_delete_button"
+ android:visibility="gone" />
+
+ </LinearLayout>
+
+</com.android.contacts.editor.TextFieldsEditorView>
diff --git a/res/menu/list.xml b/res/menu/list.xml
index 2b3f453..56d92f8 100644
--- a/res/menu/list.xml
+++ b/res/menu/list.xml
@@ -27,9 +27,9 @@
android:alphabeticShortcut="n" />
<item
- android:id="@+id/menu_display_groups"
- android:icon="@*android:drawable/ic_menu_allfriends"
- android:title="@string/menu_displayGroup" />
+ android:id="@+id/menu_settings"
+ android:icon="@drawable/ic_menu_settings_holo_light"
+ android:title="@string/menu_settings" />
<item
android:id="@+id/menu_accounts"
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index dcca0f1..6be4c49 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"سجل المكالمات فارغ."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"محو سجل المكالمات"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"هل أنت متأكد من أنك تريد محو سجل المكالمات؟"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"جارٍ محو سجل المكالمات"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"البريد الصوتي"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"لم يتم تحديد جهات اتصال."</string>
<string name="add_field" msgid="2384260056674995230">"إضافة حقل آخر"</string>
+ <string name="add_phone" msgid="4421904942555210013">"إضافة رقم هاتف جديد"</string>
+ <string name="add_email" msgid="175079666329862215">"إضافة بريد إلكتروني جديد"</string>
+ <string name="add_im" msgid="5158094627521120439">"إضافة حساب مراسلة فورية جديد"</string>
+ <string name="add_address" msgid="418292312672970688">"إضافة عنوان جديد"</string>
+ <string name="add_note" msgid="2753771325725383279">"إضافة ملاحظة جديدة"</string>
+ <string name="add_website" msgid="4312391288948517344">"إضافة موقع إلكتروني جديد"</string>
+ <string name="add_event" msgid="7488781591843886426">"إضافة حدث جديد"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"إضافة علاقة جديدة"</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>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 6f62119..7aa2c9f 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Списъкът на обажданията е празен."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Изчистване на списъка с обажданията"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Наистина ли искате да изчистите списъка на обажданията?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Списъкът с обажданията се изчиства"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Гласова поща"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"Няма избрани контакти."</string>
<string name="add_field" msgid="2384260056674995230">"Добавяне на друго поле"</string>
+ <string name="add_phone" msgid="4421904942555210013">"+ Нов телефонен номер"</string>
+ <string name="add_email" msgid="175079666329862215">"+ Нов имейл"</string>
+ <string name="add_im" msgid="5158094627521120439">"+ Нов профил за чат"</string>
+ <string name="add_address" msgid="418292312672970688">"+ Нов адрес"</string>
+ <string name="add_note" msgid="2753771325725383279">"+ Нова бележка"</string>
+ <string name="add_website" msgid="4312391288948517344">"+ Нов уебсайт"</string>
+ <string name="add_event" msgid="7488781591843886426">"+ Ново събитие"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"+ Ново взаимоотношение"</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>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 31de36e..258ce97 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"El registre de trucades és buit."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Esborrament del registre de trucades"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Esteu segur que voleu esborrar el registre de trucades?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Esborrant el registre de trucada"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Correu de veu"</string>
@@ -431,6 +432,14 @@
</plurals>
<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="add_phone" msgid="4421904942555210013">"Afegeix núm. tel. nou"</string>
+ <string name="add_email" msgid="175079666329862215">"Afegeix correu el. nou"</string>
+ <string name="add_im" msgid="5158094627521120439">"Afegeix compte MI nou"</string>
+ <string name="add_address" msgid="418292312672970688">"Afegeix adreça nova"</string>
+ <string name="add_note" msgid="2753771325725383279">"Afegeix nota nova"</string>
+ <string name="add_website" msgid="4312391288948517344">"Afegeix lloc web nou"</string>
+ <string name="add_event" msgid="7488781591843886426">"Afegeix esdevenim. nou"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Afegeix relació nova"</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>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index b4ec252..9e4e49f 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Záznam hovorů je prázdný."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Vymazat hovory"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Opravdu chcete vymazat hovory?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Vymazání hovorů"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Hlasová schránka"</string>
@@ -431,6 +432,14 @@
</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="add_phone" msgid="4421904942555210013">"Přidat nové tel. číslo"</string>
+ <string name="add_email" msgid="175079666329862215">"Přidat nový e-mail"</string>
+ <string name="add_im" msgid="5158094627521120439">"Přidat nový účet IM"</string>
+ <string name="add_address" msgid="418292312672970688">"Přidat novou adresu"</string>
+ <string name="add_note" msgid="2753771325725383279">"Přidat novou poznámku"</string>
+ <string name="add_website" msgid="4312391288948517344">"Přidat nový web"</string>
+ <string name="add_event" msgid="7488781591843886426">"Přidat novou událost"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Přidat nový vztah"</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>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 92d2477..f8a778e 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Opkaldslisten er tom."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Ryd opkaldsliste"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Er du sikker på, at du vil rydde opkaldslisten?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Rydder opkaldsliste"</string>
<string name="imei" msgid="3045126336951684285">"IMEI-nummer"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
@@ -431,6 +432,14 @@
</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="add_phone" msgid="4421904942555210013">"Tilføj nyt telefonnr."</string>
+ <string name="add_email" msgid="175079666329862215">"Tilføj ny e-mail"</string>
+ <string name="add_im" msgid="5158094627521120439">"Tilføj ny IM-konto"</string>
+ <string name="add_address" msgid="418292312672970688">"Tilføj ny adresse"</string>
+ <string name="add_note" msgid="2753771325725383279">"Tilføj ny bemærkning"</string>
+ <string name="add_website" msgid="4312391288948517344">"Tilføj nyt websted"</string>
+ <string name="add_event" msgid="7488781591843886426">"Tilføj ny begivenhed"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Tilføj nyt forhold"</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>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 8fad512..2a32026 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Anrufliste ist leer"</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Anrufprotokoll löschen"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Möchten Sie das Anrufprotokoll wirklich löschen?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Anrufprotokoll wird gelöscht."</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Mailbox"</string>
@@ -431,6 +432,14 @@
</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="add_phone" msgid="4421904942555210013">"Neue Telefonnummer"</string>
+ <string name="add_email" msgid="175079666329862215">"Neue E-Mail-Adresse"</string>
+ <string name="add_im" msgid="5158094627521120439">"Chat-Konto hinzufügen"</string>
+ <string name="add_address" msgid="418292312672970688">"Adresse hinzufügen"</string>
+ <string name="add_note" msgid="2753771325725383279">"Neue Notiz hinzufügen"</string>
+ <string name="add_website" msgid="4312391288948517344">"Website hinzufügen"</string>
+ <string name="add_event" msgid="7488781591843886426">"Ereignis hinzufügen"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Beziehung 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>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index ce14e4d..836f7d3 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Το αρχείο καταγραφής κλήσεων είναι κενό."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Εκκαθάριση αρχείου καταγραφής κλήσεων"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Είστε βέβαιοι ότι θέλετε να γίνει εκκαθάριση του αρχείου καταγραφής κλήσεων;"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Εκκαθάριση αρχείου καταγρ. κλήσεων"</string>
<string name="imei" msgid="3045126336951684285">"Αριθμός ΙΜΕΙ"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Αυτόματος τηλεφωνητής"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"Δεν έχουν επιλεγεί επαφές."</string>
<string name="add_field" msgid="2384260056674995230">"Προσθήκη άλλου πεδίου"</string>
+ <string name="add_phone" msgid="4421904942555210013">"Προσθ. νέου αριθ. τηλ."</string>
+ <string name="add_email" msgid="175079666329862215">"Προσθ.νέας διεύθ.email"</string>
+ <string name="add_im" msgid="5158094627521120439">"Προσθήκη νέου λογ. IM"</string>
+ <string name="add_address" msgid="418292312672970688">"Προσθήκη νέας διεύθ."</string>
+ <string name="add_note" msgid="2753771325725383279">"Προσθ. νέας σημείωσης"</string>
+ <string name="add_website" msgid="4312391288948517344">"Προσθ. νέου ιστότοπου"</string>
+ <string name="add_event" msgid="7488781591843886426">"Προσθ. νέου συμβάντος"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Προσθήκη νέας σχέσης"</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>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 551be6d..5a0cb53 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Call log is empty."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Clear call log"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Are you sure that you want to clear the call log?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Clearing call log"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"No contacts selected."</string>
<string name="add_field" msgid="2384260056674995230">"Add another field"</string>
+ <string name="add_phone" msgid="4421904942555210013">"Add new phone number"</string>
+ <string name="add_email" msgid="175079666329862215">"Add new email"</string>
+ <string name="add_im" msgid="5158094627521120439">"Add new IM account"</string>
+ <string name="add_address" msgid="418292312672970688">"Add new address"</string>
+ <string name="add_note" msgid="2753771325725383279">"Add new note"</string>
+ <string name="add_website" msgid="4312391288948517344">"Add new website"</string>
+ <string name="add_event" msgid="7488781591843886426">"Add new event"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Add new relationship"</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>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 1dfa06c..25145e6 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"El registro de llamadas está vacío."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Borrar registro de llamadas"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"¿Estás seguro de que deseas borrar el registro de llamadas?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Borrando el registro de llamadas"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Correo de voz"</string>
@@ -431,6 +432,14 @@
</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="add_phone" msgid="4421904942555210013">"Agregar nuevo número de teléfono"</string>
+ <string name="add_email" msgid="175079666329862215">"Agregar nuevo correo"</string>
+ <string name="add_im" msgid="5158094627521120439">"Agregar nueva cuenta de MI"</string>
+ <string name="add_address" msgid="418292312672970688">"Agregar nueva dirección"</string>
+ <string name="add_note" msgid="2753771325725383279">"Agregar nueva nota"</string>
+ <string name="add_website" msgid="4312391288948517344">"Agregar nuevo sitio web"</string>
+ <string name="add_event" msgid="7488781591843886426">"Agregar nuevo evento"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Agregar nueva relación"</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>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index c9dcac8..59d3745 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"El registro de llamadas está vacío."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Borrar registro de llamadas"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"¿Estás seguro de que quieres borrar el registro de llamadas?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Borrando registro de llamadas"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Buzón de voz"</string>
@@ -431,6 +432,14 @@
</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="add_phone" msgid="4421904942555210013">"Añadir nuevo teléfono"</string>
+ <string name="add_email" msgid="175079666329862215">"Añadir nuevo correo"</string>
+ <string name="add_im" msgid="5158094627521120439">"Añadir nueva cuenta MI"</string>
+ <string name="add_address" msgid="418292312672970688">"Añadir nueva dirección"</string>
+ <string name="add_note" msgid="2753771325725383279">"Añadir nueva nota"</string>
+ <string name="add_website" msgid="4312391288948517344">"Añadir nuevo sitio web"</string>
+ <string name="add_event" msgid="7488781591843886426">"Añadir nuevo evento"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Añadir nueva relación"</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>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 627d3eb..6a2b447 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"گزارش تماس خالی است."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"پاک کردن گزارش تماس"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"آیا مطمئن هستید که می خواهید گزارش تماس را پاک کنید؟"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"پاک کردن گزارش تماس"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"پست صوتی"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"مخاطبی انتخاب نشده است."</string>
<string name="add_field" msgid="2384260056674995230">"افزودن یک قسمت دیگر"</string>
+ <string name="add_phone" msgid="4421904942555210013">"افزودن شماره تلفن جدید"</string>
+ <string name="add_email" msgid="175079666329862215">"افزودن ایمیل جدید"</string>
+ <string name="add_im" msgid="5158094627521120439">"افزودن حساب IM جدید"</string>
+ <string name="add_address" msgid="418292312672970688">"افزودن آدرس جدید"</string>
+ <string name="add_note" msgid="2753771325725383279">"افزودن یادداشت جدید"</string>
+ <string name="add_website" msgid="4312391288948517344">"افزودن وب سایت جدید"</string>
+ <string name="add_event" msgid="7488781591843886426">"افزودن رویداد جدید"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"افزودن رابطه جدید"</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>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 6a9586c..4abc9bc 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Puheluloki on tyhjä."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Tyhjennä puheluloki"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Haluatko varmasti tyhjentää puhelulokin?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Tyhjennetään puhelulokia"</string>
<string name="imei" msgid="3045126336951684285">"IMEI-koodi"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Vastaaja"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"Ei valittuja yhteystietoja."</string>
<string name="add_field" msgid="2384260056674995230">"Lisää toinen kenttä"</string>
+ <string name="add_phone" msgid="4421904942555210013">"Lisää uusi puh.numero"</string>
+ <string name="add_email" msgid="175079666329862215">"Lisää uusi sähköposti"</string>
+ <string name="add_im" msgid="5158094627521120439">"Lisää uusi pikav.tili"</string>
+ <string name="add_address" msgid="418292312672970688">"Lisää uusi osoite"</string>
+ <string name="add_note" msgid="2753771325725383279">"Lisää uusi huomautus"</string>
+ <string name="add_website" msgid="4312391288948517344">"Lisää uusi sivusto"</string>
+ <string name="add_event" msgid="7488781591843886426">"Lisää uusi tapahtuma"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Lisää uusi suhde"</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>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index f71ef11..333ae0d 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Aucun appel."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Effacer le journal d\'appels"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Voulez-vous vraiment effacer le journal d\'appels ?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Suppression du journal d\'appels"</string>
<string name="imei" msgid="3045126336951684285">"Code IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Messagerie vocale"</string>
@@ -161,12 +162,12 @@
<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>
- <!-- 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@4e106082) -->
- <!-- 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@15301ed8) -->
+ <!-- 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@1948cc8c) -->
+ <!-- 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@7a6d084b) -->
<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@a3901c6) -->
- <!-- 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@24a37368) -->
+ <!-- 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@c3bb2b8) -->
+ <!-- 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@2352544e) -->
<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>
@@ -431,6 +432,14 @@
</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="add_phone" msgid="4421904942555210013">"Ajouter un numéro"</string>
+ <string name="add_email" msgid="175079666329862215">"Ajouter adresse e-mail"</string>
+ <string name="add_im" msgid="5158094627521120439">"Ajouter compte chat"</string>
+ <string name="add_address" msgid="418292312672970688">"Ajouter une adresse"</string>
+ <string name="add_note" msgid="2753771325725383279">"Ajouter une remarque"</string>
+ <string name="add_website" msgid="4312391288948517344">"Ajouter un site Web"</string>
+ <string name="add_event" msgid="7488781591843886426">"Ajouter un évènement"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Ajouter une relation"</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>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 3f06446..36aa9de 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Zapisnik poziva je prazan"</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Izbriši zapisnik poziva"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Jeste li sigurni da želite izbrisati zapisnik poziva?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Brisanje zapisnika poziva"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Govorna pošta"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"Nije odabran nijedan kontakt."</string>
<string name="add_field" msgid="2384260056674995230">"Dodaj drugo polje"</string>
+ <string name="add_phone" msgid="4421904942555210013">"Dodajte novi tel. broj"</string>
+ <string name="add_email" msgid="175079666329862215">"Dodajte novu e-poštu"</string>
+ <string name="add_im" msgid="5158094627521120439">"Dodajte novi IM račun"</string>
+ <string name="add_address" msgid="418292312672970688">"Dodajte novu adresu"</string>
+ <string name="add_note" msgid="2753771325725383279">"Dodajte novu napomenu"</string>
+ <string name="add_website" msgid="4312391288948517344">"Dodajte novu stranicu"</string>
+ <string name="add_event" msgid="7488781591843886426">"Dodajte novi događaj"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Dodajte novi odnos"</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>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index d7427e4..c15b051 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"A híváslista üres."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Híváslista törlése"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Biztosan törli a híváslistát?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Hívási napló törölve"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Hangposta"</string>
@@ -431,6 +432,14 @@
</plurals>
<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="add_phone" msgid="4421904942555210013">"Új telefonszám"</string>
+ <string name="add_email" msgid="175079666329862215">"Új e-mail hozzáadása"</string>
+ <string name="add_im" msgid="5158094627521120439">"Új IM-fiók hozzáadása"</string>
+ <string name="add_address" msgid="418292312672970688">"Új cím hozzáadása"</string>
+ <string name="add_note" msgid="2753771325725383279">"Új megjegyzés"</string>
+ <string name="add_website" msgid="4312391288948517344">"Új webhely hozzáadása"</string>
+ <string name="add_event" msgid="7488781591843886426">"Új esemény hozzáadása"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Új kapcsolat"</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>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index c031cac..27dae5c 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Log panggilan kosong."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Hapus log panggilan"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Apakah Anda yakin ingin menghapus log panggilan?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Membersihkan log panggilan"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Kotak Pesan"</string>
@@ -431,6 +432,14 @@
</plurals>
<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="add_phone" msgid="4421904942555210013">"Menambah no. tlp. baru"</string>
+ <string name="add_email" msgid="175079666329862215">"Menambah email baru"</string>
+ <string name="add_im" msgid="5158094627521120439">"Menambah akun IM baru"</string>
+ <string name="add_address" msgid="418292312672970688">"Menambah alamat baru"</string>
+ <string name="add_note" msgid="2753771325725383279">"Menambah catatan baru"</string>
+ <string name="add_website" msgid="4312391288948517344">"Menambah situs baru"</string>
+ <string name="add_event" msgid="7488781591843886426">"Menambah acara baru"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Menambah hubungan baru"</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>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 8568daf..b1aee60 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Il registro chiamate è vuoto."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Cancella registro chiamate"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Cancellare il registro chiamate?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Cancellazione registro chiamate"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Segreteria"</string>
@@ -431,6 +432,14 @@
</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="add_phone" msgid="4421904942555210013">"Nuovo n. telefono"</string>
+ <string name="add_email" msgid="175079666329862215">"Nuova email"</string>
+ <string name="add_im" msgid="5158094627521120439">"Nuovo account IM"</string>
+ <string name="add_address" msgid="418292312672970688">"Nuovo indirizzo"</string>
+ <string name="add_note" msgid="2753771325725383279">"Nuova nota"</string>
+ <string name="add_website" msgid="4312391288948517344">"Nuovo sito web"</string>
+ <string name="add_event" msgid="7488781591843886426">"Nuovo evento"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Nuova relazione"</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>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index a8fc33e..965a8e4 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"יומן השיחות ריק."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"נקה את יומן השיחות"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"האם אתה בטוח שברצונך לנקות את יומן השיחות?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"ניקוי יומן שיחות"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"דואר קולי"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"לא נבחרו אנשי קשר."</string>
<string name="add_field" msgid="2384260056674995230">"הוסף שדה נוסף"</string>
+ <string name="add_phone" msgid="4421904942555210013">"הוסף מספר טלפון חדש"</string>
+ <string name="add_email" msgid="175079666329862215">"הוסף דוא\"ל חדש"</string>
+ <string name="add_im" msgid="5158094627521120439">"הוסף חשבון IM חדש"</string>
+ <string name="add_address" msgid="418292312672970688">"הוסף כתובת חדשה"</string>
+ <string name="add_note" msgid="2753771325725383279">"הוסף הערה חדשה"</string>
+ <string name="add_website" msgid="4312391288948517344">"הוסף אתר חדש"</string>
+ <string name="add_event" msgid="7488781591843886426">"הוסף אירוע חדש"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"הוסף קשר חדש"</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>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 4f2354a..b1e1749 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"通話履歴なし"</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"通話履歴を消去"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"通話履歴を消去してもよろしいですか?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"通話履歴の消去中"</string>
<string name="imei" msgid="3045126336951684285">"IMEI(端末識別番号)"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"ボイスメール"</string>
@@ -431,6 +432,22 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"連絡先が選択されていません。"</string>
<string name="add_field" msgid="2384260056674995230">"別のフィールドを追加"</string>
+ <!-- no translation found for add_phone (4421904942555210013) -->
+ <skip />
+ <!-- no translation found for add_email (175079666329862215) -->
+ <skip />
+ <!-- no translation found for add_im (5158094627521120439) -->
+ <skip />
+ <!-- no translation found for add_address (418292312672970688) -->
+ <skip />
+ <!-- no translation found for add_note (2753771325725383279) -->
+ <skip />
+ <!-- no translation found for add_website (4312391288948517344) -->
+ <skip />
+ <!-- no translation found for add_event (7488781591843886426) -->
+ <skip />
+ <!-- no translation found for add_relationship (3083762399737240006) -->
+ <skip />
<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>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index ee93888..eb39ffa 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"통화기록이 없습니다."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"통화 기록 지우기"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"통화 기록을 삭제하시겠습니까?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"통화기록 지우기"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"음성사서함"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"선택한 연락처가 없습니다."</string>
<string name="add_field" msgid="2384260056674995230">"다른 입력란 추가"</string>
+ <string name="add_phone" msgid="4421904942555210013">"새 전화번호 추가"</string>
+ <string name="add_email" msgid="175079666329862215">"새 이메일 추가"</string>
+ <string name="add_im" msgid="5158094627521120439">"새 메신저 계정 추가"</string>
+ <string name="add_address" msgid="418292312672970688">"새 주소 추가"</string>
+ <string name="add_note" msgid="2753771325725383279">"새 메모 추가"</string>
+ <string name="add_website" msgid="4312391288948517344">"새 웹사이트 추가"</string>
+ <string name="add_event" msgid="7488781591843886426">"새 일정 추가"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"새 관계 추가"</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>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 7eb3506..62f91cf 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Skambučių žurnalas tuščias."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Išvalyti skambučių žurnalą"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Ar tikrai norite išvalyti skambučių žurnalą?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Valomas skambučių žurnalas"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Balso paštas"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"Nepasirinkote kontaktų."</string>
<string name="add_field" msgid="2384260056674995230">"Pridėti kitą lauką"</string>
+ <string name="add_phone" msgid="4421904942555210013">"Pridėti naują tel. nr."</string>
+ <string name="add_email" msgid="175079666329862215">"Prid. n. el. pšt. adr."</string>
+ <string name="add_im" msgid="5158094627521120439">"Pridėti naują TP pask."</string>
+ <string name="add_address" msgid="418292312672970688">"Pridėti naują adresą"</string>
+ <string name="add_note" msgid="2753771325725383279">"Pridėti naują pastabą"</string>
+ <string name="add_website" msgid="4312391288948517344">"Pridėti naują svetainę"</string>
+ <string name="add_event" msgid="7488781591843886426">"Pridėti naują įvykį"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Pridėti naujus santyk."</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>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 31f4cee..cb94fbf 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Zvanu žurnāls ir tukšs."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Notīrīt zvanu žurnālu"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Vai tiešām vēlaties notīrīt zvanu žurnālu?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Zvanu žurnāla tīrīšana"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Balss pasts"</string>
@@ -431,6 +432,14 @@
</plurals>
<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="add_phone" msgid="4421904942555210013">"Piev. jaunu tālr. nr."</string>
+ <string name="add_email" msgid="175079666329862215">"Piev. jaunu e-pastu"</string>
+ <string name="add_im" msgid="5158094627521120439">"Piev. jaunu t. ziņ. k."</string>
+ <string name="add_address" msgid="418292312672970688">"Pievienot jaunu adresi"</string>
+ <string name="add_note" msgid="2753771325725383279">"Piev. jaunu piezīmi"</string>
+ <string name="add_website" msgid="4312391288948517344">"Pievienot jaunu vietni"</string>
+ <string name="add_event" msgid="7488781591843886426">"Piev. jaunu notikumu"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Piev. jaunu saistību"</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>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index b2d7b85..62c7000 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Anropsloggen er tom."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Tøm samtalelogg"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Er du sikker på at du vil tømme samtaleloggen?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Tømming av anropslogg"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Telefonsvarer"</string>
@@ -161,12 +162,12 @@
<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>
- <!-- 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@721cdeff) -->
- <!-- 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@457471e0) -->
+ <!-- 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@b34bed0) -->
+ <!-- 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@33db4f6f) -->
<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@5fe04cbf) -->
- <!-- 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@7ecec0c5) -->
+ <!-- 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@33c1b02) -->
+ <!-- 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@5f1121f6) -->
<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>
@@ -431,6 +432,14 @@
</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="add_phone" msgid="4421904942555210013">"Legg til nytt tlf.nr."</string>
+ <string name="add_email" msgid="175079666329862215">"Legg til ny e-post"</string>
+ <string name="add_im" msgid="5158094627521120439">"Legg til nettpratkonto"</string>
+ <string name="add_address" msgid="418292312672970688">"Legg til ny adresse"</string>
+ <string name="add_note" msgid="2753771325725383279">"Legg til ny merknad:"</string>
+ <string name="add_website" msgid="4312391288948517344">"Legg til nytt nettsted"</string>
+ <string name="add_event" msgid="7488781591843886426">"Legg til ny aktivitet"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Legg til nytt forhold"</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>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index cc70f61..0da1af4 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Gesprekken is leeg"</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Oproeplogboek wissen"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Weet u zeker dat u het oproeplogboek wilt wissen?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Oproeplogboek wissen"</string>
<string name="imei" msgid="3045126336951684285">"IMEI-nummer"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
@@ -431,6 +432,14 @@
</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="add_phone" msgid="4421904942555210013">"Nieuw telefoonnummer"</string>
+ <string name="add_email" msgid="175079666329862215">"Nieuw e-mailadres"</string>
+ <string name="add_im" msgid="5158094627521120439">"Nieuw IM-account"</string>
+ <string name="add_address" msgid="418292312672970688">"Nieuw adres"</string>
+ <string name="add_note" msgid="2753771325725383279">"Nieuwe opmerking"</string>
+ <string name="add_website" msgid="4312391288948517344">"Nieuwe website"</string>
+ <string name="add_event" msgid="7488781591843886426">"Nieuwe gebeurtenis"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Nieuwe relatie"</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>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 98cc2e8..20b1333 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Rejestr połączeń jest pusty."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Wyczyść rejestr połączeń"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Czy na pewno chcesz wyczyścić rejestr połączeń?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Czyszczenie dziennika połączeń"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"Numer MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Poczta głosowa"</string>
@@ -431,6 +432,14 @@
</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="add_phone" msgid="4421904942555210013">"Dodaj nowy nr telefonu"</string>
+ <string name="add_email" msgid="175079666329862215">"Dodaj nowy e-mail"</string>
+ <string name="add_im" msgid="5158094627521120439">"Dodaj nowe konto IM"</string>
+ <string name="add_address" msgid="418292312672970688">"Dodaj nowy adres"</string>
+ <string name="add_note" msgid="2753771325725383279">"Dodaj nową notatkę"</string>
+ <string name="add_website" msgid="4312391288948517344">"Dodaj nową witrynę"</string>
+ <string name="add_event" msgid="7488781591843886426">"Dodaj nowe wydarzenie"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Dodaj nową relację"</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>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 556e529..04fc1a4 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"O registo de chamadas está vazio."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Limpar registo de chamadas"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Tem a certeza de que pretende limpar o registo de chamadas?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Limpar registo de chamadas"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Correio de voz"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"Nenhum contacto seleccionado."</string>
<string name="add_field" msgid="2384260056674995230">"Adicionar outro campo"</string>
+ <string name="add_phone" msgid="4421904942555210013">"Adic. novo n.º telef."</string>
+ <string name="add_email" msgid="175079666329862215">"Adicionar novo e-mail"</string>
+ <string name="add_im" msgid="5158094627521120439">"Adic. nova conta MI"</string>
+ <string name="add_address" msgid="418292312672970688">"Adicionar novo endereço"</string>
+ <string name="add_note" msgid="2753771325725383279">"Adicionar nova nota"</string>
+ <string name="add_website" msgid="4312391288948517344">"Adicionar novo site"</string>
+ <string name="add_event" msgid="7488781591843886426">"Adicionar novo evento"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Adicionar nova relação"</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>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 6a573f7..6d2adb6 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"O registro de chamadas está vazio."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Limpar registro de chamadas"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Tem certeza de que deseja limpar o registro de chamadas?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Limpando registro de chamadas"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Correio de voz"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"Nenhum contato selecionado."</string>
<string name="add_field" msgid="2384260056674995230">"Adicionar outro campo"</string>
+ <string name="add_phone" msgid="4421904942555210013">"Adicionar num. de tel."</string>
+ <string name="add_email" msgid="175079666329862215">"Adicionar novo e-mail"</string>
+ <string name="add_im" msgid="5158094627521120439">"Adic. conta de IM"</string>
+ <string name="add_address" msgid="418292312672970688">"Adicionar endereço"</string>
+ <string name="add_note" msgid="2753771325725383279">"Adicionar nota"</string>
+ <string name="add_website" msgid="4312391288948517344">"Adicionar novo site"</string>
+ <string name="add_event" msgid="7488781591843886426">"Adicionar evento"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Adicionar relac."</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>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index 7892abb..0e7dd35 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -153,6 +153,8 @@
<string name="recentCalls_empty" msgid="247053222448663107">"La glista da cloms e vida"</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Stizzar il protocol da cloms"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Vulais Vus propi stizzar il protocol da cloms?"</string>
+ <!-- no translation found for clearCallLogProgress_title (6870412675015656948) -->
+ <skip />
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Mailbox"</string>
@@ -469,6 +471,22 @@
</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>
+ <!-- no translation found for add_phone (4421904942555210013) -->
+ <skip />
+ <!-- no translation found for add_email (175079666329862215) -->
+ <skip />
+ <!-- no translation found for add_im (5158094627521120439) -->
+ <skip />
+ <!-- no translation found for add_address (418292312672970688) -->
+ <skip />
+ <!-- no translation found for add_note (2753771325725383279) -->
+ <skip />
+ <!-- no translation found for add_website (4312391288948517344) -->
+ <skip />
+ <!-- no translation found for add_event (7488781591843886426) -->
+ <skip />
+ <!-- no translation found for add_relationship (3083762399737240006) -->
+ <skip />
<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>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index b52a70c..23698a9 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Jurnalul de apeluri este gol."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Ştergeţi jurnalul de apeluri"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Sigur doriţi ştergerea jurnalului de apeluri?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Se goleşte jurnalul de apeluri"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Mesagerie vocală"</string>
@@ -431,6 +432,14 @@
</plurals>
<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="add_phone" msgid="4421904942555210013">"Adăugaţi nr. tel. nou"</string>
+ <string name="add_email" msgid="175079666329862215">"Adăugaţi e-mail nou"</string>
+ <string name="add_im" msgid="5158094627521120439">"Adăugaţi cont IM nou"</string>
+ <string name="add_address" msgid="418292312672970688">"Adăugaţi adresă nouă"</string>
+ <string name="add_note" msgid="2753771325725383279">"Adăugaţi notă nouă"</string>
+ <string name="add_website" msgid="4312391288948517344">"Adăugaţi site web nou"</string>
+ <string name="add_event" msgid="7488781591843886426">"Adăugaţi eveniment nou"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Adăugaţi relaţie nouă"</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>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 6d2a2a8..d7e3442 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Список вызовов пуст."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Очистить список вызовов"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Действительно очистить список вызовов?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Очистка списка вызовов"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Голосовая почта"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"Контакты не выбраны."</string>
<string name="add_field" msgid="2384260056674995230">"Добавить другое поле"</string>
+ <string name="add_phone" msgid="4421904942555210013">"Добавить тел. номер"</string>
+ <string name="add_email" msgid="175079666329862215">"Добавить эл. почту"</string>
+ <string name="add_im" msgid="5158094627521120439">"Добавить чат"</string>
+ <string name="add_address" msgid="418292312672970688">"Добавить адрес"</string>
+ <string name="add_note" msgid="2753771325725383279">"Добавить примечание"</string>
+ <string name="add_website" msgid="4312391288948517344">"Добавить веб-сайт"</string>
+ <string name="add_event" msgid="7488781591843886426">"Добавить событие"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Добавить отношение"</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>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 6a78891..514ed92 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Záznam hovorov je prázdny."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Vymazať záznam hovorov"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Naozaj chcete odstrániť denník hovorov?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Vymazávanie denníka hovorov"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Hlasová schránka"</string>
@@ -431,6 +432,14 @@
</plurals>
<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="add_phone" msgid="4421904942555210013">"Pridať nové tel. číslo"</string>
+ <string name="add_email" msgid="175079666329862215">"Pridať nový e-mail"</string>
+ <string name="add_im" msgid="5158094627521120439">"Pridať nový účet IM"</string>
+ <string name="add_address" msgid="418292312672970688">"Pridať novú adresu"</string>
+ <string name="add_note" msgid="2753771325725383279">"Pridať novú poznámku"</string>
+ <string name="add_website" msgid="4312391288948517344">"Pridať novú lokalitu"</string>
+ <string name="add_event" msgid="7488781591843886426">"Pridať novú udalosť"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Pridať nový vzťah"</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>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 0bb51c4..58cb12c 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Dnevnik klicev je prazen."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Počisti dnevnik klicev"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Ali ste prepričani, da želite počistiti dnevnik klicev?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Brisanje dnevnika klicev"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Glasovna pošta"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"Izbran ni noben stik."</string>
<string name="add_field" msgid="2384260056674995230">"Dodaj drugo polje"</string>
+ <string name="add_phone" msgid="4421904942555210013">"Dodaj novo telefonsko številko"</string>
+ <string name="add_email" msgid="175079666329862215">"Dodaj novo e-pošto"</string>
+ <string name="add_im" msgid="5158094627521120439">"Dodaj nov račun za NS"</string>
+ <string name="add_address" msgid="418292312672970688">"Dodaj nov naslov"</string>
+ <string name="add_note" msgid="2753771325725383279">"Dodaj novo opombo"</string>
+ <string name="add_website" msgid="4312391288948517344">"Dodaj novo spletno mesto"</string>
+ <string name="add_event" msgid="7488781591843886426">"Dodaj nov dogodek"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Dodaj novo razmerje"</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>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 614548e..d23d367 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Евиденција позива је празна."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Обриши евиденцију позива"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Желите ли заиста да обришете евиденцију позива?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Брисање евиденције позива"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Говорна пошта"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"Није изабран ниједан контакт."</string>
<string name="add_field" msgid="2384260056674995230">"Додај друго поље"</string>
+ <string name="add_phone" msgid="4421904942555210013">"Дод. нови бр. телефона"</string>
+ <string name="add_email" msgid="175079666329862215">"Дод. нову адр. е-пош."</string>
+ <string name="add_im" msgid="5158094627521120439">"Дод. нови нал. Трен. пор."</string>
+ <string name="add_address" msgid="418292312672970688">"Додајте нову адресу"</string>
+ <string name="add_note" msgid="2753771325725383279">"Додајте нову белешку"</string>
+ <string name="add_website" msgid="4312391288948517344">"Додајте нови веб сајт"</string>
+ <string name="add_event" msgid="7488781591843886426">"Додајте нови догађај"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Додајте нову везу"</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>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index dbe0f1b..903a668 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Samtalshistoriken är tom."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Rensa samtalshistorik"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Vill du rensa samtalshistoriken?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Rensar samtalshistorik"</string>
<string name="imei" msgid="3045126336951684285">"IMEI-kod"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Röstbrevlåda"</string>
@@ -431,6 +432,14 @@
</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="add_phone" msgid="4421904942555210013">"Lägg till nytt nummer"</string>
+ <string name="add_email" msgid="175079666329862215">"Lägg till ny e-post"</string>
+ <string name="add_im" msgid="5158094627521120439">"Lägg till IM-konto"</string>
+ <string name="add_address" msgid="418292312672970688">"Lägg till ny adress"</string>
+ <string name="add_note" msgid="2753771325725383279">"Lägg till kommentar"</string>
+ <string name="add_website" msgid="4312391288948517344">"Lägg till ny webbplats"</string>
+ <string name="add_event" msgid="7488781591843886426">"Lägg till ny aktivitet"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Lägg till ny relation"</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>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index d545369..98c7d23 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"บันทึกการโทรว่างเปล่า"</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"ล้างบันทึกการโทร"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"คุณแน่ใจหรือไม่ว่าต้องการล้างบันทึกการโทรทั้งหมด"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"การล้างบันทึกการโทร"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"ข้อความเสียง"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"ไม่ได้เลือกสมุดโทรศัพท์ไว้"</string>
<string name="add_field" msgid="2384260056674995230">"เพิ่มฟิลด์อื่น"</string>
+ <string name="add_phone" msgid="4421904942555210013">"เพิ่มหมายเลขโทรศัพท์ใหม่"</string>
+ <string name="add_email" msgid="175079666329862215">"เพิ่มอีเมลใหม่"</string>
+ <string name="add_im" msgid="5158094627521120439">"เพิ่มบัญชี IM ใหม่"</string>
+ <string name="add_address" msgid="418292312672970688">"เพิ่มที่อยู่ใหม่"</string>
+ <string name="add_note" msgid="2753771325725383279">"เพิ่มบันทึกใหม่"</string>
+ <string name="add_website" msgid="4312391288948517344">"เพิ่มเว็บไซต์ใหม่"</string>
+ <string name="add_event" msgid="7488781591843886426">"เพิ่มกิจกรรมใหม่"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"เพิ่มความเกี่ยวข้องใหม่"</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>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index d8321ce..ad17320 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Walang laman ang log ng tawag."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"I-clear ang log ng tawag"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Sigurado ka bang gusto mong i-clear ang log ng tawag?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Kini-clear ang log ng tawag"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Voicemail"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"Walang napiling contact."</string>
<string name="add_field" msgid="2384260056674995230">"Magdagdag ng ibang field"</string>
+ <string name="add_phone" msgid="4421904942555210013">"Magdagdag ng bagong numero ng telepono"</string>
+ <string name="add_email" msgid="175079666329862215">"Magdagdag ng bagong email"</string>
+ <string name="add_im" msgid="5158094627521120439">"Magdagdag ng bagong IM account"</string>
+ <string name="add_address" msgid="418292312672970688">"Magdagdag ng bagong address"</string>
+ <string name="add_note" msgid="2753771325725383279">"Magdagdag ng bagong paalala"</string>
+ <string name="add_website" msgid="4312391288948517344">"Magdagdag ng bagong website"</string>
+ <string name="add_event" msgid="7488781591843886426">"Magdagdag ng bagong kaganapan"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Magdagdag ng bagong ugnayan"</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>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 3857046..2c482cf 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Çağrı kaydı boş."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Çağrı kaydını temizle"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Çağrı kaydını silmek istediğinizden emin misiniz?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Çağrı kaydı temizleniyor"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Sesli Mesaj"</string>
@@ -431,6 +432,14 @@
</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="add_phone" msgid="4421904942555210013">"Yeni telf numarası ekl"</string>
+ <string name="add_email" msgid="175079666329862215">"Yeni e-posta ekle"</string>
+ <string name="add_im" msgid="5158094627521120439">"Yeni IM hesabı ekle"</string>
+ <string name="add_address" msgid="418292312672970688">"Yeni adres ekle"</string>
+ <string name="add_note" msgid="2753771325725383279">"Yeni not ekle"</string>
+ <string name="add_website" msgid="4312391288948517344">"Yeni web sitesi ekle"</string>
+ <string name="add_event" msgid="7488781591843886426">"Yeni etkinlik ekle"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Yeni ilişki 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>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 35576b4..43f60ed 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Журн. викл. порожній."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Очист. журнал викл."</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Дійсно очистити журнал викликів?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Очищення журналу викликів"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Голос. пошта"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"Контакти не вибрано."</string>
<string name="add_field" msgid="2384260056674995230">"Додати ще одне поле"</string>
+ <string name="add_phone" msgid="4421904942555210013">"Додати нов. номер тел."</string>
+ <string name="add_email" msgid="175079666329862215">"Додати нову ел. адресу"</string>
+ <string name="add_im" msgid="5158094627521120439">"Дод. нов.обл.зап. чату"</string>
+ <string name="add_address" msgid="418292312672970688">"Додати нову адресу"</string>
+ <string name="add_note" msgid="2753771325725383279">"Додати нову примітку"</string>
+ <string name="add_website" msgid="4312391288948517344">"Додати новий веб-сайт"</string>
+ <string name="add_event" msgid="7488781591843886426">"Додати нову подію"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Додати новий стосунок"</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>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 4c78800..b213976 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"Nhật ký cuộc gọi trống."</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"Xoá nhật ký cuộc gọi"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"Bạn có chắc chắn muốn xoá nhật ký cuộc gọi không?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"Đang xóa nhật ký cuộc gọi"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"Thư thoại"</string>
@@ -431,6 +432,14 @@
</plurals>
<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="add_phone" msgid="4421904942555210013">"Thêm số điện thoại mới"</string>
+ <string name="add_email" msgid="175079666329862215">"Thêm email mới"</string>
+ <string name="add_im" msgid="5158094627521120439">"Thêm tài khoản IM mới"</string>
+ <string name="add_address" msgid="418292312672970688">"Thêm địa chỉ mới"</string>
+ <string name="add_note" msgid="2753771325725383279">"Thêm chú thích mới"</string>
+ <string name="add_website" msgid="4312391288948517344">"Thêm trang web mới"</string>
+ <string name="add_event" msgid="7488781591843886426">"Thêm sự kiện mới"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"Thêm mối quan hệ mới"</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>
diff --git a/res/values-xlarge/dimens.xml b/res/values-xlarge/dimens.xml
index d29eba2..2d39186 100644
--- a/res/values-xlarge/dimens.xml
+++ b/res/values-xlarge/dimens.xml
@@ -16,6 +16,13 @@
<resources>
<dimen name="edit_photo_size">96dip</dimen>
<dimen name="aggregation_suggestion_icon_size">64dip</dimen>
+ <dimen name="editor_type_label_width">180dip</dimen>
+ <dimen name="editor_field_spinner_text_size">15sp</dimen>
+ <dimen name="editor_round_button_padding_left">8dip</dimen>
+ <dimen name="editor_round_button_padding_right">8dip</dimen>
+ <dimen name="editor_field_top_padding">12dip</dimen>
+ <dimen name="editor_field_bottom_padding">12dip</dimen>
+ <dimen name="detail_item_side_margin">19dip</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>
@@ -23,4 +30,5 @@
<dimen name="action_bar_search_max_width">300dip</dimen>
<dimen name="action_bar_search_spacing">12dip</dimen>
<dimen name="shortcut_icon_size">64dip</dimen>
+ <dimen name="list_section_height">37dip</dimen>
</resources>
diff --git a/res/values-xlarge/donottranslate_config.xml b/res/values-xlarge/donottranslate_config.xml
new file mode 100644
index 0000000..fcb7da9
--- /dev/null
+++ b/res/values-xlarge/donottranslate_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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>
+ <bool name="config_use_two_panes">true</bool>
+</resources>
diff --git a/res/values-xlarge/styles.xml b/res/values-xlarge/styles.xml
index e601590..dcb32ad 100644
--- a/res/values-xlarge/styles.xml
+++ b/res/values-xlarge/styles.xml
@@ -57,9 +57,6 @@
<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>
@@ -105,4 +102,15 @@
<item name="android:windowNoDisplay">true</item>
<item name="android:windowIsFloating">true</item>
</style>
+
+ <style name="ContactDetailItemType">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+ <item name="android:textColor">@color/detail_item_type_color</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">marquee</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:paddingTop">5dip</item>
+ </style>
</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index a4aef23..378f0de 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"通话记录为空。"</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"清除通话记录"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"确定要清除通话记录吗?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"正在清除通话记录"</string>
<string name="imei" msgid="3045126336951684285">"移动通信国际识别码"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"语音信箱"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"未选择联系人。"</string>
<string name="add_field" msgid="2384260056674995230">"添加其他字段"</string>
+ <string name="add_phone" msgid="4421904942555210013">"添加新电话号码"</string>
+ <string name="add_email" msgid="175079666329862215">"添加新电子邮件地址"</string>
+ <string name="add_im" msgid="5158094627521120439">"添加新即时通讯帐户"</string>
+ <string name="add_address" msgid="418292312672970688">"添加新地址"</string>
+ <string name="add_note" msgid="2753771325725383279">"添加新备注"</string>
+ <string name="add_website" msgid="4312391288948517344">"添加新网站"</string>
+ <string name="add_event" msgid="7488781591843886426">"添加新活动"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"添加新关系"</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>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 3cfddeb..91a7280 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -149,6 +149,7 @@
<string name="recentCalls_empty" msgid="247053222448663107">"無通話記錄。"</string>
<string name="clearCallLogConfirmation_title" msgid="718072843006222703">"清除通話記錄"</string>
<string name="clearCallLogConfirmation" msgid="7625927669136267636">"確定要清除通話記錄嗎?"</string>
+ <string name="clearCallLogProgress_title" msgid="6870412675015656948">"正在清除通話記錄"</string>
<string name="imei" msgid="3045126336951684285">"IMEI"</string>
<string name="meid" msgid="6210568493746275750">"MEID"</string>
<string name="voicemail" msgid="3851469869202611441">"語音留言"</string>
@@ -431,6 +432,14 @@
</plurals>
<string name="no_contacts_selected" msgid="5877803471037324613">"未選取任何聯絡人。"</string>
<string name="add_field" msgid="2384260056674995230">"新增其他欄位"</string>
+ <string name="add_phone" msgid="4421904942555210013">"新增電話號碼"</string>
+ <string name="add_email" msgid="175079666329862215">"新增電子郵件"</string>
+ <string name="add_im" msgid="5158094627521120439">"新增即時通訊帳戶"</string>
+ <string name="add_address" msgid="418292312672970688">"新增地址"</string>
+ <string name="add_note" msgid="2753771325725383279">"新增附註"</string>
+ <string name="add_website" msgid="4312391288948517344">"新增網站"</string>
+ <string name="add_event" msgid="7488781591843886426">"新增活動"</string>
+ <string name="add_relationship" msgid="3083762399737240006">"新增關係"</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>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 69f92ad..ace8869 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -21,10 +21,16 @@
<color name="quickcontact_disambig_divider">#afafaf</color>
<color name="edit_divider">#ff666666</color>
+
+ <!-- Color of the background of the contact detail and editor pages -->
+ <color name="background_primary">#FFFFFF</color>
+
<color name="background_secondary">#ff202020</color>
<color name="translucent_search_background">#cc000000</color>
+ <color name="account_selection_background">#ff000000</color>
+
<!-- Color used for the letter in the A-Z section header -->
<color name="section_header_text_color">#ff999999</color>
@@ -39,4 +45,7 @@
<!-- Color of the text indicating the type of entry (e.g. Home, Work etc) -->
<color name="detail_item_type_color">#B4B4B4</color>
+
+ <!-- Color of the text indicating the type of entry (e.g. Home, Work etc) -->
+ <color name="detail_header_view_text_color">#FFFFFF</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 47150b4..354bec7 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<resources>
- <dimen name="edit_photo_size">76dip</dimen>
+ <dimen name="edit_photo_size">120dip</dimen>
<!-- The height of the ScrollingTabWidget -->
<dimen name="tab_height">40dip</dimen>
@@ -35,20 +35,53 @@
<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_left">4dip</dimen>
+ <dimen name="editor_round_button_padding_right">4dip</dimen>
<dimen name="editor_round_button_padding_top">8dip</dimen>
<dimen name="editor_round_button_padding_bottom">8dip</dimen>
+ <!-- Font size for the structured name in the contact detail page -->
+ <dimen name="editor_structured_name_text_size">25sp</dimen>
+
<!-- Width of the Type-Label in the Editor -->
- <dimen name="editor_type_label_width">180dip</dimen>
-
+ <dimen name="editor_type_label_width">120dip</dimen>
+
+ <!-- Left padding of a field in the Editor -->
+ <dimen name="editor_field_left_padding">5dip</dimen>
+
+ <!-- Top padding of a field in the Editor -->
+ <dimen name="editor_field_top_padding">10dip</dimen>
+
+ <!-- Bottom padding of a field in the Editor -->
+ <dimen name="editor_field_bottom_padding">5dip</dimen>
+
<!-- Minimum height of a row in the Editor -->
<dimen name="editor_min_line_item_height">48dip</dimen>
+ <!-- Font size used for the value of a field in the contact editor. -->
+ <dimen name="editor_field_text_size">18sp</dimen>
+
+ <!-- Font size used for the title of a field in the contact editor. -->
+ <dimen name="editor_field_title_text_size">15sp</dimen>
+
+ <!-- Font size for the entries in a spinner in the contact editor. -->
+ <dimen name="editor_field_spinner_text_size">10sp</dimen>
+
+ <!-- Left and right padding for a contact detail item -->
+ <dimen name="detail_item_icon_margin">10dip</dimen>
+
+ <!-- Left and right padding for a contact detail item -->
+ <dimen name="detail_item_side_margin">10dip</dimen>
+
<!-- Minimum height of a row in the contact detail -->
<dimen name="detail_min_line_item_height">48dip</dimen>
+ <!-- Width of a contact detail item type (i.e. Nickname or Website). -->
+ <dimen name="detail_item_type_width">164dip</dimen>
+
+ <!-- Font size for the display name in header of the contact detail page -->
+ <dimen name="detail_header_name_text_size">30sp</dimen>
+
<!-- Padding to be used between a visible scrollbar and the contact list -->
<dimen name="list_visible_scrollbar_padding">40dip</dimen>
@@ -58,9 +91,6 @@
<!-- 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>
@@ -75,4 +105,7 @@
<!-- Size of the shortcut icon. 0dip means: use the system default -->
<dimen name="shortcut_icon_size">0dip</dimen>
+
+ <!-- Height of list sections (A, B, C) that show the first character of the contacts -->
+ <dimen name="list_section_height">25dip</dimen>
</resources>
diff --git a/res/values/donottranslate_config.xml b/res/values/donottranslate_config.xml
index f5efa13..e310953 100644
--- a/res/values/donottranslate_config.xml
+++ b/res/values/donottranslate_config.xml
@@ -99,4 +99,8 @@
<!-- If true, phonetic name is included in the contact editor by default -->
<bool name="config_editor_include_phonetic_name">false</bool>
+
+ <!-- If true, Contacts uses two panes: List and Detail. If false, Details are
+ shown in their own screens. This flag must be in sync with the layout definitions. -->
+ <bool name="config_use_two_panes">false</bool>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ff69899..b531431 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -388,6 +388,9 @@
<!-- Confirmation dialog for clearing the call log -->
<string name="clearCallLogConfirmation">Are you sure you want to clear the call log?</string>
+ <!-- Title of the "Clearing call log" progress-dialog [CHAR LIMIT=35] -->
+ <string name="clearCallLogProgress_title">Clearing call log</string>
+
<!-- The title of a dialog that displays the IMEI of the phone -->
<string name="imei">IMEI</string>
@@ -1223,7 +1226,7 @@
<string name="account_type_format"><xliff:g id="source" example="Gmail">%1$s</xliff:g> contact</string>
<!-- String describing which account a contact came from when editing it -->
- <string name="from_account_format">from <xliff:g id="source" example="user@gmail.com">%1$s</xliff:g></string>
+ <string name="from_account_format"><xliff:g id="source" example="user@gmail.com">%1$s</xliff:g></string>
<!-- Checkbox asking the user if they want to display a particular photo for a contact -->
<string name="use_photo_as_primary">Use this photo</string>
@@ -1316,6 +1319,30 @@
<!-- The add field button shown in the editor under each editable Raw Contact [CHAR LIMIT=30] -->
<string name="add_field">Add another field</string>
+ <!-- The editable field hint text to add a new phone number to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+ <string name="add_phone">Add new phone number</string>
+
+ <!-- The editable field hint text to add a new email to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+ <string name="add_email">Add new email</string>
+
+ <!-- The editable field hint text to add a new IM account to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+ <string name="add_im">Add new IM account</string>
+
+ <!-- The editable field hint text to add a new postal address to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+ <string name="add_address">Add new address</string>
+
+ <!-- The editable field hint text to add a new note to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+ <string name="add_note">Add new note</string>
+
+ <!-- The editable field hint text to add a new website to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+ <string name="add_website">Add new website</string>
+
+ <!-- The editable field hint text to add a new event to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+ <string name="add_event">Add new event</string>
+
+ <!-- The editable field hint text to add a relationship field to a contact in the Raw Contact Editor [CHAR LIMIT=22] -->
+ <string name="add_relationship">Add new relationship</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>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index db50204..2469a3d 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -14,18 +14,18 @@
limitations under the License.
-->
<resources>
- <style name="DialtactsTheme" parent="@android:Theme">
+ <style name="DialtactsTheme" parent="android:Theme.Holo.Light">
<item name="android:windowNoTitle">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
- <style name="CallDetailActivityTheme" parent="android:Theme.NoTitleBar">
+ <style name="CallDetailActivityTheme" parent="android:Theme.Holo.Light">
<item name="android:windowContentOverlay">@null</item>
</style>
- <style name="ContactDetailActivityTheme" parent="android:Theme.NoTitleBar">
+ <style name="ContactDetailActivityTheme" parent="android:Theme.Holo.Light">
<item name="android:windowContentOverlay">@null</item>
</style>
- <style name="ContactEditorActivityTheme" parent="android:Theme.NoTitleBar">
+ <style name="ContactEditorActivityTheme" parent="android:Theme.Holo.Light">
<item name="android:windowContentOverlay">@null</item>
</style>
@@ -38,38 +38,19 @@
<item name="android:windowIsFloating">true</item>
</style>
- <style name="FullyTranslucent" parent="android:Theme.Translucent.NoTitleBar">
- <item name="android:windowContentOverlay">@null</item>
+ <style name="Theme">
</style>
- <style name="FullyTranslucent.QuickContact">
- <!-- This is a hack because we want to be able to animate away the
- QuickContact window, and we close its containing activity at the
- same time. So put in a dummy animation so this guy sticks around
- while the fast track window is animating. -->
- <item name="android:windowAnimationStyle">@style/DummyAnimation</item>
- </style>
-
- <style name="QuickContact" parent="@android:Theme.Holo.Light">
- <item name="android:windowNoTitle">true</item>
- <item name="android:windowFrame">@null</item>
+ <style name="Theme.QuickContact" parent="@android:style/Theme.Holo.Light">
<item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:windowIsFloating">true</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:windowFrame">@null</item>
<item name="android:windowContentOverlay">@null</item>
- <!-- TODO: create our own animation style in framework -->
- <!--
- <item name="android:windowAnimationStyle">@*android:style/Animation.ZoomButtons</item>
- -->
- </style>
-
- <style name="QuickContactAboveAnimation">
- <item name="android:windowEnterAnimation">@anim/quickcontact_above_enter</item>
- <item name="android:windowExitAnimation">@anim/quickcontact_above_exit</item>
- </style>
-
- <style name="QuickContactBelowAnimation">
- <item name="android:windowEnterAnimation">@anim/quickcontact_below_enter</item>
- <item name="android:windowExitAnimation">@anim/quickcontact_below_exit</item>
+ <item name="android:windowAnimationStyle">@null</item>
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowNoTitle">true</item>
</style>
<style name="ContactsSearchAnimation">
@@ -110,7 +91,7 @@
<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="section_header_background">@drawable/list_title_holo</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>
@@ -152,7 +133,7 @@
<style name="JoinContactActivityTheme" parent="ContactPickerTheme" >
</style>
- <style name="ContactsPreferencesTheme" parent="@android:Theme">
+ <style name="ContactsPreferencesTheme" parent="@android:Theme.Holo.Light">
</style>
<style name="CustomContactListFilterTheme" parent="@android:Theme">
@@ -208,4 +189,15 @@
<item name="android:layout_height">2dip</item>
<item name="android:layout_width">match_parent</item>
</style>
+
+ <style name="ContactDetailItemType">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
+ <item name="android:textColor">@color/detail_item_type_color</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">marquee</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:paddingTop">5dip</item>
+ </style>
</resources>
diff --git a/src/com/android/contacts/ContactPhotoManager.java b/src/com/android/contacts/ContactPhotoManager.java
index 914b697..150856a 100644
--- a/src/com/android/contacts/ContactPhotoManager.java
+++ b/src/com/android/contacts/ContactPhotoManager.java
@@ -625,12 +625,13 @@
try {
Uri uri = Contacts.CONTENT_URI.buildUpon().appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
+ .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
+ String.valueOf(MAX_PHOTOS_TO_PRELOAD))
.build();
cursor = mResolver.query(uri, new String[] { Contacts.PHOTO_ID },
Contacts.PHOTO_ID + " NOT NULL AND " + Contacts.PHOTO_ID + "!=0",
null,
- Contacts.STARRED + " DESC, " + Contacts.LAST_TIME_CONTACTED + " DESC"
- + " LIMIT " + MAX_PHOTOS_TO_PRELOAD);
+ Contacts.STARRED + " DESC, " + Contacts.LAST_TIME_CONTACTED + " DESC");
if (cursor != null) {
while (cursor.moveToNext()) {
diff --git a/src/com/android/contacts/ContactsUtils.java b/src/com/android/contacts/ContactsUtils.java
index 0e75a7f..76cbc7d 100644
--- a/src/com/android/contacts/ContactsUtils.java
+++ b/src/com/android/contacts/ContactsUtils.java
@@ -16,6 +16,11 @@
package com.android.contacts;
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import com.google.i18n.phonenumbers.PhoneNumberUtil.MatchType;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
+
import android.content.Context;
import android.content.Intent;
import android.location.CountryDetector;
@@ -114,9 +119,19 @@
if (dataParts1.length != dataParts2.length) {
return false;
}
+ PhoneNumberUtil util = PhoneNumberUtil.getInstance();
for (int i = 0; i < dataParts1.length; i++) {
- if (!PhoneNumberUtils.compare(context, dataParts1[i], dataParts2[i])) {
- return false;
+ try {
+ PhoneNumber phoneNumber1 = util.parse(dataParts1[i], "ZZ" /* Unknown */);
+ PhoneNumber phoneNumber2 = util.parse(dataParts2[i], "ZZ" /* Unknown */);
+ MatchType matchType = util.isNumberMatch(phoneNumber1, phoneNumber2);
+ if (matchType != MatchType.SHORT_NSN_MATCH) {
+ return false;
+ }
+ } catch (NumberParseException e) {
+ if (!TextUtils.equals(dataParts1[i], dataParts2[i])) {
+ return false;
+ }
}
}
diff --git a/src/com/android/contacts/SpecialCharSequenceMgr.java b/src/com/android/contacts/SpecialCharSequenceMgr.java
index a047a68..9f9f40d 100644
--- a/src/com/android/contacts/SpecialCharSequenceMgr.java
+++ b/src/com/android/contacts/SpecialCharSequenceMgr.java
@@ -54,7 +54,7 @@
private SpecialCharSequenceMgr() {
}
- static boolean handleChars(Context context, String input, EditText textField) {
+ public static boolean handleChars(Context context, String input, EditText textField) {
return handleChars(context, input, false, textField);
}
diff --git a/src/com/android/contacts/TypePrecedence.java b/src/com/android/contacts/TypePrecedence.java
index 2adf3b5..e89c5aa 100644
--- a/src/com/android/contacts/TypePrecedence.java
+++ b/src/com/android/contacts/TypePrecedence.java
@@ -42,6 +42,7 @@
//TODO These may need to be tweaked.
private static final int[] TYPE_PRECEDENCE_PHONES = {
Phone.TYPE_CUSTOM,
+ Phone.TYPE_MAIN,
Phone.TYPE_MOBILE,
Phone.TYPE_HOME,
Phone.TYPE_WORK,
diff --git a/src/com/android/contacts/activities/CallLogActivity.java b/src/com/android/contacts/activities/CallLogActivity.java
new file mode 100644
index 0000000..13c3209
--- /dev/null
+++ b/src/com/android/contacts/activities/CallLogActivity.java
@@ -0,0 +1,133 @@
+/*
+ * 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.ContactsSearchManager;
+import com.android.contacts.R;
+import com.android.contacts.calllog.CallLogFragment;
+import com.android.internal.telephony.ITelephony;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.ViewConfiguration;
+
+/**
+ * Displays a list of call log entries.
+ */
+public class CallLogActivity extends Activity {
+ private static final String TAG = "CallLogActivity";
+
+ private CallLogFragment mFragment;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+
+ setContentView(R.layout.call_log_activity);
+
+ // Typing here goes to the dialer
+ setDefaultKeyMode(DEFAULT_KEYS_DIALER);
+
+ mFragment = (CallLogFragment) getFragmentManager().findFragmentById(
+ R.id.call_log_fragment);
+ }
+
+ public CallLogFragment getFragment() {
+ return mFragment;
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+
+ // Clear notifications only when window gains focus. This activity won't
+ // immediately receive focus if the keyguard screen is above it.
+ if (hasFocus) {
+ try {
+ ITelephony telephony =
+ ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
+ if (telephony != null) {
+ telephony.cancelMissedCallsNotification();
+ } else {
+ Log.w(TAG, "Telephony service is null, can't call " +
+ "cancelMissedCallsNotification");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to clear missed calls notification due to remote exception");
+ }
+ }
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_CALL: {
+ long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
+ if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
+ // Launch voice dialer
+ Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ }
+ return true;
+ }
+ }
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_CALL:
+ try {
+ ITelephony phone = ITelephony.Stub.asInterface(
+ ServiceManager.checkService("phone"));
+ if (phone != null && !phone.isIdle()) {
+ // Let the super class handle it
+ break;
+ }
+ } catch (RemoteException re) {
+ // Fall through and try to call the contact
+ }
+
+ mFragment.callSelectedEntry();
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @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/ContactBrowserActivity.java b/src/com/android/contacts/activities/ContactBrowserActivity.java
index e888bc0..52d7b36 100644
--- a/src/com/android/contacts/activities/ContactBrowserActivity.java
+++ b/src/com/android/contacts/activities/ContactBrowserActivity.java
@@ -105,7 +105,13 @@
private boolean mSearchMode;
private ContactBrowseListFragment mListFragment;
+
+ /**
+ * Whether we have a right-side contact pane for displaying contact info while browsing.
+ * Generally means "this is a tablet".
+ */
private boolean mContactContentDisplayed;
+
private ContactDetailFragment mDetailFragment;
private DetailFragmentListener mDetailFragmentListener = new DetailFragmentListener();
@@ -125,7 +131,6 @@
private boolean mOptionsMenuContactsAvailable;
private boolean mOptionsMenuGroupActionsEnabled;
- private boolean mOptionsMenuCustomFilterChangeable;
public ContactBrowserActivity() {
mIntentResolver = new ContactsIntentResolver(this);
@@ -215,13 +220,15 @@
setTitle(mRequest.getActivityTitle());
- mHasActionBar = getWindow().hasFeature(Window.FEATURE_ACTION_BAR);
- if (mHasActionBar) {
- ActionBar actionBar = getActionBar();
+ if (createContentView) {
+ mHasActionBar = getWindow().hasFeature(Window.FEATURE_ACTION_BAR);
+ if (mHasActionBar) {
+ ActionBar actionBar = getActionBar();
- mActionBarAdapter = new ActionBarAdapter(this);
- mActionBarAdapter.onCreate(savedState, mRequest, actionBar);
- mActionBarAdapter.setContactListFilterController(mContactListFilterController);
+ mActionBarAdapter = new ActionBarAdapter(this);
+ mActionBarAdapter.onCreate(savedState, mRequest, actionBar);
+ mActionBarAdapter.setContactListFilterController(mContactListFilterController);
+ }
}
configureFragments(savedState == null);
@@ -234,7 +241,6 @@
}
mOptionsMenuContactsAvailable = false;
- mOptionsMenuCustomFilterChangeable = false;
mOptionsMenuGroupActionsEnabled = false;
mProviderStatus = -1;
@@ -635,7 +641,7 @@
final MenuItem addContact = menu.findItem(R.id.menu_add);
addContact.setActionView(mAddContactImageView);
return true;
- } else if (mRequest.getActionCode() == ContactsRequest.ACTION_DEFAULT ||
+ } else if (mRequest.getActionCode() == ContactsRequest.ACTION_ALL_CONTACTS ||
mRequest.getActionCode() == ContactsRequest.ACTION_STREQUENT) {
inflater.inflate(R.menu.list, menu);
return true;
@@ -663,10 +669,6 @@
return true;
}
- if (mOptionsMenuCustomFilterChangeable != isCustomFilterChangeable()) {
- return true;
- }
-
if (mListFragment != null && mListFragment.isOptionsMenuChanged()) {
return true;
}
@@ -686,13 +688,8 @@
}
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);
+ if (settings != null) {
+ settings.setVisible(!ContactsPreferenceActivity.isEmpty(this));
}
mOptionsMenuGroupActionsEnabled = areGroupActionsEnabled();
@@ -723,10 +720,6 @@
return groupActionsEnabled;
}
- public boolean isCustomFilterChangeable() {
- return mRequest != null && mRequest.getActionCode() == ContactsRequest.ACTION_DEFAULT;
- }
-
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@@ -774,32 +767,15 @@
}
private void createNewContact() {
+ // We have an account switcher in "create-account" screen, so don't need to ask a user to
+ // select an account here.
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 Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+ if (accounts.size() > 0) {
+ intent.putExtra(Intents.Insert.ACCOUNT, accounts.get(0));
}
-
- 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();
+ startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
}
@Override
@@ -853,7 +829,7 @@
case SUBACTIVITY_EDIT_CONTACT:
case SUBACTIVITY_NEW_CONTACT: {
- if (resultCode == RESULT_OK) {
+ if (resultCode == RESULT_OK && mContactContentDisplayed) {
mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
mListFragment.reloadDataAndSetSelectedUri(data.getData());
}
@@ -910,8 +886,7 @@
default: {
// Bring up the search UI if the user starts typing
final int unicodeChar = event.getUnicodeChar();
-
- if (unicodeChar != 0) {
+ if (unicodeChar != 0 && !Character.isWhitespace(unicodeChar)) {
String query = new String(new int[]{ unicodeChar }, 0, 1);
if (mHasActionBar) {
if (!mActionBarAdapter.isSearchMode()) {
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index 56e8353..0036256 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -22,6 +22,7 @@
import com.android.contacts.R;
import com.android.contacts.detail.ContactDetailFragment;
import com.android.contacts.interactions.ContactDeletionInteraction;
+import com.android.contacts.util.PhoneCapabilityTester;
import android.accounts.Account;
import android.content.ActivityNotFoundException;
@@ -44,6 +45,23 @@
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
+ if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
+ // This activity must not be shown. We have to select the contact in the
+ // ContactBrowserActivity instead ==> Create a forward intent and finish
+ final 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.setClass(this, ContactBrowserActivity.class);
+ startActivity(intent);
+ finish();
+ return;
+ }
+
setContentView(R.layout.contact_detail_activity);
mFragment = (ContactDetailFragment) getFragmentManager().findFragmentById(
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index 35f7413..cedde4e 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -53,8 +53,6 @@
public static final String ACTION_SAVE_COMPLETED = "saveCompleted";
private ContactEditorFragment mFragment;
- private Button mDoneButton;
- private Button mRevertButton;
private DialogManager mDialogManager = new DialogManager(this);
@@ -90,22 +88,6 @@
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
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index 4efc43f..c255ba1 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -23,7 +23,9 @@
import com.android.contacts.list.ContactsIntentResolver;
import com.android.contacts.list.ContactsRequest;
import com.android.contacts.list.DirectoryListLoader;
+import com.android.contacts.list.EmailAddressPickerFragment;
import com.android.contacts.list.OnContactPickerActionListener;
+import com.android.contacts.list.OnEmailAddressPickerActionListener;
import com.android.contacts.list.OnPhoneNumberPickerActionListener;
import com.android.contacts.list.OnPostalAddressPickerActionListener;
import com.android.contacts.list.PhoneNumberPickerFragment;
@@ -171,6 +173,11 @@
break;
}
+ case ContactsRequest.ACTION_PICK_EMAIL: {
+ setTitle(R.string.contactPickerActivityTitle);
+ break;
+ }
+
case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
setTitle(R.string.callShortcutActivityTitle);
break;
@@ -236,6 +243,11 @@
break;
}
+ case ContactsRequest.ACTION_PICK_EMAIL: {
+ mListFragment = new EmailAddressPickerFragment();
+ break;
+ }
+
case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
PhoneNumberPickerFragment fragment = new PhoneNumberPickerFragment();
fragment.setShortcutAction(Intent.ACTION_CALL);
@@ -284,6 +296,9 @@
} else if (mListFragment instanceof PostalAddressPickerFragment) {
((PostalAddressPickerFragment) mListFragment).setOnPostalAddressPickerActionListener(
new PostalAddressPickerActionListener());
+ } else if (mListFragment instanceof EmailAddressPickerFragment) {
+ ((EmailAddressPickerFragment) mListFragment).setOnEmailAddressPickerActionListener(
+ new EmailAddressPickerActionListener());
} else {
throw new IllegalStateException("Unsupported list fragment type: " + mListFragment);
}
@@ -334,6 +349,14 @@
}
}
+ private final class EmailAddressPickerActionListener implements
+ OnEmailAddressPickerActionListener {
+ @Override
+ public void onPickEmailAddressAction(Uri dataUri) {
+ returnPickerResult(dataUri);
+ }
+ }
+
public void startActivityAndForwardResult(final Intent intent) {
intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
diff --git a/src/com/android/contacts/activities/ContactsFrontDoor.java b/src/com/android/contacts/activities/ContactsFrontDoor.java
index 3677cce..d34f2c4 100644
--- a/src/com/android/contacts/activities/ContactsFrontDoor.java
+++ b/src/com/android/contacts/activities/ContactsFrontDoor.java
@@ -17,7 +17,6 @@
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;
@@ -39,12 +38,12 @@
| 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 {
+ if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
// No tabs, just a contact list
intent.setClass(this, ContactBrowserActivity.class);
+ } else {
+ // Default to the normal dialtacts layout
+ intent.setClass(this, DialtactsActivity.class);
}
startActivity(intent);
diff --git a/src/com/android/contacts/TwelveKeyDialer.java b/src/com/android/contacts/activities/DialpadActivity.java
similarity index 98%
rename from src/com/android/contacts/TwelveKeyDialer.java
rename to src/com/android/contacts/activities/DialpadActivity.java
index b93375c..cfbdff2 100644
--- a/src/com/android/contacts/TwelveKeyDialer.java
+++ b/src/com/android/contacts/activities/DialpadActivity.java
@@ -14,8 +14,12 @@
* limitations under the License.
*/
-package com.android.contacts;
+package com.android.contacts.activities;
+import com.android.contacts.ContactsSearchManager;
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.R;
+import com.android.contacts.SpecialCharSequenceMgr;
import com.android.internal.telephony.ITelephony;
import com.android.phone.CallLogAsync;
import com.android.phone.HapticFeedback;
@@ -71,11 +75,11 @@
* Dialer activity that displays the typical twelve key interface.
*/
@SuppressWarnings("deprecation")
-public class TwelveKeyDialer extends Activity implements View.OnClickListener,
+public class DialpadActivity extends Activity implements View.OnClickListener,
View.OnLongClickListener, View.OnKeyListener,
AdapterView.OnItemClickListener, TextWatcher {
private static final String EMPTY_NUMBER = "";
- private static final String TAG = "TwelveKeyDialer";
+ private static final String TAG = "DialpadActivity";
/** The length of DTMF tones in milliseconds */
private static final int TONE_LENGTH_MS = 150;
@@ -278,7 +282,7 @@
* Overridden by subclasses to control the resource used by the content view.
*/
protected int getContentViewResource() {
- return R.layout.twelve_key_dialer;
+ return R.layout.dialpad_activity;
}
private boolean resolveIntent() {
diff --git a/src/com/android/contacts/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
similarity index 96%
rename from src/com/android/contacts/DialtactsActivity.java
rename to src/com/android/contacts/activities/DialtactsActivity.java
index dc69194..5054309 100644
--- a/src/com/android/contacts/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package com.android.contacts;
+package com.android.contacts.activities;
+import com.android.contacts.R;
import com.android.contacts.activities.ContactsFrontDoor;
import com.android.contacts.activities.ContactBrowserActivity;
+import com.android.contacts.activities.DialpadActivity;
import com.android.internal.telephony.ITelephony;
import android.app.Activity;
@@ -42,7 +44,7 @@
* The dialer tab's title is 'phone', a more common name (see strings.xml).
*/
public class DialtactsActivity extends TabActivity implements TabHost.OnTabChangeListener {
- private static final String TAG = "Dailtacts";
+ private static final String TAG = "DialtactsActivity";
private static final int TAB_INDEX_DIALER = 0;
private static final int TAB_INDEX_CALL_LOG = 1;
@@ -79,7 +81,7 @@
fixIntent(intent);
requestWindowFeature(Window.FEATURE_NO_TITLE);
- setContentView(R.layout.dialer_activity);
+ setContentView(R.layout.dialtacts_activity);
mTabHost = getTabHost();
mTabHost.setOnTabChangedListener(this);
@@ -132,7 +134,7 @@
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);
+ intent.setClass(this, CallLogActivity.class);
mTabHost.addTab(mTabHost.newTabSpec("call_log")
.setIndicator(getString(R.string.recentCallsIconLabel),
@@ -142,7 +144,7 @@
private void setupDialerTab() {
Intent intent = new Intent("com.android.phone.action.TOUCH_DIALER");
- intent.setClass(this, TwelveKeyDialer.class);
+ intent.setClass(this, DialpadActivity.class);
mTabHost.addTab(mTabHost.newTabSpec("dialer")
.setIndicator(getString(R.string.dialerIconLabel),
@@ -151,7 +153,7 @@
}
private void setupContactsTab() {
- Intent intent = new Intent(UI.LIST_DEFAULT);
+ Intent intent = new Intent(UI.LIST_ALL_CONTACTS_ACTION);
intent.setClass(this, ContactBrowserActivity.class);
mTabHost.addTab(mTabHost.newTabSpec("contacts")
diff --git a/src/com/android/contacts/RecentCallsListActivity.java b/src/com/android/contacts/calllog/CallLogFragment.java
similarity index 76%
rename from src/com/android/contacts/RecentCallsListActivity.java
rename to src/com/android/contacts/calllog/CallLogFragment.java
index 0a3c178..3850a0b 100644
--- a/src/com/android/contacts/RecentCallsListActivity.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * 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.
@@ -14,23 +14,20 @@
* limitations under the License.
*/
-package com.android.contacts;
+package com.android.contacts.calllog;
import com.android.common.widget.GroupingListAdapter;
+import com.android.contacts.CallDetailActivity;
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.R;
import com.android.internal.telephony.CallerInfo;
-import com.android.internal.telephony.ITelephony;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.ListActivity;
-import android.content.ActivityNotFoundException;
+import android.app.ListFragment;
import android.content.AsyncQueryHandler;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.content.DialogInterface.OnClickListener;
import android.database.CharArrayBuffer;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabaseCorruptException;
@@ -43,9 +40,6 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.provider.ContactsContract.CommonDataKinds.Phone;
@@ -60,15 +54,14 @@
import android.text.format.DateUtils;
import android.util.Log;
import android.view.ContextMenu;
-import android.view.KeyEvent;
+import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
-import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
@@ -81,65 +74,70 @@
/**
* Displays a list of call log entries.
*/
-public class RecentCallsListActivity extends ListActivity
+public class CallLogFragment extends ListFragment
implements View.OnCreateContextMenuListener {
- private static final String TAG = "RecentCallsList";
+ private static final String TAG = "CallLogFragment";
- /** The projection to use when querying the call log table */
- static final String[] CALL_LOG_PROJECTION = new String[] {
- Calls._ID,
- Calls.NUMBER,
- Calls.DATE,
- Calls.DURATION,
- Calls.TYPE,
- Calls.CACHED_NAME,
- Calls.CACHED_NUMBER_TYPE,
- Calls.CACHED_NUMBER_LABEL,
- Calls.COUNTRY_ISO};
+ /** The query for the call log table */
+ private static final class CallLogQuery {
+ public static final String[] _PROJECTION = new String[] {
+ Calls._ID,
+ Calls.NUMBER,
+ Calls.DATE,
+ Calls.DURATION,
+ Calls.TYPE,
+ Calls.CACHED_NAME,
+ Calls.CACHED_NUMBER_TYPE,
+ Calls.CACHED_NUMBER_LABEL,
+ Calls.COUNTRY_ISO};
- static final int ID_COLUMN_INDEX = 0;
- static final int NUMBER_COLUMN_INDEX = 1;
- static final int DATE_COLUMN_INDEX = 2;
- static final int DURATION_COLUMN_INDEX = 3;
- static final int CALL_TYPE_COLUMN_INDEX = 4;
- static final int CALLER_NAME_COLUMN_INDEX = 5;
- static final int CALLER_NUMBERTYPE_COLUMN_INDEX = 6;
- static final int CALLER_NUMBERLABEL_COLUMN_INDEX = 7;
- static final int COUNTRY_ISO_COLUMN_INDEX = 8;
+ public static final int ID = 0;
+ public static final int NUMBER = 1;
+ public static final int DATE = 2;
+ public static final int DURATION = 3;
+ public static final int CALL_TYPE = 4;
+ public static final int CALLER_NAME = 5;
+ public static final int CALLER_NUMBERTYPE = 6;
+ public static final int CALLER_NUMBERLABEL = 7;
+ public static final int COUNTRY_ISO = 8;
+ }
- /** The projection to use when querying the phones table */
- static final String[] PHONES_PROJECTION = new String[] {
- PhoneLookup._ID,
- PhoneLookup.DISPLAY_NAME,
- PhoneLookup.TYPE,
- PhoneLookup.LABEL,
- PhoneLookup.NUMBER,
- PhoneLookup.NORMALIZED_NUMBER};
+ /** The query to use for the phones table */
+ private static final class PhoneQuery {
+ public static final String[] _PROJECTION = new String[] {
+ PhoneLookup._ID,
+ PhoneLookup.DISPLAY_NAME,
+ PhoneLookup.TYPE,
+ PhoneLookup.LABEL,
+ 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;
+ public static final int PERSON_ID = 0;
+ public static final int NAME = 1;
+ public static final int PHONE_TYPE = 2;
+ public static final int LABEL = 3;
+ public static final int MATCHED_NUMBER = 4;
+ public static final int NORMALIZED_NUMBER = 5;
+ }
- 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 class MenuItems {
+ public static final int DELETE = 1;
+ }
+
+ private static final class OptionsMenuItems {
+ public static final int DELETE_ALL = 1;
+ }
private static final int QUERY_TOKEN = 53;
private static final int UPDATE_TOKEN = 54;
- private static final int DIALOG_CONFIRM_DELETE_ALL = 1;
-
- RecentCallsAdapter mAdapter;
+ private CallLogAdapter mAdapter;
private QueryHandler mQueryHandler;
- String mVoiceMailNumber;
+ private String mVoiceMailNumber;
private String mCurrentCountryIso;
-
private boolean mScrollToTop;
- static final class ContactInfo {
+ public static final class ContactInfo {
public long personId;
public String name;
public int type;
@@ -151,27 +149,27 @@
public static ContactInfo EMPTY = new ContactInfo();
}
- public static final class RecentCallsListItemViews {
- TextView line1View;
- TextView labelView;
- TextView numberView;
- TextView dateView;
- ImageView iconView;
- View callView;
- ImageView groupIndicator;
- TextView groupSize;
+ public static final class CallLogListItemViews {
+ public TextView line1View;
+ public TextView labelView;
+ public TextView numberView;
+ public TextView dateView;
+ public ImageView iconView;
+ public View callView;
+ public ImageView groupIndicator;
+ public TextView groupSize;
}
- static final class CallerInfoQuery {
- String number;
- int position;
- String name;
- int numberType;
- String numberLabel;
+ public static final class CallerInfoQuery {
+ public String number;
+ public int position;
+ public String name;
+ public int numberType;
+ public String numberLabel;
}
/** Adapter class to fill in data for the Call Log */
- final class RecentCallsAdapter extends GroupingListAdapter
+ public final class CallLogAdapter extends GroupingListAdapter
implements Runnable, ViewTreeObserver.OnPreDrawListener, View.OnClickListener {
HashMap<String,ContactInfo> mContactInfo;
private final LinkedList<CallerInfoQuery> mRequests;
@@ -195,6 +193,7 @@
private CharArrayBuffer mBuffer1 = new CharArrayBuffer(128);
private CharArrayBuffer mBuffer2 = new CharArrayBuffer(128);
+ @Override
public void onClick(View view) {
String number = (String) view.getTag();
if (!TextUtils.isEmpty(number)) {
@@ -211,6 +210,7 @@
}
}
+ @Override
public boolean onPreDraw() {
if (mFirst) {
mHandler.sendEmptyMessageDelayed(START_THREAD, 1000);
@@ -233,8 +233,8 @@
}
};
- public RecentCallsAdapter() {
- super(RecentCallsListActivity.this);
+ public CallLogAdapter() {
+ super(getActivity());
mContactInfo = new HashMap<String,ContactInfo>();
mRequests = new LinkedList<CallerInfoQuery>();
@@ -307,7 +307,7 @@
values.put(Calls.CACHED_NUMBER_LABEL, ci.label);
try {
- RecentCallsListActivity.this.getContentResolver().update(Calls.CONTENT_URI, values,
+ getActivity().getContentResolver().update(Calls.CONTENT_URI, values,
Calls.NUMBER + "='" + ciq.number + "'", null);
} catch (SQLiteDiskIOException e) {
Log.w(TAG, "Exception while updating call info", e);
@@ -369,7 +369,7 @@
String[] selectionArgs = new String[] { ciq.number.toUpperCase() };
Cursor dataTableCursor =
- RecentCallsListActivity.this.getContentResolver().query(
+ getActivity().getContentResolver().query(
contactRef,
null, // projection
selection, // selection
@@ -410,20 +410,21 @@
// "number" is a regular phone number, so use the
// PhoneLookup table:
Cursor phonesCursor =
- RecentCallsListActivity.this.getContentResolver().query(
+ getActivity().getContentResolver().query(
Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(ciq.number)),
- PHONES_PROJECTION, null, null, null);
+ PhoneQuery._PROJECTION, null, null, null);
if (phonesCursor != null) {
if (phonesCursor.moveToFirst()) {
info = new ContactInfo();
- info.personId = phonesCursor.getLong(PERSON_ID_COLUMN_INDEX);
- info.name = phonesCursor.getString(NAME_COLUMN_INDEX);
- info.type = phonesCursor.getInt(PHONE_TYPE_COLUMN_INDEX);
- info.label = phonesCursor.getString(LABEL_COLUMN_INDEX);
- info.number = phonesCursor.getString(MATCHED_NUMBER_COLUMN_INDEX);
- info.normalizedNumber =
- phonesCursor.getString(NORMALIZED_NUMBER_COLUMN_INDEX);
+ info.personId = phonesCursor.getLong(PhoneQuery.PERSON_ID);
+ info.name = phonesCursor.getString(PhoneQuery.NAME);
+ info.type = phonesCursor.getInt(PhoneQuery.PHONE_TYPE);
+ info.label = phonesCursor.getString(PhoneQuery.LABEL);
+ info.number = phonesCursor
+ .getString(PhoneQuery.MATCHED_NUMBER);
+ info.normalizedNumber = phonesCursor
+ .getString(PhoneQuery.NORMALIZED_NUMBER);
infoUpdated = true;
}
@@ -452,6 +453,7 @@
* Handles requests for contact name and number type
* @see java.lang.Runnable#run()
*/
+ @Override
public void run() {
boolean needNotify = false;
while (!mDone) {
@@ -490,11 +492,11 @@
CharArrayBuffer currentValue = mBuffer1;
CharArrayBuffer value = mBuffer2;
cursor.moveToFirst();
- cursor.copyStringToBuffer(NUMBER_COLUMN_INDEX, currentValue);
- int currentCallType = cursor.getInt(CALL_TYPE_COLUMN_INDEX);
+ cursor.copyStringToBuffer(CallLogQuery.NUMBER, currentValue);
+ int currentCallType = cursor.getInt(CallLogQuery.CALL_TYPE);
for (int i = 1; i < count; i++) {
cursor.moveToNext();
- cursor.copyStringToBuffer(NUMBER_COLUMN_INDEX, value);
+ cursor.copyStringToBuffer(CallLogQuery.NUMBER, value);
boolean sameNumber = equalPhoneNumbers(value, currentValue);
// Group adjacent calls with the same number. Make an exception
@@ -520,7 +522,7 @@
if (sameNumber && currentCallType == Calls.MISSED_TYPE) {
currentCallType = 0; // "not a missed call"
} else {
- currentCallType = cursor.getInt(CALL_TYPE_COLUMN_INDEX);
+ currentCallType = cursor.getInt(CallLogQuery.CALL_TYPE);
}
}
}
@@ -539,16 +541,16 @@
@Override
- protected View newStandAloneView(Context context, ViewGroup parent) {
+ public View newStandAloneView(Context context, ViewGroup parent) {
LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View view = inflater.inflate(R.layout.recent_calls_list_item, parent, false);
+ View view = inflater.inflate(R.layout.call_log_list_item, parent, false);
findAndCacheViews(view);
return view;
}
@Override
- protected void bindStandAloneView(View view, Context context, Cursor cursor) {
+ public void bindStandAloneView(View view, Context context, Cursor cursor) {
bindView(context, view, cursor);
}
@@ -556,7 +558,7 @@
protected View newChildView(Context context, ViewGroup parent) {
LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View view = inflater.inflate(R.layout.recent_calls_list_child_item, parent, false);
+ View view = inflater.inflate(R.layout.call_log_list_child_item, parent, false);
findAndCacheViews(view);
return view;
}
@@ -570,7 +572,7 @@
protected View newGroupView(Context context, ViewGroup parent) {
LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View view = inflater.inflate(R.layout.recent_calls_list_group_item, parent, false);
+ View view = inflater.inflate(R.layout.call_log_list_group_item, parent, false);
findAndCacheViews(view);
return view;
}
@@ -578,7 +580,7 @@
@Override
protected void bindGroupView(View view, Context context, Cursor cursor, int groupSize,
boolean expanded) {
- final RecentCallsListItemViews views = (RecentCallsListItemViews) view.getTag();
+ final CallLogListItemViews views = (CallLogListItemViews) view.getTag();
int groupIndicator = expanded
? com.android.internal.R.drawable.expander_ic_maximized
: com.android.internal.R.drawable.expander_ic_minimized;
@@ -590,7 +592,7 @@
private void findAndCacheViews(View view) {
// Get the views to bind to
- RecentCallsListItemViews views = new RecentCallsListItemViews();
+ CallLogListItemViews views = new CallLogListItemViews();
views.line1View = (TextView) view.findViewById(R.id.line1);
views.labelView = (TextView) view.findViewById(R.id.label);
views.numberView = (TextView) view.findViewById(R.id.number);
@@ -604,14 +606,14 @@
}
public void bindView(Context context, View view, Cursor c) {
- final RecentCallsListItemViews views = (RecentCallsListItemViews) view.getTag();
+ final CallLogListItemViews views = (CallLogListItemViews) view.getTag();
- String number = c.getString(NUMBER_COLUMN_INDEX);
+ String number = c.getString(CallLogQuery.NUMBER);
String formattedNumber = null;
- 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);
+ String callerName = c.getString(CallLogQuery.CALLER_NAME);
+ int callerNumberType = c.getInt(CallLogQuery.CALLER_NUMBERTYPE);
+ String callerNumberLabel = c.getString(CallLogQuery.CALLER_NUMBERLABEL);
+ String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
// Store away the number so we can call it directly if you click on the call icon
views.callView.setTag(number);
@@ -728,7 +730,7 @@
views.labelView.setVisibility(View.GONE);
}
- long date = c.getLong(DATE_COLUMN_INDEX);
+ long date = c.getLong(CallLogQuery.DATE);
// Set the date/time field by mixing relative and absolute times.
int flags = DateUtils.FORMAT_ABBREV_RELATIVE;
@@ -737,7 +739,7 @@
System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, flags));
if (views.iconView != null) {
- int type = c.getInt(CALL_TYPE_COLUMN_INDEX);
+ int type = c.getInt(CallLogQuery.CALL_TYPE);
// Set the icon
switch (type) {
case Calls.INCOMING_TYPE:
@@ -764,7 +766,7 @@
}
private static final class QueryHandler extends AsyncQueryHandler {
- private final WeakReference<RecentCallsListActivity> mActivity;
+ private final WeakReference<CallLogFragment> mFragment;
/**
* Simple handler that wraps background calls to catch
@@ -796,25 +798,26 @@
return new CatchingWorkerHandler(looper);
}
- public QueryHandler(Context context) {
- super(context.getContentResolver());
- mActivity = new WeakReference<RecentCallsListActivity>(
- (RecentCallsListActivity) context);
+ public QueryHandler(CallLogFragment fragment) {
+ super(fragment.getActivity().getContentResolver());
+ mFragment = new WeakReference<CallLogFragment>(fragment);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- final RecentCallsListActivity activity = mActivity.get();
- if (activity != null && !activity.isFinishing()) {
- final RecentCallsListActivity.RecentCallsAdapter callsAdapter = activity.mAdapter;
+ final CallLogFragment fragment = mFragment.get();
+ // TODO: Do we still need the isFinishing check if we use a fragmented-Tabs?
+ if (fragment != null && !fragment.getActivity().isFinishing()) {
+ final CallLogFragment.CallLogAdapter callsAdapter = fragment.mAdapter;
callsAdapter.setLoading(false);
callsAdapter.changeCursor(cursor);
- if (activity.mScrollToTop) {
- if (activity.mList.getFirstVisiblePosition() > 5) {
- activity.mList.setSelection(5);
+ if (fragment.mScrollToTop) {
+ final ListView listView = fragment.getListView();
+ if (listView.getFirstVisiblePosition() > 5) {
+ listView.setSelection(5);
}
- activity.mList.smoothScrollToPosition(0);
- activity.mScrollToTop = false;
+ listView.smoothScrollToPosition(0);
+ fragment.mScrollToTop = false;
}
} else {
cursor.close();
@@ -823,33 +826,39 @@
}
@Override
- protected void onCreate(Bundle state) {
+ public void onCreate(Bundle state) {
super.onCreate(state);
- setContentView(R.layout.recent_calls);
-
- // Typing here goes to the dialer
- setDefaultKeyMode(DEFAULT_KEYS_DIALER);
-
- mAdapter = new RecentCallsAdapter();
- getListView().setOnCreateContextMenuListener(this);
- setListAdapter(mAdapter);
-
- mVoiceMailNumber = ((TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE))
- .getVoiceMailNumber();
+ mVoiceMailNumber = ((TelephonyManager) getActivity().getSystemService(
+ Context.TELEPHONY_SERVICE)).getVoiceMailNumber();
mQueryHandler = new QueryHandler(this);
- mCurrentCountryIso = ContactsUtils.getCurrentCountryIso(this);
+ mCurrentCountryIso = ContactsUtils.getCurrentCountryIso(getActivity());
+
+ setHasOptionsMenu(true);
}
@Override
- protected void onStart() {
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ return inflater.inflate(R.layout.call_log_fragment, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setOnCreateContextMenuListener(this);
+ mAdapter = new CallLogAdapter();
+ setListAdapter(mAdapter);
+ }
+
+ @Override
+ public void onStart() {
mScrollToTop = true;
super.onStart();
}
@Override
- protected void onResume() {
+ public void onResume() {
// The adapter caches looked up numbers, clear it so they will get
// looked up again.
if (mAdapter != null) {
@@ -865,7 +874,7 @@
}
@Override
- protected void onPause() {
+ public void onPause() {
super.onPause();
// Kill the requests thread
@@ -873,34 +882,12 @@
}
@Override
- protected void onDestroy() {
+ public void onDestroy() {
super.onDestroy();
mAdapter.stopRequestProcessing();
mAdapter.changeCursor(null);
}
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
-
- // Clear notifications only when window gains focus. This activity won't
- // immediately receive focus if the keyguard screen is above it.
- if (hasFocus) {
- try {
- ITelephony iTelephony =
- ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
- if (iTelephony != null) {
- iTelephony.cancelMissedCallsNotification();
- } else {
- Log.w(TAG, "Telephony service is null, can't call " +
- "cancelMissedCallsNotification");
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to clear missed calls notification due to remote exception");
- }
- }
- }
-
/**
* Format the given phone number
*
@@ -944,14 +931,14 @@
// Cancel any pending queries
mQueryHandler.cancelOperation(QUERY_TOKEN);
mQueryHandler.startQuery(QUERY_TOKEN, null, Calls.CONTENT_URI,
- CALL_LOG_PROJECTION, null, null, Calls.DEFAULT_SORT_ORDER);
+ CallLogQuery._PROJECTION, null, null, Calls.DEFAULT_SORT_ORDER);
}
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, MENU_ITEM_DELETE_ALL, 0, R.string.recentCalls_deleteAll)
- .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
- return true;
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ menu.add(0, OptionsMenuItems.DELETE_ALL, 0, R.string.recentCalls_deleteAll).setIcon(
+ android.R.drawable.ic_menu_close_clear_cancel);
}
@Override
@@ -966,7 +953,7 @@
Cursor cursor = (Cursor) mAdapter.getItem(menuInfo.position);
- String number = cursor.getString(NUMBER_COLUMN_INDEX);
+ String number = cursor.getString(CallLogQuery.NUMBER);
Uri numberUri = null;
boolean isVoicemail = false;
boolean isSipNumber = false;
@@ -1033,45 +1020,14 @@
menu.add(0, 0, 0, R.string.recentCalls_addToContact)
.setIntent(intent);
}
- menu.add(0, MENU_ITEM_DELETE, 0, R.string.recentCalls_removeFromRecentList);
- }
-
- @Override
- protected Dialog onCreateDialog(int id, Bundle args) {
- switch (id) {
- case DIALOG_CONFIRM_DELETE_ALL:
- return new AlertDialog.Builder(this)
- .setTitle(R.string.clearCallLogConfirmation_title)
- .setIconAttribute(android.R.attr.alertDialogIcon)
- .setMessage(R.string.clearCallLogConfirmation)
- .setNegativeButton(android.R.string.cancel, null)
- .setPositiveButton(android.R.string.ok, new OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- getContentResolver().delete(Calls.CONTENT_URI, null, null);
- // TODO The change notification should do this automatically, but it
- // isn't working right now. Remove this when the change notification
- // is working properly.
- startQuery();
- }
- })
- .setCancelable(false)
- .create();
- }
- return null;
+ menu.add(0, MenuItems.DELETE, 0, R.string.recentCalls_removeFromRecentList);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case MENU_ITEM_DELETE_ALL: {
- 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);
+ case OptionsMenuItems.DELETE_ALL: {
+ ClearCallLogDialog.show(getFragmentManager());
return true;
}
}
@@ -1090,7 +1046,7 @@
}
switch (item.getItemId()) {
- case MENU_ITEM_DELETE: {
+ case MenuItems.DELETE: {
Cursor cursor = (Cursor)mAdapter.getItem(menuInfo.position);
int groupSize = 1;
if (mAdapter.isGroupHeader(menuInfo.position)) {
@@ -1103,58 +1059,17 @@
sb.append(",");
cursor.moveToNext();
}
- long id = cursor.getLong(ID_COLUMN_INDEX);
+ long id = cursor.getLong(CallLogQuery.ID);
sb.append(id);
}
- getContentResolver().delete(Calls.CONTENT_URI, Calls._ID + " IN (" + sb + ")",
- null);
+ getActivity().getContentResolver().delete(Calls.CONTENT_URI,
+ Calls._ID + " IN (" + sb + ")", null);
}
}
return super.onContextItemSelected(item);
}
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_CALL: {
- long callPressDiff = SystemClock.uptimeMillis() - event.getDownTime();
- if (callPressDiff >= ViewConfiguration.getLongPressTimeout()) {
- // Launch voice dialer
- Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- startActivity(intent);
- } catch (ActivityNotFoundException e) {
- }
- return true;
- }
- }
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_CALL:
- try {
- ITelephony phone = ITelephony.Stub.asInterface(
- ServiceManager.checkService("phone"));
- if (phone != null && !phone.isIdle()) {
- // Let the super class handle it
- break;
- }
- } catch (RemoteException re) {
- // Fall through and try to call the contact
- }
-
- callEntry(getListView().getSelectedItemPosition());
- return true;
- }
- return super.onKeyUp(keyCode, event);
- }
-
/*
* Get the number from the Contacts, if available, since sometimes
* the number provided by caller id may not be formatted properly
@@ -1172,14 +1087,12 @@
matchingNumber = ci.number;
} else {
try {
- Cursor phonesCursor =
- RecentCallsListActivity.this.getContentResolver().query(
- Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
- number),
- PHONES_PROJECTION, null, null, null);
+ Cursor phonesCursor = getActivity().getContentResolver().query(
+ Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number),
+ PhoneQuery._PROJECTION, null, null, null);
if (phonesCursor != null) {
if (phonesCursor.moveToFirst()) {
- matchingNumber = phonesCursor.getString(MATCHED_NUMBER_COLUMN_INDEX);
+ matchingNumber = phonesCursor.getString(PhoneQuery.MATCHED_NUMBER);
}
phonesCursor.close();
}
@@ -1195,7 +1108,8 @@
return number;
}
- private void callEntry(int position) {
+ public void callSelectedEntry() {
+ int position = getListView().getSelectedItemPosition();
if (position < 0) {
// In touch mode you may often not have something selected, so
// just call the first entry to make sure that [send] [send] calls the
@@ -1204,7 +1118,7 @@
}
final Cursor cursor = (Cursor)mAdapter.getItem(position);
if (cursor != null) {
- String number = cursor.getString(NUMBER_COLUMN_INDEX);
+ String number = cursor.getString(CallLogQuery.NUMBER);
if (TextUtils.isEmpty(number)
|| number.equals(CallerInfo.UNKNOWN_NUMBER)
|| number.equals(CallerInfo.PRIVATE_NUMBER)
@@ -1220,7 +1134,7 @@
} else {
// We're calling a regular PSTN phone number.
// Construct a tel: URI, but do some other possible cleanup first.
- int callType = cursor.getInt(CALL_TYPE_COLUMN_INDEX);
+ int callType = cursor.getInt(CallLogQuery.CALL_TYPE);
if (!number.startsWith("+") &&
(callType == Calls.INCOMING_TYPE
|| callType == Calls.MISSED_TYPE)) {
@@ -1237,23 +1151,21 @@
}
@Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
+ public void onListItemClick(ListView l, View v, int position, long id) {
if (mAdapter.isGroupHeader(position)) {
mAdapter.toggleGroup(position);
} else {
- Intent intent = new Intent(this, CallDetailActivity.class);
+ Intent intent = new Intent(getActivity(), CallDetailActivity.class);
intent.setData(ContentUris.withAppendedId(CallLog.Calls.CONTENT_URI, id));
startActivity(intent);
}
}
- @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);
- }
+ public CallLogAdapter getAdapter() {
+ return mAdapter;
+ }
+
+ public String getVoiceMailNumber() {
+ return mVoiceMailNumber;
}
}
diff --git a/src/com/android/contacts/calllog/ClearCallLogDialog.java b/src/com/android/contacts/calllog/ClearCallLogDialog.java
new file mode 100644
index 0000000..426732a
--- /dev/null
+++ b/src/com/android/contacts/calllog/ClearCallLogDialog.java
@@ -0,0 +1,78 @@
+/*
+ * 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
+ */
+
+package com.android.contacts.calllog;
+
+import com.android.contacts.R;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.app.ProgressDialog;
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.CallLog.Calls;
+
+/**
+ * Dialog that clears the call log after confirming with the user
+ */
+public class ClearCallLogDialog extends DialogFragment {
+ /** Preferred way to show this dialog */
+ public static void show(FragmentManager fragmentManager) {
+ ClearCallLogDialog dialog = new ClearCallLogDialog();
+ dialog.show(fragmentManager, "deleteCallLog");
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final ContentResolver resolver = getActivity().getContentResolver();
+ final OnClickListener okListener = new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final ProgressDialog progressDialog = ProgressDialog.show(getActivity(),
+ getString(R.string.clearCallLogProgress_title),
+ "", true, false);
+ final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ resolver.delete(Calls.CONTENT_URI, null, null);
+ return null;
+ }
+ @Override
+ protected void onPostExecute(Void result) {
+ progressDialog.dismiss();
+ }
+ };
+ // TODO: Once we have the API, we should configure this ProgressDialog
+ // to only show up after a certain time (e.g. 150ms)
+ progressDialog.show();
+ task.execute();
+ }
+ };
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.clearCallLogConfirmation_title)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setMessage(R.string.clearCallLogConfirmation)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, okListener)
+ .setCancelable(true)
+ .create();
+ }
+}
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 42b3293..88008a3 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -27,9 +27,9 @@
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.model.DataKind;
import com.android.contacts.util.Constants;
import com.android.contacts.util.DataStatus;
import com.android.contacts.util.DateUtils;
@@ -164,34 +164,26 @@
* 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 ArrayList<DetailViewEntry> mPhoneEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<DetailViewEntry> mSmsEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<DetailViewEntry> mEmailEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<DetailViewEntry> mPostalEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<DetailViewEntry> mImEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<DetailViewEntry> mNicknameEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<DetailViewEntry> mGroupEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<DetailViewEntry> mRelationEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<DetailViewEntry> mNoteEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<DetailViewEntry> mWebsiteEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<DetailViewEntry> mSipEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<DetailViewEntry> mEventEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<DetailViewEntry> mOtherEntries = new ArrayList<DetailViewEntry>();
+ private ArrayList<ViewEntry> mAllEntries = new 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
@@ -223,9 +215,6 @@
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);
@@ -304,19 +293,22 @@
return;
}
- // Set the header
- mHeaderView.loadData(mContactData);
+ // Clear old header
+ mHeaderView = null;
// Build up the contact entries
buildEntries();
- // Collapse similar data items in select sections.
+ // Collapse similar data items for select {@link DataKind}s.
Collapser.collapseList(mPhoneEntries);
Collapser.collapseList(mSmsEntries);
Collapser.collapseList(mEmailEntries);
Collapser.collapseList(mPostalEntries);
Collapser.collapseList(mImEntries);
+ // Make one aggregated list of all entries for display to the user.
+ flattenAllLists();
+
if (mAdapter == null) {
mAdapter = new ViewAdapter();
mListView.setAdapter(mAdapter);
@@ -350,10 +342,7 @@
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();
- }
+ mAllEntries.clear();
mRawContactIds.clear();
@@ -409,8 +398,8 @@
accountType, mimeType);
if (kind == null) continue;
- final ViewEntry entry = ViewEntry.fromValues(mContext, mimeType, kind, dataId,
- entryValues, mContactData.isDirectoryEntry(),
+ final DetailViewEntry entry = DetailViewEntry.fromValues(mContext, mimeType, kind,
+ dataId, entryValues, mContactData.isDirectoryEntry(),
mContactData.getDirectoryId());
final boolean hasData = !TextUtils.isEmpty(entry.data);
@@ -464,8 +453,8 @@
final String imMime = Im.CONTENT_ITEM_TYPE;
final DataKind imKind = accountTypes.getKindOrFallback(accountType,
imMime);
- final ViewEntry imEntry = ViewEntry.fromValues(mContext, imMime, imKind,
- dataId, entryValues, mContactData.isDirectoryEntry(),
+ final DetailViewEntry imEntry = DetailViewEntry.fromValues(mContext, imMime,
+ imKind, dataId, entryValues, mContactData.isDirectoryEntry(),
mContactData.getDirectoryId());
buildImActions(imEntry, entryValues);
imEntry.applyStatus(status, false);
@@ -507,7 +496,7 @@
// Build note entries
entry.uri = null;
entry.maxLines = 100;
- mOtherEntries.add(entry);
+ mNoteEntries.add(entry);
} else if (Website.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
// Build Website entries
entry.uri = null;
@@ -519,7 +508,7 @@
} catch (ParseException e) {
Log.e(TAG, "Couldn't parse website: " + entry.data);
}
- mOtherEntries.add(entry);
+ mWebsiteEntries.add(entry);
} else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
// Build SipAddress entries
entry.uri = null;
@@ -531,17 +520,17 @@
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.
+ mSipEntries.add(entry);
+ // TODO: Now that SipAddress is in its own list of entries
+ // (instead of grouped in mOtherEntries), consider
+ // repositioning 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);
+ mEventEntries.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);
@@ -567,7 +556,7 @@
}
if (!groups.isEmpty()) {
- ViewEntry entry = new ViewEntry();
+ DetailViewEntry entry = new DetailViewEntry();
Collections.sort(groups);
StringBuilder sb = new StringBuilder();
int size = groups.size();
@@ -585,6 +574,49 @@
}
/**
+ * Collapse all contact detail entries into one aggregated list with a {@link HeaderViewEntry}
+ * at the top.
+ */
+ private void flattenAllLists() {
+ // All contacts should have a header view (even if there is no data for the contact).
+ mAllEntries.add(new HeaderViewEntry());
+
+ flattenList(mPhoneEntries);
+ flattenList(mSmsEntries);
+ flattenList(mEmailEntries);
+ flattenList(mImEntries);
+ flattenList(mPostalEntries);
+ flattenList(mNicknameEntries);
+ flattenList(mNoteEntries);
+ flattenList(mWebsiteEntries);
+ flattenList(mSipEntries);
+ flattenList(mEventEntries);
+ flattenList(mOtherEntries);
+ flattenList(mRelationEntries);
+ flattenList(mGroupEntries);
+ }
+
+ /**
+ * Iterate through {@link DetailViewEntry} in the given list and add it to a list of all
+ * entries. Add a {@link SeparatorViewEntry} at the end if the length of the list was not 0.
+ * Clear the original list.
+ */
+ private void flattenList(ArrayList<DetailViewEntry> entries) {
+ int count = entries.size();
+
+ for (int i = 0; i < count; i++) {
+ mAllEntries.add(entries.get(i));
+ }
+
+ if (count > 0) {
+ mAllEntries.add(new SeparatorViewEntry());
+ }
+
+ // Clear old list because it's not needed anymore.
+ entries.clear();
+ }
+
+ /**
* Maps group ID to the corresponding group name, collapses all synonymous groups.
* Ignores default groups (e.g. My Contacts) and favorites groups.
*/
@@ -621,7 +653,7 @@
* {@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) {
+ public static void buildImActions(DetailViewEntry entry, ContentValues values) {
final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(values.getAsString(Data.MIMETYPE));
if (!isEmail && !isProtocolValid(values)) {
@@ -690,15 +722,65 @@
}
/**
- * A basic structure with the data for a contact entry in the list.
+ * Base class for an item in the {@link ViewAdapter} list of data, which is
+ * supplied to the {@link ListView}.
*/
- static class ViewEntry implements Collapsible<ViewEntry> {
+ static class ViewEntry {
+ private final int viewTypeForAdapter;
+ protected long id = -1;
+ /** Whether or not the entry can be focused on or not. */
+ protected boolean isEnabled = false;
+
+ ViewEntry(int viewType) {
+ viewTypeForAdapter = viewType;
+ }
+
+ int getViewType() {
+ return viewTypeForAdapter;
+ }
+
+ long getId() {
+ return id;
+ }
+
+ boolean isEnabled(){
+ return isEnabled;
+ }
+ }
+
+ /**
+ * Header item in the {@link ViewAdapter} list of data.
+ */
+ static class HeaderViewEntry extends ViewEntry {
+
+ HeaderViewEntry() {
+ super(ViewAdapter.VIEW_TYPE_HEADER_ENTRY);
+ }
+
+ }
+
+ /**
+ * Separator between items of the same {@link DataKind} in the
+ * {@link ViewAdapter} list of data.
+ */
+ static class SeparatorViewEntry extends ViewEntry {
+
+ SeparatorViewEntry() {
+ super(ViewAdapter.VIEW_TYPE_SEPARATOR_ENTRY);
+ }
+
+ }
+
+ /**
+ * An item with a single detail for a contact in the {@link ViewAdapter}
+ * list of data.
+ */
+ static class DetailViewEntry extends ViewEntry implements Collapsible<DetailViewEntry> {
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;
@@ -717,17 +799,19 @@
public CharSequence footerLine = null;
- ViewEntry() {
+ DetailViewEntry() {
+ super(ViewAdapter.VIEW_TYPE_DETAIL_ENTRY);
+ isEnabled = true;
}
/**
- * Build new {@link ViewEntry} and populate from the given values.
+ * Build new {@link DetailViewEntry} and populate from the given values.
*/
- public static ViewEntry fromValues(Context context, String mimeType, DataKind kind,
+ public static DetailViewEntry fromValues(Context context, String mimeType, DataKind kind,
long dataId, ContentValues values, boolean isDirectoryEntry, long directoryId) {
- final ViewEntry entry = new ViewEntry();
- entry.context = context;
+ final DetailViewEntry entry = new DetailViewEntry();
entry.id = dataId;
+ entry.context = context;
entry.uri = ContentUris.withAppendedId(Data.CONTENT_URI, entry.id);
if (isDirectoryEntry) {
entry.uri = entry.uri.buildUpon().appendQueryParameter(
@@ -770,13 +854,13 @@
}
/**
- * Apply given {@link DataStatus} values over this {@link ViewEntry}
+ * Apply given {@link DataStatus} values over this {@link DetailViewEntry}
*
* @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) {
+ public DetailViewEntry applyStatus(DataStatus status, boolean fillData) {
presence = status.getPresence();
if (fillData && status.isValid()) {
this.data = status.getStatus().toString();
@@ -787,7 +871,7 @@
}
@Override
- public boolean collapseWith(ViewEntry entry) {
+ public boolean collapseWith(DetailViewEntry entry) {
// assert equal collapse keys
if (!shouldCollapseWith(entry)) {
return false;
@@ -816,13 +900,13 @@
// 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);
+ ids.add(entry.getId());
collapseCount++;
return true;
}
@Override
- public boolean shouldCollapseWith(ViewEntry entry) {
+ public boolean shouldCollapseWith(DetailViewEntry entry) {
if (entry == null) {
return false;
}
@@ -845,23 +929,64 @@
/** 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 secondaryActionButtonContainer;
public View secondaryActionDivider;
}
private final class ViewAdapter extends BaseAdapter {
+
+ public static final int VIEW_TYPE_DETAIL_ENTRY = 0;
+ public static final int VIEW_TYPE_HEADER_ENTRY = 1;
+ public static final int VIEW_TYPE_SEPARATOR_ENTRY = 2;
+ private static final int VIEW_TYPE_COUNT = 3;
+
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- final ViewEntry entry = getEntry(position);
+ switch (getItemViewType(position)) {
+ case VIEW_TYPE_HEADER_ENTRY:
+ return getHeaderEntryView(convertView, parent);
+ case VIEW_TYPE_SEPARATOR_ENTRY:
+ return getSeparatorEntryView(convertView, parent);
+ case VIEW_TYPE_DETAIL_ENTRY:
+ return getDetailEntryView(position, convertView, parent);
+ default:
+ throw new IllegalStateException("Invalid view type ID " +
+ getItemViewType(position));
+ }
+ }
+
+ private View getHeaderEntryView(View convertView, ViewGroup parent) {
+ // We don't want to rely on the recycled header view because it may
+ // have been left over from a previously viewed contact (since we
+ // reuse the adapter), so we would have to bind the data to the
+ // header each time. However, since there is only 1 header per list,
+ // just hold onto the original header view for this contact and
+ // return that each time.
+ if (mHeaderView != null) {
+ return mHeaderView;
+ }
+ mHeaderView = (ContactDetailHeaderView) mInflater.inflate(
+ R.layout.contact_detail_header_view_list_item, parent, false);
+ mHeaderView.setListener(mHeaderViewListener);
+ mHeaderView.loadData(mContactData);
+ return mHeaderView;
+ }
+
+ private View getSeparatorEntryView(View convertView, ViewGroup parent) {
+ if (convertView != null) {
+ return convertView;
+ }
+ return mInflater.inflate(R.layout.contact_detail_separator_list_item, parent, false);
+ }
+
+ private View getDetailEntryView(int position, View convertView, ViewGroup parent) {
+ final DetailViewEntry entry = (DetailViewEntry) getItem(position);
final View v;
final ViewCache viewCache;
@@ -876,46 +1001,49 @@
// 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.secondaryActionButtonContainer = v.findViewById(
+ R.id.secondary_action_button_container);
+ viewCache.secondaryActionButtonContainer.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;
+ final ViewEntry previousEntry = position == 0 ? null : getItem(position - 1);
+ final boolean isFirstOfItsKind = (previousEntry == null) ? true :
+ (previousEntry.getViewType() != VIEW_TYPE_DETAIL_ENTRY);
// Bind the data to the view
- bindView(v, entry, isFirstOfItsKind, isLast);
+ bindView(v, entry, isFirstOfItsKind);
return v;
}
- protected void bindView(View view, ViewEntry entry, boolean isFirstOfItsKind,
- boolean isLast) {
+ private void bindView(View view, DetailViewEntry entry, boolean isFirstOfItsKind) {
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);
+ if (isFirstOfItsKind) {
+ views.kind.setText(entry.kind != null ? entry.kind.toUpperCase() : "");
+ views.kind.setVisibility(View.VISIBLE);
+ } else {
+ views.kind.setVisibility(View.GONE);
}
- views.type.setText(entry.typeString);
- views.type.setVisibility(
- TextUtils.isEmpty(entry.typeString) ? View.GONE : View.VISIBLE);
+ if (!TextUtils.isEmpty(entry.typeString)) {
+ views.type.setText(entry.typeString.toUpperCase());
+ views.type.setVisibility(View.VISIBLE);
+ if (isFirstOfItsKind) {
+ views.kind.setVisibility(View.GONE);
+ }
+ } else {
+ views.type.setVisibility(View.GONE);
+ }
views.data.setText(entry.data);
setMaxLines(views.data, entry.maxLines);
@@ -928,24 +1056,6 @@
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);
@@ -972,11 +1082,11 @@
if (entry.secondaryIntent != null && secondaryActionIcon != null) {
secondaryActionView.setImageDrawable(secondaryActionIcon);
- secondaryActionView.setTag(entry);
- secondaryActionView.setVisibility(View.VISIBLE);
+ views.secondaryActionButtonContainer.setTag(entry);
+ views.secondaryActionButtonContainer.setVisibility(View.VISIBLE);
views.secondaryActionDivider.setVisibility(View.VISIBLE);
} else {
- secondaryActionView.setVisibility(View.GONE);
+ views.secondaryActionButtonContainer.setVisibility(View.GONE);
views.secondaryActionDivider.setVisibility(View.GONE);
}
}
@@ -998,8 +1108,9 @@
if (mListener == null) return;
if (v == null) return;
final ViewEntry entry = (ViewEntry) v.getTag();
- if (entry == null) return;
- final Intent intent = entry.secondaryIntent;
+ if (entry == null || !(entry instanceof DetailViewEntry)) return;
+ final DetailViewEntry detailViewEntry = (DetailViewEntry) entry;
+ final Intent intent = detailViewEntry.secondaryIntent;
if (intent == null) return;
mListener.onItemClicked(intent);
}
@@ -1007,40 +1118,42 @@
@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;
+ return mAllEntries.size();
}
@Override
- public Object getItem(int position) {
- return getEntry(position);
+ public ViewEntry getItem(int position) {
+ return mAllEntries.get(position);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return mAllEntries.get(position).getViewType();
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return VIEW_TYPE_COUNT;
}
@Override
public long getItemId(int position) {
- final ViewEntry entry = getEntry(position);
+ final ViewEntry entry = mAllEntries.get(position);
if (entry != null) {
- return entry.id;
+ return entry.getId();
}
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 boolean areAllItemsEnabled() {
+ // Header will always be an item that is not enabled.
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return getItem(position).isEnabled();
}
}
@@ -1186,9 +1299,9 @@
@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;
+ final ViewEntry entry = mAdapter.getItem(position);
+ if (entry == null || !(entry instanceof DetailViewEntry)) return;
+ final Intent intent = ((DetailViewEntry) entry).intent;
if (intent == null) return;
mListener.onItemClicked(intent);
}
@@ -1224,7 +1337,7 @@
int index = mListView.getSelectedItemPosition();
if (index != -1) {
- final ViewEntry entry = mAdapter.getEntry(index);
+ final DetailViewEntry entry = (DetailViewEntry) mAdapter.getItem(index);
if (entry != null && entry.intent != null &&
entry.intent.getAction() == Intent.ACTION_CALL_PRIVILEGED) {
mContext.startActivity(entry.intent);
@@ -1262,7 +1375,7 @@
@Override
public void onLoadFinished(Loader<ContactLoader.Result> loader, ContactLoader.Result data) {
- if (!((ContactLoader)loader).getLookupUri().equals(mLookupUri)) {
+ if (!mLookupUri.equals(data.getUri())) {
return;
}
diff --git a/src/com/android/contacts/editor/BaseRawContactEditorView.java b/src/com/android/contacts/editor/BaseRawContactEditorView.java
index 49ca863..38636f7 100644
--- a/src/com/android/contacts/editor/BaseRawContactEditorView.java
+++ b/src/com/android/contacts/editor/BaseRawContactEditorView.java
@@ -49,7 +49,6 @@
private PhotoEditorView mPhoto;
private boolean mHasPhotoEditor = false;
- private View mHeader;
private View mBody;
private View mDivider;
@@ -67,15 +66,8 @@
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());
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index ae6788b..eb28259 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -31,6 +31,7 @@
import com.android.contacts.model.EntityDeltaList;
import com.android.contacts.model.EntityModifier;
import com.android.contacts.model.GoogleAccountType;
+import com.android.contacts.util.AccountsListAdapter;
import android.accounts.Account;
import android.app.Activity;
@@ -78,7 +79,9 @@
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.ViewStub;
+import android.widget.AdapterView;
import android.widget.LinearLayout;
+import android.widget.ListPopupWindow;
import android.widget.Toast;
import java.io.File;
@@ -90,7 +93,7 @@
import java.util.List;
public class ContactEditorFragment extends Fragment implements
- SplitContactConfirmationDialogFragment.Listener, SelectAccountDialogFragment.Listener,
+ SplitContactConfirmationDialogFragment.Listener,
AggregationSuggestionEngine.Listener, AggregationSuggestionView.Listener,
ExternalRawContactEditorView.Listener {
@@ -313,7 +316,7 @@
} else {
// No Account specified. Let the user choose
// Load Accounts async so that we can present them
- selectAccountAndCreateContact();
+ createContact();
}
} else if (ContactEditorActivity.ACTION_SAVE_COMPLETED.equals(mAction)) {
// do nothing
@@ -437,7 +440,11 @@
}
}
- private void selectAccountAndCreateContact() {
+ /**
+ * Shows the account creation screen. An account associated with the contact is automatically
+ * selected. If there's no available account, device-local contact should be created.
+ */
+ private void createContact() {
final ArrayList<Account> accounts =
AccountTypeManager.getInstance(mContext).getAccounts(true);
// No Accounts available. Create a phone-local contact.
@@ -446,21 +453,16 @@
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);
+ // We have an account switcher in "create-account" screen, so don't need to ask a user to
+ // select an account here.
+ createContact(accounts.get(0));
}
+
/**
- * @param account may be null to signal a device-local contact should
- * be created.
+ * Shows account creation screen associated with a given account.
+ *
+ * @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);
@@ -476,31 +478,66 @@
}
}
+ /**
+ * Removes a current editor ({@link #mState}) and rebinds new editor for a new account.
+ * Some of old data are reused with new restriction enforced by the new account.
+ *
+ * @param oldState Old data being editted.
+ * @param oldAccount Old account associated with oldState.
+ * @param newAccount New account to be used.
+ */
+ private void rebindEditorsForNewContact(
+ EntityDelta oldState, Account oldAccount, Account newAccount) {
+ AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
+ AccountType oldAccountType = accountTypes.getAccountType(oldAccount.type);
+ AccountType newAccountType = accountTypes.getAccountType(newAccount.type);
+
+ if (newAccountType.getCreateContactActivityClassName() != null) {
+ Log.w(TAG, "external activity called in rebind situation");
+ if (mListener != null) {
+ mListener.onCustomCreateContactActivityRequested(newAccount, mIntentExtras);
+ }
+ } else {
+ mState = null;
+ bindEditorsForNewContact(newAccount, newAccountType, oldState, oldAccountType);
+ }
+ }
+
private void bindEditorsForNewContact(Account account, final AccountType accountType) {
+ bindEditorsForNewContact(account, accountType, null, null);
+ }
+
+ private void bindEditorsForNewContact(Account newAccount, final AccountType newAccountType,
+ EntityDelta oldState, AccountType oldAccountType) {
mStatus = Status.EDITING;
final ContentValues values = new ContentValues();
- if (account != null) {
- values.put(RawContacts.ACCOUNT_NAME, account.name);
- values.put(RawContacts.ACCOUNT_TYPE, account.type);
+ if (newAccount != null) {
+ values.put(RawContacts.ACCOUNT_NAME, newAccount.name);
+ values.put(RawContacts.ACCOUNT_TYPE, newAccount.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);
+ if (oldState == null) {
+ // Parse any values from incoming intent
+ EntityModifier.parseExtras(mContext, newAccountType, insert, mIntentExtras);
+ } else {
+ EntityModifier.migrateStateForNewContact(mContext, oldState, insert,
+ oldAccountType, newAccountType);
+ }
// 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);
+ EntityModifier.ensureKindExists(insert, newAccountType, Phone.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, newAccountType, Email.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, newAccountType, Note.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, newAccountType, Organization.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, newAccountType, Event.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, newAccountType, Website.CONTENT_ITEM_TYPE);
+ EntityModifier.ensureKindExists(insert, newAccountType, StructuredPostal.CONTENT_ITEM_TYPE);
if (mState == null) {
// Create state if none exists yet
@@ -545,6 +582,13 @@
editor = (BaseRawContactEditorView)
inflater.inflate(R.layout.raw_contact_editor_view, mContent, false);
}
+ if (Intent.ACTION_INSERT.equals(mAction) && size == 1) {
+ final ArrayList<Account> accounts =
+ AccountTypeManager.getInstance(mContext).getAccounts(true);
+ if (accounts.size() > 1) {
+ addAccountSwitcher(mState.get(0), editor);
+ }
+ }
editor.setEnabled(mEnabled);
mContent.addView(editor);
@@ -613,6 +657,40 @@
}
}
+ private void addAccountSwitcher(
+ final EntityDelta currentState, BaseRawContactEditorView editor) {
+ ValuesDelta values = currentState.getValues();
+ final Account currentAccount = new Account(
+ values.getAsString(RawContacts.ACCOUNT_NAME),
+ values.getAsString(RawContacts.ACCOUNT_TYPE));
+ final View accountView = editor.findViewById(R.id.account);
+ final View anchorView = editor.findViewById(R.id.anchor_for_account_switcher);
+ accountView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final ListPopupWindow popup = new ListPopupWindow(mContext, null);
+ final AccountsListAdapter adapter = new AccountsListAdapter(mContext, true);
+ popup.setWidth(anchorView.getWidth());
+ popup.setAnchorView(anchorView);
+ popup.setAdapter(adapter);
+ popup.setModal(true);
+ popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
+ popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position,
+ long id) {
+ popup.dismiss();
+ Account newAccount = adapter.getItem(position);
+ if (!newAccount.equals(currentAccount)) {
+ rebindEditorsForNewContact(currentState, currentAccount, newAccount);
+ }
+ }
+ });
+ popup.show();
+ }
+ });
+ }
+
@Override
public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.edit, menu);
@@ -1538,24 +1616,6 @@
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;
diff --git a/src/com/android/contacts/editor/Editor.java b/src/com/android/contacts/editor/Editor.java
index d733e68..a70bf3f 100644
--- a/src/com/android/contacts/editor/Editor.java
+++ b/src/com/android/contacts/editor/Editor.java
@@ -16,8 +16,8 @@
package com.android.contacts.editor;
+import com.android.contacts.model.DataKind;
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;
@@ -27,9 +27,7 @@
* {@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.
@@ -44,14 +42,21 @@
public static final int REQUEST_PICK_PHOTO = 1;
public static final int FIELD_CHANGED = 2;
+ public static final int FIELD_TURNED_EMPTY = 3;
+ public static final int FIELD_TURNED_NON_EMPTY = 4;
// 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;
+ public static final int EDITOR_FORM_CHANGED = 5;
}
/**
- * Prepare this editor for the given {@link ValuesDelta}, which
+ * Returns whether or not all the fields are empty in this {@link Editor}.
+ */
+ public boolean isEmpty();
+
+ /**
+ * Prepares 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.
*/
diff --git a/src/com/android/contacts/editor/EventFieldEditorView.java b/src/com/android/contacts/editor/EventFieldEditorView.java
index 906dbad..bf93d26 100644
--- a/src/com/android/contacts/editor/EventFieldEditorView.java
+++ b/src/com/android/contacts/editor/EventFieldEditorView.java
@@ -20,9 +20,9 @@
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.DataKind;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.util.DateUtils;
@@ -32,10 +32,8 @@
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;
@@ -49,7 +47,7 @@
/**
* Exchange requires 8:00 for birthdays
*/
- private final int DEFAULT_HOUR = 8;
+ private final static int DEFAULT_HOUR = 8;
private Button mDateView;
@@ -65,57 +63,30 @@
super(context, attrs, defStyle);
}
+ /** {@inheritDoc} */
@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;
- }
+ protected void onFinishInflate() {
+ super.onFinishInflate();
- @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));
+ mDateView = (Button) findViewById(R.id.date_view);
+ mDateView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showDialog(R.id.dialog_event_date_picker);
+ }
+ });
}
@Override
protected void requestFocusForFirstEditField() {
- if (mDateView != null) mDateView.requestFocus();
+ mDateView.requestFocus();
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
- if (mDateView != null) mDateView.setEnabled(!isReadOnly() && enabled);
+ mDateView.setEnabled(!isReadOnly() && enabled);
}
@Override
@@ -124,20 +95,7 @@
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);
- }
+ mDateView.setEnabled(isEnabled() && !readOnly);
rebuildDateView();
}
@@ -153,6 +111,11 @@
}
@Override
+ public boolean isEmpty() {
+ return TextUtils.isEmpty(mDateView.getText());
+ }
+
+ @Override
public Dialog createDialog(Bundle bundle) {
if (bundle == null) throw new IllegalArgumentException("bundle must not be null");
int dialogId = bundle.getInt(DIALOG_ID_KEY);
@@ -269,4 +232,11 @@
oldYear, oldMonth, oldDay, isYearOptional);
return resultDialog;
}
+
+ /**
+ * @return Default hour which should be used for birthday field.
+ */
+ public static int getDefaultHourForBirthday() {
+ return DEFAULT_HOUR;
+ }
}
diff --git a/src/com/android/contacts/editor/ExternalRawContactEditorView.java b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
index aaa1e44..89cace0 100644
--- a/src/com/android/contacts/editor/ExternalRawContactEditorView.java
+++ b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
@@ -20,7 +20,7 @@
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.DataKind;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.model.EntityModifier;
@@ -60,9 +60,9 @@
private Button mEditExternallyButton;
private ViewGroup mGeneral;
- private ImageView mHeaderIcon;
- private TextView mHeaderAccountType;
- private TextView mHeaderAccountName;
+ private ImageView mAccountIcon;
+ private TextView mAccountTypeTextView;
+ private TextView mAccountNameTextView;
private String mAccountName;
private String mAccountType;
@@ -102,9 +102,9 @@
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);
+ mAccountIcon = (ImageView) findViewById(R.id.account_icon);
+ mAccountTypeTextView = (TextView) findViewById(R.id.account_type);
+ mAccountNameTextView = (TextView) findViewById(R.id.account_name);
}
/**
@@ -132,11 +132,11 @@
accountType = mContext.getString(R.string.account_phone);
}
if (!TextUtils.isEmpty(mAccountName)) {
- mHeaderAccountName.setText(
+ mAccountNameTextView.setText(
mContext.getString(R.string.from_account_format, mAccountName));
}
- mHeaderAccountType.setText(mContext.getString(R.string.account_type_format, accountType));
- mHeaderIcon.setImageDrawable(type.getDisplayIcon(mContext));
+ mAccountTypeTextView.setText(mContext.getString(R.string.account_type_format, accountType));
+ mAccountIcon.setImageDrawable(type.getDisplayIcon(mContext));
mRawContactId = values.getAsLong(RawContacts._ID);
diff --git a/src/com/android/contacts/editor/GroupMembershipView.java b/src/com/android/contacts/editor/GroupMembershipView.java
index 8845aa3..d242a1b 100644
--- a/src/com/android/contacts/editor/GroupMembershipView.java
+++ b/src/com/android/contacts/editor/GroupMembershipView.java
@@ -19,7 +19,7 @@
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.DataKind;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.model.EntityModifier;
diff --git a/src/com/android/contacts/editor/KindSectionView.java b/src/com/android/contacts/editor/KindSectionView.java
index aaeafac..b472279 100644
--- a/src/com/android/contacts/editor/KindSectionView.java
+++ b/src/com/android/contacts/editor/KindSectionView.java
@@ -18,23 +18,22 @@
import com.android.contacts.R;
import com.android.contacts.editor.Editor.EditorListener;
-import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.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.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.ArrayList;
+import java.util.List;
/**
* Custom view for an entire section of data as segmented by
@@ -45,9 +44,8 @@
private static final String TAG = "KindSectionView";
private ViewGroup mEditors;
- private View mAddPlusButtonContainer;
- private ImageButton mAddPlusButton;
- private TextView mTitle;
+ private View mAddFieldFooter;
+ private TextView mAddFieldText;
private String mTitleString;
private DataKind mKind;
@@ -56,7 +54,7 @@
private ViewIdGenerator mViewIdGenerator;
- private int mMinLineItemHeight;
+ private LayoutInflater mInflater;
public KindSectionView(Context context) {
this(context, null);
@@ -64,8 +62,6 @@
public KindSectionView(Context context, AttributeSet attrs) {
super(context, attrs);
- mMinLineItemHeight = context.getResources().getDimensionPixelSize(
- R.dimen.editor_min_line_item_height);
}
@Override
@@ -78,26 +74,13 @@
}
}
- if (mAddPlusButton != null) {
- mAddPlusButton.setEnabled(enabled && !mReadOnly);
+ if (enabled && !mReadOnly) {
+ mAddFieldFooter.setVisibility(View.VISIBLE);
+ } else {
+ mAddFieldFooter.setVisibility(View.GONE);
}
}
- @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;
}
@@ -108,37 +91,36 @@
setDrawingCacheEnabled(true);
setAlwaysDrawnWithCacheEnabled(true);
- mEditors = (ViewGroup)findViewById(R.id.kind_editors);
+ mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mAddPlusButtonContainer = findViewById(R.id.kind_plus_container);
- mAddPlusButton = (ImageButton) findViewById(R.id.kind_plus);
- mAddPlusButton.setOnClickListener(new OnClickListener() {
+ mEditors = (ViewGroup)findViewById(R.id.kind_editors);
+ mAddFieldText = (TextView) findViewById(R.id.add_text);
+ mAddFieldFooter = findViewById(R.id.add_field_footer);
+ mAddFieldFooter.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();
- }
- });
+ // Setup click listener to add an empty field when the footer is clicked.
+ mAddFieldFooter.setVisibility(View.GONE);
+ addItem();
}
});
-
- mTitle = (TextView)findViewById(R.id.kind_title);
}
/** {@inheritDoc} */
@Override
public void onDeleted(Editor editor) {
- updateAddVisible();
- updateVisible();
+ updateAddFooterVisible();
+ updateSectionVisible();
}
/** {@inheritDoc} */
@Override
public void onRequest(int request) {
- // Ignore requests
+ // If a field has become empty or non-empty, then check if another row
+ // can be added dynamically.
+ if (request == FIELD_TURNED_EMPTY || request == FIELD_TURNED_NON_EMPTY) {
+ updateAddFooterVisible();
+ }
}
public void setState(DataKind kind, EntityDelta state, boolean readOnly, ViewIdGenerator vig) {
@@ -153,11 +135,17 @@
mTitleString = (kind.titleRes == -1 || kind.titleRes == 0)
? ""
: getResources().getString(kind.titleRes);
- mTitle.setText(mTitleString);
+
+ // Set "add field" footer message according to MIME type. Some MIME types
+ // can only have max 1 field, so the resource ID will be -1 if these sections
+ // should not have an "Add field" option.
+ if (kind.addNewFieldTextResourceId != -1) {
+ mAddFieldText.setText(getResources().getString(kind.addNewFieldTextResourceId));
+ }
rebuildFromState();
- updateAddVisible();
- updateVisible();
+ updateAddFooterVisible();
+ updateSectionVisible();
}
public String getTitle() {
@@ -193,16 +181,13 @@
*/
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);
- }
+ try {
+ view = mInflater.inflate(mKind.editorLayoutResourceId, mEditors, false);
+ } catch (Exception e) {
+ throw new RuntimeException(
+ "Cannot allocate editor with layout resource ID " +
+ mKind.editorLayoutResourceId + " for MIME type " + mKind.mimeType +
+ " with error " + e.toString());
}
view.setEnabled(isEnabled());
@@ -231,26 +216,68 @@
return true;
}
- private void updateVisible() {
+ private void updateSectionVisible() {
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;
+ protected void updateAddFooterVisible() {
+ if (!mReadOnly && mKind.isList) {
+ // First determine whether there are any existing empty editors.
+ updateEmptyEditors();
+ // If there are no existing empty editors and it's possible to add
+ // another field, then make the "add footer" field visible.
+ if (!hasEmptyEditor() && EntityModifier.canInsert(mState, mKind)) {
+ mAddFieldFooter.setVisibility(View.VISIBLE);
+ return;
+ }
}
- mAddPlusButton.setVisibility(isVisible ? View.VISIBLE : View.INVISIBLE);
+ mAddFieldFooter.setVisibility(View.GONE);
+ }
+
+ /**
+ * Updates the editors being displayed to the user removing extra empty
+ * {@link Editor}s, so there is only max 1 empty {@link Editor} view at a time.
+ */
+ private void updateEmptyEditors() {
+ List<View> emptyEditors = getEmptyEditors();
+
+ // If there is more than 1 empty editor, then remove it from the list of editors.
+ if (emptyEditors.size() > 1) {
+ for (View emptyEditorView : emptyEditors) {
+ // If no child {@link View}s are being focused on within
+ // this {@link View}, then remove this empty editor.
+ if (emptyEditorView.findFocus() == null) {
+ mEditors.removeView(emptyEditorView);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a list of empty editor views in this section.
+ */
+ private List<View> getEmptyEditors() {
+ List<View> emptyEditorViews = new ArrayList<View>();
+ for (int i = 0; i < mEditors.getChildCount(); i++) {
+ View view = mEditors.getChildAt(i);
+ if (((Editor) view).isEmpty()) {
+ emptyEditorViews.add(view);
+ }
+ }
+ return emptyEditorViews;
+ }
+
+ /**
+ * Returns true if one of the editors has all of its fields empty, or false
+ * otherwise.
+ */
+ private boolean hasEmptyEditor() {
+ return getEmptyEditors().size() > 0;
}
public void addItem() {
ValuesDelta values = null;
- // if this is a list, we can freely add. if not, only allow adding the first
+ // If this is a list, we can freely add. If not, only allow adding the first.
if (!mKind.isList) {
if (getEditorCount() == 1) {
return;
@@ -277,12 +304,11 @@
}
});
- // For non-lists (e.g. Notes we can only have one field. in that case we need to disable
- // the add button
- updateAddVisible();
+ // Hide the "add field" footer because there is now a blank field.
+ mAddFieldFooter.setVisibility(View.GONE);
// Ensure we are visible
- updateVisible();
+ updateSectionVisible();
}
public int getEditorCount() {
diff --git a/src/com/android/contacts/editor/LabeledEditorView.java b/src/com/android/contacts/editor/LabeledEditorView.java
index a6815a7..af5ae65 100644
--- a/src/com/android/contacts/editor/LabeledEditorView.java
+++ b/src/com/android/contacts/editor/LabeledEditorView.java
@@ -18,21 +18,19 @@
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.DataKind;
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.text.TextUtils;
@@ -45,32 +43,36 @@
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
-import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
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.
+ * 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 {
+public abstract class LabeledEditorView extends LinearLayout 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 TextView mTitle;
private Spinner mLabel;
private EditTypeAdapter mEditTypeAdapter;
- private ImageButton mDelete;
+ private View mDeleteContainer;
+ private ImageView mDelete;
private DataKind mKind;
private ValuesDelta mEntry;
private EntityDelta mState;
private boolean mReadOnly;
+ private boolean mWasEmpty = true;
private EditType mType;
@@ -117,6 +119,39 @@
R.dimen.editor_min_line_item_height);
}
+ /** {@inheritDoc} */
+ @Override
+ protected void onFinishInflate() {
+
+ mTitle = (TextView) findViewById(R.id.title);
+
+ mLabel = (Spinner) findViewById(R.id.spinner);
+ mLabel.setOnItemSelectedListener(mSpinnerListener);
+
+ mDelete = (ImageView) findViewById(R.id.delete_button);
+ mDeleteContainer = findViewById(R.id.delete_button_container);
+ mDeleteContainer.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);
+ }
+ }
+ });
+ }
+ });
+ }
+
public boolean isReadOnly() {
return mReadOnly;
}
@@ -129,96 +164,21 @@
}
/**
- * 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);
+ if (mTitle == null) {
+ return;
+ }
+ if (shouldExist) {
mLabel.setEnabled(!mReadOnly && isEnabled());
- addView(mLabel);
- } else if (!shouldExist && mLabel != null) {
- removeView(mLabel);
- mLabel = null;
+ mLabel.setVisibility(View.VISIBLE);
+
+ // Since there's a spinner for this editor, use this as the title
+ // instead of the title TextView.
+ mTitle.setVisibility(View.GONE);
+ } else {
+ mLabel.setVisibility(View.GONE);
}
}
@@ -226,46 +186,11 @@
* 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);
- }
- }
- });
- }
- });
+ if (shouldExist) {
+ mDeleteContainer.setVisibility(View.VISIBLE);
mDelete.setEnabled(!mReadOnly && isEnabled());
- addView(mDelete);
- } else if (!shouldExist && mDelete != null) {
- removeView(mDelete);
- mDelete = null;
+ } else {
+ mDeleteContainer.setVisibility(View.GONE);
}
}
@@ -288,15 +213,15 @@
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
- if (mLabel != null) mLabel.setEnabled(!mReadOnly && enabled);
- if (mDelete != null) mDelete.setEnabled(!mReadOnly && enabled);
+ mLabel.setEnabled(!mReadOnly && enabled);
+ mDelete.setEnabled(!mReadOnly && enabled);
}
public Spinner getLabel() {
return mLabel;
}
- public ImageButton getDelete() {
+ public ImageView getDelete() {
return mDelete;
}
@@ -317,7 +242,6 @@
* possible custom label string.
*/
private void rebuildLabel() {
- if (mLabel == null) return;
mEditTypeAdapter = new EditTypeAdapter(mContext);
mLabel.setAdapter(mEditTypeAdapter);
if (mEditTypeAdapter.hasCustomSelection()) {
@@ -338,6 +262,16 @@
if (mListener != null) {
mListener.onRequest(EditorListener.FIELD_CHANGED);
}
+
+ boolean isEmpty = isEmpty();
+ if (mWasEmpty != isEmpty) {
+ if (isEmpty) {
+ mListener.onRequest(EditorListener.FIELD_TURNED_EMPTY);
+ } else {
+ mListener.onRequest(EditorListener.FIELD_TURNED_NON_EMPTY);
+ }
+ mWasEmpty = isEmpty;
+ }
}
protected boolean isFieldChanged(String column, String value) {
@@ -374,10 +308,22 @@
}
setVisibility(View.VISIBLE);
+ // TODO: handle resources from remote packages
+ String titleString = (kind.titleRes == -1 || kind.titleRes == 0)
+ ? ""
+ : getResources().getString(kind.titleRes);
+
+ // If there is a title field, then setup the title (although it may not be shown if there is
+ // a Spinner setup later). There are cases where a title may not be present (i.e. structured
+ // name).
+ if (mTitle != null) {
+ mTitle.setText(titleString.toUpperCase());
+ }
+
// Display label selector if multiple types available
final boolean hasTypes = EntityModifier.hasEditTypes(kind);
setupLabelButton(hasTypes);
- if (mLabel != null) mLabel.setEnabled(!readOnly && isEnabled());
+ mLabel.setEnabled(!readOnly && isEnabled());
if (hasTypes) {
mType = EntityModifier.getCurrentType(entry, kind);
rebuildLabel();
@@ -556,7 +502,7 @@
} else {
text = getContext().getString(type.labelRes);
}
- textView.setText(text);
+ textView.setText(text.toUpperCase());
return view;
}
}
diff --git a/src/com/android/contacts/editor/PhoneticNameEditorView.java b/src/com/android/contacts/editor/PhoneticNameEditorView.java
index d3aa00d..cfc9b13 100644
--- a/src/com/android/contacts/editor/PhoneticNameEditorView.java
+++ b/src/com/android/contacts/editor/PhoneticNameEditorView.java
@@ -16,10 +16,11 @@
package com.android.contacts.editor;
-import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.DataKind;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
+import android.content.ContentValues;
import android.content.Context;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.text.TextUtils;
@@ -60,55 +61,20 @@
}
private void parsePhoneticName(String value) {
- String family = null;
- String middle = null;
- String given = null;
-
- if (!TextUtils.isEmpty(value)) {
- String[] strings = value.split(" ", 3);
- switch (strings.length) {
- case 1:
- family = strings[0];
- break;
- case 2:
- family = strings[0];
- given = strings[1];
- break;
- case 3:
- family = strings[0];
- middle = strings[1];
- given = strings[2];
- break;
- }
- }
-
- mValues.put(StructuredName.PHONETIC_FAMILY_NAME, family);
- mValues.put(StructuredName.PHONETIC_MIDDLE_NAME, middle);
- mValues.put(StructuredName.PHONETIC_GIVEN_NAME, given);
+ ContentValues values = PhoneticNameEditorView.parsePhoneticName(value, null);
+ mValues.put(StructuredName.PHONETIC_FAMILY_NAME,
+ values.getAsString(StructuredName.PHONETIC_FAMILY_NAME));
+ mValues.put(StructuredName.PHONETIC_MIDDLE_NAME,
+ values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME));
+ mValues.put(StructuredName.PHONETIC_GIVEN_NAME,
+ values.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
}
private void buildPhoneticName() {
String family = mValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME);
String middle = mValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
String given = mValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME);
-
- if (!TextUtils.isEmpty(family) || !TextUtils.isEmpty(middle)
- || !TextUtils.isEmpty(given)) {
- StringBuilder sb = new StringBuilder();
- if (!TextUtils.isEmpty(family)) {
- sb.append(family.trim()).append(' ');
- }
- if (!TextUtils.isEmpty(middle)) {
- sb.append(middle.trim()).append(' ');
- }
- if (!TextUtils.isEmpty(given)) {
- sb.append(given.trim()).append(' ');
- }
- sb.setLength(sb.length() - 1); // Yank the last space
- mPhoneticName = sb.toString();
- } else {
- mPhoneticName = null;
- }
+ mPhoneticName = PhoneticNameEditorView.buildPhoneticName(family, middle, given);
}
@Override
@@ -122,6 +88,73 @@
}
}
+ /**
+ * Parses phonetic name and returns parsed data (family, middle, given) as ContentValues.
+ * Parsed data should be {@link StructuredName#PHONETIC_FAMILY_NAME},
+ * {@link StructuredName#PHONETIC_MIDDLE_NAME}, and
+ * {@link StructuredName#PHONETIC_GIVEN_NAME}.
+ * If this method cannot parse given phoneticName, null values will be stored.
+ *
+ * @param phoneticName Phonetic name to be parsed
+ * @param values ContentValues to be used for storing data. If null, new instance will be
+ * created.
+ * @return ContentValues with parsed data. Those data can be null.
+ */
+ public static ContentValues parsePhoneticName(String phoneticName, ContentValues values) {
+ String family = null;
+ String middle = null;
+ String given = null;
+
+ if (!TextUtils.isEmpty(phoneticName)) {
+ String[] strings = phoneticName.split(" ", 3);
+ switch (strings.length) {
+ case 1:
+ family = strings[0];
+ break;
+ case 2:
+ family = strings[0];
+ given = strings[1];
+ break;
+ case 3:
+ family = strings[0];
+ middle = strings[1];
+ given = strings[2];
+ break;
+ }
+ }
+
+ if (values == null) {
+ values = new ContentValues();
+ }
+ values.put(StructuredName.PHONETIC_FAMILY_NAME, family);
+ values.put(StructuredName.PHONETIC_MIDDLE_NAME, middle);
+ values.put(StructuredName.PHONETIC_GIVEN_NAME, given);
+ return values;
+ }
+
+ /**
+ * Constructs and returns a phonetic full name from given parts.
+ */
+ public static String buildPhoneticName(String family, String middle, String given) {
+ if (!TextUtils.isEmpty(family) || !TextUtils.isEmpty(middle)
+ || !TextUtils.isEmpty(given)) {
+ StringBuilder sb = new StringBuilder();
+ if (!TextUtils.isEmpty(family)) {
+ sb.append(family.trim()).append(' ');
+ }
+ if (!TextUtils.isEmpty(middle)) {
+ sb.append(middle.trim()).append(' ');
+ }
+ if (!TextUtils.isEmpty(given)) {
+ sb.append(given.trim()).append(' ');
+ }
+ sb.setLength(sb.length() - 1); // Yank the last space
+ return sb.toString();
+ } else {
+ return null;
+ }
+ }
+
public PhoneticNameEditorView(Context context) {
super(context);
}
diff --git a/src/com/android/contacts/editor/PhotoEditorView.java b/src/com/android/contacts/editor/PhotoEditorView.java
index 89633fd..7b86291 100644
--- a/src/com/android/contacts/editor/PhotoEditorView.java
+++ b/src/com/android/contacts/editor/PhotoEditorView.java
@@ -17,8 +17,8 @@
package com.android.contacts.editor;
import com.android.contacts.R;
+import com.android.contacts.model.DataKind;
import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.AccountType.DataKind;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import android.content.Context;
@@ -178,4 +178,9 @@
public void setDeletable(boolean deletable) {
// Photo is not deletable
}
+
+ @Override
+ public boolean isEmpty() {
+ return !mHasSetPhoto;
+ }
}
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index 633b343..4effb5e 100644
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -19,8 +19,8 @@
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.DataKind;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.model.EntityModifier;
@@ -68,9 +68,9 @@
private ViewGroup mFields;
- private ImageView mHeaderIcon;
- private TextView mHeaderAccountType;
- private TextView mHeaderAccountName;
+ private ImageView mAccountIcon;
+ private TextView mAccountTypeTextView;
+ private TextView mAccountNameTextView;
private Button mAddFieldButton;
@@ -130,16 +130,15 @@
final int photoSize = getResources().getDimensionPixelSize(R.dimen.edit_photo_size);
mName = (StructuredNameEditorView)findViewById(R.id.edit_name);
- mName.setMinimumHeight(photoSize);
mName.setDeletable(false);
mPhoneticName = (PhoneticNameEditorView)findViewById(R.id.edit_phonetic_name);
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);
+ mAccountIcon = (ImageView) findViewById(R.id.account_icon);
+ mAccountTypeTextView = (TextView) findViewById(R.id.account_type);
+ mAccountNameTextView = (TextView) findViewById(R.id.account_name);
mAddFieldButton = (Button) findViewById(R.id.button_add_field);
mAddFieldButton.setOnClickListener(new OnClickListener() {
@@ -178,11 +177,11 @@
accountType = mContext.getString(R.string.account_phone);
}
if (!TextUtils.isEmpty(accountName)) {
- mHeaderAccountName.setText(
+ mAccountNameTextView.setText(
mContext.getString(R.string.from_account_format, accountName));
}
- mHeaderAccountType.setText(mContext.getString(R.string.account_type_format, accountType));
- mHeaderIcon.setImageDrawable(type.getDisplayIcon(mContext));
+ mAccountTypeTextView.setText(mContext.getString(R.string.account_type_format, accountType));
+ mAccountIcon.setImageDrawable(type.getDisplayIcon(mContext));
mRawContactId = values.getAsLong(RawContacts._ID);
@@ -191,8 +190,8 @@
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));
+ mName.setEditorTextSize(mContext.getResources().getDimensionPixelSize(
+ R.dimen.editor_structured_name_text_size));
mPhoneticName.setEnabled(isEnabled());
diff --git a/src/com/android/contacts/editor/StructuredNameEditorView.java b/src/com/android/contacts/editor/StructuredNameEditorView.java
index cf97c6c..6daf7b4 100644
--- a/src/com/android/contacts/editor/StructuredNameEditorView.java
+++ b/src/com/android/contacts/editor/StructuredNameEditorView.java
@@ -16,7 +16,7 @@
package com.android.contacts.editor;
-import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.DataKind;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
@@ -121,9 +121,31 @@
}
String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
+ ContentValues tmpCVs = buildStructuredNameFromFullName(
+ getContext(), displayName, null);
+ if (tmpCVs.size() > 0) {
+ eraseFullName(values);
+ values.put(StructuredName.PREFIX, tmpCVs.getAsString(StructuredName.PREFIX));
+ values.put(StructuredName.GIVEN_NAME, tmpCVs.getAsString(StructuredName.GIVEN_NAME));
+ values.put(StructuredName.MIDDLE_NAME, tmpCVs.getAsString(StructuredName.MIDDLE_NAME));
+ values.put(StructuredName.FAMILY_NAME, tmpCVs.getAsString(StructuredName.FAMILY_NAME));
+ values.put(StructuredName.SUFFIX, tmpCVs.getAsString(StructuredName.SUFFIX));
+ }
+
+ mSnapshot.clear();
+ mSnapshot.putAll(values.getCompleteValues());
+ mSnapshot.put(StructuredName.DISPLAY_NAME, displayName);
+ }
+
+ public static ContentValues buildStructuredNameFromFullName(
+ Context context, String displayName, ContentValues contentValues) {
+ if (contentValues == null) {
+ contentValues = new ContentValues();
+ }
+
Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name");
appendQueryParameter(builder, StructuredName.DISPLAY_NAME, displayName);
- Cursor cursor = getContext().getContentResolver().query(builder.build(), new String[]{
+ Cursor cursor = context.getContentResolver().query(builder.build(), new String[]{
StructuredName.PREFIX,
StructuredName.GIVEN_NAME,
StructuredName.MIDDLE_NAME,
@@ -133,20 +155,17 @@
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));
+ contentValues.put(StructuredName.PREFIX, cursor.getString(0));
+ contentValues.put(StructuredName.GIVEN_NAME, cursor.getString(1));
+ contentValues.put(StructuredName.MIDDLE_NAME, cursor.getString(2));
+ contentValues.put(StructuredName.FAMILY_NAME, cursor.getString(3));
+ contentValues.put(StructuredName.SUFFIX, cursor.getString(4));
}
} finally {
cursor.close();
}
- mSnapshot.clear();
- mSnapshot.putAll(values.getCompleteValues());
- mSnapshot.put(StructuredName.DISPLAY_NAME, displayName);
+ return contentValues;
}
private void switchFromStructuredNameToFullName() {
@@ -164,24 +183,11 @@
String familyName = values.getAsString(StructuredName.FAMILY_NAME);
String suffix = values.getAsString(StructuredName.SUFFIX);
- Uri.Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath(
- "complete_name");
- appendQueryParameter(builder, StructuredName.PREFIX, prefix);
- appendQueryParameter(builder, StructuredName.GIVEN_NAME, givenName);
- appendQueryParameter(builder, StructuredName.MIDDLE_NAME, middleName);
- appendQueryParameter(builder, StructuredName.FAMILY_NAME, familyName);
- appendQueryParameter(builder, StructuredName.SUFFIX, suffix);
- Cursor cursor = getContext().getContentResolver().query(builder.build(), 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();
+ String displayName = buildFullNameFromStructuredName(getContext(),
+ prefix, givenName, middleName, familyName, suffix);
+ if (!TextUtils.isEmpty(displayName)) {
+ eraseStructuredName(values);
+ values.put(StructuredName.DISPLAY_NAME, displayName);
}
mSnapshot.clear();
@@ -193,6 +199,30 @@
mSnapshot.put(StructuredName.SUFFIX, suffix);
}
+ public static String buildFullNameFromStructuredName(Context context,
+ String prefix, String given, String middle, String family, String suffix) {
+ Uri.Builder builder = ContactsContract.AUTHORITY_URI.buildUpon()
+ .appendPath("complete_name");
+ appendQueryParameter(builder, StructuredName.PREFIX, prefix);
+ appendQueryParameter(builder, StructuredName.GIVEN_NAME, given);
+ appendQueryParameter(builder, StructuredName.MIDDLE_NAME, middle);
+ appendQueryParameter(builder, StructuredName.FAMILY_NAME, family);
+ appendQueryParameter(builder, StructuredName.SUFFIX, suffix);
+ Cursor cursor = context.getContentResolver().query(builder.build(), new String[]{
+ StructuredName.DISPLAY_NAME,
+ }, null, null, null);
+
+ try {
+ if (cursor.moveToFirst()) {
+ return cursor.getString(0);
+ }
+ } finally {
+ cursor.close();
+ }
+
+ return null;
+ }
+
private void eraseFullName(ValuesDelta values) {
values.putNull(StructuredName.DISPLAY_NAME);
}
@@ -205,7 +235,7 @@
values.putNull(StructuredName.SUFFIX);
}
- private void appendQueryParameter(Uri.Builder builder, String field, String value) {
+ private static void appendQueryParameter(Uri.Builder builder, String field, String value) {
if (!TextUtils.isEmpty(value)) {
builder.appendQueryParameter(field, value);
}
diff --git a/src/com/android/contacts/editor/TextFieldsEditorView.java b/src/com/android/contacts/editor/TextFieldsEditorView.java
index 031ab18..e74492c 100644
--- a/src/com/android/contacts/editor/TextFieldsEditorView.java
+++ b/src/com/android/contacts/editor/TextFieldsEditorView.java
@@ -18,40 +18,45 @@
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.DataKind;
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.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
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.Gravity;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.EditText;
-import android.widget.ImageButton;
+import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.Toast;
/**
- * 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.
+ * 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 ViewGroup mFields = null;
+ private View mExpansionViewContainer;
+ private ImageView mExpansionView;
private boolean mHideOptional = true;
private boolean mHasShortAndLongForms;
- private int mEditorTextSize;
+ private int mEditorTextSize = 0;
public TextFieldsEditorView(Context context) {
super(context);
@@ -65,97 +70,46 @@
super(context, attrs, defStyle);
}
- public void setEditorTextSize(int textSize) {
- this.mEditorTextSize = textSize;
- }
-
+ /** {@inheritDoc} */
@Override
- protected int getLineItemCount() {
- int count = mFieldEditTexts == null ? 0 : mFieldEditTexts.length;
- return Math.max(count, super.getLineItemCount());
- }
+ protected void onFinishInflate() {
+ super.onFinishInflate();
- @Override
- protected boolean isLineItemVisible(int row) {
- return mFieldEditTexts != null && mFieldEditTexts[row].getVisibility() != View.GONE;
- }
+ setDrawingCacheEnabled(true);
+ setAlwaysDrawnWithCacheEnabled(true);
- @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;
- }
+ mFields = (ViewGroup) findViewById(R.id.editors);
+ mExpansionView = (ImageView) findViewById(R.id.expansion_view);
+ mExpansionViewContainer = findViewById(R.id.expansion_view_container);
+ mExpansionViewContainer.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Save focus
+ final View focusedChild = getFocusedChild();
+ final int focusedViewId = focusedChild == null ? -1 : focusedChild.getId();
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
+ // Reconfigure GUI
+ mHideOptional = !mHideOptional;
+ onOptionalFieldVisibilityChange();
+ rebuildValues();
- 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);
+ // Restore focus
+ View newFocusView = findViewById(focusedViewId);
+ if (newFocusView == null || newFocusView.getVisibility() == GONE) {
+ // find first visible child
+ newFocusView = TextFieldsEditorView.this;
}
+ newFocusView.requestFocus();
}
- }
+ });
}
- @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));
+ /**
+ * Set the text size of the value of all fields in this class, which will override the default
+ * text appearance style for the associated {@link DataKind}.
+ */
+ public void setEditorTextSize(int textSize) {
+ mEditorTextSize = textSize;
}
@Override
@@ -167,59 +121,20 @@
mFieldEditTexts[index].setEnabled(!isReadOnly() && enabled);
}
}
- if (mMoreOrLess != null) mMoreOrLess.setEnabled(!isReadOnly() && enabled);
+ mExpansionView.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) {
+ private void setupExpansionView(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
+ mExpansionViewContainer.setVisibility(View.VISIBLE);
+ mExpansionView.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;
+ } else {
+ mExpansionViewContainer.setVisibility(View.GONE);
}
}
@@ -250,7 +165,7 @@
// Remove edit texts that we currently have
if (mFieldEditTexts != null) {
for (EditText fieldEditText : mFieldEditTexts) {
- removeView(fieldEditText);
+ mFields.removeView(fieldEditText);
}
}
boolean hidePossible = false;
@@ -262,10 +177,11 @@
final EditText fieldView = new EditText(mContext);
fieldView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
- fieldView.setGravity(Gravity.TOP);
+ fieldView.setTextAppearance(getContext(), kind.textAppearanceResourceId);
if (mEditorTextSize != 0) {
fieldView.setTextSize(mEditorTextSize);
}
+ fieldView.setGravity(Gravity.TOP);
mFieldEditTexts[index] = fieldView;
fieldView.setId(vig.getId(state, kind, entry, index));
if (field.titleRes > 0) {
@@ -319,12 +235,23 @@
hidePossible = hidePossible || couldHide;
}
- addView(fieldView);
+ mFields.addView(fieldView);
}
// When hiding fields, place expandable
- setupMoreOrLessButton(hidePossible, mHideOptional);
- if (mMoreOrLess != null) mMoreOrLess.setEnabled(!readOnly && isEnabled());
+ setupExpansionView(hidePossible, mHideOptional);
+ mExpansionView.setEnabled(!readOnly && isEnabled());
+ }
+
+ @Override
+ public boolean isEmpty() {
+ for (int i = 0; i < mFields.getChildCount(); i++) {
+ EditText editText = (EditText) mFields.getChildAt(i);
+ if (!TextUtils.isEmpty(editText.getText())) {
+ return false;
+ }
+ }
+ return true;
}
/**
diff --git a/src/com/android/contacts/editor/ViewIdGenerator.java b/src/com/android/contacts/editor/ViewIdGenerator.java
index 93bb002..c3cd08b 100644
--- a/src/com/android/contacts/editor/ViewIdGenerator.java
+++ b/src/com/android/contacts/editor/ViewIdGenerator.java
@@ -16,8 +16,8 @@
package com.android.contacts.editor;
+import com.android.contacts.model.DataKind;
import com.android.contacts.model.EntityDelta;
-import com.android.contacts.model.AccountType.DataKind;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import android.os.Bundle;
diff --git a/src/com/android/contacts/interactions/ContactDeletionInteraction.java b/src/com/android/contacts/interactions/ContactDeletionInteraction.java
index 08f8d00..f195c2f 100644
--- a/src/com/android/contacts/interactions/ContactDeletionInteraction.java
+++ b/src/com/android/contacts/interactions/ContactDeletionInteraction.java
@@ -206,8 +206,13 @@
final Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
showDialog(mMessageId, contactUri);
+
+ // We don't want onLoadFinished() calls any more, which may come when the database is
+ // updating.
+ getLoaderManager().destroyLoader(R.id.dialog_delete_contact_loader_id);
}
+ @Override
public void onLoaderReset(Loader<Cursor> loader) {
}
@@ -235,7 +240,6 @@
public void onDismiss(DialogInterface dialog) {
mActive = false;
mDialog = null;
- getLoaderManager().destroyLoader(R.id.dialog_delete_contact_loader_id);
}
@Override
diff --git a/src/com/android/contacts/interactions/PhoneNumberInteraction.java b/src/com/android/contacts/interactions/PhoneNumberInteraction.java
index 277fa56..399b0ea 100644
--- a/src/com/android/contacts/interactions/PhoneNumberInteraction.java
+++ b/src/com/android/contacts/interactions/PhoneNumberInteraction.java
@@ -16,14 +16,19 @@
package com.android.contacts.interactions;
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import com.google.i18n.phonenumbers.PhoneNumberUtil.MatchType;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
+
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 com.android.contacts.model.DataKind;
import android.app.Activity;
import android.app.AlertDialog;
@@ -46,7 +51,7 @@
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
-import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -112,8 +117,16 @@
}
public boolean shouldCollapseWith(PhoneItem phoneItem) {
- if (PhoneNumberUtils.compareStrictly(phoneNumber, phoneItem.phoneNumber)) {
- return true;
+ try {
+ PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+ PhoneNumber phoneNumber1 = util.parse(phoneNumber, "ZZ" /* Unknown */);
+ PhoneNumber phoneNumber2 = util.parse(phoneItem.phoneNumber, "ZZ" /* Unknown */);
+ MatchType matchType = util.isNumberMatch(phoneNumber1, phoneNumber2);
+ if (matchType == MatchType.SHORT_NSN_MATCH) {
+ return true;
+ }
+ } catch (NumberParseException e) {
+ return TextUtils.equals(phoneNumber, phoneItem.phoneNumber);
}
return false;
}
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index be920db..1ecfff1 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -583,7 +583,7 @@
public void viewContact(Uri contactUri) {
setSelectedContactUri(contactUri, false, false, true, false);
- if (mListener != null) { mListener.onViewContactAction(contactUri); }
+ if (mListener != null) mListener.onViewContactAction(contactUri);
}
public void editContact(Uri contactUri) {
diff --git a/src/com/android/contacts/list/ContactsIntentResolver.java b/src/com/android/contacts/list/ContactsIntentResolver.java
index afe29df..3ef68d8 100644
--- a/src/com/android/contacts/list/ContactsIntentResolver.java
+++ b/src/com/android/contacts/list/ContactsIntentResolver.java
@@ -27,6 +27,7 @@
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.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.Contacts;
@@ -95,6 +96,8 @@
} else if (ContactMethods.CONTENT_POSTAL_TYPE.equals(resolvedType)) {
request.setActionCode(ContactsRequest.ACTION_PICK_POSTAL);
request.setLegacyCompatibilityMode(true);
+ } else if (Email.CONTENT_TYPE.equals(resolvedType)) {
+ request.setActionCode(ContactsRequest.ACTION_PICK_EMAIL);
}
} else if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
String component = intent.getComponent().getClassName();
diff --git a/src/com/android/contacts/list/ContactsRequest.java b/src/com/android/contacts/list/ContactsRequest.java
index aefa451..e20d189 100644
--- a/src/com/android/contacts/list/ContactsRequest.java
+++ b/src/com/android/contacts/list/ContactsRequest.java
@@ -62,6 +62,9 @@
/** Show all postal addresses and pick them when clicking */
public static final int ACTION_PICK_POSTAL = 100;
+ /** Show all postal addresses and pick them when clicking */
+ public static final int ACTION_PICK_EMAIL = 105;
+
/** Show all contacts and create a shortcut for the picked contact */
public static final int ACTION_CREATE_SHORTCUT_CONTACT = 110;
diff --git a/src/com/android/contacts/list/EmailAddressListAdapter.java b/src/com/android/contacts/list/EmailAddressListAdapter.java
new file mode 100644
index 0000000..96c69f9
--- /dev/null
+++ b/src/com/android/contacts/list/EmailAddressListAdapter.java
@@ -0,0 +1,190 @@
+/*
+ * 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.
+ */
+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.ContactCounts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A cursor adapter for the {@link Email#CONTENT_TYPE} content type.
+ */
+public class EmailAddressListAdapter extends ContactEntryListAdapter {
+
+ static final String[] EMAILS_PROJECTION = new String[] {
+ Email._ID, // 0
+ Email.TYPE, // 1
+ Email.LABEL, // 2
+ Email.DATA, // 3
+ Email.DISPLAY_NAME_PRIMARY, // 4
+ Email.DISPLAY_NAME_ALTERNATIVE, // 5
+ Email.PHOTO_ID, // 6
+ };
+
+ protected static final int EMAIL_ID_COLUMN_INDEX = 0;
+ protected static final int EMAIL_TYPE_COLUMN_INDEX = 1;
+ protected static final int EMAIL_LABEL_COLUMN_INDEX = 2;
+ protected static final int EMAIL_ADDRESS_COLUMN_INDEX = 3;
+ protected static final int EMAIL_PRIMARY_DISPLAY_NAME_COLUMN_INDEX = 4;
+ protected static final int EMAIL_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX = 5;
+ protected static final int EMAIL_PHOTO_ID_COLUMN_INDEX = 6;
+
+ private CharSequence mUnknownNameText;
+ private int mDisplayNameColumnIndex;
+ private int mAlternativeDisplayNameColumnIndex;
+
+ public EmailAddressListAdapter(Context context) {
+ super(context);
+
+ mUnknownNameText = context.getText(android.R.string.unknownName);
+ }
+
+ @Override
+ public void configureLoader(CursorLoader loader, long directoryId) {
+ final Builder builder;
+ if (isSearchMode()) {
+ builder = Email.CONTENT_FILTER_URI.buildUpon();
+ String query = getQueryString();
+ builder.appendPath(TextUtils.isEmpty(query) ? "" : query);
+ } else {
+ builder = Email.CONTENT_URI.buildUpon();
+ }
+ builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(directoryId));
+ applyDataRestriction(builder);
+ loader.setUri(builder.build());
+ loader.setProjection(EMAILS_PROJECTION);
+
+ if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
+ loader.setSortOrder(Email.SORT_KEY_PRIMARY);
+ } else {
+ loader.setSortOrder(Email.SORT_KEY_ALTERNATIVE);
+ }
+ }
+
+ protected static Builder buildSectionIndexerUri(Uri uri) {
+ return uri.buildUpon()
+ .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true");
+ }
+
+ @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 = EMAIL_PRIMARY_DISPLAY_NAME_COLUMN_INDEX;
+ mAlternativeDisplayNameColumnIndex = EMAIL_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX;
+ } else {
+ mDisplayNameColumnIndex = EMAIL_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX;
+ mAlternativeDisplayNameColumnIndex = EMAIL_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(EMAIL_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);
+ bindEmailAddress(view, cursor);
+ }
+
+ protected void bindEmailAddress(ContactListItemView view, Cursor cursor) {
+ CharSequence label = null;
+ if (!cursor.isNull(EMAIL_TYPE_COLUMN_INDEX)) {
+ final int type = cursor.getInt(EMAIL_TYPE_COLUMN_INDEX);
+ final String customLabel = cursor.getString(EMAIL_LABEL_COLUMN_INDEX);
+
+ // TODO cache
+ label = Email.getTypeLabel(getContext().getResources(), type, customLabel);
+ }
+ view.setLabel(label);
+ view.showData(cursor, EMAIL_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(EMAIL_PHOTO_ID_COLUMN_INDEX)) {
+ photoId = cursor.getLong(EMAIL_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/EmailAddressPickerFragment.java b/src/com/android/contacts/list/EmailAddressPickerFragment.java
new file mode 100644
index 0000000..168e135
--- /dev/null
+++ b/src/com/android/contacts/list/EmailAddressPickerFragment.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+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 an email list for picking.
+ */
+public class EmailAddressPickerFragment extends ContactEntryListFragment<ContactEntryListAdapter> {
+ private OnEmailAddressPickerActionListener mListener;
+
+ public EmailAddressPickerFragment() {
+ setQuickContactEnabled(false);
+ setPhotoLoaderEnabled(true);
+ setSectionHeaderDisplayEnabled(true);
+ setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DATA_SHORTCUT);
+ }
+
+ public void setOnEmailAddressPickerActionListener(OnEmailAddressPickerActionListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ protected void onItemClick(int position, long id) {
+ EmailAddressListAdapter adapter = (EmailAddressListAdapter)getAdapter();
+ pickEmailAddress(adapter.getDataUri(position));
+ }
+
+ @Override
+ protected ContactEntryListAdapter createListAdapter() {
+ EmailAddressListAdapter adapter = new EmailAddressListAdapter(getActivity());
+ adapter.setSectionHeaderDisplayEnabled(true);
+ adapter.setDisplayPhotos(true);
+ return adapter;
+ }
+
+ @Override
+ protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+ return inflater.inflate(R.layout.contacts_list_content, null);
+ }
+
+ private void pickEmailAddress(Uri uri) {
+ mListener.onPickEmailAddressAction(uri);
+ }
+}
diff --git a/src/com/android/contacts/list/OnEmailAddressPickerActionListener.java b/src/com/android/contacts/list/OnEmailAddressPickerActionListener.java
new file mode 100644
index 0000000..e785323
--- /dev/null
+++ b/src/com/android/contacts/list/OnEmailAddressPickerActionListener.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+package com.android.contacts.list;
+
+import android.net.Uri;
+
+/**
+ * Action callbacks that can be sent by a email address picker.
+ */
+public interface OnEmailAddressPickerActionListener {
+
+ /**
+ * Returns the selected phone number to the requester.
+ */
+ void onPickEmailAddressAction(Uri dataUri);
+}
diff --git a/src/com/android/contacts/list/PostalAddressListAdapter.java b/src/com/android/contacts/list/PostalAddressListAdapter.java
index e622ec6..8b3c75d 100644
--- a/src/com/android/contacts/list/PostalAddressListAdapter.java
+++ b/src/com/android/contacts/list/PostalAddressListAdapter.java
@@ -131,7 +131,7 @@
final String customLabel = cursor.getString(POSTAL_LABEL_COLUMN_INDEX);
// TODO cache
- label = StructuredPostal.getTypeLabel(getContext().getResources(), type, label);
+ label = StructuredPostal.getTypeLabel(getContext().getResources(), type, customLabel);
}
view.setLabel(label);
view.showData(cursor, POSTAL_ADDRESS_COLUMN_INDEX);
diff --git a/src/com/android/contacts/list/PostalAddressPickerFragment.java b/src/com/android/contacts/list/PostalAddressPickerFragment.java
index f14b718..5f7ca56 100644
--- a/src/com/android/contacts/list/PostalAddressPickerFragment.java
+++ b/src/com/android/contacts/list/PostalAddressPickerFragment.java
@@ -73,7 +73,7 @@
return inflater.inflate(R.layout.contacts_list_content, null);
}
- public void pickPostalAddress(Uri uri) {
+ private void pickPostalAddress(Uri uri) {
mListener.onPickPostalAddressAction(uri);
}
}
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index aef5c06..3cce25d 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -30,15 +30,12 @@
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
@@ -160,87 +157,6 @@
}
/**
- * 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 static final String PSEUDO_MIME_TYPE_DISPLAY_NAME = "#displayName";
- public static final String PSEUDO_MIME_TYPE_PHONETIC_NAME = "#phoneticName";
- public static final String PSEUDO_COLUMN_PHONETIC_NAME = "#phoneticName";
-
- 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
@@ -250,6 +166,10 @@
public int rawValue;
public int labelRes;
public boolean secondary;
+ /**
+ * The number of entries allowed for the type. -1 if not specified.
+ * @see DataKind#typeOverallMax
+ */
public int specificMax;
public String customColumn;
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index 06bb9bd..58195a1 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -16,7 +16,6 @@
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;
diff --git a/src/com/android/contacts/model/BaseAccountType.java b/src/com/android/contacts/model/BaseAccountType.java
index d4a7496..ee08522 100644
--- a/src/com/android/contacts/model/BaseAccountType.java
+++ b/src/com/android/contacts/model/BaseAccountType.java
@@ -99,7 +99,8 @@
protected DataKind addDataKindStructuredName(Context context) {
DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
- R.string.nameLabelsGroup, -1, -1, true));
+ R.string.nameLabelsGroup, -1, -1, true, R.layout.structured_name_editor_view,
+ android.R.style.TextAppearance_Large, -1));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
@@ -128,7 +129,8 @@
protected DataKind addDataKindDisplayName(Context context) {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
- R.string.nameLabelsGroup, -1, -1, true));
+ R.string.nameLabelsGroup, -1, -1, true, R.layout.text_fields_editor_view,
+ android.R.style.TextAppearance_Large, -1));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
@@ -168,7 +170,8 @@
protected DataKind addDataKindPhoneticName(Context context) {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
- R.string.name_phonetic, -1, -1, true));
+ R.string.name_phonetic, -1, -1, true, R.layout.phonetic_name_editor_view,
+ android.R.style.TextAppearance_Medium, -1));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
@@ -187,8 +190,8 @@
protected DataKind addDataKindNickname(Context context) {
DataKind kind = addKind(new DataKind(Nickname.CONTENT_ITEM_TYPE,
- R.string.nicknameLabelsGroup, -1, 115, true));
-
+ R.string.nicknameLabelsGroup, -1, 115, true,
+ R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium, -1));
kind.isList = false;
kind.actionHeader = new SimpleInflater(R.string.nicknameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
@@ -204,7 +207,9 @@
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));
+ android.R.drawable.sym_action_call, 10, true,
+ R.layout.text_fields_editor_view, android.R.style.TextAppearance_Large,
+ R.string.add_phone));
kind.iconAltRes = R.drawable.sym_action_sms;
kind.actionHeader = new PhoneActionInflater();
kind.actionAltHeader = new PhoneActionAltInflater();
@@ -243,7 +248,9 @@
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));
+ R.drawable.sym_action_email_holo_light, 15, true,
+ R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+ R.string.add_email));
kind.actionHeader = new EmailActionInflater();
kind.actionBody = new SimpleInflater(Email.DATA);
kind.typeColumn = Email.TYPE;
@@ -264,7 +271,8 @@
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));
+ true, R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+ R.string.add_address));
kind.actionHeader = new PostalActionInflater();
kind.actionBody = new SimpleInflater(StructuredPostal.FORMATTED_ADDRESS);
kind.typeColumn = StructuredPostal.TYPE;
@@ -285,7 +293,9 @@
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));
+ R.drawable.sym_action_talk_holo_light, 20, true,
+ R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+ R.string.add_im));
kind.actionHeader = new ImActionInflater();
kind.actionBody = new SimpleInflater(Im.DATA);
@@ -316,7 +326,8 @@
protected DataKind addDataKindOrganization(Context context) {
DataKind kind = addKind(new DataKind(Organization.CONTENT_ITEM_TYPE,
- R.string.organizationLabelsGroup, -1, 5, true));
+ R.string.organizationLabelsGroup, -1, 5, true,
+ R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium, -1));
kind.actionHeader = new SimpleInflater(Organization.COMPANY);
kind.actionBody = new SimpleInflater(Organization.TITLE);
kind.isList = false;
@@ -331,7 +342,8 @@
}
protected DataKind addDataKindPhoto(Context context) {
- DataKind kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, -1, -1, true));
+ DataKind kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, -1, -1, true, -1,
+ android.R.style.TextAppearance_Medium, -1));
kind.fieldList = Lists.newArrayList();
kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
return kind;
@@ -339,7 +351,9 @@
protected DataKind addDataKindNote(Context context) {
DataKind kind = addKind(new DataKind(Note.CONTENT_ITEM_TYPE,
- R.string.label_notes, -1, 110, true));
+ R.string.label_notes, -1, 110, true,
+ R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+ R.string.add_note));
kind.isList = false;
kind.actionHeader = new SimpleInflater(R.string.label_notes);
kind.actionBody = new SimpleInflater(Note.NOTE);
@@ -352,7 +366,8 @@
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));
+ true, R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+ R.string.add_website));
kind.actionHeader = new SimpleInflater(R.string.websiteLabelsGroup);
kind.actionBody = new SimpleInflater(Website.URL);
kind.defaultValues = new ContentValues();
@@ -374,7 +389,9 @@
// 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));
+ R.string.label_sip_address, android.R.drawable.sym_action_call, 130, true,
+ R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+ -1));
kind.isList = false;
kind.actionHeader = new SimpleInflater(R.string.label_sip_address);
@@ -389,7 +406,8 @@
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));
+ R.string.groupsLabel, android.R.drawable.sym_contact_card, 999, true, -1,
+ android.R.style.TextAppearance_Medium, -1));
kind.isList = false;
kind.fieldList = Lists.newArrayList();
diff --git a/src/com/android/contacts/model/DataKind.java b/src/com/android/contacts/model/DataKind.java
new file mode 100644
index 0000000..52769c3
--- /dev/null
+++ b/src/com/android/contacts/model/DataKind.java
@@ -0,0 +1,106 @@
+package com.android.contacts.model;
+
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType.EditField;
+import com.android.contacts.model.AccountType.EditType;
+import com.android.contacts.model.AccountType.StringInflater;
+
+import android.content.ContentValues;
+import android.provider.ContactsContract.Data;
+
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+/**
+ * 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 class DataKind {
+
+ public static final String PSEUDO_MIME_TYPE_DISPLAY_NAME = "#displayName";
+ public static final String PSEUDO_MIME_TYPE_PHONETIC_NAME = "#phoneticName";
+ public static final String PSEUDO_COLUMN_PHONETIC_NAME = "#phoneticName";
+
+ 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;
+
+ /** Layout resource id for an editor view to edit this {@link DataKind}. */
+ public final int editorLayoutResourceId;
+
+ /** Text appearance resource id for the value of a field in this {@link DataKind}. */
+ public final int textAppearanceResourceId;
+
+ /**
+ * String resource id for the "add field" footer. This is equal to -1 if it
+ * is not applicable to add a new field to this class (i.e. for a structured
+ * name because a user should only have one structured name).
+ */
+ public final int addNewFieldTextResourceId;
+
+ /**
+ * 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() {
+ editorLayoutResourceId = R.layout.text_fields_editor_view;
+ textAppearanceResourceId = android.R.style.TextAppearance_Medium;
+ addNewFieldTextResourceId = -1;
+ }
+
+ public DataKind(String mimeType, int titleRes, int iconRes, int weight, boolean editable,
+ int editorLayoutResourceId, int textAppearanceResourceId,
+ int addNewFieldTextResourceId) {
+ this.mimeType = mimeType;
+ this.titleRes = titleRes;
+ this.iconRes = iconRes;
+ this.weight = weight;
+ this.editable = editable;
+ this.isList = true;
+ this.typeOverallMax = -1;
+ this.editorLayoutResourceId = editorLayoutResourceId;
+ this.textAppearanceResourceId = textAppearanceResourceId;
+ this.addNewFieldTextResourceId = addNewFieldTextResourceId;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/model/EntityDelta.java b/src/com/android/contacts/model/EntityDelta.java
index c5d0d9e..fe084f4 100644
--- a/src/com/android/contacts/model/EntityDelta.java
+++ b/src/com/android/contacts/model/EntityDelta.java
@@ -330,7 +330,7 @@
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("\n(");
- builder.append(mValues.toString());
+ builder.append(mValues != null ? mValues.toString() : "null");
builder.append(") = {");
for (ArrayList<ValuesDelta> mimeEntries : mEntries.values()) {
for (ValuesDelta child : mimeEntries) {
diff --git a/src/com/android/contacts/model/EntityModifier.java b/src/com/android/contacts/model/EntityModifier.java
index 1e014ef..25afe4d 100644
--- a/src/com/android/contacts/model/EntityModifier.java
+++ b/src/com/android/contacts/model/EntityModifier.java
@@ -16,12 +16,17 @@
package com.android.contacts.model;
+import com.google.android.collect.Lists;
+
import com.android.contacts.ContactsUtils;
-import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.editor.EventFieldEditorView;
+import com.android.contacts.editor.PhoneticNameEditorView;
+import com.android.contacts.editor.StructuredNameEditorView;
import com.android.contacts.model.AccountType.EditField;
import com.android.contacts.model.AccountType.EditType;
+import com.android.contacts.model.AccountType.EventEditType;
import com.android.contacts.model.EntityDelta.ValuesDelta;
-import com.google.android.collect.Lists;
+import com.android.contacts.util.DateUtils;
import android.content.ContentValues;
import android.content.Context;
@@ -31,14 +36,19 @@
import android.provider.ContactsContract;
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.provider.ContactsContract.Data;
import android.provider.ContactsContract.Intents;
import android.provider.ContactsContract.Intents.Insert;
@@ -47,9 +57,18 @@
import android.util.Log;
import android.util.SparseIntArray;
+import java.text.ParsePosition;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
/**
* Helper methods for modifying an {@link EntityDelta}, such as inserting
@@ -878,4 +897,512 @@
return child;
}
+
+ /**
+ * Generic mime types with type support (e.g. TYPE_HOME).
+ * Here, "type support" means if the data kind has CommonColumns#TYPE or not. Data kinds which
+ * have their own migrate methods aren't listed here.
+ */
+ private static final Set<String> sGenericMimeTypesWithTypeSupport = new HashSet<String>(
+ Arrays.asList(Phone.CONTENT_ITEM_TYPE,
+ Email.CONTENT_ITEM_TYPE,
+ Im.CONTENT_ITEM_TYPE,
+ Nickname.CONTENT_ITEM_TYPE,
+ Website.CONTENT_ITEM_TYPE,
+ Relation.CONTENT_ITEM_TYPE,
+ SipAddress.CONTENT_ITEM_TYPE));
+ private static final Set<String> sGenericMimeTypesWithoutTypeSupport = new HashSet<String>(
+ Arrays.asList(Organization.CONTENT_ITEM_TYPE,
+ Note.CONTENT_ITEM_TYPE,
+ Photo.CONTENT_ITEM_TYPE,
+ GroupMembership.CONTENT_ITEM_TYPE));
+ // CommonColumns.TYPE cannot be accessed as it is protected interface, so use
+ // Phone.TYPE instead.
+ private static final String COLUMN_FOR_TYPE = Phone.TYPE;
+ private static final String COLUMN_FOR_LABEL = Phone.LABEL;
+ private static final int TYPE_CUSTOM = Phone.TYPE_CUSTOM;
+
+ /**
+ * Migrates old EntityDelta to newly created one with a new restriction supplied from
+ * newAccountType.
+ *
+ * This is only for account switch during account creation (which must be insert operation).
+ */
+ public static void migrateStateForNewContact(Context context,
+ EntityDelta oldState, EntityDelta newState,
+ AccountType oldAccountType, AccountType newAccountType) {
+ if (newAccountType == oldAccountType) {
+ // Just copying all data in oldState isn't enough, but we can still rely on a lot of
+ // shortcuts.
+ for (DataKind kind : newAccountType.getSortedDataKinds()) {
+ final String mimeType = kind.mimeType;
+ // The fields with short/long form capability must be treated properly.
+ if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ migrateStructuredName(context, oldState, newState, kind);
+ } else {
+ List<ValuesDelta> entryList = oldState.getMimeEntries(mimeType);
+ if (entryList != null && !entryList.isEmpty()) {
+ for (ValuesDelta entry : entryList) {
+ ContentValues values = entry.getAfter();
+ if (values != null) {
+ newState.addEntry(ValuesDelta.fromAfter(values));
+ }
+ }
+ }
+ }
+ }
+ } else {
+ // Migrate data supported by the new account type.
+ // All the other data inside oldState are silently dropped.
+ for (DataKind kind : newAccountType.getSortedDataKinds()) {
+ final String mimeType = kind.mimeType;
+ final int fieldCount = kind.fieldList.size();
+ final Set<String> allowedColumns = new HashSet<String>();
+ if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
+ || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
+ // Ignore pseude data.
+ continue;
+ } else if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ migrateStructuredName(context, oldState, newState, kind);
+ } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ migratePostal(oldState, newState, kind);
+ } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ migrateEvent(oldState, newState, kind, null /* default Year */);
+ } else if (sGenericMimeTypesWithoutTypeSupport.contains(mimeType)) {
+ migrateGenericWithoutTypeColumn(oldState, newState, kind);
+ } else {
+ migrateGenericWithTypeColumn(oldState, newState, kind);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks {@link DataKind#isList} and {@link DataKind#typeOverallMax}, and restricts
+ * the number of entries (ValuesDelta) inside newState.
+ */
+ private static ArrayList<ValuesDelta> ensureEntryMaxSize(EntityDelta newState, DataKind kind,
+ ArrayList<ValuesDelta> mimeEntries) {
+ if (mimeEntries == null) {
+ return null;
+ }
+
+ int typeOverallMax = kind.typeOverallMax;
+ if (!kind.isList) {
+ typeOverallMax = 1;
+ }
+ if (typeOverallMax >= 0 && (mimeEntries.size() > typeOverallMax)) {
+ ArrayList<ValuesDelta> newMimeEntries = new ArrayList<ValuesDelta>(typeOverallMax);
+ for (int i = 0; i < typeOverallMax; i++) {
+ newMimeEntries.add(mimeEntries.get(i));
+ }
+ mimeEntries = newMimeEntries;
+ }
+ return mimeEntries;
+ }
+
+ /** @hide Public only for testing. */
+ public static void migrateStructuredName(
+ Context context, EntityDelta oldState, EntityDelta newState, DataKind newDataKind) {
+ final ContentValues values =
+ oldState.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE).getAfter();
+ if (values == null) {
+ return;
+ }
+
+ boolean supportDisplayName = false;
+ boolean supportPhoneticFullName = false;
+ boolean supportPhoneticFamilyName = false;
+ boolean supportPhoneticMiddleName = false;
+ boolean supportPhoneticGivenName = false;
+ for (EditField editField : newDataKind.fieldList) {
+ if (StructuredName.DISPLAY_NAME.equals(editField.column)) {
+ supportDisplayName = true;
+ }
+ if (DataKind.PSEUDO_COLUMN_PHONETIC_NAME.equals(editField.column)) {
+ supportPhoneticFullName = true;
+ }
+ if (StructuredName.PHONETIC_FAMILY_NAME.equals(editField.column)) {
+ supportPhoneticFamilyName = true;
+ }
+ if (StructuredName.PHONETIC_MIDDLE_NAME.equals(editField.column)) {
+ supportPhoneticMiddleName = true;
+ }
+ if (StructuredName.PHONETIC_GIVEN_NAME.equals(editField.column)) {
+ supportPhoneticGivenName = true;
+ }
+ }
+
+ // DISPLAY_NAME <-> PREFIX, GIVEN_NAME, MIDDLE_NAME, FAMILY_NAME, SUFFIX
+ final String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
+ if (!TextUtils.isEmpty(displayName)) {
+ if (!supportDisplayName) {
+ // Old data has a display name, while the new account doesn't allow it.
+ StructuredNameEditorView.buildStructuredNameFromFullName(
+ context, displayName, values);
+
+ // We don't want to migrate unseen data which may confuse users after the creation.
+ values.remove(StructuredName.DISPLAY_NAME);
+ }
+ } else {
+ if (supportDisplayName) {
+ // Old data does not have display name, while the new account requires it.
+ values.put(StructuredName.DISPLAY_NAME,
+ StructuredNameEditorView.buildFullNameFromStructuredName(context,
+ values.getAsString(StructuredName.PREFIX),
+ values.getAsString(StructuredName.GIVEN_NAME),
+ values.getAsString(StructuredName.MIDDLE_NAME),
+ values.getAsString(StructuredName.FAMILY_NAME),
+ values.getAsString(StructuredName.SUFFIX)));
+
+ values.remove(StructuredName.PREFIX);
+ values.remove(StructuredName.GIVEN_NAME);
+ values.remove(StructuredName.MIDDLE_NAME);
+ values.remove(StructuredName.FAMILY_NAME);
+ values.remove(StructuredName.SUFFIX);
+ }
+ }
+
+ // Phonetic (full) name <-> PHONETIC_FAMILY_NAME, PHONETIC_MIDDLE_NAME, PHONETIC_GIVEN_NAME
+ final String phoneticFullName = values.getAsString(DataKind.PSEUDO_COLUMN_PHONETIC_NAME);
+ if (!TextUtils.isEmpty(phoneticFullName)) {
+ if (!supportPhoneticFullName) {
+ // Old data has a phonetic (full) name, while the new account doesn't allow it.
+ final ContentValues tmpValues =
+ PhoneticNameEditorView.parsePhoneticName(phoneticFullName, null);
+ values.remove(DataKind.PSEUDO_COLUMN_PHONETIC_NAME);
+ if (supportPhoneticFamilyName) {
+ values.put(StructuredName.PHONETIC_FAMILY_NAME,
+ tmpValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME));
+ } else {
+ values.remove(StructuredName.PHONETIC_FAMILY_NAME);
+ }
+ if (supportPhoneticMiddleName) {
+ values.put(StructuredName.PHONETIC_MIDDLE_NAME,
+ tmpValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME));
+ } else {
+ values.remove(StructuredName.PHONETIC_MIDDLE_NAME);
+ }
+ if (supportPhoneticGivenName) {
+ values.put(StructuredName.PHONETIC_GIVEN_NAME,
+ tmpValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
+ } else {
+ values.remove(StructuredName.PHONETIC_GIVEN_NAME);
+ }
+ }
+ } else {
+ if (supportPhoneticFullName) {
+ // Old data does not have a phonetic (full) name, while the new account requires it.
+ values.put(DataKind.PSEUDO_COLUMN_PHONETIC_NAME,
+ PhoneticNameEditorView.buildPhoneticName(
+ values.getAsString(StructuredName.PHONETIC_FAMILY_NAME),
+ values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME),
+ values.getAsString(StructuredName.PHONETIC_GIVEN_NAME)));
+ }
+ if (!supportPhoneticFamilyName) {
+ values.remove(StructuredName.PHONETIC_FAMILY_NAME);
+ }
+ if (!supportPhoneticMiddleName) {
+ values.remove(StructuredName.PHONETIC_MIDDLE_NAME);
+ }
+ if (!supportPhoneticGivenName) {
+ values.remove(StructuredName.PHONETIC_GIVEN_NAME);
+ }
+ }
+
+ newState.addEntry(ValuesDelta.fromAfter(values));
+ }
+
+ /** @hide Public only for testing. */
+ public static void migratePostal(EntityDelta oldState, EntityDelta newState,
+ DataKind newDataKind) {
+ final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
+ oldState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE));
+ if (mimeEntries == null || mimeEntries.isEmpty()) {
+ return;
+ }
+
+ boolean supportFormattedAddress = false;
+ boolean supportStreet = false;
+ final String firstColumn = newDataKind.fieldList.get(0).column;
+ for (EditField editField : newDataKind.fieldList) {
+ if (StructuredPostal.FORMATTED_ADDRESS.equals(editField.column)) {
+ supportFormattedAddress = true;
+ }
+ if (StructuredPostal.STREET.equals(editField.column)) {
+ supportStreet = true;
+ }
+ }
+
+ final Set<Integer> supportedTypes = new HashSet<Integer>();
+ if (newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) {
+ for (EditType editType : newDataKind.typeList) {
+ supportedTypes.add(editType.rawValue);
+ }
+ }
+
+ for (ValuesDelta entry : mimeEntries) {
+ final ContentValues values = entry.getAfter();
+ if (values == null) {
+ continue;
+ }
+ final Integer oldType = values.getAsInteger(StructuredPostal.TYPE);
+ if (!supportedTypes.contains(oldType)) {
+ int defaultType;
+ if (newDataKind.defaultValues != null) {
+ defaultType = newDataKind.defaultValues.getAsInteger(StructuredPostal.TYPE);
+ } else {
+ defaultType = newDataKind.typeList.get(0).rawValue;
+ }
+ values.put(StructuredPostal.TYPE, defaultType);
+ if (oldType != null && oldType == StructuredPostal.TYPE_CUSTOM) {
+ values.remove(StructuredPostal.LABEL);
+ }
+ }
+
+ final String formattedAddress = values.getAsString(StructuredPostal.FORMATTED_ADDRESS);
+ if (!TextUtils.isEmpty(formattedAddress)) {
+ if (!supportFormattedAddress) {
+ // Old data has a formatted address, while the new account doesn't allow it.
+ values.remove(StructuredPostal.FORMATTED_ADDRESS);
+
+ // Unlike StructuredName we don't have logic to split it, so first
+ // try to use street field and. If the new account doesn't have one,
+ // then select first one anyway.
+ if (supportStreet) {
+ values.put(StructuredPostal.STREET, formattedAddress);
+ } else {
+ values.put(firstColumn, formattedAddress);
+ }
+ }
+ } else {
+ if (supportFormattedAddress) {
+ // Old data does not have formatted address, while the new account requires it.
+ // Unlike StructuredName we don't have logic to join multiple address values.
+ // Use poor join heuristics for now.
+ String[] structuredData;
+ final boolean useJapaneseOrder =
+ Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage());
+ if (useJapaneseOrder) {
+ structuredData = new String[] {
+ values.getAsString(StructuredPostal.COUNTRY),
+ values.getAsString(StructuredPostal.POSTCODE),
+ values.getAsString(StructuredPostal.REGION),
+ values.getAsString(StructuredPostal.CITY),
+ values.getAsString(StructuredPostal.NEIGHBORHOOD),
+ values.getAsString(StructuredPostal.STREET),
+ values.getAsString(StructuredPostal.POBOX) };
+ } else {
+ structuredData = new String[] {
+ values.getAsString(StructuredPostal.POBOX),
+ values.getAsString(StructuredPostal.STREET),
+ values.getAsString(StructuredPostal.NEIGHBORHOOD),
+ values.getAsString(StructuredPostal.CITY),
+ values.getAsString(StructuredPostal.REGION),
+ values.getAsString(StructuredPostal.POSTCODE),
+ values.getAsString(StructuredPostal.COUNTRY) };
+ }
+ final StringBuilder builder = new StringBuilder();
+ for (String elem : structuredData) {
+ if (!TextUtils.isEmpty(elem)) {
+ builder.append(elem + "\n");
+ }
+ }
+ values.put(StructuredPostal.FORMATTED_ADDRESS, builder.toString());
+
+ values.remove(StructuredPostal.POBOX);
+ values.remove(StructuredPostal.STREET);
+ values.remove(StructuredPostal.NEIGHBORHOOD);
+ values.remove(StructuredPostal.CITY);
+ values.remove(StructuredPostal.REGION);
+ values.remove(StructuredPostal.POSTCODE);
+ values.remove(StructuredPostal.COUNTRY);
+ }
+ }
+
+ newState.addEntry(ValuesDelta.fromAfter(values));
+ }
+ }
+
+ /** @hide Public only for testing. */
+ public static void migrateEvent(EntityDelta oldState, EntityDelta newState,
+ DataKind newDataKind, Integer defaultYear) {
+ final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
+ oldState.getMimeEntries(Event.CONTENT_ITEM_TYPE));
+ if (mimeEntries == null || mimeEntries.isEmpty()) {
+ return;
+ }
+
+ final Map<Integer, EventEditType> allowedTypes = new HashMap<Integer, EventEditType>();
+ for (EditType editType : newDataKind.typeList) {
+ allowedTypes.put(editType.rawValue, (EventEditType) editType);
+ }
+ for (ValuesDelta entry : mimeEntries) {
+ final ContentValues values = entry.getAfter();
+ if (values == null) {
+ continue;
+ }
+ final String dateString = values.getAsString(Event.START_DATE);
+ final Integer type = values.getAsInteger(Event.TYPE);
+ if (type != null && allowedTypes.containsKey(type) && !TextUtils.isEmpty(dateString)) {
+ EventEditType suitableType = allowedTypes.get(type);
+
+ final ParsePosition position = new ParsePosition(0);
+ boolean yearOptional = false;
+ Date date = DateUtils.DATE_AND_TIME_FORMAT.parse(dateString, position);
+ if (date == null) {
+ yearOptional = true;
+ date = DateUtils.NO_YEAR_DATE_FORMAT.parse(dateString, position);
+ }
+ if (date != null) {
+ if (yearOptional && !suitableType.isYearOptional()) {
+ // The new EditType doesn't allow optional year. Supply default.
+ final Calendar calendar = Calendar.getInstance(DateUtils.UTC_TIMEZONE,
+ Locale.US);
+ if (defaultYear == null) {
+ defaultYear = calendar.get(Calendar.YEAR);
+ }
+ calendar.setTime(date);
+ final int month = calendar.get(Calendar.MONTH);
+ final int day = calendar.get(Calendar.DAY_OF_MONTH);
+ // Exchange requires 8:00 for birthdays
+ calendar.set(defaultYear, month, day,
+ EventFieldEditorView.getDefaultHourForBirthday(), 0, 0);
+ values.put(Event.START_DATE,
+ DateUtils.FULL_DATE_FORMAT.format(calendar.getTime()));
+ }
+ }
+ newState.addEntry(ValuesDelta.fromAfter(values));
+ } else {
+ // Just drop it.
+ }
+ }
+ }
+
+ /** @hide Public only for testing. */
+ public static void migrateGenericWithoutTypeColumn(
+ EntityDelta oldState, EntityDelta newState, DataKind newDataKind) {
+ final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
+ oldState.getMimeEntries(newDataKind.mimeType));
+ if (mimeEntries == null || mimeEntries.isEmpty()) {
+ return;
+ }
+
+ for (ValuesDelta entry : mimeEntries) {
+ ContentValues values = entry.getAfter();
+ if (values != null) {
+ newState.addEntry(ValuesDelta.fromAfter(values));
+ }
+ }
+ }
+
+ /** @hide Public only for testing. */
+ public static void migrateGenericWithTypeColumn(
+ EntityDelta oldState, EntityDelta newState, DataKind newDataKind) {
+ if (!sGenericMimeTypesWithTypeSupport.contains(newDataKind.mimeType)) {
+ throw new RuntimeException("not supported: " + newDataKind.mimeType);
+ }
+
+ final ArrayList<ValuesDelta> mimeEntries = oldState.getMimeEntries(newDataKind.mimeType);
+ if (mimeEntries == null || mimeEntries.isEmpty()) {
+ return;
+ }
+
+ // Note that type specified with the old account may be invalid with the new account, while
+ // we want to preserve its data as much as possible. e.g. if a user typed a phone number
+ // with a type which is valid with an old account but not with a new account, the user
+ // probably wants to have the number with default type, rather than seeing complete data
+ // loss.
+ //
+ // Specifically, this method works as follows:
+ // 1. detect defaultType
+ // 2. prepare constants & variables for iteration
+ // 3. iterate over mimeEntries:
+ // 3.1 stop iteration if total number of mimeEntries reached typeOverallMax specified in
+ // DataKind
+ // 3.2 replace unallowed types with defaultType
+ // 3.3 check if the number of entries is below specificMax specified in AccountType
+
+ // Here, defaultType can be supplied in two ways
+ // - via kind.defaultValues
+ // - via kind.typeList.get(0).rawValue
+ Integer defaultType = null;
+ if (newDataKind.defaultValues != null) {
+ defaultType = newDataKind.defaultValues.getAsInteger(COLUMN_FOR_TYPE);
+ }
+ final Set<Integer> allowedTypes = new HashSet<Integer>();
+ // key: type, value: the number of entries allowed for the type (specificMax)
+ final Map<Integer, Integer> typeSpecificMaxMap = new HashMap<Integer, Integer>();
+ if (defaultType != null) {
+ allowedTypes.add(defaultType);
+ typeSpecificMaxMap.put(defaultType, -1);
+ }
+ // Note: typeList may be used in different purposes when defaultValues are specified.
+ // Especially in IM, typeList contains available protocols (e.g. PROTOCOL_GOOGLE_TALK)
+ // instead of "types" which we want to treate here (e.g. TYPE_HOME). So we don't add
+ // anything other than defaultType into allowedTypes and typeSpecificMapMax.
+ if (!Im.CONTENT_ITEM_TYPE.equals(newDataKind.mimeType) &&
+ newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) {
+ for (EditType editType : newDataKind.typeList) {
+ allowedTypes.add(editType.rawValue);
+ typeSpecificMaxMap.put(editType.rawValue, editType.specificMax);
+ }
+ if (defaultType == null) {
+ defaultType = newDataKind.typeList.get(0).rawValue;
+ }
+ }
+
+ if (defaultType == null) {
+ Log.w(TAG, "Default type isn't available for mimetype " + newDataKind.mimeType);
+ }
+
+ final int typeOverallMax = newDataKind.isList ? newDataKind.typeOverallMax : 1;
+
+ // key: type, value: the number of current entries.
+ final Map<Integer, Integer> currentEntryCount = new HashMap<Integer, Integer>();
+ int totalCount = 0;
+
+ for (ValuesDelta entry : mimeEntries) {
+ if (typeOverallMax != -1 && totalCount >= typeOverallMax) {
+ break;
+ }
+
+ final ContentValues values = entry.getAfter();
+ if (values == null) {
+ continue;
+ }
+
+ final Integer oldType = entry.getAsInteger(COLUMN_FOR_TYPE);
+ final Integer typeForNewAccount;
+ if (!allowedTypes.contains(oldType)) {
+ // The new account doesn't support the type.
+ if (defaultType != null) {
+ typeForNewAccount = defaultType.intValue();
+ values.put(COLUMN_FOR_TYPE, defaultType.intValue());
+ if (oldType != null && oldType == TYPE_CUSTOM) {
+ values.remove(COLUMN_FOR_LABEL);
+ }
+ } else {
+ typeForNewAccount = null;
+ values.remove(COLUMN_FOR_TYPE);
+ }
+ } else {
+ typeForNewAccount = oldType;
+ }
+ if (typeForNewAccount != null) {
+ final int specificMax = (typeSpecificMaxMap.containsKey(typeForNewAccount) ?
+ typeSpecificMaxMap.get(typeForNewAccount) : 0);
+ if (specificMax >= 0) {
+ final int currentCount = (currentEntryCount.get(typeForNewAccount) != null ?
+ currentEntryCount.get(typeForNewAccount) : 0);
+ if (currentCount >= specificMax) {
+ continue;
+ }
+ currentEntryCount.put(typeForNewAccount, currentCount + 1);
+ }
+ }
+ newState.addEntry(ValuesDelta.fromAfter(values));
+ totalCount++;
+ }
+ }
}
diff --git a/src/com/android/contacts/model/ExchangeAccountType.java b/src/com/android/contacts/model/ExchangeAccountType.java
index 2b1a520..22a2ba2 100644
--- a/src/com/android/contacts/model/ExchangeAccountType.java
+++ b/src/com/android/contacts/model/ExchangeAccountType.java
@@ -17,7 +17,6 @@
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;
@@ -65,7 +64,8 @@
@Override
protected DataKind addDataKindStructuredName(Context context) {
DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
- R.string.nameLabelsGroup, -1, -1, true));
+ R.string.nameLabelsGroup, -1, -1, true,
+ R.layout.structured_name_editor_view, android.R.style.TextAppearance_Large, -1));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
@@ -94,7 +94,8 @@
@Override
protected DataKind addDataKindDisplayName(Context context) {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
- R.string.nameLabelsGroup, -1, -1, true));
+ R.string.nameLabelsGroup, -1, -1, true,
+ R.layout.text_fields_editor_view, android.R.style.TextAppearance_Large, -1));
boolean displayOrderPrimary =
context.getResources().getBoolean(R.bool.config_editor_field_order_primary);
@@ -127,7 +128,8 @@
@Override
protected DataKind addDataKindPhoneticName(Context context) {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
- R.string.name_phonetic, -1, -1, true));
+ R.string.name_phonetic, -1, -1, true,
+ R.layout.phonetic_name_editor_view, android.R.style.TextAppearance_Medium, -1));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
@@ -292,7 +294,9 @@
protected DataKind addDataKindEvent(Context context) {
DataKind kind = addKind(
- new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, -1, 150, true));
+ new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, -1, 150, true,
+ R.layout.event_field_editor_view, android.R.style.TextAppearance_Medium,
+ R.string.add_event));
kind.actionHeader = new EventActionInflater();
kind.actionBody = new SimpleInflater(Event.START_DATE);
@@ -303,7 +307,6 @@
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));
diff --git a/src/com/android/contacts/model/GoogleAccountType.java b/src/com/android/contacts/model/GoogleAccountType.java
index af25691..bd445b1 100644
--- a/src/com/android/contacts/model/GoogleAccountType.java
+++ b/src/com/android/contacts/model/GoogleAccountType.java
@@ -17,7 +17,6 @@
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;
@@ -63,9 +62,10 @@
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_HOME));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_MAIN));
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));
@@ -99,7 +99,9 @@
private DataKind addDataKindRelation(Context context) {
DataKind kind = addKind(new DataKind(Relation.CONTENT_ITEM_TYPE,
- R.string.relationLabelsGroup, -1, 160, true));
+ R.string.relationLabelsGroup, -1, 160, true,
+ R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium,
+ R.string.add_relationship));
kind.actionHeader = new RelationActionInflater();
kind.actionBody = new SimpleInflater(Relation.NAME);
@@ -134,10 +136,11 @@
private DataKind addDataKindEvent(Context context) {
DataKind kind = addKind(new DataKind(Event.CONTENT_ITEM_TYPE,
- R.string.eventLabelsGroup, -1, 150, true));
+ R.string.eventLabelsGroup, -1, 150, true,
+ R.layout.event_field_editor_view, android.R.style.TextAppearance_Medium,
+ R.string.add_event));
kind.actionHeader = new EventActionInflater();
kind.actionBody = new SimpleInflater(Event.START_DATE);
- kind.editorClass = EventFieldEditorView.class;
kind.typeColumn = Event.TYPE;
kind.typeList = Lists.newArrayList();
diff --git a/src/com/android/contacts/quickcontact/DataAction.java b/src/com/android/contacts/quickcontact/DataAction.java
index bd59856..787d482 100644
--- a/src/com/android/contacts/quickcontact/DataAction.java
+++ b/src/com/android/contacts/quickcontact/DataAction.java
@@ -2,7 +2,7 @@
import com.android.contacts.ContactsUtils;
import com.android.contacts.R;
-import com.android.contacts.model.AccountType.DataKind;
+import com.android.contacts.model.DataKind;
import com.android.contacts.util.Constants;
import com.android.contacts.util.PhoneCapabilityTester;
diff --git a/src/com/android/contacts/quickcontact/FloatingChildLayout.java b/src/com/android/contacts/quickcontact/FloatingChildLayout.java
new file mode 100644
index 0000000..ddba609
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/FloatingChildLayout.java
@@ -0,0 +1,231 @@
+/*
+ * 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.
+ */
+
+package com.android.contacts.quickcontact;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
+import android.widget.PopupWindow;
+
+/**
+ * Layout containing single child {@link View} which it attempts to center
+ * around {@link #setChildTargetScreen(Rect)}.
+ * <p>
+ * Updates drawable state to be {@link android.R.attr#state_first} when child is
+ * above target, and {@link android.R.attr#state_last} when child is below
+ * target. Also updates {@link Drawable#setLevel(int)} on child
+ * {@link View#getBackground()} to reflect horizontal center of target.
+ * <p>
+ * The reason for this approach is because target {@link Rect} is in screen
+ * coordinates disregarding decor insets; otherwise something like
+ * {@link PopupWindow} might work better.
+ */
+public class FloatingChildLayout extends FrameLayout {
+ private static final String TAG = "FloatingChild";
+
+ public FloatingChildLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ private View mChild;
+
+ private Rect mTargetScreen = new Rect();
+
+ private int mCalloutState = 0;
+ private int mCalloutLeft;
+
+ @Override
+ protected void onFinishInflate() {
+ mChild = findViewById(android.R.id.content);
+ mChild.setDuplicateParentStateEnabled(true);
+ }
+
+ public View getChild() {
+ return mChild;
+ }
+
+ /**
+ * Set {@link Rect} in screen coordinates that {@link #getChild()} should be
+ * centered around.
+ */
+ public void setChildTargetScreen(Rect targetScreen) {
+ mTargetScreen = targetScreen;
+ requestLayout();
+ }
+
+ /**
+ * Return {@link #mTargetScreen} in local window coordinates, taking any
+ * decor insets into account.
+ */
+ private Rect getTargetInWindow() {
+ final Rect windowScreen = new Rect();
+ getWindowVisibleDisplayFrame(windowScreen);
+
+ final Rect target = new Rect(mTargetScreen);
+ target.offset(-windowScreen.left, -windowScreen.top);
+ return target;
+ }
+
+ private void updateCallout(int calloutState, int calloutLeft) {
+ if (mCalloutState != calloutState) {
+ mCalloutState = calloutState;
+ mChild.refreshDrawableState();
+ }
+
+ final Drawable background = mChild.getBackground();
+ if (background != null && mCalloutLeft != calloutLeft) {
+ mCalloutLeft = calloutLeft;
+ background.setLevel(calloutLeft);
+ }
+ }
+
+ @Override
+ protected int[] onCreateDrawableState(int extraSpace) {
+ final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+ mergeDrawableStates(drawableState, new int[] { mCalloutState });
+ return drawableState;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+
+ final View child = mChild;
+ final Rect target = getTargetInWindow();
+
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+
+ // default is no callout, left-aligned, and vertically centered
+ int calloutState = 0;
+ int childLeft = target.left;
+ int childTop = target.centerY() - (childHeight / 2);
+
+ // when target is wide, horizontally center instead of left-align
+ if (target.width() > childWidth / 2) {
+ childLeft = target.centerX() - (childWidth / 2);
+ }
+
+ final int areaAboveTarget = target.top;
+ final int areaBelowTarget = getHeight() - target.bottom;
+
+ if (areaAboveTarget >= childHeight) {
+ // enough room above target, place above and callout down
+ calloutState = android.R.attr.state_first;
+ childTop = target.top - childHeight;
+
+ } else if (areaBelowTarget >= childHeight) {
+ // enough room below target, place below and callout up
+ calloutState = android.R.attr.state_last;
+ childTop = target.bottom;
+ }
+
+ // when child is outside bounds, nudge back inside
+ childLeft = clampDimension(childLeft, childWidth, getWidth());
+ childTop = clampDimension(childTop, childHeight, getHeight());
+
+ final int calloutLeft = target.centerX() - childLeft;
+ updateCallout(calloutState, calloutLeft);
+ layoutChild(child, childLeft, childTop);
+
+ }
+
+ private static int clampDimension(int value, int size, int max) {
+ // when larger than bounds, just center
+ if (size > max) {
+ return (max - size) / 2;
+ }
+
+ // clamp to lower bound
+ value = Math.max(value, 0);
+ // clamp to higher bound
+ value = Math.min(value, max - size);
+
+ return value;
+ }
+
+ private static void layoutChild(View child, int left, int top) {
+ child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
+ }
+
+ /**
+ * Begin animating {@link #getChild()} visible.
+ */
+ public void showChild() {
+ final boolean calloutAbove = mCalloutState == android.R.attr.state_first;
+ final Animation anim = AnimationUtils.loadAnimation(getContext(),
+ calloutAbove ? R.anim.quickcontact_above_enter : R.anim.quickcontact_below_enter);
+ mChild.startAnimation(anim);
+ mChild.setVisibility(View.VISIBLE);
+ }
+
+ /**
+ * Begin animating {@link #getChild()} invisible.
+ */
+ public void hideChild(final Runnable onAnimationEnd) {
+ final boolean calloutAbove = mCalloutState == android.R.attr.state_first;
+ final Animation anim = AnimationUtils.loadAnimation(getContext(),
+ calloutAbove ? R.anim.quickcontact_above_exit : R.anim.quickcontact_below_exit);
+
+ if (onAnimationEnd != null) {
+ anim.setAnimationListener(new AnimationListener() {
+ /** {@inheritDoc} */
+ public void onAnimationStart(Animation animation) {
+ // ignored
+ }
+
+ /** {@inheritDoc} */
+ public void onAnimationRepeat(Animation animation) {
+ // ignored
+ }
+
+ /** {@inheritDoc} */
+ public void onAnimationEnd(Animation animation) {
+ onAnimationEnd.run();
+ }
+ });
+ }
+
+ mChild.startAnimation(anim);
+ mChild.setVisibility(View.INVISIBLE);
+ }
+
+ private View.OnTouchListener mOutsideTouchListener;
+
+ public void setOnOutsideTouchListener(View.OnTouchListener listener) {
+ mOutsideTouchListener = listener;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ // at this point, touch wasn't handled by child view; assume outside
+ if (mOutsideTouchListener != null) {
+ return mOutsideTouchListener.onTouch(this, event);
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
deleted file mode 100644
index 5032386..0000000
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ /dev/null
@@ -1,121 +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.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
index 9118480..15311f9 100644
--- a/src/com/android/contacts/quickcontact/QuickContactBackgroundDrawable.java
+++ b/src/com/android/contacts/quickcontact/QuickContactBackgroundDrawable.java
@@ -26,37 +26,43 @@
import android.graphics.drawable.Drawable;
/**
- * Drawable that draws three pictures for the QuickContact-Background. ColorFilter is ignored
+ * Background {@link Drawable} for {@link QuickContactWindow} that draws arrow
+ * centered around requested position.
*/
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;
+ public QuickContactBackgroundDrawable(Resources res) {
+ mLeftDrawable = res.getDrawable(R.drawable.quickactions_arrow_left_holo_light);
+ mMiddleDrawable = res.getDrawable(R.drawable.quickactions_arrow_middle_holo_light);
+ mRightDrawable = res.getDrawable(R.drawable.quickactions_arrow_right_holo_light);
+ }
+
@Override
public void setAlpha(int alpha) {
- mAlpha = alpha;
- setChildAlpha();
+ mLeftDrawable.setAlpha(alpha);
+ mMiddleDrawable.setAlpha(alpha);
+ mRightDrawable.setAlpha(alpha);
}
/**
- * Overrides the bottom bounds. This is used for the animation when the QuickContact
- * expands/collapses options
+ * Overrides the bottom bounds. This is used for the animation when the
+ * QuickContact expands/collapses options
*/
public void setBottomOverride(int value) {
mBottomOverride = value;
- setChildBounds();
+ onBoundsChange(getBounds());
invalidateSelf();
}
public void clearBottomOverride() {
mBottomOverride = Integer.MIN_VALUE;
+ onBoundsChange(getBounds());
invalidateSelf();
- setChildBounds();
}
public float getBottomOverride() {
@@ -64,7 +70,29 @@
}
@Override
+ public boolean isStateful() {
+ return true;
+ }
+
+ @Override
+ protected boolean onStateChange(int[] state) {
+ super.onStateChange(state);
+ mLeftDrawable.setState(state);
+ mMiddleDrawable.setState(state);
+ mRightDrawable.setState(state);
+ return true;
+ }
+
+ @Override
+ protected boolean onLevelChange(int level) {
+ return true;
+ }
+
+ @Override
public void setColorFilter(ColorFilter cf) {
+ mLeftDrawable.setColorFilter(cf);
+ mMiddleDrawable.setColorFilter(cf);
+ mRightDrawable.setColorFilter(cf);
}
@Override
@@ -72,54 +100,22 @@
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();
- }
+ final int requestedX = getLevel();
- 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;
+ int middleLeft = requestedX - mMiddleDrawable.getIntrinsicWidth() / 2;
+ int middleRight = requestedX + mMiddleDrawable.getIntrinsicWidth() / 2;
// ensure left drawable is not smaller than its Intrinsic Width
- final int leftExtra = (middleLeft - bounds.left) - mLeftDrawable.getIntrinsicWidth();
+ 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();
+ final int rightExtra = (bounds.right - middleRight) - mRightDrawable.getIntrinsicWidth();
if (rightExtra < 0) {
middleLeft += rightExtra;
middleRight += rightExtra;
diff --git a/src/com/android/contacts/quickcontact/QuickContactRootLayout.java b/src/com/android/contacts/quickcontact/QuickContactRootLayout.java
deleted file mode 100644
index 007783a..0000000
--- a/src/com/android/contacts/quickcontact/QuickContactRootLayout.java
+++ /dev/null
@@ -1,54 +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.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
index 147e364..5f4bcc9 100644
--- a/src/com/android/contacts/quickcontact/QuickContactWindow.java
+++ b/src/com/android/contacts/quickcontact/QuickContactWindow.java
@@ -20,18 +20,20 @@
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.model.DataKind;
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 com.google.common.base.Preconditions;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
+import android.app.Activity;
import android.content.ActivityNotFoundException;
+import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -41,6 +43,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Im;
@@ -54,30 +57,16 @@
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;
@@ -88,72 +77,44 @@
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}.
+ * Mostly translucent {@link Activity} that shows QuickContact dialog. It loads
+ * data asynchronously, and then shows a popup with details centered around
+ * {@link Intent#getSourceBounds()}.
*/
-public class QuickContactWindow implements Window.Callback,
+public class QuickContactWindow extends Activity implements
NotifyingAsyncQueryHandler.AsyncQueryListener, View.OnClickListener,
- AbsListView.OnItemClickListener, KeyEvent.Callback, OnGlobalLayoutListener,
- QuickContactRootLayout.Listener {
+ AbsListView.OnItemClickListener {
+ private static final String TAG = "QuickContact";
- private static final String TAG = "QuickContactWindow";
+ private static final boolean TRACE_LAUNCH = false;
+ private static final String TRACE_TAG = "quickcontact";
- /**
- * 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;
-
- /**
- * If the anchor is wider than (quick contact width * this constant) then
- * center quick contact. Otherwise, left-align.
- */
- private static final double MIN_RELATIVE_ANCHOR_WIDTH_TO_CENTER = 0.5;
-
- 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 static final int ANIMATION_FADE_IN_TIME = 100;
+ private static final int ANIMATION_FADE_OUT_TIME = 100;
+ private static final int ANIMATION_EXPAND_TIME = 100;
+ private static final int ANIMATION_COLLAPSE_TIME = 100;
private NotifyingAsyncQueryHandler mHandler;
- private OnDismissListener mDismissListener;
private Uri mLookupUri;
- private Rect mAnchor;
-
- private int mScreenWidth;
- private int mUseableScreenHeight;
- private int mRequestedY;
+ private int mMode;
+ private String[] mExcludeMimes;
private boolean mHasValidSocial = false;
- private int mMode;
- private QuickContactRootLayout mRootView;
+ private FloatingChildLayout mFloatingLayout;
private QuickContactBackgroundDrawable mBackground;
- private View mHeader;
- private HorizontalScrollView mTrackScroll;
- private ViewGroup mTrack;
+ private View mHeader;
+ private ViewGroup mTrack;
private FrameLayout mFooter;
private LinearLayout mFooterDisambig;
private LinearLayout mFooterClearDefaults;
+
private ListView mResolveList;
private CheckableImageView mLastAction;
private CheckBox mSetPrimaryCheckBox;
@@ -165,9 +126,6 @@
*/
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}
@@ -176,14 +134,6 @@
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,
@@ -221,82 +171,48 @@
});
private static final int TOKEN_DATA = 1;
- static final boolean TRACE_LAUNCH = false;
- static final String TRACE_TAG = "quickcontact";
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
- /**
- * 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);
+ setContentView(R.layout.quickcontact_activity);
- mWindow = PolicyManager.makeNewWindow(mContext);
- mWindow.setCallback(this);
- mWindow.setWindowManager(mWindowManager, null, null);
- mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED);
+ mBackground = new QuickContactBackgroundDrawable(getResources());
- mWindow.setContentView(R.layout.quickcontact);
+ mFloatingLayout = findTypedViewById(R.id.floating_layout);
+ mFloatingLayout.getChild().setBackgroundDrawable(mBackground);
+ mFloatingLayout.setOnOutsideTouchListener(mOnOutsideTouchListener);
- mRootView = (QuickContactRootLayout)mWindow.findViewById(R.id.root);
- mRootView.setListener(this);
- mRootView.setFocusable(true);
- mRootView.setFocusableInTouchMode(true);
- mRootView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+ mTrack = findTypedViewById(R.id.quickcontact);
+ mFooter = findTypedViewById(R.id.footer);
+ mFooterDisambig = findTypedViewById(R.id.footer_disambig);
+ mFooterClearDefaults = findTypedViewById(R.id.footer_clear_defaults);
+ mResolveList = findTypedViewById(android.R.id.list);
+ mSetPrimaryCheckBox = findTypedViewById(android.R.id.checkbox);
- mBackground = new QuickContactBackgroundDrawable();
- mRootView.setBackgroundDrawable(mBackground);
+ mDefaultsListView = findTypedViewById(R.id.defaults_list);
- 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;
+ mClearDefaultsButton = findTypedViewById(R.id.clear_defaults_button);
+ mClearDefaultsButton.setOnClickListener(mOnClearDefaultsClickListener);
- mTrack = (ViewGroup) mWindow.findViewById(R.id.quickcontact);
- mTrackScroll = (HorizontalScrollView) mWindow.findViewById(R.id.scroll);
+ mResolveList.setOnItemClickListener(this);
- 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);
+ mHandler = new NotifyingAsyncQueryHandler(this, this);
- 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;
+ show();
}
private View getHeaderView(int mode) {
View header = null;
switch (mode) {
case QuickContact.MODE_SMALL:
- header = mWindow.findViewById(R.id.header_small);
+ header = findViewById(R.id.header_small);
break;
case QuickContact.MODE_MEDIUM:
- header = mWindow.findViewById(R.id.header_medium);
+ header = findViewById(R.id.header_medium);
break;
case QuickContact.MODE_LARGE:
- header = mWindow.findViewById(R.id.header_large);
+ header = findViewById(R.id.header_large);
break;
}
@@ -311,60 +227,52 @@
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();
- }
+ private void show() {
- if (TRACE_LAUNCH && !android.os.Debug.isMethodTracingActive()) {
+ if (TRACE_LAUNCH) {
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");
+ final Intent intent = getIntent();
+
+ 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));
}
- if (anchor == null) {
- throw new IllegalArgumentException("Missing anchor rectangle");
+ mLookupUri = Preconditions.checkNotNull(lookupUri, "missing lookupUri");
+
+ // Read requested parameters for displaying
+ final Rect targetScreen = intent.getSourceBounds();
+ Preconditions.checkNotNull(targetScreen, "missing targetScreen");
+ mFloatingLayout.setChildTargetScreen(targetScreen);
+
+ mMode = intent.getIntExtra(QuickContact.EXTRA_MODE, QuickContact.MODE_MEDIUM);
+ mExcludeMimes = intent.getStringArrayExtra(QuickContact.EXTRA_EXCLUDE_MIMES);
+
+ switch (mMode) {
+ case QuickContact.MODE_SMALL:
+ case QuickContact.MODE_MEDIUM:
+ case QuickContact.MODE_LARGE:
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected mode: " + mMode);
}
- // Prepare header view for requested mode
- mLookupUri = lookupUri;
- mAnchor = new Rect(anchor);
- mMode = mode;
- mExcludeMimes = excludeMimes;
-
- mHeader = getHeaderView(mode);
-
+ // find and prepare correct header view
+ mHeader = getHeaderView(mMode);
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);
+ final Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
mHandler.cancelOperation(TOKEN_DATA);
// Only request photo data when required by mode
@@ -380,222 +288,85 @@
}
}
- /**
- * 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) {
- return Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
+ @SuppressWarnings("unchecked")
+ private <T> T findTypedViewById(int id) {
+ return (T) super.findViewById(id);
}
- /**
- * Creates and configures the background resource
- */
- private void configureBackground(boolean arrowUp, int requestedX) {
- mBackground.configure(mContext.getResources(), arrowUp, requestedX);
+ private View.OnTouchListener mOnOutsideTouchListener = new View.OnTouchListener() {
+ /** {@inheritDoc} */
+ public boolean onTouch(View v, MotionEvent event) {
+ hide(true);
+ return true;
+ }
+ };
+
+ private View.OnClickListener mOnClearDefaultsClickListener = new View.OnClickListener() {
+ /** {@inheritDoc} */
+ public void onClick(View v) {
+ clearDefaults();
+ }
+ };
+
+ private void hide(boolean withAnimation) {
+ // cancel any pending queries
+ mHandler.cancelOperation(TOKEN_DATA);
+
+ if (withAnimation) {
+ mFloatingLayout.hideChild(new Runnable() {
+ /** {@inheritDoc} */
+ public void run() {
+ finish();
+ }
+ });
+ } else {
+ mFloatingLayout.hideChild(null);
+ finish();
+ }
}
- /**
- * 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 or center if the anchor is wide
- if (mAnchor.left + layoutParams.width <= mScreenWidth) {
- if (mAnchor.width() > layoutParams.width * MIN_RELATIVE_ANCHOR_WIDTH_TO_CENTER) {
- layoutParams.x = mAnchor.left + (mAnchor.width() - layoutParams.width) / 2;
- } else {
- 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");
- }
+ @Override
+ public void onBackPressed() {
+ hide(true);
}
/** {@inheritDoc} */
- @Override
- public void onGlobalLayout() {
- layoutInScreen();
- }
+ public synchronized void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ try {
+ if (isFinishing()) {
+ hide(false);
+ return;
+ } else if (cursor == null || cursor.getCount() == 0) {
+ Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_LONG).show();
+ hide(false);
+ return;
+ }
- /**
- * 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;
+ bindData(cursor);
- 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();
+ if (TRACE_LAUNCH) {
+ android.os.Debug.stopMethodTracing();
+ }
+
+ // data bound and ready, pull curtain to show
+ mFloatingLayout.showChild();
+
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
}
- /** {@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));
+ setHeaderText(id, getText(resId));
}
/** Assign this string to the view, if found in {@link #mHeader}. */
@@ -633,26 +404,22 @@
/**
* Handle the result from the {@link #TOKEN_DATA} query.
*/
- private void handleData(Cursor cursor) {
- final ResolveCache cache = ResolveCache.getInstance(mContext);
- if (cursor == null) return;
- if (cursor.getCount() == 0) {
- Toast.makeText(mContext, R.string.invalidContactMessage, Toast.LENGTH_LONG).show();
- dismiss();
- return;
- }
+ private void bindData(Cursor cursor) {
+ final ResolveCache cache = ResolveCache.getInstance(this);
+ final Context context = this;
if (!isMimeExcluded(Contacts.CONTENT_ITEM_TYPE)) {
// Add the profile shortcut action
- final Action action = new ProfileAction(mContext, mLookupUri);
+ final Action action = new ProfileAction(context, 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);
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
+ context.getApplicationContext());
+ final ImageView photoView = (ImageView) mHeader.findViewById(R.id.photo);
Bitmap photoBitmap = null;
while (cursor.moveToNext()) {
@@ -684,7 +451,7 @@
// 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 Action action = new DataAction(context, mimeType, kind, dataId, cursor);
final boolean wasAdded = considerAdd(action, cache);
if (wasAdded) {
// Remember the default
@@ -696,7 +463,7 @@
// 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,
+ final DataAction action = new DataAction(context, Constants.MIME_TYPE_SMS_ADDRESS,
kind, dataId, cursor);
considerAdd(action, cache);
}
@@ -709,7 +476,7 @@
final DataKind imKind = accountTypes.getKindOrFallback(accountType,
Im.CONTENT_ITEM_TYPE);
if (imKind != null) {
- final DataAction action = new DataAction(mContext, Im.CONTENT_ITEM_TYPE, imKind,
+ final DataAction action = new DataAction(context, Im.CONTENT_ITEM_TYPE, imKind,
dataId, cursor);
considerAdd(action, cache);
isIm = true;
@@ -722,7 +489,7 @@
final DataKind imKind = accountTypes.getKindOrFallback(accountType,
Im.CONTENT_ITEM_TYPE);
if (imKind != null) {
- final DataAction chatAction = new DataAction(mContext,
+ final DataAction chatAction = new DataAction(context,
Constants.MIME_TYPE_VIDEO_CHAT, imKind, dataId, cursor);
considerAdd(chatAction, cache);
}
@@ -730,6 +497,11 @@
}
}
+ // Collapse Action Lists (remove e.g. duplicate e-mail addresses from different sources)
+ for (ArrayList<Action> actionChildren : mActions.values()) {
+ Collapser.collapseList(actionChildren);
+ }
+
// Make sure that we only display the "clear default" action if there
// are actually several items to chose from
boolean shouldDisplayClearDefaults = false;
@@ -751,7 +523,7 @@
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);
+ context, presence, chatCapability);
setHeaderText(R.id.name, name);
setHeaderImage(R.id.presence, statusIcon);
@@ -767,7 +539,7 @@
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));
+ setHeaderText(R.id.timestamp, status.getTimestampLabel(context));
}
// Turn our list of actions into UI elements
@@ -781,7 +553,7 @@
for (String mimeType : PRECEDING_MIMETYPES) {
if (containedTypes.contains(mimeType)) {
hasData = true;
- mTrack.addView(inflateAction(mimeType, cache));
+ mTrack.addView(inflateAction(mimeType, cache, mTrack));
containedTypes.remove(mimeType);
}
}
@@ -793,7 +565,7 @@
for (String mimeType : FOLLOWING_MIMETYPES) {
if (containedTypes.contains(mimeType)) {
hasData = true;
- mTrack.addView(inflateAction(mimeType, cache));
+ mTrack.addView(inflateAction(mimeType, cache, mTrack));
containedTypes.remove(mimeType);
}
}
@@ -802,9 +574,11 @@
if (containedTypes.contains(ClearDefaultsAction.PSEUDO_MIME_TYPE)) {
final ClearDefaultsAction action = (ClearDefaultsAction) mActions.get(
ClearDefaultsAction.PSEUDO_MIME_TYPE).get(0);
- final CheckableImageView view = obtainView();
+ final CheckableImageView view = (CheckableImageView) getLayoutInflater().inflate(
+ R.layout.quickcontact_item, mTrack, false);
+
view.setChecked(false);
- final String description = mContext.getResources().getString(
+ final String description = context.getResources().getString(
R.string.quickcontact_clear_defaults_description);
view.setContentDescription(description);
view.setImageResource(R.drawable.ic_menu_settings_holo_light);
@@ -820,12 +594,13 @@
if (remainingTypes.length > 0) hasData = true;
Arrays.sort(remainingTypes);
for (String mimeType : remainingTypes) {
- mTrack.addView(inflateAction(mimeType, cache), index++);
+ mTrack.addView(inflateAction(mimeType, cache, mTrack), 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);
+ View view = getLayoutInflater().inflate(
+ R.layout.quickcontact_item_nodata, mTrack, false);
mTrack.addView(view, index++);
}
}
@@ -834,7 +609,9 @@
* Clears the defaults currently set on the Contact
*/
private void clearDefaults() {
+ final Context context = this;
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);
@@ -842,9 +619,9 @@
// 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);
+ final Intent intent = ContactSaveService.createClearPrimaryIntent(
+ context, action.getDataId());
+ context.startService(intent);
mDefaultsMap.remove(mimeType);
}
@@ -855,7 +632,6 @@
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;
}
@@ -879,41 +655,15 @@
}
/**
- * 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();
+ private View inflateAction(String mimeType, ResolveCache resolveCache, ViewGroup root) {
+ final CheckableImageView view = (CheckableImageView) getLayoutInflater().inflate(
+ R.layout.quickcontact_item, root, false);
// 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);
@@ -990,6 +740,13 @@
expandAnimator.setDuration(ANIMATION_EXPAND_TIME);
expandAnimator.start();
+ expandAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBackground.clearBottomOverride();
+ }
+ });
+
final ObjectAnimator fadeInAnimator = ObjectAnimator.ofFloat(mFooter,
"alpha", 0.0f, 1.0f);
fadeInAnimator.setDuration(ANIMATION_FADE_IN_TIME);
@@ -1002,9 +759,12 @@
/** {@inheritDoc} */
@Override
public void onClick(View view) {
+ final Context context = this;
+
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;
@@ -1045,29 +805,28 @@
@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);
+ if (convertView == null) {
+ convertView = getLayoutInflater().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);
+ final TextView text1 = (TextView) convertView.findViewById(
+ android.R.id.text1);
+ final TextView text2 = (TextView) convertView.findViewById(
+ android.R.id.text2);
text1.setText(defaultAction.getHeader());
text2.setText(defaultAction.getBody());
- result.setTag(defaultAction);
- return result;
+ convertView.setTag(defaultAction);
+ return convertView;
}
});
animateExpand(true);
- // Make sure we resize to make room for ListView
- if (mDecor != null) {
- mDecor.forceLayout();
- mDecor.invalidate();
- }
}
};
if (mFooter.getVisibility() == View.VISIBLE) {
@@ -1116,24 +875,24 @@
public void run() {
// Incoming tag is concrete intent, so try launching
try {
- mContext.startActivity(action.getIntent());
+ context.startActivity(action.getIntent());
} catch (ActivityNotFoundException e) {
- Toast.makeText(mContext, R.string.quickcontact_missing_app,
+ Toast.makeText(context, 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);
+ context, dataId);
+ context.startService(serviceIntent);
}
+
+ hide(false);
}
};
if (isActionView && mFooter.getVisibility() == View.VISIBLE) {
@@ -1178,31 +937,31 @@
@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);
+ if (convertView == null) {
+ convertView = getLayoutInflater().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);
+ final TextView text1 = (TextView) convertView.findViewById(
+ android.R.id.text1);
+ final TextView text2 = (TextView) convertView.findViewById(
+ android.R.id.text2);
text1.setText(listAction.getHeader());
text2.setText(listAction.getBody());
- result.setTag(listAction);
- return result;
+ convertView.setTag(listAction);
+ return convertView;
}
});
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
@@ -1213,191 +972,6 @@
}
}
- @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 boolean dispatchGenericMotionEvent(MotionEvent event) {
- return mWindow.superDispatchGenericMotionEvent(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[] {
diff --git a/src/com/android/contacts/util/NotifyingAsyncQueryHandler.java b/src/com/android/contacts/util/NotifyingAsyncQueryHandler.java
index 83fae29..c8cfc8d 100644
--- a/src/com/android/contacts/util/NotifyingAsyncQueryHandler.java
+++ b/src/com/android/contacts/util/NotifyingAsyncQueryHandler.java
@@ -29,8 +29,6 @@
* <p>
* This pattern can be used to perform background queries without leaking
* {@link Context} objects.
- *
- * @hide pending API council review
*/
public class NotifyingAsyncQueryHandler extends AsyncQueryHandler {
private WeakReference<AsyncQueryListener> mListener;
diff --git a/src/com/android/contacts/util/PhoneCapabilityTester.java b/src/com/android/contacts/util/PhoneCapabilityTester.java
index 23319aa..d3a8060 100644
--- a/src/com/android/contacts/util/PhoneCapabilityTester.java
+++ b/src/com/android/contacts/util/PhoneCapabilityTester.java
@@ -16,6 +16,8 @@
package com.android.contacts.util;
+import com.android.contacts.R;
+
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -79,4 +81,12 @@
Uri.fromParts(Constants.SCHEME_SMSTO, "", null));
return isIntentRegistered(context, intent);
}
+
+ /**
+ * True if we are using two-pane layouts ("tablet mode"), false if we are using single views
+ * ("phone mode")
+ */
+ public static boolean isUsingTwoPanes(Context context) {
+ return context.getResources().getBoolean(R.bool.config_use_two_panes);
+ }
}
diff --git a/src/com/android/contacts/vcard/ExportProcessor.java b/src/com/android/contacts/vcard/ExportProcessor.java
index 67c9c4a..7b5e001 100644
--- a/src/com/android/contacts/vcard/ExportProcessor.java
+++ b/src/com/android/contacts/vcard/ExportProcessor.java
@@ -22,18 +22,22 @@
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.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContactsEntity;
import android.text.TextUtils;
import android.util.Log;
-import android.widget.RemoteViews;
+import java.io.BufferedWriter;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
/**
* Class for processing one export request from a user. Dropped after exporting requested Uri(s).
@@ -92,6 +96,7 @@
if (DEBUG) Log.d(LOG_TAG, String.format("vCard export (id: %d) has started.", mJobId));
final ExportRequest request = mExportRequest;
VCardComposer composer = null;
+ Writer writer = null;
boolean successful = false;
try {
if (isCancelled()) {
@@ -129,9 +134,14 @@
// VCardConfig.FLAG_USE_QP_TO_PRIMARY_PROPERTIES);
// composer = new VCardComposer(ExportVCardActivity.this, vcardType, true);
- composer.addHandler(composer.new HandlerForOutputStream(outputStream));
-
- if (!composer.init()) {
+ writer = new BufferedWriter(new OutputStreamWriter(outputStream));
+ final Uri contentUriForRawContactsEntity = RawContactsEntity.CONTENT_URI.buildUpon()
+ .appendQueryParameter(RawContactsEntity.FOR_EXPORT_ONLY, "1")
+ .build();
+ // TODO: should provide better selection.
+ if (!composer.init(Contacts.CONTENT_URI, new String[] {Contacts._ID},
+ null, null,
+ null, contentUriForRawContactsEntity)) {
final String errorReason = composer.getErrorReason();
Log.e(LOG_TAG, "initialization of vCard composer failed: " + errorReason);
final String translatedErrorReason =
@@ -157,7 +167,9 @@
Log.i(LOG_TAG, "Export request is cancelled during composing vCard");
return;
}
- if (!composer.createOneEntry()) {
+ try {
+ writer.write(composer.createOneEntry());
+ } catch (IOException e) {
final String errorReason = composer.getErrorReason();
Log.e(LOG_TAG, "Failed to read a contact: " + errorReason);
final String translatedErrorReason =
@@ -192,7 +204,13 @@
if (composer != null) {
composer.terminate();
}
-
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ Log.w(LOG_TAG, "IOException is thrown during close(). Ignored. " + e);
+ }
+ }
mService.handleFinishExportNotification(mJobId, successful);
}
}
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
index b3376e9..17b89c3 100644
--- a/src/com/android/contacts/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -21,7 +21,6 @@
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;
@@ -399,7 +398,6 @@
final ContentResolver resolver = ImportVCardActivity.this.getContentResolver();
VCardEntryCounter counter = null;
VCardSourceDetector detector = null;
- VCardInterpreterCollection interpreter = null;
int vcardVersion = VCARD_VERSION_V21;
try {
boolean shouldUseV30 = false;
@@ -408,9 +406,9 @@
try {
counter = new VCardEntryCounter();
detector = new VCardSourceDetector();
- interpreter = new VCardInterpreterCollection(
- Arrays.asList(counter, detector));
- mVCardParser.parse(is, interpreter);
+ mVCardParser.addInterpreter(counter);
+ mVCardParser.addInterpreter(detector);
+ mVCardParser.parse(is);
} catch (VCardVersionException e1) {
try {
is.close();
@@ -423,9 +421,9 @@
try {
counter = new VCardEntryCounter();
detector = new VCardSourceDetector();
- interpreter = new VCardInterpreterCollection(
- Arrays.asList(counter, detector));
- mVCardParser.parse(is, interpreter);
+ mVCardParser.addInterpreter(counter);
+ mVCardParser.addInterpreter(detector);
+ mVCardParser.parse(is);
} catch (VCardVersionException e2) {
throw new VCardException("vCard with unspported version.");
}
diff --git a/tests/res/values/donottranslate_strings.xml b/tests/res/values/donottranslate_strings.xml
index 8557fd6..bd272c3 100644
--- a/tests/res/values/donottranslate_strings.xml
+++ b/tests/res/values/donottranslate_strings.xml
@@ -32,6 +32,7 @@
<item>ACTION_PICK: phone (legacy)</item>
<item>ACTION_PICK: postal</item>
<item>ACTION_PICK: postal (legacy)</item>
+ <item>ACTION_PICK: e-mail</item>
<item>ACTION_CREATE_SHORTCUT: contact</item>
<item>ACTION_CREATE_SHORTCUT: dial</item>
<item>ACTION_CREATE_SHORTCUT: message</item>
diff --git a/tests/src/com/android/contacts/DialerLaunchPerformance.java b/tests/src/com/android/contacts/DialerLaunchPerformance.java
index ae78082..0803c6b 100644
--- a/tests/src/com/android/contacts/DialerLaunchPerformance.java
+++ b/tests/src/com/android/contacts/DialerLaunchPerformance.java
@@ -32,7 +32,7 @@
mIntent.setAction(Intent.ACTION_MAIN);
mIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mIntent.setComponent(new ComponentName("com.android.contacts",
- "com.android.contacts.DialtactsActivity"));
+ "com.android.contacts.activities.DialtactsActivity"));
start();
}
diff --git a/tests/src/com/android/contacts/EntityModifierTests.java b/tests/src/com/android/contacts/EntityModifierTests.java
index 4acaa92..63dd84d 100644
--- a/tests/src/com/android/contacts/EntityModifierTests.java
+++ b/tests/src/com/android/contacts/EntityModifierTests.java
@@ -20,23 +20,31 @@
import static android.content.ContentProviderOperation.TYPE_INSERT;
import static android.content.ContentProviderOperation.TYPE_UPDATE;
+import com.google.android.collect.Lists;
+
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.DataKind;
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.ExchangeAccountType;
+import com.android.contacts.model.GoogleAccountType;
+import com.android.contacts.tests.mocks.ContactsMockContext;
import com.android.contacts.tests.mocks.MockAccountTypeManager;
-import com.google.android.collect.Lists;
+import com.android.contacts.tests.mocks.MockContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.content.Context;
import android.content.Entity;
+import android.net.Uri;
import android.os.Bundle;
+import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
@@ -81,45 +89,54 @@
MockContactsSource() {
this.accountType = TEST_ACCOUNT_TYPE;
+ final DataKind nameKind = new DataKind(StructuredName.CONTENT_ITEM_TYPE,
+ R.string.nameLabelsGroup, -1, -1, true, -1, -1, -1);
+ nameKind.typeOverallMax = 1;
+ addKind(nameKind);
+
// Phone allows maximum 2 home, 1 work, and unlimited other, with
// constraint of 5 numbers maximum.
- DataKind kind = new DataKind(Phone.CONTENT_ITEM_TYPE, -1, -1, 10, true);
+ final DataKind phoneKind = new DataKind(
+ Phone.CONTENT_ITEM_TYPE, -1, -1, 10, true, -1, -1, -1);
- kind.typeOverallMax = 5;
- kind.typeColumn = Phone.TYPE;
- kind.typeList = Lists.newArrayList();
- kind.typeList.add(new EditType(Phone.TYPE_HOME, -1).setSpecificMax(2));
- kind.typeList.add(new EditType(Phone.TYPE_WORK, -1).setSpecificMax(1));
- kind.typeList.add(new EditType(Phone.TYPE_FAX_WORK, -1).setSecondary(true));
- kind.typeList.add(new EditType(Phone.TYPE_OTHER, -1));
+ phoneKind.typeOverallMax = 5;
+ phoneKind.typeColumn = Phone.TYPE;
+ phoneKind.typeList = Lists.newArrayList();
+ phoneKind.typeList.add(new EditType(Phone.TYPE_HOME, -1).setSpecificMax(2));
+ phoneKind.typeList.add(new EditType(Phone.TYPE_WORK, -1).setSpecificMax(1));
+ phoneKind.typeList.add(new EditType(Phone.TYPE_FAX_WORK, -1).setSecondary(true));
+ phoneKind.typeList.add(new EditType(Phone.TYPE_OTHER, -1));
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Phone.NUMBER, -1, -1));
- kind.fieldList.add(new EditField(Phone.LABEL, -1, -1));
+ phoneKind.fieldList = Lists.newArrayList();
+ phoneKind.fieldList.add(new EditField(Phone.NUMBER, -1, -1));
+ phoneKind.fieldList.add(new EditField(Phone.LABEL, -1, -1));
- addKind(kind);
+ addKind(phoneKind);
// Email is unlimited
- kind = new DataKind(Email.CONTENT_ITEM_TYPE, -1, -1, 10, true);
- kind.typeOverallMax = -1;
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Email.DATA, -1, -1));
- addKind(kind);
+ final DataKind emailKind = new DataKind(
+ Email.CONTENT_ITEM_TYPE, -1, -1, 10, true, -1, -1, -1);
+ emailKind.typeOverallMax = -1;
+ emailKind.fieldList = Lists.newArrayList();
+ emailKind.fieldList.add(new EditField(Email.DATA, -1, -1));
+ addKind(emailKind);
// IM is only one
- kind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, -1, 10, true);
- kind.typeOverallMax = 1;
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Im.DATA, -1, -1));
- addKind(kind);
+ final DataKind imKind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, -1, 10,
+ true, -1, -1, -1);
+ imKind.typeOverallMax = 1;
+ imKind.fieldList = Lists.newArrayList();
+ imKind.fieldList.add(new EditField(Im.DATA, -1, -1));
+ addKind(imKind);
// Organization is only one
- kind = new DataKind(Organization.CONTENT_ITEM_TYPE, -1, -1, 10, true);
- kind.typeOverallMax = 1;
- kind.fieldList = Lists.newArrayList();
- kind.fieldList.add(new EditField(Organization.COMPANY, -1, -1));
- kind.fieldList.add(new EditField(Organization.TITLE, -1, -1));
- addKind(kind);
+ final DataKind orgKind = new DataKind(
+ Organization.CONTENT_ITEM_TYPE, -1, -1, 10, true, -1, -1, -1);
+ orgKind.typeOverallMax = 1;
+ orgKind.fieldList = Lists.newArrayList();
+ orgKind.fieldList.add(new EditField(Organization.COMPANY, -1, -1));
+ orgKind.fieldList.add(new EditField(Organization.TITLE, -1, -1));
+ addKind(orgKind);
}
@Override
@@ -330,7 +347,7 @@
// Test row that has type values, but core fields are empty
final EntityDelta state = getEntity(TEST_ID);
- final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome);
+ EntityModifier.insertChild(state, kindPhone, typeHome);
// Build diff, expecting insert for data row and update enforcement
final ArrayList<ContentProviderOperation> diff = Lists.newArrayList();
@@ -420,7 +437,7 @@
public void testTrimEmptyUntouched() {
final AccountType source = getAccountType();
final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
- final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
+ EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
// Build "before" that has empty row
final EntityDelta state = getEntity(TEST_ID);
@@ -497,7 +514,7 @@
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);
+ EntityModifier.getType(kindPhone, Phone.TYPE_HOME);
// Try creating a contact without any child entries
final EntityDelta state = getEntity(null);
@@ -528,7 +545,7 @@
// Try creating a contact with single empty entry
final EntityDelta state = getEntity(null);
- final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome);
+ EntityModifier.insertChild(state, kindPhone, typeHome);
final EntityDeltaList set = EntityDeltaList.fromSingle(state);
// Build diff, expecting two insert operations
@@ -746,4 +763,448 @@
final int count = state.getMimeEntries(Organization.CONTENT_ITEM_TYPE).size();
assertEquals("Expected to create organization", 1, count);
}
+
+ public void testMigrateWithDisplayNameFromGoogleToExchange1() {
+ AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+ AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+ DataKind kind = newAccountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
+
+ ContactsMockContext context = new ContactsMockContext(getContext());
+
+ EntityDelta oldState = new EntityDelta();
+ ContentValues mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+ mockNameValues.put(StructuredName.PREFIX, "prefix");
+ mockNameValues.put(StructuredName.GIVEN_NAME, "given");
+ mockNameValues.put(StructuredName.MIDDLE_NAME, "middle");
+ mockNameValues.put(StructuredName.FAMILY_NAME, "family");
+ mockNameValues.put(StructuredName.SUFFIX, "suffix");
+ mockNameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "PHONETIC_FAMILY");
+ mockNameValues.put(StructuredName.PHONETIC_MIDDLE_NAME, "PHONETIC_MIDDLE");
+ mockNameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "PHONETIC_GIVEN");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ EntityDelta newState = new EntityDelta();
+ EntityModifier.migrateStructuredName(context, oldState, newState, kind);
+ List<ValuesDelta> list = newState.getMimeEntries(StructuredName.CONTENT_ITEM_TYPE);
+ assertEquals(1, list.size());
+
+ ContentValues output = list.get(0).getAfter();
+ assertEquals("prefix", output.getAsString(StructuredName.PREFIX));
+ assertEquals("given", output.getAsString(StructuredName.GIVEN_NAME));
+ assertEquals("middle", output.getAsString(StructuredName.MIDDLE_NAME));
+ assertEquals("family", output.getAsString(StructuredName.FAMILY_NAME));
+ assertEquals("suffix", output.getAsString(StructuredName.SUFFIX));
+ // Phonetic middle name isn't supported by Exchange.
+ assertEquals("PHONETIC_FAMILY", output.getAsString(StructuredName.PHONETIC_FAMILY_NAME));
+ assertEquals("PHONETIC_GIVEN", output.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
+ }
+
+ public void testMigrateWithDisplayNameFromGoogleToExchange2() {
+ AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+ AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+ DataKind kind = newAccountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
+
+ ContactsMockContext context = new ContactsMockContext(getContext());
+ MockContentProvider provider = context.getContactsProvider();
+
+ String inputDisplayName = "prefix given middle family suffix";
+ // The method will ask the provider to split/join StructuredName.
+ Uri uriForBuildDisplayName =
+ ContactsContract.AUTHORITY_URI
+ .buildUpon()
+ .appendPath("complete_name")
+ .appendQueryParameter(StructuredName.DISPLAY_NAME, inputDisplayName)
+ .build();
+ provider.expectQuery(uriForBuildDisplayName)
+ .returnRow("prefix", "given", "middle", "family", "suffix")
+ .withProjection(StructuredName.PREFIX, StructuredName.GIVEN_NAME,
+ StructuredName.MIDDLE_NAME, StructuredName.FAMILY_NAME,
+ StructuredName.SUFFIX);
+
+ EntityDelta oldState = new EntityDelta();
+ ContentValues mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+ mockNameValues.put(StructuredName.DISPLAY_NAME, inputDisplayName);
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ EntityDelta newState = new EntityDelta();
+ EntityModifier.migrateStructuredName(context, oldState, newState, kind);
+ List<ValuesDelta> list = newState.getMimeEntries(StructuredName.CONTENT_ITEM_TYPE);
+ assertEquals(1, list.size());
+
+ ContentValues outputValues = list.get(0).getAfter();
+ assertEquals("prefix", outputValues.getAsString(StructuredName.PREFIX));
+ assertEquals("given", outputValues.getAsString(StructuredName.GIVEN_NAME));
+ assertEquals("middle", outputValues.getAsString(StructuredName.MIDDLE_NAME));
+ assertEquals("family", outputValues.getAsString(StructuredName.FAMILY_NAME));
+ assertEquals("suffix", outputValues.getAsString(StructuredName.SUFFIX));
+ }
+
+ public void testMigrateWithStructuredNameFromExchangeToGoogle() {
+ AccountType oldAccountType = new ExchangeAccountType(getContext(), "");
+ AccountType newAccountType = new GoogleAccountType(getContext(), "");
+ DataKind kind = newAccountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
+
+ ContactsMockContext context = new ContactsMockContext(getContext());
+ MockContentProvider provider = context.getContactsProvider();
+
+ // The method will ask the provider to split/join StructuredName.
+ Uri uriForBuildDisplayName =
+ ContactsContract.AUTHORITY_URI
+ .buildUpon()
+ .appendPath("complete_name")
+ .appendQueryParameter(StructuredName.PREFIX, "prefix")
+ .appendQueryParameter(StructuredName.GIVEN_NAME, "given")
+ .appendQueryParameter(StructuredName.MIDDLE_NAME, "middle")
+ .appendQueryParameter(StructuredName.FAMILY_NAME, "family")
+ .appendQueryParameter(StructuredName.SUFFIX, "suffix")
+ .build();
+ provider.expectQuery(uriForBuildDisplayName)
+ .returnRow("prefix given middle family suffix")
+ .withProjection(StructuredName.DISPLAY_NAME);
+
+ EntityDelta oldState = new EntityDelta();
+ ContentValues mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+ mockNameValues.put(StructuredName.PREFIX, "prefix");
+ mockNameValues.put(StructuredName.GIVEN_NAME, "given");
+ mockNameValues.put(StructuredName.MIDDLE_NAME, "middle");
+ mockNameValues.put(StructuredName.FAMILY_NAME, "family");
+ mockNameValues.put(StructuredName.SUFFIX, "suffix");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ EntityDelta newState = new EntityDelta();
+ EntityModifier.migrateStructuredName(context, oldState, newState, kind);
+
+ List<ValuesDelta> list = newState.getMimeEntries(StructuredName.CONTENT_ITEM_TYPE);
+ assertNotNull(list);
+ assertEquals(1, list.size());
+ ContentValues outputValues = list.get(0).getAfter();
+ assertEquals("prefix given middle family suffix",
+ outputValues.getAsString(StructuredName.DISPLAY_NAME));
+ }
+
+ public void testMigratePostalFromGoogleToExchange() {
+ AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+ AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+ DataKind kind = newAccountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
+
+ EntityDelta oldState = new EntityDelta();
+ ContentValues mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
+ mockNameValues.put(StructuredPostal.FORMATTED_ADDRESS, "formatted_address");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ EntityDelta newState = new EntityDelta();
+ EntityModifier.migratePostal(oldState, newState, kind);
+
+ List<ValuesDelta> list = newState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE);
+ assertNotNull(list);
+ assertEquals(1, list.size());
+ ContentValues outputValues = list.get(0).getAfter();
+ // FORMATTED_ADDRESS isn't supported by Exchange.
+ assertNull(outputValues.getAsString(StructuredPostal.FORMATTED_ADDRESS));
+ assertEquals("formatted_address", outputValues.getAsString(StructuredPostal.STREET));
+ }
+
+ public void testMigratePostalFromExchangeToGoogle() {
+ AccountType oldAccountType = new ExchangeAccountType(getContext(), "");
+ AccountType newAccountType = new GoogleAccountType(getContext(), "");
+ DataKind kind = newAccountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
+
+ EntityDelta oldState = new EntityDelta();
+ ContentValues mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
+ mockNameValues.put(StructuredPostal.COUNTRY, "country");
+ mockNameValues.put(StructuredPostal.POSTCODE, "postcode");
+ mockNameValues.put(StructuredPostal.REGION, "region");
+ mockNameValues.put(StructuredPostal.CITY, "city");
+ mockNameValues.put(StructuredPostal.STREET, "street");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ EntityDelta newState = new EntityDelta();
+ EntityModifier.migratePostal(oldState, newState, kind);
+
+ List<ValuesDelta> list = newState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE);
+ assertNotNull(list);
+ assertEquals(1, list.size());
+ ContentValues outputValues = list.get(0).getAfter();
+
+ // Check FORMATTED_ADDRESS contains all info.
+ String formattedAddress = outputValues.getAsString(StructuredPostal.FORMATTED_ADDRESS);
+ assertNotNull(formattedAddress);
+ assertTrue(formattedAddress.contains("country"));
+ assertTrue(formattedAddress.contains("postcode"));
+ assertTrue(formattedAddress.contains("region"));
+ assertTrue(formattedAddress.contains("postcode"));
+ assertTrue(formattedAddress.contains("city"));
+ assertTrue(formattedAddress.contains("street"));
+ }
+
+ public void testMigrateEventFromGoogleToExchange1() {
+ testMigrateEventCommon(new GoogleAccountType(getContext(), ""),
+ new ExchangeAccountType(getContext(), ""));
+ }
+
+ public void testMigrateEventFromExchangeToGoogle() {
+ testMigrateEventCommon(new ExchangeAccountType(getContext(), ""),
+ new GoogleAccountType(getContext(), ""));
+ }
+
+ private void testMigrateEventCommon(AccountType oldAccountType, AccountType newAccountType) {
+ DataKind kind = newAccountType.getKindForMimetype(Event.CONTENT_ITEM_TYPE);
+
+ EntityDelta oldState = new EntityDelta();
+ ContentValues mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Event.START_DATE, "1972-02-08");
+ mockNameValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ EntityDelta newState = new EntityDelta();
+ EntityModifier.migrateEvent(oldState, newState, kind, 1990);
+
+ List<ValuesDelta> list = newState.getMimeEntries(Event.CONTENT_ITEM_TYPE);
+ assertNotNull(list);
+ assertEquals(1, list.size()); // Anniversary should be dropped.
+ ContentValues outputValues = list.get(0).getAfter();
+
+ assertEquals("1972-02-08", outputValues.getAsString(Event.START_DATE));
+ assertEquals(Event.TYPE_BIRTHDAY, outputValues.getAsInteger(Event.TYPE).intValue());
+ }
+
+ public void testMigrateEventFromGoogleToExchange2() {
+ AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+ AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+ DataKind kind = newAccountType.getKindForMimetype(Event.CONTENT_ITEM_TYPE);
+
+ EntityDelta oldState = new EntityDelta();
+ ContentValues mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
+ // No year format is not supported by Exchange.
+ mockNameValues.put(Event.START_DATE, "--06-01");
+ mockNameValues.put(Event.TYPE, Event.TYPE_BIRTHDAY);
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+ mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Event.START_DATE, "1980-08-02");
+ // Anniversary is not supported by Exchange
+ mockNameValues.put(Event.TYPE, Event.TYPE_ANNIVERSARY);
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ EntityDelta newState = new EntityDelta();
+ EntityModifier.migrateEvent(oldState, newState, kind, 1990);
+
+ List<ValuesDelta> list = newState.getMimeEntries(Event.CONTENT_ITEM_TYPE);
+ assertNotNull(list);
+ assertEquals(1, list.size()); // Anniversary should be dropped.
+ ContentValues outputValues = list.get(0).getAfter();
+
+ // Default year should be used.
+ assertEquals("1990-06-01", outputValues.getAsString(Event.START_DATE));
+ assertEquals(Event.TYPE_BIRTHDAY, outputValues.getAsInteger(Event.TYPE).intValue());
+ }
+
+ public void testMigrateEmailFromGoogleToExchange() {
+ AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+ AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+ DataKind kind = newAccountType.getKindForMimetype(Email.CONTENT_ITEM_TYPE);
+
+ EntityDelta oldState = new EntityDelta();
+ ContentValues mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Email.TYPE, Email.TYPE_CUSTOM);
+ mockNameValues.put(Email.LABEL, "custom_type");
+ mockNameValues.put(Email.ADDRESS, "address1");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+ mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Email.TYPE, Email.TYPE_HOME);
+ mockNameValues.put(Email.ADDRESS, "address2");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+ mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Email.TYPE, Email.TYPE_WORK);
+ mockNameValues.put(Email.ADDRESS, "address3");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+ // Exchange can have up to 3 email entries. This 4th entry should be dropped.
+ mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Email.TYPE, Email.TYPE_OTHER);
+ mockNameValues.put(Email.ADDRESS, "address4");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ EntityDelta newState = new EntityDelta();
+ EntityModifier.migrateGenericWithTypeColumn(oldState, newState, kind);
+
+ List<ValuesDelta> list = newState.getMimeEntries(Email.CONTENT_ITEM_TYPE);
+ assertNotNull(list);
+ assertEquals(3, list.size());
+
+ ContentValues outputValues = list.get(0).getAfter();
+ assertEquals(Email.TYPE_CUSTOM, outputValues.getAsInteger(Email.TYPE).intValue());
+ assertEquals("custom_type", outputValues.getAsString(Email.LABEL));
+ assertEquals("address1", outputValues.getAsString(Email.ADDRESS));
+
+ outputValues = list.get(1).getAfter();
+ assertEquals(Email.TYPE_HOME, outputValues.getAsInteger(Email.TYPE).intValue());
+ assertEquals("address2", outputValues.getAsString(Email.ADDRESS));
+
+ outputValues = list.get(2).getAfter();
+ assertEquals(Email.TYPE_WORK, outputValues.getAsInteger(Email.TYPE).intValue());
+ assertEquals("address3", outputValues.getAsString(Email.ADDRESS));
+ }
+
+ public void testMigrateImFromGoogleToExchange() {
+ AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+ AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+ DataKind kind = newAccountType.getKindForMimetype(Im.CONTENT_ITEM_TYPE);
+
+ EntityDelta oldState = new EntityDelta();
+ ContentValues mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+ // Exchange doesn't support TYPE_HOME
+ mockNameValues.put(Im.TYPE, Im.TYPE_HOME);
+ mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_JABBER);
+ mockNameValues.put(Im.DATA, "im1");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+ // Exchange doesn't support TYPE_WORK
+ mockNameValues.put(Im.TYPE, Im.TYPE_WORK);
+ mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_YAHOO);
+ mockNameValues.put(Im.DATA, "im2");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Im.TYPE, Im.TYPE_OTHER);
+ mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM);
+ mockNameValues.put(Im.CUSTOM_PROTOCOL, "custom_protocol");
+ mockNameValues.put(Im.DATA, "im3");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ // Exchange can have up to 3 IM entries. This 4th entry should be dropped.
+ mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Im.TYPE, Im.TYPE_OTHER);
+ mockNameValues.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
+ mockNameValues.put(Im.DATA, "im4");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ EntityDelta newState = new EntityDelta();
+ EntityModifier.migrateGenericWithTypeColumn(oldState, newState, kind);
+
+ List<ValuesDelta> list = newState.getMimeEntries(Im.CONTENT_ITEM_TYPE);
+ assertNotNull(list);
+ assertEquals(3, list.size());
+
+ assertNotNull(kind.defaultValues.getAsInteger(Im.TYPE));
+
+ int defaultType = kind.defaultValues.getAsInteger(Im.TYPE);
+
+ ContentValues outputValues = list.get(0).getAfter();
+ // HOME should become default type.
+ assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue());
+ assertEquals(Im.PROTOCOL_JABBER, outputValues.getAsInteger(Im.PROTOCOL).intValue());
+ assertEquals("im1", outputValues.getAsString(Im.DATA));
+
+ outputValues = list.get(1).getAfter();
+ assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue());
+ assertEquals(Im.PROTOCOL_YAHOO, outputValues.getAsInteger(Im.PROTOCOL).intValue());
+ assertEquals("im2", outputValues.getAsString(Im.DATA));
+
+ outputValues = list.get(2).getAfter();
+ assertEquals(defaultType, outputValues.getAsInteger(Im.TYPE).intValue());
+ assertEquals(Im.PROTOCOL_CUSTOM, outputValues.getAsInteger(Im.PROTOCOL).intValue());
+ assertEquals("custom_protocol", outputValues.getAsString(Im.CUSTOM_PROTOCOL));
+ assertEquals("im3", outputValues.getAsString(Im.DATA));
+ }
+
+ public void testMigratePhoneFromGoogleToExchange() {
+ AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+ AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+ DataKind kind = newAccountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
+
+ EntityDelta oldState = new EntityDelta();
+ ContentValues mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Phone.TYPE, Phone.TYPE_HOME);
+ mockNameValues.put(Phone.NUMBER, "1");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+ mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Phone.TYPE, Phone.TYPE_MOBILE);
+ mockNameValues.put(Phone.NUMBER, "2");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+ mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+ // Exchange doesn't support this type. Default to HOME
+ mockNameValues.put(Phone.TYPE, Phone.TYPE_CUSTOM);
+ mockNameValues.put(Phone.LABEL, "custom_type");
+ mockNameValues.put(Phone.NUMBER, "3");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+ mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK);
+ mockNameValues.put(Phone.NUMBER, "4");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+ mockNameValues = new ContentValues();
+
+ // This field should be ignored, as Exchange only allows 2 HOME phone numbers while we
+ // already have that number of HOME phones.
+ mockNameValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Phone.TYPE, Phone.TYPE_WORK_MOBILE);
+ mockNameValues.put(Phone.NUMBER, "5");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ EntityDelta newState = new EntityDelta();
+ EntityModifier.migrateGenericWithTypeColumn(oldState, newState, kind);
+
+ List<ValuesDelta> list = newState.getMimeEntries(Phone.CONTENT_ITEM_TYPE);
+ assertNotNull(list);
+ assertEquals(4, list.size());
+
+ int defaultType = kind.typeList.get(0).rawValue;
+
+ ContentValues outputValues = list.get(0).getAfter();
+ assertEquals(Phone.TYPE_HOME, outputValues.getAsInteger(Phone.TYPE).intValue());
+ assertEquals("1", outputValues.getAsString(Phone.NUMBER));
+ outputValues = list.get(1).getAfter();
+ assertEquals(Phone.TYPE_MOBILE, outputValues.getAsInteger(Phone.TYPE).intValue());
+ assertEquals("2", outputValues.getAsString(Phone.NUMBER));
+ outputValues = list.get(2).getAfter();
+ assertEquals(defaultType, outputValues.getAsInteger(Phone.TYPE).intValue());
+ assertNull(outputValues.getAsInteger(Phone.LABEL));
+ assertEquals("3", outputValues.getAsString(Phone.NUMBER));
+ outputValues = list.get(3).getAfter();
+ assertEquals(Phone.TYPE_WORK, outputValues.getAsInteger(Phone.TYPE).intValue());
+ assertEquals("4", outputValues.getAsString(Phone.NUMBER));
+ }
+
+ public void testMigrateOrganizationFromGoogleToExchange() {
+ AccountType oldAccountType = new GoogleAccountType(getContext(), "");
+ AccountType newAccountType = new ExchangeAccountType(getContext(), "");
+ DataKind kind = newAccountType.getKindForMimetype(Organization.CONTENT_ITEM_TYPE);
+
+ EntityDelta oldState = new EntityDelta();
+ ContentValues mockNameValues = new ContentValues();
+ mockNameValues.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
+ mockNameValues.put(Organization.COMPANY, "company1");
+ mockNameValues.put(Organization.DEPARTMENT, "department1");
+ oldState.addEntry(ValuesDelta.fromAfter(mockNameValues));
+
+ EntityDelta newState = new EntityDelta();
+ EntityModifier.migrateGenericWithoutTypeColumn(oldState, newState, kind);
+
+ List<ValuesDelta> list = newState.getMimeEntries(Organization.CONTENT_ITEM_TYPE);
+ assertNotNull(list);
+ assertEquals(1, list.size());
+
+ ContentValues outputValues = list.get(0).getAfter();
+ assertEquals("company1", outputValues.getAsString(Organization.COMPANY));
+ assertEquals("department1", outputValues.getAsString(Organization.DEPARTMENT));
+ }
}
diff --git a/tests/src/com/android/contacts/RecentCallsListActivityTests.java b/tests/src/com/android/contacts/activities/CallLogActivityTests.java
similarity index 92%
rename from tests/src/com/android/contacts/RecentCallsListActivityTests.java
rename to tests/src/com/android/contacts/activities/CallLogActivityTests.java
index 3c2a9fe..3eedef0 100644
--- a/tests/src/com/android/contacts/RecentCallsListActivityTests.java
+++ b/tests/src/com/android/contacts/activities/CallLogActivityTests.java
@@ -14,23 +14,21 @@
* limitations under the License.
*/
-package com.android.contacts;
+package com.android.contacts.activities;
+
+import com.android.contacts.calllog.CallLogFragment;
+import com.android.internal.telephony.CallerInfo;
import android.content.res.Resources;
-import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
import android.provider.CallLog.Calls;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
-import com.android.contacts.RecentCallsListActivity;
-import com.android.internal.telephony.CallerInfo;
+
import java.util.Date;
import java.util.Formatter;
import java.util.HashMap;
@@ -47,9 +45,9 @@
* -w com.android.contacts.tests/android.test.InstrumentationTestRunner
*/
-public class RecentCallsListActivityTests
- extends ActivityInstrumentationTestCase2<RecentCallsListActivity> {
- static private final String TAG = "RecentCallsListActivityTests";
+public class CallLogActivityTests
+ extends ActivityInstrumentationTestCase2<CallLogActivity> {
+ static private final String TAG = "CallLogActivityTests";
static private final String[] CALL_LOG_PROJECTION = new String[] {
Calls._ID,
Calls.NUMBER,
@@ -66,11 +64,12 @@
// We get the call list activity and assign is a frame to build
// its list. mAdapter is an inner class of
- // RecentCallsListActivity to build the rows (view) in the call
+ // CallLogActivity to build the rows (view) in the call
// list. We reuse it with our own in-mem DB.
- private RecentCallsListActivity mActivity;
+ private CallLogActivity mActivity;
+ private CallLogFragment mFragment;
private FrameLayout mParentView;
- private RecentCallsListActivity.RecentCallsAdapter mAdapter;
+ private CallLogFragment.CallLogAdapter mAdapter;
private String mVoicemail;
// In memory array to hold the rows corresponding to the 'calls' table.
@@ -85,13 +84,13 @@
private HashMap<Integer, Bitmap> mCallTypeIcons;
// An item in the call list. All the methods performing checks use it.
- private RecentCallsListActivity.RecentCallsListItemViews mItem;
+ private CallLogFragment.CallLogListItemViews mItem;
// The list of views representing the data in the DB. View are in
// reverse order compare to the DB.
private View[] mList;
- public RecentCallsListActivityTests() {
- super("com.android.contacts", RecentCallsListActivity.class);
+ public CallLogActivityTests() {
+ super("com.android.contacts", CallLogActivity.class);
mIndex = 1;
mRnd = new Random();
}
@@ -99,8 +98,9 @@
@Override
public void setUp() {
mActivity = getActivity();
- mVoicemail = mActivity.mVoiceMailNumber;
- mAdapter = mActivity.mAdapter;
+ mFragment = mActivity.getFragment();
+ mVoicemail = mFragment.getVoiceMailNumber();
+ mAdapter = mFragment.getAdapter();
mParentView = new FrameLayout(mActivity);
mCursor = new MatrixCursor(CALL_LOG_PROJECTION);
buildIconMap();
@@ -170,7 +170,7 @@
if (null == mList[i]) {
break;
}
- mItem = (RecentCallsListActivity.RecentCallsListItemViews) mList[i].getTag();
+ mItem = (CallLogFragment.CallLogListItemViews) mList[i].getTag();
// callView tag is the phone number.
String number = (String) mItem.callView.getTag();
diff --git a/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java b/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java
index 912950f..d165963 100644
--- a/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java
+++ b/tests/src/com/android/contacts/activities/ContactBrowserActivityTest.java
@@ -16,6 +16,7 @@
package com.android.contacts.activities;
+import com.android.contacts.ContactPhotoManager;
import com.android.contacts.ContactsApplication;
import com.android.contacts.R;
import com.android.contacts.detail.ContactDetailFragment;
@@ -26,9 +27,11 @@
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.MockContactPhotoManager;
import com.android.contacts.tests.mocks.MockContentProvider;
import com.android.contacts.tests.mocks.MockContentProvider.Query;
import com.android.contacts.tests.mocks.MockSharedPreferences;
+import com.android.contacts.util.PhoneCapabilityTester;
import android.accounts.Account;
import android.content.ContentValues;
@@ -85,7 +88,8 @@
InjectedServices services = new InjectedServices();
services.setContentResolver(mContext.getContentResolver());
services.setSharedPreferences(new MockSharedPreferences());
-
+ services.setSystemService(ContactPhotoManager.CONTACT_PHOTO_SERVICE,
+ new MockContactPhotoManager());
AccountType accountType = new BaseAccountType();
accountType.accountType = TEST_ACCOUNT_TYPE;
@@ -98,6 +102,10 @@
}
public void testSingleAccountNoGroups() {
+ // This two-pane UI test only makes sense if we run with two panes.
+ // Let's ignore this in the single pane case
+ if (!PhoneCapabilityTester.isUsingTwoPanes(mContext)) return;
+
expectSettingsQueriesAndReturnDefault();
expectProviderStatusQueryAndReturnNormal();
expectGroupsQueryAndReturnEmpty();
diff --git a/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java b/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
index 4cee32c..cfab94a 100644
--- a/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
+++ b/tests/src/com/android/contacts/detail/ContactDetailFragmentTests.java
@@ -16,7 +16,7 @@
package com.android.contacts.detail;
-import com.android.contacts.detail.ContactDetailFragment.ViewEntry;
+import com.android.contacts.detail.ContactDetailFragment.DetailViewEntry;
import android.content.ContentValues;
import android.content.Intent;
@@ -42,7 +42,7 @@
values.put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK);
values.put(Im.DATA, TEST_ADDRESS);
- ViewEntry entry = new ContactDetailFragment.ViewEntry();
+ DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
ContactDetailFragment.buildImActions(entry, values);
assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
@@ -59,7 +59,7 @@
values.put(Im.DATA, TEST_ADDRESS);
values.put(Im.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO);
- ViewEntry entry = new ContactDetailFragment.ViewEntry();
+ DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
ContactDetailFragment.buildImActions(entry, values);
assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
@@ -78,7 +78,7 @@
values.put(Im.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO |
Im.CAPABILITY_HAS_VOICE);
- ViewEntry entry = new ContactDetailFragment.ViewEntry();
+ DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
ContactDetailFragment.buildImActions(entry, values);
assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
@@ -97,7 +97,7 @@
values.put(Im.CUSTOM_PROTOCOL, TEST_PROTOCOL);
values.put(Im.DATA, TEST_ADDRESS);
- ViewEntry entry = new ContactDetailFragment.ViewEntry();
+ DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
ContactDetailFragment.buildImActions(entry, values);
assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
@@ -120,7 +120,7 @@
values.put(Email.CHAT_CAPABILITY, Im.CAPABILITY_HAS_VOICE | Im.CAPABILITY_HAS_VIDEO |
Im.CAPABILITY_HAS_VOICE);
- ViewEntry entry = new ContactDetailFragment.ViewEntry();
+ DetailViewEntry entry = new ContactDetailFragment.DetailViewEntry();
ContactDetailFragment.buildImActions(entry, values);
assertEquals(Intent.ACTION_SENDTO, entry.intent.getAction());
assertEquals("xmpp:" + TEST_ADDRESS + "?message", entry.intent.getData().toString());
diff --git a/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java b/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
index 43e844e..c401a8c 100644
--- a/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
+++ b/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
@@ -124,6 +124,26 @@
assertEquals("sms:456", interaction.startedIntent.getDataString());
}
+ public void testShouldCollapseWith() {
+ PhoneNumberInteraction.PhoneItem phoneItem1 = new PhoneNumberInteraction.PhoneItem();
+ PhoneNumberInteraction.PhoneItem phoneItem2 = new PhoneNumberInteraction.PhoneItem();
+
+ phoneItem1.phoneNumber = "123";
+ phoneItem2.phoneNumber = "123";
+
+ assertTrue(phoneItem1.shouldCollapseWith(phoneItem2));
+
+ phoneItem1.phoneNumber = "123";
+ phoneItem2.phoneNumber = "456";
+
+ assertFalse(phoneItem1.shouldCollapseWith(phoneItem2));
+
+ phoneItem1.phoneNumber = "123#,123";
+ phoneItem2.phoneNumber = "123#,456";
+
+ assertFalse(phoneItem1.shouldCollapseWith(phoneItem2));
+ }
+
public void testCallNumberWhenThereAreDuplicates() {
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
expectQuery(contactUri)
diff --git a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
index 50a1f1f..03b6302 100644
--- a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
+++ b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
@@ -73,6 +73,7 @@
ACTION_PICK_PHONE_LEGACY,
ACTION_PICK_POSTAL,
ACTION_PICK_POSTAL_LEGACY,
+ ACTION_PICK_EMAIL,
ACTION_CREATE_SHORTCUT_CONTACT,
ACTION_CREATE_SHORTCUT_DIAL,
ACTION_CREATE_SHORTCUT_MESSAGE,
@@ -195,6 +196,11 @@
startContactSelectionActivityForResult(intent);
break;
}
+ case ACTION_PICK_EMAIL: {
+ startContactSelectionActivityForResult(
+ new Intent(Intent.ACTION_PICK, Email.CONTENT_URI));
+ break;
+ }
case ACTION_CREATE_SHORTCUT_CONTACT: {
Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
startContactSelectionActivityForResult(intent);
diff --git a/tests/src/com/android/contacts/tests/mocks/MockContactPhotoManager.java b/tests/src/com/android/contacts/tests/mocks/MockContactPhotoManager.java
new file mode 100644
index 0000000..6da205b
--- /dev/null
+++ b/tests/src/com/android/contacts/tests/mocks/MockContactPhotoManager.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package com.android.contacts.tests.mocks;
+
+import com.android.contacts.ContactPhotoManager;
+
+import android.net.Uri;
+import android.widget.ImageView;
+
+/**
+ * A photo preloader that always uses the "no contact" picture and never executes any real
+ * db queries
+ */
+public class MockContactPhotoManager extends ContactPhotoManager {
+ @Override
+ public void loadPhoto(ImageView view, long photoId) {
+ view.setImageResource(mDefaultResourceId);
+ }
+
+ @Override
+ public void loadPhoto(ImageView view, Uri photoUri) {
+ view.setImageResource(mDefaultResourceId);
+ }
+
+ @Override
+ public void pause() {
+ }
+
+ @Override
+ public void resume() {
+ }
+
+ @Override
+ public void refreshCache() {
+ }
+
+ @Override
+ public void preloadPhotosInBackground() {
+ }
+}