Merge "Remove "recent_updates" string from values-w470dp folder"
diff --git a/res/drawable-hdpi/ic_launcher_folder_live_contacts.png b/res/drawable-hdpi/ic_launcher_folder_live_contacts.png
deleted file mode 100644
index 84babe2..0000000
--- a/res/drawable-hdpi/ic_launcher_folder_live_contacts.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_folder_live_contacts_phone.png b/res/drawable-hdpi/ic_launcher_folder_live_contacts_phone.png
deleted file mode 100644
index 004e849..0000000
--- a/res/drawable-hdpi/ic_launcher_folder_live_contacts_phone.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_folder_live_contacts_starred.png b/res/drawable-hdpi/ic_launcher_folder_live_contacts_starred.png
deleted file mode 100644
index 73b4fa5..0000000
--- a/res/drawable-hdpi/ic_launcher_folder_live_contacts_starred.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_see_contacts_holo_dark.png b/res/drawable-hdpi/ic_see_contacts_holo_dark.png
deleted file mode 100644
index 8e2182f..0000000
--- a/res/drawable-hdpi/ic_see_contacts_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_unheard_voicemail_holo_dark.png b/res/drawable-hdpi/ic_unheard_voicemail_holo_dark.png
deleted file mode 100644
index 4f48e77..0000000
--- a/res/drawable-hdpi/ic_unheard_voicemail_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_folder_live_contacts.png b/res/drawable-mdpi/ic_launcher_folder_live_contacts.png
deleted file mode 100644
index d49cc7b..0000000
--- a/res/drawable-mdpi/ic_launcher_folder_live_contacts.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_folder_live_contacts_phone.png b/res/drawable-mdpi/ic_launcher_folder_live_contacts_phone.png
deleted file mode 100644
index 0127f84..0000000
--- a/res/drawable-mdpi/ic_launcher_folder_live_contacts_phone.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_folder_live_contacts_starred.png b/res/drawable-mdpi/ic_launcher_folder_live_contacts_starred.png
deleted file mode 100644
index 8d56b31..0000000
--- a/res/drawable-mdpi/ic_launcher_folder_live_contacts_starred.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_see_contacts_holo_dark.png b/res/drawable-mdpi/ic_see_contacts_holo_dark.png
deleted file mode 100644
index ef1d2df..0000000
--- a/res/drawable-mdpi/ic_see_contacts_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_unheard_voicemail_holo_dark.png b/res/drawable-mdpi/ic_unheard_voicemail_holo_dark.png
deleted file mode 100644
index 8992be2..0000000
--- a/res/drawable-mdpi/ic_unheard_voicemail_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_folder_live_contacts.png b/res/drawable-xhdpi/ic_launcher_folder_live_contacts.png
deleted file mode 100644
index 99bb9f8..0000000
--- a/res/drawable-xhdpi/ic_launcher_folder_live_contacts.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_folder_live_contacts_phone.png b/res/drawable-xhdpi/ic_launcher_folder_live_contacts_phone.png
deleted file mode 100644
index fd416b8..0000000
--- a/res/drawable-xhdpi/ic_launcher_folder_live_contacts_phone.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_folder_live_contacts_starred.png b/res/drawable-xhdpi/ic_launcher_folder_live_contacts_starred.png
deleted file mode 100644
index ff9e326..0000000
--- a/res/drawable-xhdpi/ic_launcher_folder_live_contacts_starred.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_see_contacts_holo_dark.png b/res/drawable-xhdpi/ic_see_contacts_holo_dark.png
deleted file mode 100644
index 279fff9..0000000
--- a/res/drawable-xhdpi/ic_see_contacts_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_unheard_voicemail_holo_dark.png b/res/drawable-xhdpi/ic_unheard_voicemail_holo_dark.png
deleted file mode 100644
index 75e771c..0000000
--- a/res/drawable-xhdpi/ic_unheard_voicemail_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/quickcontact_list_item_background.xml b/res/drawable/quickcontact_list_item_background.xml
deleted file mode 100644
index afb3947..0000000
--- a/res/drawable/quickcontact_list_item_background.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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_window_focused="false"
- android:drawable="@drawable/list_background_holo" />
- <item
- android:state_focused="true"
- android:state_pressed="true"
- android:drawable="@drawable/list_pressed_holo" />
- <item
- android:state_focused="false"
- android:state_pressed="true"
- android:drawable="@drawable/list_pressed_holo" />
- <item
- android:state_focused="true"
- android:drawable="@drawable/list_focused_holo" />
- <item
- android:drawable="@color/quickcontact_list_background" />
-</selector>
diff --git a/res/layout-sw580dp-w1000dp/detail_header_contact_with_updates.xml b/res/layout-sw580dp-w1000dp/detail_header_contact_with_updates.xml
index 6ce811f..b9a9dbd 100644
--- a/res/layout-sw580dp-w1000dp/detail_header_contact_with_updates.xml
+++ b/res/layout-sw580dp-w1000dp/detail_header_contact_with_updates.xml
@@ -33,11 +33,12 @@
android:layout_height="@dimen/detail_contact_photo_size" />
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="0dip"
android:layout_height="match_parent"
+ android:layout_weight="1"
android:orientation="vertical"
android:paddingLeft="16dip"
- android:paddingRight="16dip">
+ android:paddingRight="4dip">
<TextView
android:id="@+id/name"
@@ -55,14 +56,9 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorSecondary" />
- <CheckBox
- android:id="@+id/star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:contentDescription="@string/description_star"
- style="?android:attr/starStyle" />
-
</LinearLayout>
+ <include
+ layout="@layout/favorites_star" />
+
</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp-w1000dp/detail_header_contact_without_updates.xml b/res/layout-sw580dp-w1000dp/detail_header_contact_without_updates.xml
index be82d88..6ef4651 100644
--- a/res/layout-sw580dp-w1000dp/detail_header_contact_without_updates.xml
+++ b/res/layout-sw580dp-w1000dp/detail_header_contact_without_updates.xml
@@ -25,12 +25,24 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <TextView
- android:id="@+id/name"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textSize="@dimen/detail_header_name_text_size" />
+ android:paddingRight="16dip">
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textSize="@dimen/detail_header_name_text_size"
+ android:paddingRight="16dip" />
+
+ <include
+ layout="@layout/favorites_star" />
+
+ </LinearLayout>
<TextView
android:id="@+id/company"
@@ -39,12 +51,4 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorSecondary" />
- <CheckBox
- android:id="@+id/star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:contentDescription="@string/description_star"
- style="?android:attr/starStyle" />
-
</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp-w1000dp/group_detail_fragment.xml b/res/layout-sw580dp-w1000dp/group_detail_fragment.xml
deleted file mode 100644
index 45419a8..0000000
--- a/res/layout-sw580dp-w1000dp/group_detail_fragment.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/group_detail"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/panel_content">
-
- <!-- Static header containing the group title, size, and group source (if applicable) -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingLeft="@dimen/group_detail_border_padding"
- android:orientation="vertical" >
-
- <TextView
- android:id="@+id/group_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingTop="12dip"
- android:paddingLeft="8dip"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textStyle="bold" />
-
- <TextView
- android:id="@+id/group_size"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="12dip"
- android:paddingLeft="8dip"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorSecondary" />
-
- </LinearLayout>
-
- <FrameLayout
- android:id="@+id/group_source_view_container"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingRight="@dimen/group_detail_border_padding" />
-
- </LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="@color/people_app_theme_color"
- android:layout_marginLeft="@dimen/group_detail_border_padding"
- android:layout_marginRight="@dimen/group_detail_border_padding" />
-
- <!-- List of group members -->
- <ListView android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginLeft="@dimen/group_detail_border_padding"
- android:layout_marginRight="@dimen/group_detail_border_padding"
- android:cacheColorHint="#00000000"
- android:fadingEdge="none"
- android:divider="@null" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp/detail_header_contact_with_updates.xml b/res/layout-sw580dp/detail_header_contact_with_updates.xml
index 6b6f02b..fa6f301 100644
--- a/res/layout-sw580dp/detail_header_contact_with_updates.xml
+++ b/res/layout-sw580dp/detail_header_contact_with_updates.xml
@@ -26,7 +26,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:layout_marginTop="30dip">
+ android:layout_marginTop="30dip"
+ android:paddingBottom="8dip">
<!-- Add a first item that gives us enough space to show the carousel -->
<view
@@ -43,12 +44,26 @@
</view>
- <TextView
- android:id="@+id/name"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textSize="@dimen/detail_header_name_text_size" />
+ android:orientation="horizontal"
+ android:paddingTop="8dip"
+ android:paddingRight="8dip">
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingRight="24dip"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textSize="@dimen/detail_header_name_text_size" />
+
+ <include
+ layout="@layout/favorites_star" />
+
+ </LinearLayout>
<TextView
android:id="@+id/company"
@@ -57,12 +72,4 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorSecondary" />
- <CheckBox
- android:id="@+id/star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:contentDescription="@string/description_star"
- style="?android:attr/starStyle" />
-
</LinearLayout>
diff --git a/res/layout-sw580dp/detail_header_contact_without_updates.xml b/res/layout-sw580dp/detail_header_contact_without_updates.xml
index 995e8f7..a55764d 100644
--- a/res/layout-sw580dp/detail_header_contact_without_updates.xml
+++ b/res/layout-sw580dp/detail_header_contact_without_updates.xml
@@ -23,7 +23,8 @@
xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:paddingBottom="8dip">
<!-- Ensure that the contact photo for a contact WITHOUT social updates is the same width and
height as a contact WITH social updates (where the photo is 2/3 of the screen width). -->
@@ -57,12 +58,26 @@
</view>
- <TextView
- android:id="@+id/name"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textSize="@dimen/detail_header_name_text_size" />
+ android:orientation="horizontal"
+ android:paddingTop="8dip"
+ android:paddingRight="8dip">
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingRight="24dip"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textSize="@dimen/detail_header_name_text_size" />
+
+ <include
+ layout="@layout/favorites_star" />
+
+ </LinearLayout>
<TextView
android:id="@+id/company"
@@ -71,12 +86,4 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorSecondary" />
- <CheckBox
- android:id="@+id/star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:contentDescription="@string/description_star"
- style="?android:attr/starStyle" />
-
</LinearLayout>
diff --git a/res/layout-sw580dp/favorites_star.xml b/res/layout-sw580dp/favorites_star.xml
new file mode 100644
index 0000000..7959df3
--- /dev/null
+++ b/res/layout-sw580dp/favorites_star.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="50dip">
+
+ <CheckBox
+ android:id="@+id/star"
+ android:duplicateParentState="true"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:contentDescription="@string/description_star"
+ style="?android:attr/starStyle"/>
+
+</FrameLayout>
diff --git a/res/layout-sw580dp/group_detail_fragment.xml b/res/layout-sw580dp/group_detail_fragment.xml
index fbe695b..8a4ce86 100644
--- a/res/layout-sw580dp/group_detail_fragment.xml
+++ b/res/layout-sw580dp/group_detail_fragment.xml
@@ -21,44 +21,66 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/background_primary"
+ android:background="@drawable/panel_content"
android:paddingLeft="@dimen/group_detail_border_padding"
android:paddingRight="@dimen/group_detail_border_padding">
- <TextView
- android:id="@+id/group_title"
+ <!-- Static header containing the group title, size, and group source (if applicable) -->
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingLeft="8dip"
- android:paddingTop="12dip"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textStyle="bold" />
+ android:orientation="horizontal" >
- <TextView
- android:id="@+id/group_size"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="8dip"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorTertiary" />
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingLeft="8dip"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/group_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:paddingTop="12dip"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textStyle="bold" />
+
+ <TextView
+ android:id="@+id/group_size"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:paddingBottom="12dip"
+ android:textAppearance="@style/GroupMembershipSizeTextAppearance"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/group_source_view_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent" />
+
+ </LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dip"
- android:background="@color/people_app_theme_color"
- android:layout_marginTop="12dip" />
+ android:background="@color/people_app_theme_color" />
- <FrameLayout
- android:id="@+id/group_source_view_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
-
+ <!-- List of group members -->
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:cacheColorHint="#00000000"
+ android:cacheColorHint="@android:color/black"
android:fadingEdge="none"
android:divider="@null" />
+ <include
+ layout="@layout/group_detail_fragment_empty_view"/>
+
</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp/group_source_button.xml b/res/layout-sw580dp/group_source_button.xml
index e0fe4a9..43c85e9 100644
--- a/res/layout-sw580dp/group_source_button.xml
+++ b/res/layout-sw580dp/group_source_button.xml
@@ -34,7 +34,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:duplicateParentState="true"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
+ android:textSize="14sp"
+ style="@android:style/Widget.Holo.ActionBar.TabText"/>
<ImageView
android:id="@android:id/icon"
diff --git a/res/layout-sw580dp/list_separator.xml b/res/layout-sw580dp/list_separator.xml
index a53bd6b..34e8d79 100644
--- a/res/layout-sw580dp/list_separator.xml
+++ b/res/layout-sw580dp/list_separator.xml
@@ -17,12 +17,6 @@
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/title"
- style="?android:attr/listSeparatorTextViewStyle"
- android:layout_height="32dip"
+ style="@style/ContactListSeparatorTextViewStyle"
android:paddingLeft="8dip"
- android:paddingRight="8dip"
- android:background="@drawable/list_section_divider_holo_custom"
- android:textColor="@color/people_app_theme_color"
- android:textAllCaps="true"
- android:singleLine="true"
- android:ellipsize="end" />
\ No newline at end of file
+ android:paddingRight="8dip" />
diff --git a/res/layout/account_filter_header.xml b/res/layout/account_filter_header.xml
index 4d45d25..26207f0 100644
--- a/res/layout/account_filter_header.xml
+++ b/res/layout/account_filter_header.xml
@@ -22,7 +22,6 @@
android:id="@+id/account_filter_header_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="?attr/list_item_header_height"
android:orientation="vertical"
android:paddingTop="@dimen/contact_browser_list_top_margin"
android:layout_marginLeft="@dimen/contact_browser_list_header_left_margin"
@@ -31,17 +30,6 @@
android:visibility="gone">
<TextView
android:id="@+id/account_filter_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textStyle="bold"
- android:textAllCaps="true"
- android:paddingLeft="@dimen/contact_browser_list_item_text_indent"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary" />
- <View
- android:id="@+id/account_filter_header_bottom_divider"
- android:layout_height="1dip"
- style="@style/SectionDivider" />
+ style="@style/ContactListSeparatorTextViewStyle"
+ android:paddingLeft="@dimen/contact_browser_list_item_text_indent" />
</LinearLayout>
diff --git a/res/layout/call_log_list_item.xml b/res/layout/call_log_list_item.xml
index 4768bf6..777c7af 100644
--- a/res/layout/call_log_list_item.xml
+++ b/res/layout/call_log_list_item.xml
@@ -129,20 +129,11 @@
<TextView
android:id="@+id/call_log_header"
- style="?android:attr/listSeparatorTextViewStyle"
+ style="@style/ContactListSeparatorTextViewStyle"
android:layout_marginLeft="@dimen/call_log_outer_margin"
android:layout_marginRight="@dimen/call_log_outer_margin"
android:paddingTop="@dimen/call_log_inner_margin"
- android:paddingBottom="@dimen/call_log_inner_margin"
- android:background="@drawable/list_section_divider_holo_custom"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textStyle="bold"
- android:textColor="?attr/call_log_header_color"
- android:textAllCaps="true"
- android:singleLine="true"
- android:ellipsize="end"
- android:focusable="true"
- />
+ android:paddingBottom="@dimen/call_log_inner_margin" />
<View
android:id="@+id/call_log_divider"
diff --git a/res/layout/carousel_updates_tab.xml b/res/layout/carousel_updates_tab.xml
index 91a72d3..d471d93 100644
--- a/res/layout/carousel_updates_tab.xml
+++ b/res/layout/carousel_updates_tab.xml
@@ -56,7 +56,7 @@
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="@color/detail_update_tab_text_color"
android:textStyle="bold"
- android:maxLines="3"/>
+ android:maxLines="@integer/updates_tab_snippet_max_lines"/>
<View
android:id="@+id/alpha_overlay"
diff --git a/res/layout/contacts_list_content.xml b/res/layout/contact_list_content.xml
similarity index 100%
rename from res/layout/contacts_list_content.xml
rename to res/layout/contact_list_content.xml
diff --git a/res/layout/contact_list_filter_custom.xml b/res/layout/contact_list_filter_custom.xml
index a4fe48e..40d9c78 100644
--- a/res/layout/contact_list_filter_custom.xml
+++ b/res/layout/contact_list_filter_custom.xml
@@ -26,7 +26,8 @@
android:layout_height="0dip"
android:layout_weight="1"
android:layout_marginLeft="@dimen/contact_filter_left_margin"
- android:layout_marginRight="@dimen/contact_filter_right_margin" />
+ android:layout_marginRight="@dimen/contact_filter_right_margin"
+ android:overScrollMode="always" />
<View
android:layout_width="match_parent"
diff --git a/res/layout/directory_header.xml b/res/layout/directory_header.xml
index a1516ef..a2adf48 100644
--- a/res/layout/directory_header.xml
+++ b/res/layout/directory_header.xml
@@ -20,7 +20,9 @@
style="@style/DirectoryHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="?attr/list_item_header_height"
+ android:minHeight="@dimen/list_section_divider_min_height"
+ android:background="@drawable/list_section_divider_holo_custom"
+ android:paddingTop="@dimen/contact_browser_list_top_margin"
android:paddingLeft="?attr/list_item_padding_left"
android:paddingRight="?attr/list_item_padding_right">
<TextView
@@ -32,7 +34,7 @@
android:layout_centerVertical="true"
android:layout_marginLeft="8dip"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary"
+ android:textColor="@color/people_app_theme_color"
android:singleLine="true"
android:textStyle="bold"
android:textAllCaps="true" />
@@ -55,12 +57,8 @@
android:layout_marginLeft="8dip"
android:layout_marginRight="8dip"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary"
+ android:textColor="@color/people_app_theme_color"
android:singleLine="true"
android:textStyle="bold"
android:textAllCaps="true" />
- <View
- android:id="@+id/contact_filter_header_bottom_divider"
- style="@style/SectionDivider"
- android:layout_below="@id/display_name" />
</RelativeLayout>
diff --git a/res/layout/edit_kind_title.xml b/res/layout/edit_kind_title.xml
index f178876..1cd6833 100644
--- a/res/layout/edit_kind_title.xml
+++ b/res/layout/edit_kind_title.xml
@@ -20,20 +20,13 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="32dip"
+ android:layout_height="wrap_content"
android:paddingLeft="8dip"
android:paddingRight="8dip"
android:focusable="false">
-
<TextView
android:id="@+id/kind_title"
- style="?android:attr/listSeparatorTextViewStyle"
+ style="@style/ContactListSeparatorTextViewStyle"
android:paddingLeft="8dip"
- android:paddingRight="8dip"
- android:background="@drawable/list_section_divider_holo_custom"
- android:textColor="@color/people_app_theme_color"
- android:textAllCaps="true"
- android:singleLine="true"
- android:ellipsize="end" />
-
-</FrameLayout>
\ No newline at end of file
+ android:paddingRight="8dip" />
+</FrameLayout>
diff --git a/res/layout/group_detail_fragment.xml b/res/layout/group_detail_fragment.xml
index 2b020c9..735b29d 100644
--- a/res/layout/group_detail_fragment.xml
+++ b/res/layout/group_detail_fragment.xml
@@ -43,6 +43,9 @@
android:scrollbarStyle="outsideOverlay"
android:divider="@null"/>
+ <include
+ layout="@layout/group_detail_fragment_empty_view"/>
+
<!--
Shadow overlay over the list of group members (since we have a fake stacked
action bar)
diff --git a/res/layout/group_detail_fragment_empty_view.xml b/res/layout/group_detail_fragment_empty_view.xml
new file mode 100644
index 0000000..89a6cf8
--- /dev/null
+++ b/res/layout/group_detail_fragment_empty_view.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingTop="24dip"
+ android:visibility="gone">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="@string/emptyGroup"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:text="@string/addPeopleToGroup"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+</LinearLayout>
diff --git a/res/layout/join_contact_picker_show_all.xml b/res/layout/join_contact_picker_show_all.xml
deleted file mode 100644
index d332649..0000000
--- a/res/layout/join_contact_picker_show_all.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2009, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <View
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/dividerHorizontal" />
-
- <TextView
- android:id="@+id/text"
- android:layout_height="48dip"
- android:layout_width="match_parent"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary"
- android:gravity="center_vertical"
- android:text="@string/showAllContactsJoinItem" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/dividerHorizontal" />
-</LinearLayout>
diff --git a/res/layout/list_separator.xml b/res/layout/list_separator.xml
index 3ffaca7..d94e18c 100644
--- a/res/layout/list_separator.xml
+++ b/res/layout/list_separator.xml
@@ -17,20 +17,13 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="32dip"
+ android:layout_height="wrap_content"
android:paddingLeft="16dip"
android:paddingRight="16dip"
android:focusable="false">
-
<TextView
android:id="@+id/title"
- style="?android:attr/listSeparatorTextViewStyle"
+ style="@style/ContactListSeparatorTextViewStyle"
android:paddingLeft="8dip"
- android:paddingRight="8dip"
- android:background="@drawable/list_section_divider_holo_custom"
- android:textColor="@color/people_app_theme_color"
- android:textAllCaps="true"
- android:singleLine="true"
- android:ellipsize="end" />
-
+ android:paddingRight="8dip" />
</FrameLayout>
diff --git a/res/layout/phone_favorite_account_filter_header.xml b/res/layout/phone_favorite_account_filter_header.xml
deleted file mode 100644
index d63382a..0000000
--- a/res/layout/phone_favorite_account_filter_header.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?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 showing the type of account filter in phone favorite screen.
-
- Note: This xml is based on account_filter_header.xml and list_separator.xml.
- Some configurations (e.g. padding) are from account_filter_header.xml, while
- some other configurations (e.g. background, height, text style) are from
- list_separator.xml. See also bug 5432231. -->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/account_filter_header_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="32dip"
- android:paddingTop="@dimen/contact_browser_list_top_margin"
- android:layout_marginLeft="@dimen/contact_browser_list_header_left_margin"
- android:layout_marginRight="@dimen/contact_browser_list_header_right_margin"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone">
- <TextView
- android:id="@+id/account_filter_header"
- style="?android:attr/listSeparatorTextViewStyle"
- android:singleLine="true"
- android:ellipsize="end"
- android:textStyle="bold"
- android:textAllCaps="true"
- android:textColor="@color/people_app_theme_color"
- android:paddingLeft="@dimen/contact_browser_list_item_text_indent"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:background="@drawable/list_section_divider_holo_custom" />
-</FrameLayout>
diff --git a/res/values-sw580dp-w1000dp/dimens.xml b/res/values-sw580dp-w1000dp/dimens.xml
index f1b607c..334cc34 100644
--- a/res/values-sw580dp-w1000dp/dimens.xml
+++ b/res/values-sw580dp-w1000dp/dimens.xml
@@ -15,6 +15,7 @@
-->
<resources>
<dimen name="group_detail_border_padding">32dip</dimen>
+ <dimen name="group_detail_side_margin">32dip</dimen>
<dimen name="detail_contact_photo_margin">16dip</dimen>
<dimen name="contact_detail_list_top_padding">32dip</dimen>
</resources>
diff --git a/res/values-sw580dp-w1000dp/styles.xml b/res/values-sw580dp-w1000dp/styles.xml
new file mode 100644
index 0000000..9fa3d08
--- /dev/null
+++ b/res/values-sw580dp-w1000dp/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <style name="GroupMembershipSizeTextAppearance" parent="@android:style/TextAppearance.Medium" />
+</resources>
diff --git a/res/values-sw580dp-w720dp/styles.xml b/res/values-sw580dp-w720dp/styles.xml
index 5ad25c6..0d750d7 100644
--- a/res/values-sw580dp-w720dp/styles.xml
+++ b/res/values-sw580dp-w720dp/styles.xml
@@ -21,6 +21,7 @@
<item name="android:actionBarTabStyle">@style/ContactsActionBarTabView</item>
<item name="android:textColorPrimary">@color/primary_text_color</item>
<item name="android:textColorSecondary">@color/secondary_text_color</item>
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
<item name="list_item_height">66dip</item>
<item name="activated_background">@drawable/list_item_activated_background</item>
<item name="section_header_background">@drawable/list_title_holo</item>
diff --git a/res/values-sw580dp/integers.xml b/res/values-sw580dp/integers.xml
index c629f7e..56e837e 100644
--- a/res/values-sw580dp/integers.xml
+++ b/res/values-sw580dp/integers.xml
@@ -15,4 +15,5 @@
-->
<resources>
<integer name="contact_tile_column_count">2</integer>
+ <integer name="updates_tab_snippet_max_lines">5</integer>
</resources>
diff --git a/res/values-sw580dp/styles.xml b/res/values-sw580dp/styles.xml
index 3551378..3cf50e2 100644
--- a/res/values-sw580dp/styles.xml
+++ b/res/values-sw580dp/styles.xml
@@ -21,6 +21,7 @@
<item name="android:actionBarTabStyle">@style/ContactsActionBarTabView</item>
<item name="android:textColorPrimary">@color/primary_text_color</item>
<item name="android:textColorSecondary">@color/secondary_text_color</item>
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
<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/list_title_holo</item>
@@ -56,6 +57,7 @@
</style>
<style name="ContactPickerTheme" parent="@android:Theme.Holo.Light.Dialog">
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
<item name="list_item_height">?android:attr/listPreferredItemHeight</item>
<item name="section_header_background">@drawable/list_title_holo</item>
<item name="list_item_divider">?android:attr/listDivider</item>
@@ -95,6 +97,7 @@
<style name="ContactListFilterTheme" parent="@android:Theme.Holo.Light.Dialog">
<item name="android:windowCloseOnTouchOutside">true</item>
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
</style>
<style name="CustomContactListFilterView" parent="ContactListFilterTheme">
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 8d6b877..12ae918 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -142,7 +142,10 @@
<dimen name="group_editor_autocomplete_left_padding">8dip</dimen>
<!-- Border padding for the group detail fragment -->
- <dimen name="group_detail_border_padding">32dip</dimen>
+ <dimen name="group_detail_border_padding">0dip</dimen>
+
+ <!-- Left and right margin for the divider in the group detail fragment -->
+ <dimen name="group_detail_side_margin">0dip</dimen>
<!-- Height of edit text in dialpad fragment -->
<dimen name="dialpad_horizontal_margin">0dip</dimen>
@@ -211,4 +214,9 @@
<integer name="dialpad_layout_weight_digits">20</integer>
<integer name="dialpad_layout_weight_dialpad">65</integer>
<integer name="dialpad_layout_weight_additional_buttons">15</integer>
+
+ <!-- Minimum height used with @drawable/list_section_divider_holo_custom.
+ Right now the drawable has implicit 32dip minimal height, which is confusing.
+ This value is for making the hidden configuration explicit in xml. -->
+ <dimen name="list_section_divider_min_height">32dip</dimen>
</resources>
diff --git a/res/values/integers.xml b/res/values/integers.xml
index 89246f9..3cae01b 100644
--- a/res/values/integers.xml
+++ b/res/values/integers.xml
@@ -23,4 +23,7 @@
<!-- Determines the number of columns in a ContactTileRow -->
<integer name="contact_tile_column_count">2</integer>
+
+ <!-- Max lines to display of a contact's snippet in the "updates" tab of the contact card tab carousel -->
+ <integer name="updates_tab_snippet_max_lines">3</integer>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3fc1784..ae2c680 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -269,6 +269,12 @@
<!-- The text displayed when the contacts list is empty while displaying only contacts that have phone numbers -->
<string name="noContactsWithPhoneNumbers">No contacts with phone numbers.</string>
+ <!-- The text displayed when there are no members in the group while displaying the group detail page [CHAR LIMIT=40] -->
+ <string name="emptyGroup">No people in this group.</string>
+
+ <!-- The text displayed to instruct users to add members to a group (when viewing a group detail page for a group with no members) [CHAR LIMIT=50] -->
+ <string name="addPeopleToGroup">To add some, edit the group.</string>
+
<!-- Displayed in a spinner dialog after the user creates a contact and it's being saved to the database -->
<string name="savingContact">Saving contact\u2026</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index e5fd752..ece881e 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -21,6 +21,7 @@
<item name="android:actionBarStyle">@style/DialtactsActionBarStyle</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowBackground">@drawable/background_dial_holo_dark</item>
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
<item name="activated_background">@drawable/list_item_activated_background</item>
<item name="section_header_background">@drawable/list_title_holo</item>
<item name="list_section_header_height">32dip</item>
@@ -68,6 +69,7 @@
<style name="CallDetailActivityTheme" parent="android:Theme.Holo">
<item name="android:windowBackground">@android:color/black</item>
<item name="android:gravity">top</item>
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
<!-- CallLog -->
<item name="call_log_primary_text_color">#FFFFFF</item>
<item name="call_log_primary_background_color">#000000</item>
@@ -87,6 +89,7 @@
<item name="android:actionBarWidgetTheme">@style/ContactsActionBarTheme</item>
<item name="android:textColorPrimary">@color/primary_text_color</item>
<item name="android:textColorSecondary">@color/secondary_text_color</item>
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
</style>
<style name="EditorActivityTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
@@ -96,6 +99,7 @@
<item name="android:windowContentOverlay">@null</item>
<item name="android:textColorPrimary">@color/primary_text_color</item>
<item name="android:textColorSecondary">@color/secondary_text_color</item>
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
</style>
<style name="BackgroundOnlyTheme" parent="@android:style/Theme.Holo.Light">
@@ -120,6 +124,7 @@
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
</style>
<style name="PeopleTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
@@ -129,6 +134,7 @@
<item name="android:actionBarTabStyle">@style/ContactsActionBarTabView</item>
<item name="android:textColorPrimary">@color/primary_text_color</item>
<item name="android:textColorSecondary">@color/secondary_text_color</item>
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
<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/list_title_holo</item>
@@ -186,6 +192,10 @@
<item name="android:listSelector">?android:attr/listChoiceBackgroundIndicator</item>
</style>
+ <style name="ListViewStyle" parent="@android:style/Widget.Holo.Light.ListView">
+ <item name="android:overScrollMode">always</item>
+ </style>
+
<style name="ContactPickerTheme" parent="@style/PeopleTheme" />
<style name="ContactPickerLayout" parent="ContactPickerTheme">
@@ -197,9 +207,11 @@
</style>
<style name="ContactsPreferencesTheme" parent="@android:Theme.Holo.Light">
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
</style>
<style name="ContactListFilterTheme" parent="@android:Theme.Holo.Light">
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
</style>
<style name="CustomContactListFilterView" parent="ContactListFilterTheme">
@@ -225,6 +237,7 @@
<item name="android:windowCloseOnTouchOutside">true</item>
<item name="android:textColorPrimary">@color/primary_text_color</item>
<item name="android:textColorSecondary">@color/secondary_text_color</item>
+ <item name="android:listViewStyle">@style/ListViewStyle</item>
</style>
<style name="SectionDivider">
@@ -300,4 +313,28 @@
<item name="android:layout_height">2dip</item>
<item name="android:background">@color/quickcontact_tab_indicator</item>
</style>
+
+
+ <style name="GroupMembershipSizeTextAppearance" parent="@android:style/TextAppearance.Small"/>
+
+ <!-- TextView style with blue underline. It is most suitable for headers.
+
+ This is similar to ?android:attr/listSeparatorTextView but uses different
+ background and text color. See also android:style/Widget.Holo.TextView.ListSeparator
+ (which is private, so we cannot specify it as a parent style). -->
+ <style name="ContactListSeparatorTextViewStyle">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <!-- See comments for @dimen/list_section_divider_min_height -->
+ <item name="android:minHeight">@dimen/list_section_divider_min_height</item>
+ <item name="android:background">@drawable/list_section_divider_holo_custom</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+ <item name="android:textStyle">bold</item>
+ <item name="android:textColor">@color/people_app_theme_color</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:paddingLeft">8dip</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
</resources>
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 541b7bf..caa8bce 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -294,7 +294,8 @@
public Void doInBackground(Void... params) {
ContentValues values = new ContentValues();
values.put(Voicemails.IS_READ, true);
- getContentResolver().update(voicemailUri, values, null, null);
+ getContentResolver().update(voicemailUri, values,
+ Voicemails.IS_READ + " = 0", null);
return null;
}
});
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index 6d55184..77caaf7 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -75,6 +75,7 @@
private final MyTabListener mTabListener = new MyTabListener();
private boolean mShowHomeIcon;
+ private boolean mShowTabsAsText;
public enum TabState {
GROUPS,
@@ -98,7 +99,8 @@
private static final TabState DEFAULT_TAB = TabState.ALL;
private TabState mCurrentTab = DEFAULT_TAB;
- public ActionBarAdapter(Context context, Listener listener, ActionBar actionBar) {
+ public ActionBarAdapter(Context context, Listener listener, ActionBar actionBar,
+ boolean isUsingTwoPanes) {
mContext = context;
mListener = listener;
mActionBar = actionBar;
@@ -106,6 +108,9 @@
mShowHomeIcon = mContext.getResources().getBoolean(R.bool.show_home_icon);
+ // On wide screens, show the tabs as text (instead of icons)
+ mShowTabsAsText = isUsingTwoPanes;
+
// Set up search view.
View customSearchView = LayoutInflater.from(mActionBar.getThemedContext()).inflate(
R.layout.custom_action_bar, null);
@@ -153,12 +158,16 @@
mListener = listener;
}
- private void addTab(TabState tabState, int icon, int contentDescription) {
+ private void addTab(TabState tabState, int icon, int description) {
final Tab tab = mActionBar.newTab();
tab.setTag(tabState);
- tab.setIcon(icon);
- tab.setContentDescription(contentDescription);
tab.setTabListener(mTabListener);
+ if (mShowTabsAsText) {
+ tab.setText(description);
+ } else {
+ tab.setIcon(icon);
+ tab.setContentDescription(description);
+ }
mActionBar.addTab(tab);
}
@@ -211,14 +220,16 @@
return mCurrentTab;
}
+ /**
+ * @return Whether in search mode, i.e. if the search view is visible/expanded.
+ *
+ * Note even if the action bar is in search mode, if the query is empty, the search fragment
+ * will not be in search mode.
+ */
public boolean isSearchMode() {
return mSearchMode;
}
- public boolean shouldShowSearchResult() {
- return mSearchMode && !TextUtils.isEmpty(mQueryString);
- }
-
public void setSearchMode(boolean flag) {
if (mSearchMode != flag) {
mSearchMode = flag;
@@ -235,7 +246,7 @@
}
public String getQueryString() {
- return mQueryString;
+ return mSearchMode ? mQueryString : null;
}
public void setQueryString(String query) {
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index 6305d8e..e68c7bc 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -327,7 +327,6 @@
case ContactsRequest.ACTION_PICK_CONTACT: {
ContactPickerFragment fragment = new ContactPickerFragment();
- fragment.setSearchMode(mRequest.isSearchMode());
fragment.setIncludeProfile(mRequest.shouldIncludeProfile());
mListFragment = fragment;
break;
@@ -341,8 +340,6 @@
case ContactsRequest.ACTION_CREATE_SHORTCUT_CONTACT: {
ContactPickerFragment fragment = new ContactPickerFragment();
- fragment.setSearchMode(mRequest.isSearchMode());
- fragment.setQueryString(mRequest.getQueryString(), false);
fragment.setShortcutRequested(true);
mListFragment = fragment;
break;
@@ -362,7 +359,6 @@
case ContactsRequest.ACTION_CREATE_SHORTCUT_CALL: {
PhoneNumberPickerFragment fragment = new PhoneNumberPickerFragment();
fragment.setShortcutAction(Intent.ACTION_CALL);
- fragment.setSearchMode(mRequest.isSearchMode());
mListFragment = fragment;
break;
@@ -387,9 +383,6 @@
}
mListFragment.setLegacyCompatibilityMode(mRequest.isLegacyCompatibilityMode());
- mListFragment.setContactsRequest(mRequest);
- mListFragment.setSearchMode(mRequest.isSearchMode());
- mListFragment.setQueryString(mRequest.getQueryString(), false);
mListFragment.setDirectoryResultLimit(DEFAULT_DIRECTORY_RESULT_LIMIT);
getFragmentManager().beginTransaction()
@@ -537,7 +530,6 @@
@Override
public boolean onQueryTextChange(String newText) {
mListFragment.setQueryString(newText, true);
- mListFragment.setSearchMode(!TextUtils.isEmpty(newText));
return false;
}
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 571a988..917d8e0 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -20,14 +20,13 @@
import com.android.contacts.calllog.CallLogFragment;
import com.android.contacts.dialpad.DialpadFragment;
import com.android.contacts.interactions.PhoneNumberInteraction;
-import com.android.contacts.list.AccountFilterActivity;
-import com.android.contacts.list.ContactListFilter;
import com.android.contacts.list.ContactListFilterController;
import com.android.contacts.list.ContactListFilterController.ContactListFilterListener;
import com.android.contacts.list.ContactListItemView;
import com.android.contacts.list.OnPhoneNumberPickerActionListener;
import com.android.contacts.list.PhoneFavoriteFragment;
import com.android.contacts.list.PhoneNumberPickerFragment;
+import com.android.contacts.util.AccountFilterUtil;
import com.android.internal.telephony.ITelephony;
import android.app.ActionBar;
@@ -109,6 +108,8 @@
"DialtactsActivity_last_manually_selected_tab";
private static final int PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT = TAB_INDEX_DIALER;
+ private static final int SUBACTIVITY_ACCOUNT_FILTER = 1;
+
/**
* Listener interface for Fragments accommodated in {@link ViewPager} enabling them to know
* when it becomes visible or invisible inside the ViewPager.
@@ -301,10 +302,8 @@
new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
- final Intent intent =
- new Intent(DialtactsActivity.this, AccountFilterActivity.class);
- ContactListFilter filter = mContactListFilterController.getFilter();
- startActivityForResult(intent, AccountFilterActivity.DEFAULT_REQUEST_CODE);
+ AccountFilterUtil.startAccountFilterActivityForResult(
+ DialtactsActivity.this, SUBACTIVITY_ACCOUNT_FILTER);
return true;
}
};
@@ -363,7 +362,6 @@
public boolean onQueryTextChange(String newText) {
// Show search result with non-empty text. Show a bare list otherwise.
mSearchFragment.setQueryString(newText, true);
- mSearchFragment.setSearchMode(!TextUtils.isEmpty(newText));
return true;
}
};
@@ -926,17 +924,9 @@
return;
}
switch (requestCode) {
- case AccountFilterActivity.DEFAULT_REQUEST_CODE: {
- ContactListFilter filter = (ContactListFilter) data.getParcelableExtra(
- AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER);
- if (filter == null) {
- return;
- }
- if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
- mContactListFilterController.selectCustomFilter();
- } else {
- mContactListFilterController.setContactListFilter(filter, true);
- }
+ case SUBACTIVITY_ACCOUNT_FILTER: {
+ AccountFilterUtil.handleAccountFilterResult(
+ mContactListFilterController, resultCode, data);
}
break;
}
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index b68f566..772fa54 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -33,7 +33,6 @@
import com.android.contacts.interactions.ContactDeletionInteraction;
import com.android.contacts.interactions.ImportExportDialogFragment;
import com.android.contacts.interactions.PhoneNumberInteraction;
-import com.android.contacts.list.AccountFilterActivity;
import com.android.contacts.list.ContactBrowseListFragment;
import com.android.contacts.list.ContactEntryListFragment;
import com.android.contacts.list.ContactListFilter;
@@ -53,6 +52,7 @@
import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.preference.ContactsPreferenceActivity;
import com.android.contacts.preference.DisplayOptionsPreferenceFragment;
+import com.android.contacts.util.AccountFilterUtil;
import com.android.contacts.util.AccountPromptUtils;
import com.android.contacts.util.AccountSelectionUtil;
import com.android.contacts.util.AccountsListAdapter;
@@ -61,7 +61,6 @@
import com.android.contacts.util.DialogManager;
import com.android.contacts.util.PhoneCapabilityTester;
-import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
@@ -110,8 +109,9 @@
private static final int SUBACTIVITY_NEW_GROUP = 2;
private static final int SUBACTIVITY_EDIT_GROUP = 3;
+ private static final int SUBACTIVITY_ACCOUNT_FILTER = 4;
- private DialogManager mDialogManager = new DialogManager(this);
+ private final DialogManager mDialogManager = new DialogManager(this);
private ContactsIntentResolver mIntentResolver;
private ContactsRequest mRequest;
@@ -317,7 +317,8 @@
final FragmentTransaction transaction = fragmentManager.beginTransaction();
// Prepare the fragments which are used both on 1-pane and on 2-pane.
- if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
+ boolean isUsingTwoPanes = PhoneCapabilityTester.isUsingTwoPanes(this);
+ if (isUsingTwoPanes) {
mFavoritesFragment = getFragment(R.id.favorites_fragment);
mAllFragment = getFragment(R.id.all_fragment);
mGroupsFragment = getFragment(R.id.groups_fragment);
@@ -367,7 +368,7 @@
transaction.hide(mAllFragment);
transaction.hide(mGroupsFragment);
- if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
+ if (isUsingTwoPanes) {
// Prepare 2-pane only fragments/views...
// Container views for fragments
@@ -410,7 +411,7 @@
}
// Configure action bar
- mActionBarAdapter = new ActionBarAdapter(this, this, getActionBar());
+ mActionBarAdapter = new ActionBarAdapter(this, this, getActionBar(), isUsingTwoPanes);
mActionBarAdapter.initialize(savedState, mRequest);
invalidateOptionsMenuIfNeeded();
@@ -566,12 +567,12 @@
invalidateOptionsMenu();
break;
case STOP_SEARCH_MODE:
- clearSearch();
+ setQueryTextToFragment("");
updateFragmentsVisibility();
invalidateOptionsMenu();
break;
case CHANGE_SEARCH_QUERY:
- loadSearch(mActionBarAdapter.getQueryString());
+ setQueryTextToFragment(mActionBarAdapter.getQueryString());
break;
default:
throw new IllegalStateException("Unkonwn ActionBarAdapter action: " + action);
@@ -842,26 +843,19 @@
}
}
- private void clearSearch() {
- loadSearch("");
- }
-
- private void loadSearch(String query) {
- configureFragments(false /* from request */);
+ private void setQueryTextToFragment(String query) {
mAllFragment.setQueryString(query, true);
+ mAllFragment.setVisibleScrollbarEnabled(!mAllFragment.isSearchMode());
}
private void configureContactListFragmentForRequest() {
- mAllFragment.setContactsRequest(mRequest);
-
Uri contactUri = mRequest.getContactUri();
if (contactUri != null) {
mAllFragment.setSelectedContactUri(contactUri);
}
mAllFragment.setFilter(mContactListFilterController.getFilter());
- mAllFragment.setSearchMode(mActionBarAdapter.isSearchMode());
- mAllFragment.setQueryString(mActionBarAdapter.getQueryString(), false);
+ setQueryTextToFragment(mActionBarAdapter.getQueryString());
if (mRequest.isDirectorySearchEnabled()) {
mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT);
@@ -874,11 +868,7 @@
// Filter may be changed when this Activity is in background.
mAllFragment.setFilter(mContactListFilterController.getFilter());
- final boolean showSearchResult = mActionBarAdapter.shouldShowSearchResult();
- mAllFragment.setSearchMode(showSearchResult);
-
final boolean useTwoPane = PhoneCapabilityTester.isUsingTwoPanes(this);
- mAllFragment.setVisibleScrollbarEnabled(!showSearchResult);
mAllFragment.setVerticalScrollbarPosition(
useTwoPane
? View.SCROLLBAR_POSITION_LEFT
@@ -1365,8 +1355,8 @@
return true;
}
case R.id.menu_contacts_filter: {
- final Intent intent = new Intent(this, AccountFilterActivity.class);
- startActivityForResult(intent, AccountFilterActivity.DEFAULT_REQUEST_CODE);
+ AccountFilterUtil.startAccountFilterActivityForResult(this,
+ SUBACTIVITY_ACCOUNT_FILTER);
return true;
}
case R.id.menu_search: {
@@ -1444,19 +1434,9 @@
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
- case AccountFilterActivity.DEFAULT_REQUEST_CODE: {
- if (resultCode == Activity.RESULT_OK) {
- ContactListFilter filter = (ContactListFilter) data.getParcelableExtra(
- AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER);
- if (filter == null) {
- return;
- }
- if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
- mContactListFilterController.selectCustomFilter();
- } else {
- mContactListFilterController.setContactListFilter(filter, true);
- }
- }
+ case SUBACTIVITY_ACCOUNT_FILTER: {
+ AccountFilterUtil.handleAccountFilterResult(
+ mContactListFilterController, resultCode, data);
break;
}
diff --git a/src/com/android/contacts/editor/GroupMembershipView.java b/src/com/android/contacts/editor/GroupMembershipView.java
index 42d2edd..82acc33 100644
--- a/src/com/android/contacts/editor/GroupMembershipView.java
+++ b/src/com/android/contacts/editor/GroupMembershipView.java
@@ -255,6 +255,7 @@
ListView listView = mPopup.getListView();
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ listView.setOverScrollMode(OVER_SCROLL_ALWAYS);
int count = mAdapter.getCount();
for (int i = 0; i < count; i++) {
listView.setItemChecked(i, mAdapter.getItem(i).isChecked());
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index d4f6e29..b3472ab 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -98,6 +98,7 @@
private TextView mGroupTitle;
private TextView mGroupSize;
private ListView mMemberListView;
+ private View mEmptyView;
private Listener mListener;
@@ -149,6 +150,7 @@
mGroupSize = (TextView) mRootView.findViewById(R.id.group_size);
mGroupSourceViewContainer = (ViewGroup) mRootView.findViewById(
R.id.group_source_view_container);
+ mEmptyView = mRootView.findViewById(android.R.id.empty);
mMemberListView = (ListView) mRootView.findViewById(android.R.id.list);
mMemberListView.setAdapter(mAdapter);
@@ -258,6 +260,7 @@
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
updateSize(data.getCount());
mAdapter.setContactCursor(data);
+ mMemberListView.setEmptyView(mEmptyView);
}
@Override
diff --git a/src/com/android/contacts/list/AccountFilterActivity.java b/src/com/android/contacts/list/AccountFilterActivity.java
index aa1fe27..0b4c6e0 100644
--- a/src/com/android/contacts/list/AccountFilterActivity.java
+++ b/src/com/android/contacts/list/AccountFilterActivity.java
@@ -55,15 +55,6 @@
private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 0;
- /**
- * Request code for this Activity, which will be used with
- * {@link Activity#startActivityForResult(Intent, int)}.
- *
- * This is useful when we want to share one request code among multiple Activities and
- * Fragments.
- */
- public static final int DEFAULT_REQUEST_CODE = 10001;
-
public static final String KEY_EXTRA_CONTACT_LIST_FILTER = "contactListFilter";
private static final int FILTER_LOADER_ID = 0;
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index 2bde608..061646e 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -178,7 +178,7 @@
}
@Override
- public void setSearchMode(boolean flag) {
+ protected void setSearchMode(boolean flag) {
if (isSearchMode() != flag) {
if (!flag) {
restoreSelectedUri(true);
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 91c582c..8b9aaf5 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -108,7 +108,6 @@
private String mQueryString;
private int mDirectorySearchMode = DirectoryListLoader.SEARCH_MODE_NONE;
private boolean mSelectionVisible;
- private ContactsRequest mRequest;
private boolean mLegacyCompatibility;
private boolean mEnabled = true;
@@ -249,7 +248,6 @@
outState.putBoolean(KEY_LEGACY_COMPATIBILITY, mLegacyCompatibility);
outState.putString(KEY_QUERY_STRING, mQueryString);
outState.putInt(KEY_DIRECTORY_RESULT_LIMIT, mDirectoryResultLimit);
- outState.putParcelable(KEY_REQUEST, mRequest);
outState.putBoolean(KEY_DARK_THEME, mDarkTheme);
if (mListView != null) {
@@ -282,27 +280,12 @@
mLegacyCompatibility = savedState.getBoolean(KEY_LEGACY_COMPATIBILITY);
mQueryString = savedState.getString(KEY_QUERY_STRING);
mDirectoryResultLimit = savedState.getInt(KEY_DIRECTORY_RESULT_LIMIT);
- mRequest = savedState.getParcelable(KEY_REQUEST);
mDarkTheme = savedState.getBoolean(KEY_DARK_THEME);
// Retrieve list state. This will be applied in onLoadFinished
mListState = savedState.getParcelable(KEY_LIST_STATE);
}
- /**
- * Returns the parsed intent that started the activity hosting this fragment.
- */
- public ContactsRequest getContactsRequest() {
- return mRequest;
- }
-
- /**
- * Sets a parsed intent that started the activity hosting this fragment.
- */
- public void setContactsRequest(ContactsRequest request) {
- mRequest = request;
- }
-
@Override
public void onStart() {
super.onStart();
@@ -608,7 +591,14 @@
}
}
- public void setSearchMode(boolean flag) {
+ /**
+ * Enter/exit search mode. By design, a fragment enters search mode only when it has a
+ * non-empty query text, so the mode must be tightly related to the current query.
+ * For this reason this method must only be called by {@link #setQueryString}.
+ *
+ * Also note this method doesn't call {@link #reloadData()}; {@link #setQueryString} does it.
+ */
+ protected void setSearchMode(boolean flag) {
if (mSearchMode != flag) {
mSearchMode = flag;
setSectionHeaderDisplayEnabled(!mSearchMode);
@@ -632,7 +622,6 @@
}
}
mAdapter.configureDefaultPartition(false, flag);
- reloadData();
}
if (mListView != null) {
@@ -641,17 +630,22 @@
}
}
- public boolean isSearchMode() {
+ public final boolean isSearchMode() {
return mSearchMode;
}
- public String getQueryString() {
+ public final String getQueryString() {
return mQueryString;
}
public void setQueryString(String queryString, boolean delaySelection) {
+ // Normalize the empty query.
+ if (TextUtils.isEmpty(queryString)) queryString = null;
+
if (!TextUtils.equals(mQueryString, queryString)) {
mQueryString = queryString;
+ setSearchMode(!TextUtils.isEmpty(mQueryString));
+
if (mAdapter != null) {
mAdapter.setQueryString(queryString);
reloadData();
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 30c3c48..0cc211a 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -17,13 +17,14 @@
import com.android.contacts.R;
import com.android.contacts.editor.ContactEditorFragment;
+import com.android.contacts.util.AccountFilterUtil;
-import android.app.Activity;
import android.content.CursorLoader;
import android.content.Intent;
import android.database.Cursor;
import android.provider.ContactsContract.Contacts;
import android.text.TextUtils;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -40,11 +41,13 @@
* picking a contact with one of the PICK intents).
*/
public class DefaultContactBrowseListFragment extends ContactBrowseListFragment {
+ private static final String TAG = DefaultContactBrowseListFragment.class.getSimpleName();
+
+ private static final int REQUEST_CODE_ACCOUNT_FILTER = 1;
private TextView mCounterHeaderView;
private View mSearchHeaderView;
- private TextView mAccountFilterHeaderView;
- private View mAccountFilterHeaderContainer;
+ private View mAccountFilterHeader;
private FrameLayout mProfileHeaderContainer;
private View mProfileHeader;
private Button mProfileMessage;
@@ -56,12 +59,8 @@
private class FilterHeaderClickListener implements OnClickListener {
@Override
public void onClick(View view) {
- final Activity activity = getActivity();
- if (activity != null) {
- final Intent intent = new Intent(activity, AccountFilterActivity.class);
- activity.startActivityForResult(
- intent, AccountFilterActivity.DEFAULT_REQUEST_CODE);
- }
+ AccountFilterUtil.startAccountFilterActivityForResult(
+ DefaultContactBrowseListFragment.this, REQUEST_CODE_ACCOUNT_FILTER);
}
}
private OnClickListener mFilterHeaderClickListener = new FilterHeaderClickListener();
@@ -92,17 +91,15 @@
@Override
protected View inflateView(LayoutInflater inflater, ViewGroup container) {
- return inflater.inflate(R.layout.contacts_list_content, null);
+ return inflater.inflate(R.layout.contact_list_content, null);
}
@Override
protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
super.onCreateView(inflater, container);
- mAccountFilterHeaderView = (TextView) getView().findViewById(R.id.account_filter_header);
- mAccountFilterHeaderContainer =
- getView().findViewById(R.id.account_filter_header_container);
- mAccountFilterHeaderContainer.setOnClickListener(mFilterHeaderClickListener);
+ mAccountFilterHeader = getView().findViewById(R.id.account_filter_header_container);
+ mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
mCounterHeaderView = (TextView) getView().findViewById(R.id.contacts_count);
// Create an empty user profile header and hide it for now (it will be visible if the
@@ -120,7 +117,7 @@
}
@Override
- public void setSearchMode(boolean flag) {
+ protected void setSearchMode(boolean flag) {
super.setSearchMode(flag);
checkHeaderViewVisibility();
}
@@ -144,29 +141,17 @@
}
private void updateFilterHeaderView() {
- ContactListFilter filter = getFilter();
- if (mAccountFilterHeaderView == null) {
+ if (mAccountFilterHeader == null) {
return; // Before onCreateView -- just ignore it.
}
+ final ContactListFilter filter = getFilter();
if (filter != null && !isSearchMode()) {
- if (filter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
- mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
- mAccountFilterHeaderView.setText(getContext().getString(
- R.string.listSingleContact));
- return;
- } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
- mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
- mAccountFilterHeaderView.setText(getContext().getString(
- R.string.listCustomView));
- return;
- } else if (filter.filterType != ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
- mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
- mAccountFilterHeaderView.setText(getContext().getString(
- R.string.listAllContactsInAccount, filter.accountName));
- return;
- }
+ final boolean shouldShowHeader = AccountFilterUtil.updateAccountFilterTitleForPeople(
+ mAccountFilterHeader, filter, false, false);
+ mAccountFilterHeader.setVisibility(shouldShowHeader ? View.VISIBLE : View.GONE);
+ } else {
+ mAccountFilterHeader.setVisibility(View.GONE);
}
- mAccountFilterHeaderContainer.setVisibility(View.GONE);
}
@Override
@@ -244,6 +229,18 @@
showEmptyUserProfile(!mUserProfileExists && !isSearchMode());
}
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_ACCOUNT_FILTER) {
+ if (getActivity() != null) {
+ AccountFilterUtil.handleAccountFilterResult(
+ ContactListFilterController.getInstance(getActivity()), resultCode, data);
+ } else {
+ Log.e(TAG, "getActivity() returns null during Fragment#onActivityResult()");
+ }
+ }
+ }
+
private void showEmptyUserProfile(boolean show) {
// Changing visibility of just the mProfileHeader doesn't do anything unless
// you change visibility of its children, hence the call to mCounterHeaderView
diff --git a/src/com/android/contacts/list/EmailAddressPickerFragment.java b/src/com/android/contacts/list/EmailAddressPickerFragment.java
index 168e135..14b6c23 100644
--- a/src/com/android/contacts/list/EmailAddressPickerFragment.java
+++ b/src/com/android/contacts/list/EmailAddressPickerFragment.java
@@ -55,7 +55,7 @@
@Override
protected View inflateView(LayoutInflater inflater, ViewGroup container) {
- return inflater.inflate(R.layout.contacts_list_content, null);
+ return inflater.inflate(R.layout.contact_list_content, null);
}
private void pickEmailAddress(Uri uri) {
diff --git a/src/com/android/contacts/list/PhoneFavoriteFragment.java b/src/com/android/contacts/list/PhoneFavoriteFragment.java
index 17247a4..9ee2d3c 100644
--- a/src/com/android/contacts/list/PhoneFavoriteFragment.java
+++ b/src/com/android/contacts/list/PhoneFavoriteFragment.java
@@ -19,6 +19,7 @@
import com.android.contacts.ContactTileLoaderFactory;
import com.android.contacts.R;
import com.android.contacts.preference.ContactsPreferences;
+import com.android.contacts.util.AccountFilterUtil;
import android.app.Activity;
import android.app.Fragment;
@@ -39,6 +40,7 @@
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.TextView;
@@ -62,6 +64,8 @@
private static final String KEY_FILTER = "filter";
+ private static final int REQUEST_CODE_ACCOUNT_FILTER = 1;
+
public interface Listener {
public void onContactSelected(Uri contactUri);
}
@@ -93,7 +97,7 @@
// Show the filter header with "loading" state.
updateFilterHeaderView();
- mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
+ mAccountFilterHeader.setVisibility(View.VISIBLE);
}
@Override
@@ -137,12 +141,8 @@
private class FilterHeaderClickListener implements OnClickListener {
@Override
public void onClick(View view) {
- final Activity activity = getActivity();
- if (activity != null) {
- final Intent intent = new Intent(activity, AccountFilterActivity.class);
- activity.startActivityForResult(
- intent, AccountFilterActivity.DEFAULT_REQUEST_CODE);
- }
+ AccountFilterUtil.startAccountFilterActivityForResult(
+ PhoneFavoriteFragment.this, REQUEST_CODE_ACCOUNT_FILTER);
}
}
@@ -164,6 +164,7 @@
// FastScroller should be visible only when the user is seeing "all" contacts section.
final boolean shouldShow = mAdapter.shouldShowFirstScroller(firstVisibleItem);
if (shouldShow != mShouldShowFastScroller) {
+ mListView.setVerticalScrollBarEnabled(shouldShow);
mListView.setFastScrollEnabled(shouldShow);
mListView.setFastScrollAlwaysVisible(shouldShow);
mShouldShowFastScroller = shouldShow;
@@ -196,8 +197,11 @@
private TextView mEmptyView;
private ListView mListView;
- private View mAccountFilterHeaderContainer;
- private TextView mAccountFilterHeaderView;
+ /**
+ * Layout containing {@link #mAccountFilterHeader}. Used to limit area being "pressed".
+ */
+ private FrameLayout mAccountFilterHeaderContainer;
+ private View mAccountFilterHeader;
private final ContactTileAdapter.Listener mContactTileAdapterListener =
new ContactTileAdapterListener();
@@ -239,7 +243,7 @@
mListView = (ListView) listLayout.findViewById(R.id.contact_tile_list);
mListView.setItemsCanFocus(true);
mListView.setOnItemClickListener(this);
- mListView.setVerticalScrollBarEnabled(true);
+ mListView.setVerticalScrollBarEnabled(false);
mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
@@ -299,11 +303,10 @@
}
// Create the account filter header but keep it hidden until "all" contacts are loaded.
- mAccountFilterHeaderContainer = inflater.inflate(
- R.layout.phone_favorite_account_filter_header, mListView, false);
- mAccountFilterHeaderView =
- (TextView) mAccountFilterHeaderContainer.findViewById(R.id.account_filter_header);
- mAccountFilterHeaderContainer.setOnClickListener(mFilterHeaderClickListener);
+ mAccountFilterHeaderContainer = new FrameLayout(context, null);
+ mAccountFilterHeader = inflater.inflate(R.layout.account_filter_header, mListView, false);
+ mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
+ mAccountFilterHeaderContainer.addView(mAccountFilterHeader);
mAccountFilterHeaderContainer.setVisibility(View.GONE);
mAdapter = new PhoneFavoriteMergedAdapter(context,
@@ -355,6 +358,18 @@
}
}
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_ACCOUNT_FILTER) {
+ if (getActivity() != null) {
+ AccountFilterUtil.handleAccountFilterResult(
+ ContactListFilterController.getInstance(getActivity()), resultCode, data);
+ } else {
+ Log.e(TAG, "getActivity() returns null during Fragment#onActivityResult()");
+ }
+ }
+ }
+
private boolean loadContactsPreferences() {
if (mContactsPrefs == null || mAllContactsAdapter == null) {
return false;
@@ -400,27 +415,12 @@
}
private void updateFilterHeaderView() {
- if (mAccountFilterHeaderContainer == null || mAllContactsAdapter == null) {
+ final ContactListFilter filter = getFilter();
+ if (mAccountFilterHeader == null || mAllContactsAdapter == null || filter == null) {
return;
}
-
- final ContactListFilter filter = getFilter();
- if (mAllContactsAdapter.isLoading()) {
- mAccountFilterHeaderView.setText(R.string.contact_list_loading);
- } else if (filter != null) {
- if (filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
- mAccountFilterHeaderView.setText(R.string.list_filter_phones);
- } else if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
- mAccountFilterHeaderView.setText(getString(
- R.string.listAllContactsInAccount, filter.accountName));
- } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
- mAccountFilterHeaderView.setText(R.string.listCustomView);
- } else {
- Log.w(TAG, "Filter type \"" + filter.filterType + "\" isn't expected.");
- }
- } else {
- Log.w(TAG, "Filter is null.");
- }
+ AccountFilterUtil.updateAccountFilterTitleForPhone(
+ mAccountFilterHeader, filter, mAllContactsAdapter.isLoading(), true);
}
public ContactListFilter getFilter() {
diff --git a/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java b/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java
index f817b4c..205e156 100644
--- a/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java
+++ b/src/com/android/contacts/list/PhoneFavoriteMergedAdapter.java
@@ -184,9 +184,10 @@
if (position < contactTileAdapterCount) {
return mContactTileAdapter.isEnabled(position);
} else if (position == contactTileAdapterCount) {
- return mAccountFilterHeaderContainer.isEnabled();
+ // This will be handled by View's onClick event instead of ListView's onItemClick event.
+ return false;
} else {
- final int localPosition = position - contactTileAdapterCount + 1;
+ final int localPosition = position - contactTileAdapterCount - 1;
return mContactEntryListAdapter.isEnabled(localPosition);
}
}
diff --git a/src/com/android/contacts/list/PhoneNumberPickerFragment.java b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
index 64d5f13..052c32f 100644
--- a/src/com/android/contacts/list/PhoneNumberPickerFragment.java
+++ b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
@@ -17,8 +17,8 @@
import com.android.contacts.R;
import com.android.contacts.list.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
+import com.android.contacts.util.AccountFilterUtil;
-import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -28,7 +28,6 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.TextView;
/**
* Fragment containing a phone number list for picking.
@@ -37,15 +36,16 @@
implements OnShortcutIntentCreatedListener {
private static final String TAG = PhoneNumberPickerFragment.class.getSimpleName();
+ private static final int REQUEST_CODE_ACCOUNT_FILTER = 1;
+
private OnPhoneNumberPickerActionListener mListener;
private String mShortcutAction;
private ContactListFilter mFilter;
- private TextView mAccountFilterHeaderView;
- private View mAccountFilterHeaderContainer;
+ private View mAccountFilterHeader;
/**
- * Lives as ListView's header and is shown when {@link #mAccountFilterHeaderContainer} is set
+ * Lives as ListView's header and is shown when {@link #mAccountFilterHeader} is set
* to View.GONE.
*/
private View mPaddingView;
@@ -58,17 +58,11 @@
private ContactListItemView.PhotoPosition mPhotoPosition =
ContactListItemView.DEFAULT_PHOTO_POSITION;
- // A complete copy from DefaultContactBrowserListFragment
- // TODO: should be able to share logic around filter header.
private class FilterHeaderClickListener implements OnClickListener {
@Override
public void onClick(View view) {
- final Activity activity = getActivity();
- if (activity != null) {
- final Intent intent = new Intent(activity, AccountFilterActivity.class);
- activity.startActivityForResult(
- intent, AccountFilterActivity.DEFAULT_REQUEST_CODE);
- }
+ AccountFilterUtil.startAccountFilterActivityForResult(
+ PhoneNumberPickerFragment.this, REQUEST_CODE_ACCOUNT_FILTER);
}
}
private OnClickListener mFilterHeaderClickListener = new FilterHeaderClickListener();
@@ -96,45 +90,30 @@
mPaddingView = paddingView.findViewById(R.id.contact_detail_list_padding);
getListView().addHeaderView(paddingView);
- mAccountFilterHeaderView = (TextView) getView().findViewById(R.id.account_filter_header);
- mAccountFilterHeaderContainer =
- getView().findViewById(R.id.account_filter_header_container);
- mAccountFilterHeaderContainer.setOnClickListener(mFilterHeaderClickListener);
+ mAccountFilterHeader = getView().findViewById(R.id.account_filter_header_container);
+ mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
updateFilterHeaderView();
}
@Override
- public void setSearchMode(boolean flag) {
+ protected void setSearchMode(boolean flag) {
super.setSearchMode(flag);
updateFilterHeaderView();
}
private void updateFilterHeaderView() {
- if (mAccountFilterHeaderView != null) {
- ContactListFilter filter = getFilter();
- if (filter != null && !isSearchMode()) {
- if (filter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
- mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
- mAccountFilterHeaderView.setText(getContext().getString(
- R.string.listSingleContact));
- mPaddingView.setVisibility(View.GONE);
- return;
- } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
- mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
- mAccountFilterHeaderView.setText(getContext().getString(
- R.string.listCustomView));
- mPaddingView.setVisibility(View.GONE);
- return;
- } else if (filter.filterType != ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
- mAccountFilterHeaderContainer.setVisibility(View.VISIBLE);
- mAccountFilterHeaderView.setText(getContext().getString(
- R.string.listAllContactsInAccount, filter.accountName));
- mPaddingView.setVisibility(View.GONE);
- return;
- }
- }
- mAccountFilterHeaderContainer.setVisibility(View.GONE);
+ final ContactListFilter filter = getFilter();
+ if (mAccountFilterHeader == null || filter == null) {
+ return;
+ }
+ final boolean shouldShowHeader = AccountFilterUtil.updateAccountFilterTitleForPhone(
+ mAccountFilterHeader, filter, false, false);
+ if (shouldShowHeader) {
+ mPaddingView.setVisibility(View.GONE);
+ mAccountFilterHeader.setVisibility(View.VISIBLE);
+ } else {
mPaddingView.setVisibility(View.VISIBLE);
+ mAccountFilterHeader.setVisibility(View.GONE);
}
}
@@ -233,7 +212,7 @@
@Override
protected View inflateView(LayoutInflater inflater, ViewGroup container) {
- return inflater.inflate(R.layout.contacts_list_content, null);
+ return inflater.inflate(R.layout.contact_list_content, null);
}
public void pickPhoneNumber(Uri uri) {
@@ -257,6 +236,18 @@
mListener.onPickPhoneNumberAction(data.getData());
}
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_CODE_ACCOUNT_FILTER) {
+ if (getActivity() != null) {
+ AccountFilterUtil.handleAccountFilterResult(
+ ContactListFilterController.getInstance(getActivity()), resultCode, data);
+ } else {
+ Log.e(TAG, "getActivity() returns null during Fragment#onActivityResult()");
+ }
+ }
+ }
+
public ContactListFilter getFilter() {
return mFilter;
}
diff --git a/src/com/android/contacts/list/PostalAddressPickerFragment.java b/src/com/android/contacts/list/PostalAddressPickerFragment.java
index 5f7ca56..85fc155 100644
--- a/src/com/android/contacts/list/PostalAddressPickerFragment.java
+++ b/src/com/android/contacts/list/PostalAddressPickerFragment.java
@@ -70,7 +70,7 @@
@Override
protected View inflateView(LayoutInflater inflater, ViewGroup container) {
- return inflater.inflate(R.layout.contacts_list_content, null);
+ return inflater.inflate(R.layout.contact_list_content, null);
}
private void pickPostalAddress(Uri uri) {
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index 654adeb..23d39d3 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -32,7 +32,6 @@
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
@@ -83,11 +82,35 @@
*/
private HashMap<String, DataKind> mMimeKinds = Maps.newHashMap();
+ protected boolean mIsInitialized;
+
+ protected static class DefinitionException extends Exception {
+ public DefinitionException(String message) {
+ super(message);
+ }
+
+ public DefinitionException(String message, Exception inner) {
+ super(message, inner);
+ }
+ }
+
/**
* Whether this account type was able to be fully initialized. This may be false if
* (for example) the package name associated with the account type could not be found.
*/
- public boolean isInitialized() {
+ public final boolean isInitialized() {
+ return mIsInitialized;
+ }
+
+ /**
+ * @return Whether this type is an "embedded" type. i.e. any of {@link FallbackAccountType},
+ * {@link GoogleAccountType} or {@link ExternalAccountType}.
+ *
+ * If an embedded type cannot be initialized (i.e. if {@link #isInitialized()} returns
+ * {@code false}) it's considered critical, and the application will crash. On the other
+ * hand if it's not an embedded type, we just skip loading the type.
+ */
+ public boolean isEmbedded() {
return true;
}
@@ -274,10 +297,10 @@
/**
* Add given {@link DataKind} to list of those provided by this source.
*/
- public DataKind addKind(DataKind kind) {
+ public DataKind addKind(DataKind kind) throws DefinitionException {
if (mMimeKinds.get(kind.mimeType) != null) {
- // TODO Make it exception.
- Log.w(TAG, "mime type '" + kind.mimeType + "' is already registered");
+ throw new DefinitionException(
+ "mime type '" + kind.mimeType + "' is already registered");
}
kind.resPackageName = this.resPackageName;
@@ -337,6 +360,16 @@
public int hashCode() {
return rawValue;
}
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName()
+ + " rawValue=" + rawValue
+ + " labelRes=" + labelRes
+ + " secondary=" + secondary
+ + " specificMax=" + specificMax
+ + " customColumn=" + customColumn;
+ }
}
public static class EventEditType extends EditType {
@@ -354,6 +387,11 @@
mYearOptional = yearOptional;
return this;
}
+
+ @Override
+ public String toString() {
+ return super.toString() + " mYearOptional=" + mYearOptional;
+ }
}
/**
@@ -403,6 +441,19 @@
public boolean isMultiLine() {
return (inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) != 0;
}
+
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + ":"
+ + " column=" + column
+ + " titleRes=" + titleRes
+ + " inputType=" + inputType
+ + " minLines=" + minLines
+ + " optional=" + optional
+ + " shortForm=" + shortForm
+ + " longForm=" + longForm;
+ }
}
/**
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index 92323fa..43bb579 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -411,7 +411,12 @@
Log.d(TAG, "Registering external account type=" + type
+ ", packageName=" + auth.packageName);
accountType = new ExternalAccountType(mContext, auth.packageName, false);
- if (!accountType.isInitialized()) {
+ }
+ if (!accountType.isInitialized()) {
+ if (accountType.isEmbedded()) {
+ throw new IllegalStateException("Problem initializing embedded type "
+ + accountType.getClass().getCanonicalName());
+ } else {
// Skip external account types that couldn't be initialized.
continue;
}
diff --git a/src/com/android/contacts/model/BaseAccountType.java b/src/com/android/contacts/model/BaseAccountType.java
index 34beca2..0d766da 100644
--- a/src/com/android/contacts/model/BaseAccountType.java
+++ b/src/com/android/contacts/model/BaseAccountType.java
@@ -17,6 +17,7 @@
package com.android.contacts.model;
import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DefinitionException;
import com.android.contacts.util.DateUtils;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -107,16 +108,6 @@
static final int GROUP_MEMBERSHIP = 999;
}
- protected static class DefinitionException extends Exception {
- public DefinitionException(String message) {
- super(message);
- }
-
- public DefinitionException(String message, Exception inner) {
- super(message, inner);
- }
- }
-
public BaseAccountType() {
this.accountType = null;
this.dataSet = null;
@@ -148,11 +139,12 @@
return new EditType(type, Relation.getTypeLabelResource(type));
}
- protected DataKind addDataKindStructuredName(Context context) {
+ protected DataKind addDataKindStructuredName(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
R.string.nameLabelsGroup, -1, true, R.layout.structured_name_editor_view));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
+ kind.typeOverallMax = 1;
kind.fieldList = Lists.newArrayList();
kind.fieldList.add(new EditField(StructuredName.DISPLAY_NAME,
@@ -177,11 +169,12 @@
return kind;
}
- protected DataKind addDataKindDisplayName(Context context) {
+ protected DataKind addDataKindDisplayName(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
R.string.nameLabelsGroup, -1, true, R.layout.text_fields_editor_view));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
+ kind.typeOverallMax = 1;
kind.fieldList = Lists.newArrayList();
kind.fieldList.add(new EditField(StructuredName.DISPLAY_NAME,
@@ -217,11 +210,12 @@
return kind;
}
- protected DataKind addDataKindPhoneticName(Context context) {
+ protected DataKind addDataKindPhoneticName(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
R.string.name_phonetic, -1, true, R.layout.phonetic_name_editor_view));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
kind.actionBody = new SimpleInflater(Nickname.NAME);
+ kind.typeOverallMax = 1;
kind.fieldList = Lists.newArrayList();
kind.fieldList.add(new EditField(DataKind.PSEUDO_COLUMN_PHONETIC_NAME,
@@ -236,7 +230,7 @@
return kind;
}
- protected DataKind addDataKindNickname(Context context) {
+ protected DataKind addDataKindNickname(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Nickname.CONTENT_ITEM_TYPE,
R.string.nicknameLabelsGroup, 115, true, R.layout.text_fields_editor_view));
kind.typeOverallMax = 1;
@@ -252,7 +246,7 @@
return kind;
}
- protected DataKind addDataKindPhone(Context context) {
+ protected DataKind addDataKindPhone(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Phone.CONTENT_ITEM_TYPE, R.string.phoneLabelsGroup,
10, true, R.layout.text_fields_editor_view));
kind.iconAltRes = R.drawable.ic_text_holo_light;
@@ -262,8 +256,8 @@
kind.actionBody = new SimpleInflater(Phone.NUMBER);
kind.typeColumn = Phone.TYPE;
kind.typeList = Lists.newArrayList();
- kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
kind.typeList.add(buildPhoneType(Phone.TYPE_MOBILE));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_HOME));
kind.typeList.add(buildPhoneType(Phone.TYPE_WORK));
kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_WORK).setSecondary(true));
kind.typeList.add(buildPhoneType(Phone.TYPE_FAX_HOME).setSecondary(true));
@@ -282,8 +276,7 @@
kind.typeList.add(buildPhoneType(Phone.TYPE_TTY_TDD).setSecondary(true));
kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_MOBILE).setSecondary(true));
kind.typeList.add(buildPhoneType(Phone.TYPE_WORK_PAGER).setSecondary(true));
- kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true).setCustomColumn(
- Phone.LABEL));
+ kind.typeList.add(buildPhoneType(Phone.TYPE_ASSISTANT).setSecondary(true));
kind.typeList.add(buildPhoneType(Phone.TYPE_MMS).setSecondary(true));
kind.fieldList = Lists.newArrayList();
@@ -292,7 +285,7 @@
return kind;
}
- protected DataKind addDataKindEmail(Context context) {
+ protected DataKind addDataKindEmail(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Email.CONTENT_ITEM_TYPE, R.string.emailLabelsGroup,
15, true, R.layout.text_fields_editor_view));
kind.actionHeader = new EmailActionInflater();
@@ -312,7 +305,7 @@
return kind;
}
- protected DataKind addDataKindStructuredPostal(Context context) {
+ protected DataKind addDataKindStructuredPostal(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(StructuredPostal.CONTENT_ITEM_TYPE,
R.string.postalLabelsGroup, 25, true, R.layout.text_fields_editor_view));
kind.actionHeader = new PostalActionInflater();
@@ -333,7 +326,7 @@
return kind;
}
- protected DataKind addDataKindIm(Context context) {
+ protected DataKind addDataKindIm(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Im.CONTENT_ITEM_TYPE, R.string.imLabelsGroup, 20, true,
R.layout.text_fields_editor_view));
kind.actionHeader = new ImActionInflater();
@@ -364,7 +357,7 @@
return kind;
}
- protected DataKind addDataKindOrganization(Context context) {
+ protected DataKind addDataKindOrganization(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Organization.CONTENT_ITEM_TYPE,
R.string.organizationLabelsGroup, 5, true,
R.layout.text_fields_editor_view));
@@ -381,14 +374,15 @@
return kind;
}
- protected DataKind addDataKindPhoto(Context context) {
+ protected DataKind addDataKindPhoto(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Photo.CONTENT_ITEM_TYPE, -1, -1, true, -1));
+ kind.typeOverallMax = 1;
kind.fieldList = Lists.newArrayList();
kind.fieldList.add(new EditField(Photo.PHOTO, -1, -1));
return kind;
}
- protected DataKind addDataKindNote(Context context) {
+ protected DataKind addDataKindNote(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Note.CONTENT_ITEM_TYPE,
R.string.label_notes, 110, true, R.layout.text_fields_editor_view));
kind.typeOverallMax = 1;
@@ -400,7 +394,7 @@
return kind;
}
- protected DataKind addDataKindWebsite(Context context) {
+ protected DataKind addDataKindWebsite(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Website.CONTENT_ITEM_TYPE,
R.string.websiteLabelsGroup, 120, true, R.layout.text_fields_editor_view));
kind.actionHeader = new SimpleInflater(R.string.websiteLabelsGroup);
@@ -414,7 +408,7 @@
return kind;
}
- protected DataKind addDataKindSipAddress(Context context) {
+ protected DataKind addDataKindSipAddress(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(SipAddress.CONTENT_ITEM_TYPE,
R.string.label_sip_address, 130, true, R.layout.text_fields_editor_view));
@@ -428,7 +422,7 @@
return kind;
}
- protected DataKind addDataKindGroupMembership(Context context) {
+ protected DataKind addDataKindGroupMembership(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(GroupMembership.CONTENT_ITEM_TYPE,
R.string.groupsLabel, 999, true, -1));
@@ -496,6 +490,13 @@
return null;
}
}
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName()
+ + " mStringRes=" + mStringRes
+ + " mColumnName" + mColumnName;
+ }
}
public static abstract class CommonInflater implements StringInflater {
@@ -535,6 +536,11 @@
final String label = values.getAsString(getLabelColumn());
return getTypeLabel(context.getResources(), type, label);
}
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
}
public static class PhoneActionInflater extends CommonInflater {
@@ -801,15 +807,14 @@
kind.actionBody = actionBody;
kind.fieldList = Lists.newArrayList();
- kind.typeOverallMax = getAttr(attrs, Attr.MAX_OCCURRENCE, -1);
-
- // Handle "types".
- // If a kind has the type column, contacts.xml must have at least one type definition.
- // Otherwise, it mustn't have a type definition.
- //
- // If it's a pseudo data kind (== data kind that doesn't have the corresponding
- // DataKind tag in the XML), we just skip this process.
+ // Get more information from the tag...
+ // A pseudo data kind doesn't have corresponding tag the XML, so we skip this.
if (!isPseudo) {
+ kind.typeOverallMax = getAttr(attrs, Attr.MAX_OCCURRENCE, -1);
+
+ // Process "Type" tags.
+ // If a kind has the type column, contacts.xml must have at least one type
+ // definition. Otherwise, it mustn't have a type definition.
if (kind.typeColumn != null) {
// Parse and add types.
kind.typeList = Lists.newArrayList();
@@ -957,7 +962,6 @@
throwIfList(ks);
kinds.add(ks);
-
// Note about setLongForm/setShortForm below.
// We need to set this only when the type supports display name. (=supportsDisplayName)
// Otherwise (i.e. Exchange) we don't set these flags, but instead make some fields
@@ -988,6 +992,7 @@
R.string.nameLabelsGroup, Weight.NONE, R.layout.text_fields_editor_view,
new SimpleInflater(R.string.nameLabelsGroup),
new SimpleInflater(Nickname.NAME));
+ kd.typeOverallMax = 1;
kinds.add(kd);
kd.fieldList.add(new EditField(StructuredName.DISPLAY_NAME,
@@ -1023,25 +1028,18 @@
R.string.name_phonetic, Weight.NONE, R.layout.phonetic_name_editor_view,
new SimpleInflater(R.string.nameLabelsGroup),
new SimpleInflater(Nickname.NAME));
+ kp.typeOverallMax = 1;
kinds.add(kp);
+ // We may want to change the order depending on displayOrderPrimary too.
kp.fieldList.add(new EditField(DataKind.PSEUDO_COLUMN_PHONETIC_NAME,
R.string.name_phonetic, FLAGS_PHONETIC).setShortForm(true));
- if (!displayOrderPrimary) {
- kp.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
- R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true));
- kp.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
- R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true));
- kp.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
- R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true));
- } else {
- kp.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
- R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true));
- kp.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
- R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true));
- kp.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
- R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true));
- }
+ kp.fieldList.add(new EditField(StructuredName.PHONETIC_FAMILY_NAME,
+ R.string.name_phonetic_family, FLAGS_PHONETIC).setLongForm(true));
+ kp.fieldList.add(new EditField(StructuredName.PHONETIC_MIDDLE_NAME,
+ R.string.name_phonetic_middle, FLAGS_PHONETIC).setLongForm(true));
+ kp.fieldList.add(new EditField(StructuredName.PHONETIC_GIVEN_NAME,
+ R.string.name_phonetic_given, FLAGS_PHONETIC).setLongForm(true));
return kinds;
}
}
diff --git a/src/com/android/contacts/model/DataKind.java b/src/com/android/contacts/model/DataKind.java
index aaf7bb5..857f3e4 100644
--- a/src/com/android/contacts/model/DataKind.java
+++ b/src/com/android/contacts/model/DataKind.java
@@ -20,6 +20,7 @@
import com.android.contacts.model.AccountType.EditField;
import com.android.contacts.model.AccountType.EditType;
import com.android.contacts.model.AccountType.StringInflater;
+import com.google.common.collect.Iterators;
import android.content.ContentValues;
import android.provider.ContactsContract.Data;
@@ -95,4 +96,43 @@
this.typeOverallMax = -1;
this.editorLayoutResourceId = editorLayoutResourceId;
}
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("DataKind:");
+ sb.append(" resPackageName=").append(resPackageName);
+ sb.append(" mimeType=").append(mimeType);
+ sb.append(" titleRes=").append(titleRes);
+ sb.append(" iconAltRes=").append(iconAltRes);
+ sb.append(" iconAltDescriptionRes=").append(iconAltDescriptionRes);
+ sb.append(" weight=").append(weight);
+ sb.append(" editable=").append(editable);
+ sb.append(" actionHeader=").append(actionHeader);
+ sb.append(" actionAltHeader=").append(actionAltHeader);
+ sb.append(" actionBody=").append(actionBody);
+ sb.append(" actionBodySocial=").append(actionBodySocial);
+ sb.append(" typeColumn=").append(typeColumn);
+ sb.append(" typeOverallMax=").append(typeOverallMax);
+ sb.append(" typeList=").append(toString(typeList));
+ sb.append(" fieldList=").append(toString(fieldList));
+ sb.append(" defaultValues=").append(defaultValues);
+ sb.append(" editorLayoutResourceId=").append(editorLayoutResourceId);
+ sb.append(" dateFormatWithoutYear=").append(toString(dateFormatWithoutYear));
+ sb.append(" dateFormatWithYear=").append(toString(dateFormatWithYear));
+
+ return sb.toString();
+ }
+
+ public static String toString(SimpleDateFormat format) {
+ return format == null ? "(null)" : format.toPattern();
+ }
+
+ public static String toString(Iterable<?> list) {
+ if (list == null) {
+ return "(null)";
+ } else {
+ return Iterators.toString(list.iterator());
+ }
+ }
}
\ No newline at end of file
diff --git a/src/com/android/contacts/model/ExchangeAccountType.java b/src/com/android/contacts/model/ExchangeAccountType.java
index bb11cf6..e5491d2 100644
--- a/src/com/android/contacts/model/ExchangeAccountType.java
+++ b/src/com/android/contacts/model/ExchangeAccountType.java
@@ -17,6 +17,7 @@
package com.android.contacts.model;
import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DefinitionException;
import com.android.contacts.util.DateUtils;
import com.google.android.collect.Lists;
@@ -33,10 +34,12 @@
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.util.Log;
import java.util.Locale;
public class ExchangeAccountType extends BaseAccountType {
+ private static final String TAG = "ExchangeAccountType";
public static final String ACCOUNT_TYPE = "com.android.exchange";
@@ -45,24 +48,30 @@
this.resPackageName = null;
this.summaryResPackageName = resPackageName;
- addDataKindStructuredName(context);
- addDataKindDisplayName(context);
- addDataKindPhoneticName(context);
- addDataKindNickname(context);
- addDataKindPhone(context);
- addDataKindEmail(context);
- addDataKindStructuredPostal(context);
- addDataKindIm(context);
- addDataKindOrganization(context);
- addDataKindPhoto(context);
- addDataKindNote(context);
- addDataKindEvent(context);
- addDataKindWebsite(context);
- addDataKindGroupMembership(context);
+ try {
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindEvent(context);
+ addDataKindWebsite(context);
+ addDataKindGroupMembership(context);
+
+ mIsInitialized = true;
+ } catch (DefinitionException e) {
+ Log.e(TAG, "Problem building account type", e);
+ }
}
@Override
- protected DataKind addDataKindStructuredName(Context context) {
+ protected DataKind addDataKindStructuredName(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(StructuredName.CONTENT_ITEM_TYPE,
R.string.nameLabelsGroup, -1, true, R.layout.structured_name_editor_view));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
@@ -91,7 +100,7 @@
}
@Override
- protected DataKind addDataKindDisplayName(Context context) {
+ protected DataKind addDataKindDisplayName(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME,
R.string.nameLabelsGroup, -1, true, R.layout.text_fields_editor_view));
@@ -124,7 +133,7 @@
}
@Override
- protected DataKind addDataKindPhoneticName(Context context) {
+ protected DataKind addDataKindPhoneticName(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME,
R.string.name_phonetic, -1, true, R.layout.phonetic_name_editor_view));
kind.actionHeader = new SimpleInflater(R.string.nameLabelsGroup);
@@ -142,7 +151,7 @@
}
@Override
- protected DataKind addDataKindNickname(Context context) {
+ protected DataKind addDataKindNickname(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindNickname(context);
kind.typeOverallMax = 1;
@@ -155,7 +164,7 @@
}
@Override
- protected DataKind addDataKindPhone(Context context) {
+ protected DataKind addDataKindPhone(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindPhone(context);
kind.typeColumn = Phone.TYPE;
@@ -185,7 +194,7 @@
}
@Override
- protected DataKind addDataKindEmail(Context context) {
+ protected DataKind addDataKindEmail(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindEmail(context);
kind.typeOverallMax = 3;
@@ -197,7 +206,7 @@
}
@Override
- protected DataKind addDataKindStructuredPostal(Context context) {
+ protected DataKind addDataKindStructuredPostal(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindStructuredPostal(context);
final boolean useJapaneseOrder =
@@ -237,7 +246,7 @@
}
@Override
- protected DataKind addDataKindIm(Context context) {
+ protected DataKind addDataKindIm(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindIm(context);
// Types are not supported for IM. There can be 3 IMs, but OWA only shows only the first
@@ -253,7 +262,7 @@
}
@Override
- protected DataKind addDataKindOrganization(Context context) {
+ protected DataKind addDataKindOrganization(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindOrganization(context);
kind.typeOverallMax = 1;
@@ -268,7 +277,7 @@
}
@Override
- protected DataKind addDataKindPhoto(Context context) {
+ protected DataKind addDataKindPhoto(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindPhoto(context);
kind.typeOverallMax = 1;
@@ -280,7 +289,7 @@
}
@Override
- protected DataKind addDataKindNote(Context context) {
+ protected DataKind addDataKindNote(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindNote(context);
kind.fieldList = Lists.newArrayList();
@@ -289,7 +298,7 @@
return kind;
}
- protected DataKind addDataKindEvent(Context context) {
+ protected DataKind addDataKindEvent(Context context) throws DefinitionException {
DataKind kind = addKind(
new DataKind(Event.CONTENT_ITEM_TYPE, R.string.eventLabelsGroup, 150, true,
R.layout.event_field_editor_view));
@@ -311,7 +320,7 @@
}
@Override
- protected DataKind addDataKindWebsite(Context context) {
+ protected DataKind addDataKindWebsite(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindWebsite(context);
kind.typeOverallMax = 1;
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
index 968993a..4eaa976 100644
--- a/src/com/android/contacts/model/ExternalAccountType.java
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -16,8 +16,6 @@
package com.android.contacts.model;
-import com.android.contacts.R;
-import com.android.contacts.model.BaseAccountType.DefinitionException;
import com.google.common.annotations.VisibleForTesting;
import org.xmlpull.v1.XmlPullParser;
@@ -88,30 +86,49 @@
private List<String> mExtensionPackageNames;
private String mAccountTypeLabelAttribute;
private String mAccountTypeIconAttribute;
- private boolean mInitSuccessful;
private boolean mHasContactsMetadata;
private boolean mHasEditSchema;
public ExternalAccountType(Context context, String resPackageName, boolean isExtension) {
+ this(context, resPackageName, isExtension, null);
+ }
+
+ /**
+ * Constructor used for testing to initialize with any arbitrary XML.
+ *
+ * @param injectedMetadata If non-null, it'll be used to initialize the type. Only set by
+ * tests. If null, the metadata is loaded from the specified package.
+ */
+ ExternalAccountType(Context context, String resPackageName, boolean isExtension,
+ XmlResourceParser injectedMetadata) {
this.mIsExtension = isExtension;
this.resPackageName = resPackageName;
this.summaryResPackageName = resPackageName;
- // Handle unknown sources by searching their package
final PackageManager pm = context.getPackageManager();
- XmlResourceParser parser = null;
+ final XmlResourceParser parser;
+ if (injectedMetadata == null) {
+ try {
+ parser = loadContactsXml(context, resPackageName);
+ } catch (NameNotFoundException e1) {
+ // If the package name is not found, we can't initialize this account type.
+ return;
+ }
+ } else {
+ parser = injectedMetadata;
+ }
try {
- PackageInfo packageInfo = pm.getPackageInfo(resPackageName,
- PackageManager.GET_SERVICES|PackageManager.GET_META_DATA);
- for (ServiceInfo serviceInfo : packageInfo.services) {
- parser = serviceInfo.loadXmlMetaData(pm,
- METADATA_CONTACTS);
- if (parser == null) continue;
+ if (parser != null) {
inflate(context, parser);
}
- } catch (NameNotFoundException nnfe) {
- // If the package name is not found, we can't initialize this account type.
- return;
+
+ if (!mHasEditSchema) {
+ // Bring in name and photo from fallback source, which are non-optional
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindPhoto(context);
+ }
} catch (DefinitionException e) {
String message = "Problem reading XML";
if (parser != null) {
@@ -135,16 +152,42 @@
iconRes = resolveExternalResId(context, mAccountTypeIconAttribute,
this.resPackageName, ATTR_ACCOUNT_ICON);
- if (!mHasEditSchema) {
- // Bring in name and photo from fallback source, which are non-optional
- addDataKindStructuredName(context);
- addDataKindDisplayName(context);
- addDataKindPhoneticName(context);
- addDataKindPhoto(context);
- }
-
// If we reach this point, the account type has been successfully initialized.
- mInitSuccessful = true;
+ mIsInitialized = true;
+ }
+
+ /**
+ * Returns the CONTACTS_STRUCTURE metadata (aka "contacts.xml") in the given apk package.
+ *
+ * Unfortunately, there's no public way to determine which service defines a sync service for
+ * which account type, so this method looks through all services in the package, and just
+ * returns the first CONTACTS_STRUCTURE metadata defined in any of them.
+ *
+ * Returns {@code null} if the package has no CONTACTS_STRUCTURE metadata. In this case
+ * the account type *will* be initialized with minimal configuration.
+ *
+ * On the other hand, if the package is not found, it throws a {@link NameNotFoundException},
+ * in which case the account type will *not* be initialized.
+ */
+ private XmlResourceParser loadContactsXml(Context context, String resPackageName)
+ throws NameNotFoundException {
+ final PackageManager pm = context.getPackageManager();
+ PackageInfo packageInfo = pm.getPackageInfo(resPackageName,
+ PackageManager.GET_SERVICES|PackageManager.GET_META_DATA);
+ for (ServiceInfo serviceInfo : packageInfo.services) {
+ final XmlResourceParser parser = serviceInfo.loadXmlMetaData(pm,
+ METADATA_CONTACTS);
+ if (parser != null) {
+ return parser;
+ }
+ }
+ // Package was found, but that doesn't contain the CONTACTS_STRUCTURE metadata.
+ return null;
+ }
+
+ @Override
+ public boolean isEmbedded() {
+ return false;
}
@Override
@@ -153,11 +196,6 @@
}
@Override
- public boolean isInitialized() {
- return mInitSuccessful;
- }
-
- @Override
public boolean areContactsWritable() {
return mHasEditSchema;
}
diff --git a/src/com/android/contacts/model/FallbackAccountType.java b/src/com/android/contacts/model/FallbackAccountType.java
index 216d6d0..d81f2f5 100644
--- a/src/com/android/contacts/model/FallbackAccountType.java
+++ b/src/com/android/contacts/model/FallbackAccountType.java
@@ -19,31 +19,52 @@
import com.android.contacts.R;
import android.content.Context;
+import android.util.Log;
public class FallbackAccountType extends BaseAccountType {
+ private static final String TAG = "FallbackAccountType";
- public FallbackAccountType(Context context) {
+ private FallbackAccountType(Context context, String resPackageName) {
this.accountType = null;
this.dataSet = null;
this.titleRes = R.string.account_phone;
this.iconRes = R.mipmap.ic_launcher_contacts;
- this.resPackageName = null;
+ this.resPackageName = resPackageName;
this.summaryResPackageName = resPackageName;
- addDataKindStructuredName(context);
- addDataKindDisplayName(context);
- addDataKindPhoneticName(context);
- addDataKindNickname(context);
- addDataKindPhone(context);
- addDataKindEmail(context);
- addDataKindStructuredPostal(context);
- addDataKindIm(context);
- addDataKindOrganization(context);
- addDataKindPhoto(context);
- addDataKindNote(context);
- addDataKindWebsite(context);
- addDataKindSipAddress(context);
+ try {
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindWebsite(context);
+ addDataKindSipAddress(context);
+
+ mIsInitialized = true;
+ } catch (DefinitionException e) {
+ Log.e(TAG, "Problem building account type", e);
+ }
+ }
+
+ public FallbackAccountType(Context context) {
+ this(context, null);
+ }
+
+ /**
+ * Used to compare with an {@link ExternalAccountType} built from a test contacts.xml.
+ * In order to build {@link DataKind}s with the same resource package name,
+ * {@code resPackageName} is injectable.
+ */
+ static AccountType createForTest(Context context, String resPackageName) {
+ return new FallbackAccountType(context, resPackageName);
}
@Override
diff --git a/src/com/android/contacts/model/GoogleAccountType.java b/src/com/android/contacts/model/GoogleAccountType.java
index c101602..822d829 100644
--- a/src/com/android/contacts/model/GoogleAccountType.java
+++ b/src/com/android/contacts/model/GoogleAccountType.java
@@ -17,6 +17,7 @@
package com.android.contacts.model;
import com.android.contacts.R;
+import com.android.contacts.model.AccountType.DefinitionException;
import com.android.contacts.util.DateUtils;
import com.google.android.collect.Lists;
@@ -26,10 +27,13 @@
import android.provider.ContactsContract.CommonDataKinds.Event;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.util.Log;
import java.util.List;
public class GoogleAccountType extends BaseAccountType {
+ private static final String TAG = "GoogleAccountType";
+
public static final String ACCOUNT_TYPE = "com.google";
private static final List<String> mExtensionPackages =
@@ -40,22 +44,28 @@
this.resPackageName = null;
this.summaryResPackageName = resPackageName;
- addDataKindStructuredName(context);
- addDataKindDisplayName(context);
- addDataKindPhoneticName(context);
- addDataKindNickname(context);
- addDataKindPhone(context);
- addDataKindEmail(context);
- addDataKindStructuredPostal(context);
- addDataKindIm(context);
- addDataKindOrganization(context);
- addDataKindPhoto(context);
- addDataKindNote(context);
- addDataKindWebsite(context);
- addDataKindSipAddress(context);
- addDataKindGroupMembership(context);
- addDataKindRelation(context);
- addDataKindEvent(context);
+ try {
+ addDataKindStructuredName(context);
+ addDataKindDisplayName(context);
+ addDataKindPhoneticName(context);
+ addDataKindNickname(context);
+ addDataKindPhone(context);
+ addDataKindEmail(context);
+ addDataKindStructuredPostal(context);
+ addDataKindIm(context);
+ addDataKindOrganization(context);
+ addDataKindPhoto(context);
+ addDataKindNote(context);
+ addDataKindWebsite(context);
+ addDataKindSipAddress(context);
+ addDataKindGroupMembership(context);
+ addDataKindRelation(context);
+ addDataKindEvent(context);
+
+ mIsInitialized = true;
+ } catch (DefinitionException e) {
+ Log.e(TAG, "Problem building account type", e);
+ }
}
@Override
@@ -64,7 +74,7 @@
}
@Override
- protected DataKind addDataKindPhone(Context context) {
+ protected DataKind addDataKindPhone(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindPhone(context);
kind.typeColumn = Phone.TYPE;
@@ -87,7 +97,7 @@
}
@Override
- protected DataKind addDataKindEmail(Context context) {
+ protected DataKind addDataKindEmail(Context context) throws DefinitionException {
final DataKind kind = super.addDataKindEmail(context);
kind.typeColumn = Email.TYPE;
@@ -104,7 +114,7 @@
return kind;
}
- private DataKind addDataKindRelation(Context context) {
+ private DataKind addDataKindRelation(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Relation.CONTENT_ITEM_TYPE,
R.string.relationLabelsGroup, 160, true, R.layout.text_fields_editor_view));
kind.actionHeader = new RelationActionInflater();
@@ -139,7 +149,7 @@
return kind;
}
- private DataKind addDataKindEvent(Context context) {
+ private DataKind addDataKindEvent(Context context) throws DefinitionException {
DataKind kind = addKind(new DataKind(Event.CONTENT_ITEM_TYPE,
R.string.eventLabelsGroup, 150, true, R.layout.event_field_editor_view));
kind.actionHeader = new EventActionInflater();
diff --git a/src/com/android/contacts/util/AccountFilterUtil.java b/src/com/android/contacts/util/AccountFilterUtil.java
new file mode 100644
index 0000000..955c195
--- /dev/null
+++ b/src/com/android/contacts/util/AccountFilterUtil.java
@@ -0,0 +1,165 @@
+/*
+ * 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.util;
+
+import com.android.contacts.R;
+import com.android.contacts.list.AccountFilterActivity;
+import com.android.contacts.list.ContactListFilter;
+import com.android.contacts.list.ContactListFilterController;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * Utility class for account filter manipulation.
+ */
+public class AccountFilterUtil {
+ private static final String TAG = AccountFilterUtil.class.getSimpleName();
+
+ /**
+ * Find TextView with the id "account_filter_header" and set correct text for the account
+ * filter header.
+ *
+ * @param filterContainer View containing TextView with id "account_filter_header"
+ * @return true when header text is set in the call. You may use this for conditionally
+ * showing or hiding this entire view.
+ */
+ public static boolean updateAccountFilterTitleForPeople(View filterContainer,
+ ContactListFilter filter, boolean isLoading, boolean showTitleForAllAccounts) {
+ return updateAccountFilterTitle(
+ filterContainer, filter, isLoading, showTitleForAllAccounts, false);
+ }
+
+ /**
+ * Similar to {@link #updateAccountFilterTitleForPeople(View, ContactListFilter, boolean,
+ * boolean)}, but for Phone UI.
+ */
+ public static boolean updateAccountFilterTitleForPhone(View filterContainer,
+ ContactListFilter filter, boolean isLoading, boolean showTitleForAllAccounts) {
+ return updateAccountFilterTitle(
+ filterContainer, filter, isLoading, showTitleForAllAccounts, true);
+ }
+
+ private static boolean updateAccountFilterTitle(View filterContainer,
+ ContactListFilter filter, boolean isLoading, boolean showTitleForAllAccounts,
+ boolean forPhone) {
+ final Context context = filterContainer.getContext();
+ final TextView headerTextView = (TextView)
+ filterContainer.findViewById(R.id.account_filter_header);
+
+ boolean textWasSet = false;
+ if (isLoading) {
+ headerTextView.setText(R.string.contact_list_loading);
+ } else if (filter != null) {
+ if (forPhone) {
+ if (filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
+ if (showTitleForAllAccounts) {
+ headerTextView.setText(R.string.list_filter_phones);
+ textWasSet = true;
+ }
+ } else if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ headerTextView.setText(context.getString(
+ R.string.listAllContactsInAccount, filter.accountName));
+ textWasSet = true;
+ } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
+ headerTextView.setText(R.string.listCustomView);
+ textWasSet = true;
+ } else {
+ Log.w(TAG, "Filter type \"" + filter.filterType + "\" isn't expected.");
+ }
+ } else {
+ if (filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
+ if (showTitleForAllAccounts) {
+ headerTextView.setText(R.string.list_filter_all_accounts);
+ textWasSet = true;
+ }
+ } else if (filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+ headerTextView.setText(context.getString(
+ R.string.listAllContactsInAccount, filter.accountName));
+ textWasSet = true;
+ } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
+ headerTextView.setText(R.string.listCustomView);
+ textWasSet = true;
+ } else if (filter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
+ headerTextView.setText(R.string.listSingleContact);
+ textWasSet = true;
+ } else {
+ Log.w(TAG, "Filter type \"" + filter.filterType + "\" isn't expected.");
+ }
+ }
+ } else {
+ Log.w(TAG, "Filter is null.");
+ }
+ return textWasSet;
+ }
+
+ /**
+ * Launches account filter setting Activity using
+ * {@link Activity#startActivityForResult(Intent, int)}.
+ *
+ * @param activity
+ * @param requestCode requestCode for {@link Activity#startActivityForResult(Intent, int)}
+ */
+ public static void startAccountFilterActivityForResult(
+ Activity activity, int requestCode) {
+ final Intent intent = new Intent(activity, AccountFilterActivity.class);
+ activity.startActivityForResult(intent, requestCode);
+ }
+
+ /**
+ * Very similar to {@link #startAccountFilterActivityForResult(Activity, int)} but uses
+ * Fragment instead.
+ */
+ public static void startAccountFilterActivityForResult(
+ Fragment fragment, int requestCode) {
+ final Activity activity = fragment.getActivity();
+ if (activity != null) {
+ final Intent intent = new Intent(activity, AccountFilterActivity.class);
+ fragment.startActivityForResult(intent, requestCode);
+ } else {
+ Log.w(TAG, "getActivity() returned null. Ignored");
+ }
+ }
+
+ /**
+ * Useful method to handle onActivityResult() for
+ * {@link #startAccountFilterActivityForResult(Activity, int)} or
+ * {@link #startAccountFilterActivityForResult(Fragment, int)}.
+ *
+ * This will update filter via a given ContactListFilterController.
+ */
+ public static void handleAccountFilterResult(
+ ContactListFilterController filterController, int resultCode, Intent data) {
+ if (resultCode == Activity.RESULT_OK) {
+ final ContactListFilter filter = (ContactListFilter)
+ data.getParcelableExtra(AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER);
+ if (filter == null) {
+ return;
+ }
+ if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
+ filterController.selectCustomFilter();
+ } else {
+ filterController.setContactListFilter(filter, true);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/res/xml/contacts_fallback.xml b/tests/res/xml/contacts_fallback.xml
new file mode 100644
index 0000000..ae262eb
--- /dev/null
+++ b/tests/res/xml/contacts_fallback.xml
@@ -0,0 +1,96 @@
+<?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.
+ */
+-->
+
+<!--
+ contacts.xml to build "fallback account type" equivalent.
+ This is directly used in ExternalAccountTypeTest to test the parser. There's no sync adapter
+ that actually defined with this definition.
+-->
+
+<ContactsAccountType
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ >
+ <EditSchema
+ >
+ <DataKind kind="name"
+ maxOccurs="1"
+ supportsDisplayName="true"
+ supportsPrefix="true"
+ supportsMiddleName="true"
+ supportsSuffix="true"
+ supportsPhoneticFamilyName="true"
+ supportsPhoneticMiddleName="true"
+ supportsPhoneticGivenName="true"
+ >
+ </DataKind>
+ <DataKind kind="photo" maxOccurs="1" />
+ <DataKind kind="phone" >
+ <Type type="mobile" />
+ <Type type="home" />
+ <Type type="work" />
+ <Type type="fax_work" />
+ <Type type="fax_home" />
+ <Type type="pager" />
+ <Type type="other" />
+ <Type type="custom"/>
+ <Type type="callback" />
+ <Type type="car" />
+ <Type type="company_main" />
+ <Type type="isdn" />
+ <Type type="main" />
+ <Type type="other_fax" />
+ <Type type="radio" />
+ <Type type="telex" />
+ <Type type="tty_tdd" />
+ <Type type="work_mobile"/>
+ <Type type="work_pager" />
+ <Type type="assistant" />
+ <Type type="mms" />
+ </DataKind>
+ <DataKind kind="email" >
+ <Type type="home" />
+ <Type type="work" />
+ <Type type="other" />
+ <Type type="mobile" />
+ <Type type="custom" />
+ </DataKind>
+ <DataKind kind="nickname" maxOccurs="1" />
+ <DataKind kind="im" >
+ <Type type="aim" />
+ <Type type="msn" />
+ <Type type="yahoo" />
+ <Type type="skype" />
+ <Type type="qq" />
+ <Type type="google_talk" />
+ <Type type="icq" />
+ <Type type="jabber" />
+ <Type type="custom" />
+ </DataKind>
+ <DataKind kind="postal" needsStructured="false" >
+ <Type type="home" />
+ <Type type="work" />
+ <Type type="other" />
+ <Type type="custom" />
+ </DataKind>
+ <DataKind kind="organization" maxOccurs="1" />
+ <DataKind kind="website" />
+ <DataKind kind="sip_address" maxOccurs="1" />
+ <DataKind kind="note" maxOccurs="1" />
+ </EditSchema>
+</ContactsAccountType>
diff --git a/tests/res/xml/test_basic_contacts.xml b/tests/res/xml/test_basic_contacts.xml
index ad82706..0047204 100644
--- a/tests/res/xml/test_basic_contacts.xml
+++ b/tests/res/xml/test_basic_contacts.xml
@@ -17,10 +17,6 @@
*/
-->
-<!--
- contacts.xml to build "fallback account type" equivalent.
--->
-
<ContactsAccountType
xmlns:android="http://schemas.android.com/apk/res/android"
>
@@ -98,8 +94,8 @@
<DataKind kind="phone" >
<!-- Note: Google type doesn't have obsolete ones -->
<Type type="mobile" />
- <Type type="work" />
<Type type="home" />
+ <Type type="work" />
<Type type="fax_work" />
<Type type="fax_home" />
<Type type="pager" />
@@ -139,9 +135,9 @@
<!--
Email
-->
- <!-- Fallback/ Google definition. -->
+ <!-- Fallback/Google definition. -->
<DataKind kind="email" >
- <!-- Note: Google type doesn't support some of these. -->
+ <!-- Note: Google type doesn't have obsolete ones -->
<Type type="home" />
<Type type="work" />
<Type type="other" />
@@ -248,17 +244,13 @@
<!--
Event
-
- The parser should be able to handle it, but not tested.
-->
- <!-- Google definition.
<DataKind kind="event" dateWithTime="false">
<Type type="birthday" maxOccurs="1" yearOptional="true" />
<Type type="anniversary" />
<Type type="other" />
<Type type="custom" />
</DataKind>
- -->
<!--
Exchange definition. dateWithTime is needed only for Exchange.
@@ -268,11 +260,9 @@
-->
<!--
- Relationship.
-
- The parser should be able to handle it, but not tested.
-
- <DataKind kind="relation" >
+ Relationship
+ -->
+ <DataKind kind="relationship" >
<Type type="assistant" />
<Type type="brother" />
<Type type="child" />
@@ -289,8 +279,5 @@
<Type type="spouse" />
<Type type="custom" />
</DataKind>
- -->
-
</EditSchema>
-
</ContactsAccountType>
diff --git a/tests/src/com/android/contacts/EntityModifierTests.java b/tests/src/com/android/contacts/EntityModifierTests.java
index 4db73b3..6872604 100644
--- a/tests/src/com/android/contacts/EntityModifierTests.java
+++ b/tests/src/com/android/contacts/EntityModifierTests.java
@@ -20,8 +20,6 @@
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.EditType;
import com.android.contacts.model.AccountTypeManager;
@@ -35,10 +33,10 @@
import com.android.contacts.tests.mocks.ContactsMockContext;
import com.android.contacts.tests.mocks.MockAccountTypeManager;
import com.android.contacts.tests.mocks.MockContentProvider;
+import com.google.android.collect.Lists;
import android.content.ContentProviderOperation;
import android.content.ContentValues;
-import android.content.Context;
import android.content.Entity;
import android.net.Uri;
import android.os.Bundle;
@@ -87,56 +85,60 @@
public static class MockContactsSource extends AccountType {
MockContactsSource() {
- this.accountType = TEST_ACCOUNT_TYPE;
+ try {
+ this.accountType = TEST_ACCOUNT_TYPE;
- final DataKind nameKind = new DataKind(StructuredName.CONTENT_ITEM_TYPE,
- R.string.nameLabelsGroup, -1, true, -1);
- nameKind.typeOverallMax = 1;
- addKind(nameKind);
+ final DataKind nameKind = new DataKind(StructuredName.CONTENT_ITEM_TYPE,
+ R.string.nameLabelsGroup, -1, true, -1);
+ nameKind.typeOverallMax = 1;
+ addKind(nameKind);
- // Phone allows maximum 2 home, 1 work, and unlimited other, with
- // constraint of 5 numbers maximum.
- final DataKind phoneKind = new DataKind(
- Phone.CONTENT_ITEM_TYPE, -1, 10, true, -1);
+ // Phone allows maximum 2 home, 1 work, and unlimited other, with
+ // constraint of 5 numbers maximum.
+ final DataKind phoneKind = new DataKind(
+ Phone.CONTENT_ITEM_TYPE, -1, 10, true, -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));
+ 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));
- phoneKind.fieldList = Lists.newArrayList();
- phoneKind.fieldList.add(new EditField(Phone.NUMBER, -1, -1));
- phoneKind.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(phoneKind);
+ addKind(phoneKind);
- // Email is unlimited
- final DataKind emailKind = new DataKind(
- Email.CONTENT_ITEM_TYPE, -1, 10, true, -1);
- emailKind.typeOverallMax = -1;
- emailKind.fieldList = Lists.newArrayList();
- emailKind.fieldList.add(new EditField(Email.DATA, -1, -1));
- addKind(emailKind);
+ // Email is unlimited
+ final DataKind emailKind = new DataKind(
+ Email.CONTENT_ITEM_TYPE, -1, 10, true, -1);
+ emailKind.typeOverallMax = -1;
+ emailKind.fieldList = Lists.newArrayList();
+ emailKind.fieldList.add(new EditField(Email.DATA, -1, -1));
+ addKind(emailKind);
- // IM is only one
- final DataKind imKind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, 10,
- true, -1);
- imKind.typeOverallMax = 1;
- imKind.fieldList = Lists.newArrayList();
- imKind.fieldList.add(new EditField(Im.DATA, -1, -1));
- addKind(imKind);
+ // IM is only one
+ final DataKind imKind = new DataKind(Im.CONTENT_ITEM_TYPE, -1, 10,
+ true, -1);
+ imKind.typeOverallMax = 1;
+ imKind.fieldList = Lists.newArrayList();
+ imKind.fieldList.add(new EditField(Im.DATA, -1, -1));
+ addKind(imKind);
- // Organization is only one
- final DataKind orgKind = new DataKind(
- Organization.CONTENT_ITEM_TYPE, -1, 10, true, -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);
+ // Organization is only one
+ final DataKind orgKind = new DataKind(
+ Organization.CONTENT_ITEM_TYPE, -1, 10, true, -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);
+ } catch (DefinitionException e) {
+ throw new RuntimeException(e);
+ }
}
@Override
diff --git a/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java b/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
index 15a1320..ba3d0eb 100644
--- a/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
+++ b/tests/src/com/android/contacts/model/ExternalAccountTypeTest.java
@@ -19,12 +19,15 @@
import com.android.contacts.tests.R;
import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.XmlResourceParser;
import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Event;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Note;
import android.provider.ContactsContract.CommonDataKinds.Organization;
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;
@@ -32,6 +35,10 @@
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import java.util.List;
+
+import libcore.util.Objects;
+
/**
* Test case for {@link ExternalAccountType}.
*
@@ -40,7 +47,6 @@
*/
@SmallTest
public class ExternalAccountTypeTest extends AndroidTestCase {
-
public void testResolveExternalResId() {
final Context c = getContext();
// In this test we use the test package itself as an external package.
@@ -63,12 +69,36 @@
"@string/test_string", packageName, ""));
}
+ /**
+ * Initialize with an invalid package name and see if type type will *not* be initialized.
+ */
+ public void testNoPackage() {
+ final ExternalAccountType type = new ExternalAccountType(getContext(),
+ "!!!no such package name!!!", false);
+ assertFalse(type.isInitialized());
+ }
+
+ /**
+ * Initialize with the name of an existing package, which has no contacts.xml metadata.
+ */
+ public void testNoMetadata() {
+ // Use the main application package, which does exist, but has no contacts.xml in it.
+ String packageName = getContext().getPackageName();
+ final ExternalAccountType type = new ExternalAccountType(getContext(),
+ packageName, false);
+ assertTrue(type.isInitialized());
+ }
+
+ /**
+ * Initialize with the test package itself and see if EditSchema is correctly parsed.
+ */
public void testEditSchema() {
final ExternalAccountType type = new ExternalAccountType(getContext(),
getTestContext().getPackageName(), false);
assertTrue(type.isInitialized());
+ // Let's just check if the DataKinds are registered.
assertNotNull(type.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME));
assertNotNull(type.getKindForMimetype(DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME));
@@ -80,8 +110,69 @@
assertNotNull(type.getKindForMimetype(Note.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(Website.CONTENT_ITEM_TYPE));
assertNotNull(type.getKindForMimetype(SipAddress.CONTENT_ITEM_TYPE));
- assertNotNull(type.getKindForMimetype(GroupMembership.CONTENT_ITEM_TYPE));
+ assertNotNull(type.getKindForMimetype(Event.CONTENT_ITEM_TYPE));
+ assertNotNull(type.getKindForMimetype(Relation.CONTENT_ITEM_TYPE));
+ }
- // TODO Write more extensive check -- compare to FallbackAccountType?
+ /**
+ * Initialize with "contacts_fallback.xml" and compare the DataKinds to those of
+ * {@link FallbackAccountType}.
+ */
+ public void testEditSchema_fallback() {
+ final ExternalAccountType type = new ExternalAccountType(getContext(),
+ getTestContext().getPackageName(), false,
+ getTestContext().getResources().getXml(R.xml.contacts_fallback)
+ );
+
+ assertTrue(type.isInitialized());
+
+ // Create a fallback type with the same resource package name, and compare all the data
+ // kinds to its.
+ final AccountType reference = FallbackAccountType.createForTest(
+ getContext(), type.resPackageName);
+
+ assertsDataKindEquals(reference.getSortedDataKinds(), type.getSortedDataKinds());
+ }
+
+ private static void assertsDataKindEquals(List<DataKind> expectedKinds,
+ List<DataKind> actualKinds) {
+ final int count = Math.max(actualKinds.size(), expectedKinds.size());
+ for (int i = 0; i < count; i++) {
+ String actual = actualKinds.size() > i ? actualKinds.get(i).toString() : "(n/a)";
+ String expected = expectedKinds.size() > i ? expectedKinds.get(i).toString() : "(n/a)";
+
+ // Because assertEquals()'s output is not very friendly when comparing two similar
+ // strings, we manually do the check.
+ if (!Objects.equal(actual, expected)) {
+ final int commonPrefixEnd = findCommonPrefixEnd(actual, expected);
+ fail("Kind #" + i
+ + "\n[Actual]\n" + insertMarkerAt(actual, commonPrefixEnd)
+ + "\n[Expected]\n" + insertMarkerAt(expected, commonPrefixEnd));
+ }
+ }
+ }
+
+ private static int findCommonPrefixEnd(String s1, String s2) {
+ int i = 0;
+ for (;;) {
+ final boolean s1End = (s1.length() <= i);
+ final boolean s2End = (s2.length() <= i);
+ if (s1End || s2End) {
+ return i;
+ }
+ if (s1.charAt(i) != s2.charAt(i)) {
+ return i;
+ }
+ i++;
+ }
+ }
+
+ private static String insertMarkerAt(String s, int position) {
+ final String MARKER = "***";
+ if (position > s.length()) {
+ return s + MARKER;
+ } else {
+ return new StringBuilder(s).insert(position, MARKER).toString();
+ }
}
}