Merge "Delay computing of PhoneNumber as much as possible."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d2cc492..61f0960 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -41,6 +41,8 @@
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="com.android.voicemail.permission.READ_WRITE_OWN_VOICEMAIL" />
<uses-permission android:name="com.android.voicemail.permission.READ_WRITE_ALL_VOICEMAIL" />
+ <!-- allow broadcasting secret code intents that reboot the phone -->
+ <uses-permission android:name="android.permission.REBOOT" />
<application
android:name="com.android.contacts.ContactsApplication"
@@ -255,7 +257,7 @@
android:resource="@xml/searchable"
/>
</activity>
-
+
<activity android:name=".activities.ContactSelectionActivity"
android:label="@string/contactsList"
android:theme="@style/ContactPickerTheme"
diff --git a/res/layout-sw580dp-w1000dp/group_detail_fragment.xml b/res/layout-sw580dp-w1000dp/group_detail_fragment.xml
new file mode 100644
index 0000000..11779cb
--- /dev/null
+++ b/res/layout-sw580dp-w1000dp/group_detail_fragment.xml
@@ -0,0 +1,84 @@
+<?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:paddingBottom="@dimen/group_detail_vertical_padding"
+ android:orientation="horizontal" >
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/group_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/group_detail_border_padding"
+ android:paddingTop="@dimen/group_detail_vertical_padding"
+ 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:paddingLeft="@dimen/group_detail_border_padding"
+ 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:layout_gravity="center_vertical"
+ android:paddingRight="@dimen/group_detail_border_padding" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_marginBottom="@dimen/group_detail_vertical_padding"
+ android:layout_marginLeft="@dimen/group_detail_divider_margin"
+ android:layout_marginRight="@dimen/group_detail_divider_margin" />
+
+ <!-- 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:divider="@null" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp/group_detail_fragment.xml b/res/layout-sw580dp/group_detail_fragment.xml
index 7c65036..a7db154 100644
--- a/res/layout-sw580dp/group_detail_fragment.xml
+++ b/res/layout-sw580dp/group_detail_fragment.xml
@@ -49,6 +49,13 @@
android:layout_marginLeft="@dimen/group_detail_divider_margin"
android:layout_marginRight="@dimen/group_detail_divider_margin" />
+ <FrameLayout
+ android:id="@+id/group_source_view_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingLeft="@dimen/group_detail_border_padding" />
+
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/res/layout-sw580dp/group_source_button.xml b/res/layout-sw580dp/group_source_button.xml
new file mode 100644
index 0000000..6b0b8fe
--- /dev/null
+++ b/res/layout-sw580dp/group_source_button.xml
@@ -0,0 +1,46 @@
+<?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 for the button that will launch the user into the source application
+ that the group came from (on a group detail page).
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/group_source"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:padding="10dip" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:duplicateParentState="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/view_updates_from_group"/>
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:duplicateParentState="true"
+ android:layout_width="30dip"
+ android:layout_height="30dip"
+ android:layout_marginLeft="7dip"
+ android:layout_gravity="center_vertical"/>
+
+</LinearLayout>
diff --git a/res/layout-w470dp/group_source_button.xml b/res/layout-w470dp/group_source_button.xml
new file mode 100644
index 0000000..b725512
--- /dev/null
+++ b/res/layout-w470dp/group_source_button.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ Layout for the button that will launch the user into the source application
+ that the group came from (on a group detail page). This will be used in the
+ action bar, so it has a vertical divider.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:divider="?android:attr/dividerVertical"
+ android:showDividers="end"
+ android:dividerPadding="12dip"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ style="?android:attr/actionButtonStyle"
+ android:id="@+id/group_source"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingRight="5dip"
+ android:orientation="horizontal">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/view_updates_from_group"/>
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="25dip"
+ android:layout_height="25dip"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:layout_centerVertical="true"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/call_detail.xml b/res/layout/call_detail.xml
index 1e40964..c20df49 100644
--- a/res/layout/call_detail.xml
+++ b/res/layout/call_detail.xml
@@ -36,12 +36,24 @@
android:src="@drawable/ic_call_log_home"
/>
</LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/voicemail_status"
+ android:layout_below="@id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:visibility="gone"
+ >
+ <include layout="@layout/call_log_voicemail_status"/>
+ </FrameLayout>
+
<ImageView
android:id="@+id/contact_background"
android:layout_width="match_parent"
android:layout_height="?attr/call_detail_contact_background_height"
android:layout_alignParentLeft="true"
- android:layout_below="@id/action_bar"
+ android:layout_below="@id/voicemail_status"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:background="@drawable/ic_contact_picture"
diff --git a/res/layout/call_log_fragment.xml b/res/layout/call_log_fragment.xml
index 573969b..2d0b9b5 100644
--- a/res/layout/call_log_fragment.xml
+++ b/res/layout/call_log_fragment.xml
@@ -19,7 +19,16 @@
android:layout_height="match_parent"
android:orientation="vertical"
>
- <include layout="@layout/call_log_voicemail_status"/>
+ <FrameLayout
+ android:id="@+id/voicemail_status"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:visibility="gone">
+ <include layout="@layout/call_log_voicemail_status"
+ />
+ </FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -29,7 +38,6 @@
android:layout_height="match_parent"
android:scrollbarStyle="outsideOverlay"
/>
-
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -39,4 +47,3 @@
/>
</FrameLayout>
</LinearLayout>
-
diff --git a/res/layout/call_log_voicemail_status.xml b/res/layout/call_log_voicemail_status.xml
index fee2210..c7def4d 100644
--- a/res/layout/call_log_voicemail_status.xml
+++ b/res/layout/call_log_voicemail_status.xml
@@ -13,41 +13,40 @@
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/voicemail_status"
- android:layout_width="match_parent"
- android:layout_height="?attr/call_log_voicemail_status_height"
- android:layout_alignParentLeft="true"
- android:layout_alignParentBottom="true"
- android:background="?attr/call_log_voicemail_status_background_color"
- android:baselineAligned="false"
- android:visibility="gone">
- <TextView
- android:id="@+id/voicemail_status_message"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="14dip"
- android:paddingRight="14dip"
- android:textColor="?attr/call_log_voicemail_status_text_color"
- android:layout_gravity="left"
- android:layout_weight="5"/>
- <View android:id="@+id/divider"
- android:layout_width="1px"
- android:layout_height="wrap_content"
- android:layout_marginTop="5dip"
- android:layout_marginBottom="5dip"
- android:layout_marginLeft="11dip"
- android:background="@drawable/divider_vertical_dark"/>
- <TextView
- android:id="@+id/voicemail_status_action"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:paddingLeft="14dip"
- android:paddingRight="14dip"
- android:textColor="?attr/call_log_voicemail_status_text_color"
- android:gravity="right"
- android:layout_alignParentRight="true"
- android:clickable="true"
- />
-</LinearLayout>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="?attr/call_log_voicemail_status_height"
+ android:background="?attr/call_log_voicemail_status_background_color">
+ <TextView
+ android:id="@+id/voicemail_status_message"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingLeft="14dip"
+ android:paddingRight="14dip"
+ android:textColor="?attr/call_log_voicemail_status_text_color"
+ android:layout_gravity="left"
+ android:layout_weight="5"
+ />
+ <View android:id="@+id/divider"
+ android:layout_width="1px"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dip"
+ android:layout_marginBottom="5dip"
+ android:layout_marginLeft="11dip"
+ android:background="@drawable/divider_vertical_dark"
+ />
+ <TextView
+ android:id="@+id/voicemail_status_action"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="right"
+ android:paddingLeft="14dip"
+ android:paddingRight="14dip"
+ android:textColor="?attr/call_log_voicemail_status_text_color"
+ android:gravity="right"
+ android:layout_alignParentRight="true"
+ android:clickable="true"
+ />
+ </LinearLayout>
+</merge>
diff --git a/res/layout/group_editor_fragment.xml b/res/layout/group_editor_fragment.xml
index dc82642..42321f4 100644
--- a/res/layout/group_editor_fragment.xml
+++ b/res/layout/group_editor_fragment.xml
@@ -48,6 +48,7 @@
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:inputType="textCapWords"
+ android:hint="@string/group_name_hint"
android:layout_marginBottom="@dimen/group_editor_padding"/>
<include
diff --git a/res/layout/group_editor_header.xml b/res/layout/group_editor_header.xml
index c6388fe..9b8bf88 100644
--- a/res/layout/group_editor_header.xml
+++ b/res/layout/group_editor_header.xml
@@ -38,6 +38,7 @@
android:id="@+id/group_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:hint="@string/group_name_hint"
android:textAppearance="?android:attr/textAppearanceLarge"
android:inputType="textCapWords"/>
diff --git a/res/layout/group_list_header_item.xml b/res/layout/group_list_header_item.xml
new file mode 100644
index 0000000..b38920f
--- /dev/null
+++ b/res/layout/group_list_header_item.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/group_list_header_padding"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/account_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@color/people_app_theme_color"
+ android:textStyle="bold"
+ android:singleLine="true"/>
+
+ <TextView
+ android:id="@+id/account_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/group_list_header_padding"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:singleLine="true"
+ android:textColor="@color/people_app_theme_color"/>
+
+ <TextView
+ android:id="@+id/group_count"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="right"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary"/>
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginTop="@dimen/group_list_header_padding"
+ android:background="@color/people_app_theme_color"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/group_source_button.xml b/res/layout/group_source_button.xml
new file mode 100644
index 0000000..0aa9c58
--- /dev/null
+++ b/res/layout/group_source_button.xml
@@ -0,0 +1,48 @@
+<?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 for the button that will launch the user into the source application
+ that the group came from (on a group detail page).
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/group_source"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:padding="10dip" >
+
+ <TextView
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:duplicateParentState="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/view_updates_from_group"/>
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="30dip"
+ android:layout_height="30dip"
+ android:duplicateParentState="true"
+ android:layout_marginLeft="7dip"
+ android:layout_marginRight="7dip"
+ android:layout_centerVertical="true"/>
+
+</LinearLayout>
diff --git a/res/menu-sw580dp-w720dp/view_group.xml b/res/menu-sw580dp-w720dp/view_group.xml
index 8b0867b..8f7b7d6 100644
--- a/res/menu-sw580dp-w720dp/view_group.xml
+++ b/res/menu-sw580dp-w720dp/view_group.xml
@@ -23,11 +23,6 @@
android:showAsAction="always" />
<item
- android:id="@+id/menu_rename_group"
- android:icon="@drawable/ic_menu_settings_holo_light"
- android:title="@string/menu_renameGroup" />
-
- <item
android:id="@+id/menu_delete_group"
android:icon="@drawable/ic_menu_trash_holo_light"
android:title="@string/menu_deleteGroup" />
diff --git a/res/menu/group_source.xml b/res/menu/group_source.xml
new file mode 100644
index 0000000..6d1af2d
--- /dev/null
+++ b/res/menu/group_source.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/menu_group_source"
+ android:showAsAction="always" />
+</menu>
diff --git a/res/values-sw580dp/donottranslate_config.xml b/res/values-sw580dp/donottranslate_config.xml
index 9841fd5..5c59f16 100644
--- a/res/values-sw580dp/donottranslate_config.xml
+++ b/res/values-sw580dp/donottranslate_config.xml
@@ -21,4 +21,5 @@
<bool name="config_use_two_panes">true</bool>
<bool name="always_show_search_view">true</bool>
<bool name="show_home_icon">true</bool>
+ <bool name="config_show_group_action_in_action_bar">false</bool>
</resources>
diff --git a/res/values-w470dp/donottranslate_config.xml b/res/values-w470dp/donottranslate_config.xml
new file mode 100644
index 0000000..462015a
--- /dev/null
+++ b/res/values-w470dp/donottranslate_config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+ <bool name="config_show_group_action_in_action_bar">true</bool>
+</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index c676f2d..7f4e237 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -60,6 +60,7 @@
<color name="call_log_missed_call_highlight_color">#FF0000</color>
<!-- Color of the text describing an unconsumed voicemail. -->
+
<color name="call_log_voicemail_highlight_color">#33b5e5</color>
<!-- Colour background for the voicemail playback ui. -->
@@ -73,4 +74,7 @@
<!-- Colour of text that appears on the voicemail ui. -->
<color name="voicemail_playback_ui_text">#cc696969</color>
+
+ <!-- Color of the theme of the People app -->
+ <color name="people_app_theme_color">#33B5E5</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3bc1bc6..0d868e9 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -155,6 +155,9 @@
<!-- Height of list sections (A, B, C) that show the first character of the contacts -->
<dimen name="list_section_height">25dip</dimen>
+ <!-- Border padding for the group list header for each account -->
+ <dimen name="group_list_header_padding">5dip</dimen>
+
<!-- Border padding for the group detail fragment header -->
<dimen name="group_detail_border_padding">20dip</dimen>
diff --git a/res/values/donottranslate_config.xml b/res/values/donottranslate_config.xml
index 3447392..0c719c4 100644
--- a/res/values/donottranslate_config.xml
+++ b/res/values/donottranslate_config.xml
@@ -110,4 +110,10 @@
<!-- If true, the "home" icon on the action bar will be shown. -->
<bool name="show_home_icon">false</bool>
+
+ <!--
+ If true, the "view updates from group" button in the action bar will be
+ shown. Otherwise it will be part of the content on the group detail page.
+ -->
+ <bool name="config_show_group_action_in_action_bar">false</bool>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 209aab7..ab1f86f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1614,6 +1614,9 @@
<!-- The title of the activity that edits an existing group [CHAR LIMIT=NONE] -->
<string name="editGroup_title_edit">Edit group</string>
+ <!-- Button to view the updates from the current group on the group detail page [CHAR LIMIT=20] -->
+ <string name="view_updates_from_group">View updates</string>
+
<!-- Title of the notification of new voicemail. -->
<string name="notification_voicemail_title">New voicemail</string>
@@ -1653,4 +1656,7 @@
<!-- The counter for calls in a group in the call log [CHAR LIMIT=5] -->
<string name="call_log_item_count">(%1$d)</string>
+
+ <!-- Hint text in the group name box in the edit group view. [CHAR LIMIT=20]-->
+ <string name="group_name_hint">Group\'s Name</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 3fa411c..9a73373 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -71,6 +71,10 @@
<item name="call_log_secondary_text_color">#FFFFFF</item>
<item name="call_log_secondary_background_color">#333333</item>
<item name="call_log_header_color">#33b5e5</item>
+ <!-- VoicemailStatus -->
+ <item name="call_log_voicemail_status_height">40dip</item>
+ <item name="call_log_voicemail_status_background_color">#FFFFE0</item>
+ <item name="call_log_voicemail_status_text_color">#000000</item>
</style>
<style name="ContactDetailActivityTheme" parent="android:Theme.Holo.Light">
diff --git a/src/com/android/contacts/ContactTileLoaderFactory.java b/src/com/android/contacts/ContactTileLoaderFactory.java
index dc4c935..20ad26e 100644
--- a/src/com/android/contacts/ContactTileLoaderFactory.java
+++ b/src/com/android/contacts/ContactTileLoaderFactory.java
@@ -19,6 +19,8 @@
import android.content.Context;
import android.content.CursorLoader;
+import android.net.Uri;
+import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
/**
@@ -44,6 +46,13 @@
return new CursorLoader(context, Contacts.CONTENT_STREQUENT_URI, COLUMNS, null, null, null);
}
+ public static CursorLoader createStrequentPhoneOnlyLoader(Context context) {
+ Uri uri = Contacts.CONTENT_STREQUENT_URI.buildUpon()
+ .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build();
+
+ return new CursorLoader(context, uri, COLUMNS, null, null, null);
+ }
+
public static CursorLoader createStarredLoader(Context context) {
return new CursorLoader(context, Contacts.CONTENT_URI, COLUMNS,
Contacts.STARRED + "=?", new String[]{"1"}, Contacts.DISPLAY_NAME + " ASC");
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index de262b2..14378d9 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -141,9 +141,14 @@
}
}
- ActionBar actionBar = getActionBar();
+ // We want the UP affordance but no app icon.
+ // Setting HOME_AS_UP, SHOW_TITLE and clearing SHOW_HOME does the trick.
+ ActionBar actionBar = getActionBar();
if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE,
+ ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE
+ | ActionBar.DISPLAY_SHOW_HOME);
+ actionBar.setTitle("");
}
Log.i(TAG, getIntent().getData().toString());
@@ -293,6 +298,7 @@
@Override
public void onEditRequested(Uri contactLookupUri) {
startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri));
+ finish();
}
@Override
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index 6655c81..28bc3a3 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -148,9 +148,11 @@
}
@Override
- public void onSaveFinished(int resultCode, Intent resultIntent, boolean navigateHome) {
- setResult(resultCode, resultIntent);
- if (navigateHome) {
+ public void onSaveFinished(Intent resultIntent) {
+ if (resultIntent != null) {
+ startActivity(resultIntent);
+ } else {
+ // Navigate home
Intent intent = new Intent(ContactEditorActivity.this, PeopleActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
@@ -165,7 +167,6 @@
@Override
public void onContactNotFound() {
- setResult(Activity.RESULT_CANCELED, null);
finish();
}
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 2209ab9..62b8321 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -334,7 +334,6 @@
} else if (fragment instanceof ContactTileListFragment) {
mStrequentFragment = (ContactTileListFragment) fragment;
mStrequentFragment.enableQuickContact(false);
- mStrequentFragment.enableSecondaryTarget(true);
mStrequentFragment.setListener(mStrequentListener);
} else if (fragment instanceof PhoneNumberPickerFragment) {
mSearchFragment = (PhoneNumberPickerFragment) fragment;
diff --git a/src/com/android/contacts/activities/GroupDetailActivity.java b/src/com/android/contacts/activities/GroupDetailActivity.java
index dcd35fa..9f6aa90 100644
--- a/src/com/android/contacts/activities/GroupDetailActivity.java
+++ b/src/com/android/contacts/activities/GroupDetailActivity.java
@@ -18,18 +18,30 @@
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
+import com.android.contacts.group.GroupDetailDisplayUtils;
import com.android.contacts.group.GroupDetailFragment;
import android.app.ActionBar;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.text.TextUtils;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
public class GroupDetailActivity extends ContactsActivity {
private static final String TAG = "GroupDetailActivity";
+ private boolean mShowGroupSourceInActionBar;
+
+ private String mAccountTypeString;
+ private String mGroupSourceAction;
+ private String mGroupSourceUri;
+
@Override
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
@@ -39,9 +51,13 @@
setContentView(R.layout.group_detail_activity);
+ mShowGroupSourceInActionBar = getResources().getBoolean(
+ R.bool.config_show_group_action_in_action_bar);
+
GroupDetailFragment fragment = (GroupDetailFragment) getFragmentManager().findFragmentById(
R.id.group_detail_fragment);
fragment.setListener(mFragmentListener);
+ fragment.setShowGroupSourceInActionBar(mShowGroupSourceInActionBar);
fragment.loadGroup(getIntent().getData());
fragment.closeActivityAfterDelete(true);
@@ -65,6 +81,15 @@
}
@Override
+ public void onGroupSourceUpdated(
+ String accountTypeString, String groupSourceAction, String groupSourceActionUri) {
+ mAccountTypeString = accountTypeString;
+ mGroupSourceAction = groupSourceAction;
+ mGroupSourceUri = groupSourceActionUri;
+ invalidateOptionsMenu();
+ }
+
+ @Override
public void onEditRequested(Uri groupUri) {
final Intent intent = new Intent(GroupDetailActivity.this, GroupEditorActivity.class);
intent.setData(groupUri);
@@ -76,11 +101,49 @@
public void onContactSelected(Uri contactUri) {
startActivity(new Intent(Intent.ACTION_VIEW, contactUri));
}
+
};
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ if (mShowGroupSourceInActionBar) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.group_source, menu);
+ }
+ return true;
+ }
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ if (!mShowGroupSourceInActionBar) {
+ return false;
+ }
+ MenuItem groupSourceMenuItem = menu.findItem(R.id.menu_group_source);
+ if (groupSourceMenuItem == null) {
+ return false;
+ }
+ if (TextUtils.isEmpty(mAccountTypeString) || TextUtils.isEmpty(mGroupSourceAction) ||
+ TextUtils.isEmpty(mGroupSourceUri)) {
+ groupSourceMenuItem.setVisible(false);
+ return false;
+ }
+ View groupSourceView = GroupDetailDisplayUtils.getNewGroupSourceView(this);
+ GroupDetailDisplayUtils.bindGroupSourceView(this, groupSourceView,
+ mAccountTypeString);
+ groupSourceView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startActivity(new Intent(mGroupSourceAction, Uri.parse(mGroupSourceUri)));
+ }
+ });
+ groupSourceMenuItem.setActionView(groupSourceView);
+ groupSourceMenuItem.setVisible(true);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 5d60f82..0b403cb 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -106,12 +106,10 @@
private static final String TAG = "PeopleActivity";
private static final Boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE
- private static final int SUBACTIVITY_NEW_CONTACT = 2;
- private static final int SUBACTIVITY_EDIT_CONTACT = 3;
- private static final int SUBACTIVITY_NEW_GROUP = 4;
- private static final int SUBACTIVITY_EDIT_GROUP = 5;
- private static final int SUBACTIVITY_ACCOUNT_FILTER = 6;
- private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 7;
+ 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 static final int SUBACTIVITY_CUSTOMIZE_FILTER = 5;
private static final String KEY_SEARCH_MODE = "searchMode";
@@ -361,11 +359,6 @@
if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
// Prepare 2-pane only fragments/views...
- // If setting 1-pane properties, make sure the fragment is created first
- // by moving code after call to executePendingTransactions
- mFavoritesFragment.setDisplayType(DisplayType.STARRED_ONLY);
- mFavoritesFragment.enableQuickContact(true);
-
// Container views for fragments
mFavoritesView = getView(R.id.favorites_view);
mDetailsView = getView(R.id.details_view);
@@ -398,6 +391,14 @@
transaction.commit();
fragmentManager.executePendingTransactions();
+ // Setting Properties after fragment is created
+ if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
+ mFavoritesFragment.enableQuickContact(true);
+ mFavoritesFragment.setDisplayType(DisplayType.STARRED_ONLY);
+ } else {
+ mFavoritesFragment.setDisplayType(DisplayType.STREQUENT);
+ }
+
// Configure action bar
mActionBarAdapter = new ActionBarAdapter(this, this, getActionBar());
mActionBarAdapter.initialize(savedState, mRequest);
@@ -539,7 +540,8 @@
public void onAction(Action action) {
switch (action) {
case START_SEARCH_MODE:
- clearSearch();
+ // Tell the fragments that we're in the search mode
+ configureFragments(false /* from request */);
updateFragmentsVisibility();
invalidateOptionsMenu();
break;
@@ -922,7 +924,7 @@
if (extras != null) {
intent.putExtras(extras);
}
- startActivityForResult(intent, SUBACTIVITY_EDIT_CONTACT);
+ startActivity(intent);
}
@Override
@@ -1008,8 +1010,7 @@
@Override
public void onEditRequested(Uri contactLookupUri) {
- startActivityForResult(
- new Intent(Intent.ACTION_EDIT, contactLookupUri), SUBACTIVITY_EDIT_CONTACT);
+ startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri));
}
@Override
@@ -1107,6 +1108,13 @@
}
@Override
+ public void onGroupSourceUpdated(
+ String accountTypeString, String groupSourceAction, String groupSourceUri) {
+ // Nothing needs to be done here because the group source will be displayed in the
+ // detail fragment
+ }
+
+ @Override
public void onEditRequested(Uri groupUri) {
final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class);
intent.setData(groupUri);
@@ -1286,7 +1294,7 @@
}
case R.id.menu_add_contact: {
final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
- startActivityForResult(intent, SUBACTIVITY_NEW_CONTACT);
+ startActivity(intent);
return true;
}
case R.id.menu_add_group: {
@@ -1376,14 +1384,6 @@
}
break;
}
- case SUBACTIVITY_EDIT_CONTACT:
- case SUBACTIVITY_NEW_CONTACT: {
- if (resultCode == RESULT_OK && PhoneCapabilityTester.isUsingTwoPanes(this)) {
- mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
- mAllFragment.reloadDataAndSetSelectedUri(data.getData());
- }
- break;
- }
case SUBACTIVITY_NEW_GROUP:
case SUBACTIVITY_EDIT_GROUP: {
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index c04e62b..32cc412 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -113,6 +113,13 @@
/** The value of the "section" column for the items of the old section. */
public static final int SECTION_OLD_ITEM = 3;
+ /** The call log projection including the section name. */
+ public static final String[] EXTENDED_PROJECTION;
+ static {
+ EXTENDED_PROJECTION = new String[_PROJECTION.length + 1];
+ System.arraycopy(_PROJECTION, 0, EXTENDED_PROJECTION, 0, _PROJECTION.length);
+ EXTENDED_PROJECTION[_PROJECTION.length] = SECTION_NAME;
+ }
public static boolean isSectionHeader(Cursor cursor) {
int section = cursor.getInt(CallLogQuery.SECTION);
@@ -176,9 +183,14 @@
public static ContactInfo EMPTY = new ContactInfo();
}
+ public interface GroupCreator {
+ public void addGroup(int cursorPosition, int size, boolean expanded);
+ }
+
/** Adapter class to fill in data for the Call Log */
public final class CallLogAdapter extends GroupingListAdapter
- implements Runnable, ViewTreeObserver.OnPreDrawListener, View.OnClickListener {
+ implements Runnable, ViewTreeObserver.OnPreDrawListener, View.OnClickListener,
+ GroupCreator {
/** The time in millis to delay starting the thread processing requests. */
private static final int START_PROCESSING_REQUESTS_DELAY_MILLIS = 1000;
@@ -507,12 +519,6 @@
mCallLogGroupBuilder.addGroups(cursor);
}
- /** Expands visibility to this package. */
- @Override
- protected void addGroup(int cursorPosition, int size, boolean expanded) {
- super.addGroup(cursorPosition, size, expanded);
- }
-
@VisibleForTesting
@Override
public View newStandAloneView(Context context, ViewGroup parent) {
@@ -730,6 +736,11 @@
public void injectContactInfoForTest(String number, ContactInfo contactInfo) {
mContactInfoCache.put(number, contactInfo);
}
+
+ @Override
+ public void addGroup(int cursorPosition, int size, boolean expanded) {
+ super.addGroup(cursorPosition, size, expanded);
+ }
}
@Override
diff --git a/src/com/android/contacts/calllog/CallLogGroupBuilder.java b/src/com/android/contacts/calllog/CallLogGroupBuilder.java
index b12fe19..95d85da 100644
--- a/src/com/android/contacts/calllog/CallLogGroupBuilder.java
+++ b/src/com/android/contacts/calllog/CallLogGroupBuilder.java
@@ -32,9 +32,9 @@
/** Reusable char array buffer. */
private CharArrayBuffer mBuffer2 = new CharArrayBuffer(128);
- private final CallLogFragment.CallLogAdapter mAdapter;
+ private final CallLogFragment.GroupCreator mAdapter;
- public CallLogGroupBuilder(CallLogFragment.CallLogAdapter adapter) {
+ public CallLogGroupBuilder(CallLogFragment.GroupCreator adapter) {
mAdapter = adapter;
}
diff --git a/src/com/android/contacts/calllog/CallLogQueryHandler.java b/src/com/android/contacts/calllog/CallLogQueryHandler.java
index a56f778..62e308c 100644
--- a/src/com/android/contacts/calllog/CallLogQueryHandler.java
+++ b/src/com/android/contacts/calllog/CallLogQueryHandler.java
@@ -95,18 +95,10 @@
mFragment = new WeakReference<CallLogFragment>(fragment);
}
- /** Returns the list of columns for the headers. */
- private String[] getHeaderColumns() {
- int length = CallLogQuery._PROJECTION.length;
- String[] columns = new String[length + 1];
- System.arraycopy(CallLogQuery._PROJECTION, 0, columns, 0, length);
- columns[length] = CallLogQuery.SECTION_NAME;
- return columns;
- }
-
/** Creates a cursor that contains a single row and maps the section to the given value. */
private Cursor createHeaderCursorFor(int section) {
- MatrixCursor matrixCursor = new MatrixCursor(getHeaderColumns());
+ MatrixCursor matrixCursor =
+ new MatrixCursor(CallLogFragment.CallLogQuery.EXTENDED_PROJECTION);
// The values in this row correspond to default values for _PROJECTION from CallLogQuery
// plus the section value.
matrixCursor.addRow(new Object[]{ -1L, "", 0L, 0L, 0, "", "", section });
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 9b89f84..6afe8b9 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -970,7 +970,6 @@
case SaveMode.CLOSE:
case SaveMode.HOME:
final Intent resultIntent;
- final int resultCode;
if (success && contactLookupUri != null) {
final String requestAuthority =
mLookupUri == null ? null : mLookupUri.getAuthority();
@@ -978,6 +977,7 @@
final String legacyAuthority = "contacts";
resultIntent = new Intent();
+ resultIntent.setAction(Intent.ACTION_VIEW);
if (legacyAuthority.equals(requestAuthority)) {
// Build legacy Uri when requested by caller
final long contactId = ContentUris.parseId(Contacts.lookupContact(
@@ -991,15 +991,12 @@
resultIntent.setData(contactLookupUri);
}
- resultCode = Activity.RESULT_OK;
} else {
- resultCode = Activity.RESULT_CANCELED;
resultIntent = null;
}
// It is already saved, so prevent that it is saved again
mStatus = Status.CLOSING;
- if (mListener != null) mListener.onSaveFinished(resultCode, resultIntent,
- saveMode == SaveMode.HOME);
+ if (mListener != null) mListener.onSaveFinished(resultIntent);
break;
case SaveMode.RELOAD:
@@ -1101,7 +1098,7 @@
/**
* Contact was saved and the Fragment can now be closed safely.
*/
- void onSaveFinished(int resultCode, Intent resultIntent, boolean navigateHome);
+ void onSaveFinished(Intent resultIntent);
/**
* User decided to delete the contact.
diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java
index 29aa737..cc15213 100644
--- a/src/com/android/contacts/group/GroupBrowseListAdapter.java
+++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java
@@ -18,8 +18,10 @@
import com.android.contacts.GroupMetaData;
import com.android.contacts.R;
-import com.android.contacts.list.ContactListPinnedHeaderView;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+import android.accounts.Account;
import android.content.ContentUris;
import android.content.Context;
import android.net.Uri;
@@ -41,8 +43,9 @@
*/
public class GroupBrowseListAdapter extends BaseAdapter {
- private Context mContext;
- private LayoutInflater mLayoutInflater;
+ private final Context mContext;
+ private final LayoutInflater mLayoutInflater;
+ private final AccountTypeManager mAccountTypeManager;
private List<GroupListEntry> mGroupList = new ArrayList<GroupListEntry>();
private boolean mSelectionVisible;
@@ -54,14 +57,16 @@
private static final int VIEW_TYPE_COUNT = ViewType.values().length;
- public GroupBrowseListAdapter(Context context, Map<String, List<GroupMetaData>> groupMap) {
+ public GroupBrowseListAdapter(Context context, Map<Account, List<GroupMetaData>> groupMap) {
mContext = context;
mLayoutInflater = LayoutInflater.from(context);
- for (String accountName : groupMap.keySet()) {
- List<GroupMetaData> groupsListForAccount = groupMap.get(accountName);
+ mAccountTypeManager = AccountTypeManager.getInstance(mContext);
- // Add account name as header for section
- mGroupList.add(GroupListEntry.createEntryForHeader(accountName,
+ for (Account account : groupMap.keySet()) {
+ List<GroupMetaData> groupsListForAccount = groupMap.get(account);
+
+ // Add account name, type, and # of groups as header for section
+ mGroupList.add(GroupListEntry.createEntryForHeader(account.name, account.type,
groupsListForAccount.size()));
// Add groups within that account as subsequent list items.
@@ -151,15 +156,23 @@
}
private View getHeaderView(GroupListEntry entry, View convertView, ViewGroup parent) {
- ContactListPinnedHeaderView result = (ContactListPinnedHeaderView) (convertView == null ?
- new ContactListPinnedHeaderView(mContext, null) :
+ View result = (convertView == null ?
+ mLayoutInflater.inflate(R.layout.group_list_header_item, parent, false) :
convertView);
+
+ TextView accountTypeTextView = (TextView) result.findViewById(R.id.account_type);
+ AccountType accountType = mAccountTypeManager.getAccountType(entry.accountType);
+ accountTypeTextView.setText(accountType.getDisplayLabel(mContext).toString().toUpperCase());
+
+ TextView accountNameTextView = (TextView) result.findViewById(R.id.account_name);
+ accountNameTextView.setText(entry.accountName);
+
String groupCountString = mContext.getResources().getQuantityString(
R.plurals.num_groups_in_account, entry.count, entry.count);
- // TODO: Format this correctly by using 2 TextViews when the
- // ContactListPinnedHeaderView is refactored.
- result.setSectionHeader(entry.title + " " + groupCountString);
- return result;
+ TextView groupCountTextView = (TextView) result.findViewById(R.id.group_count);
+ groupCountTextView.setText(groupCountString);
+
+ return result;
}
private View getGroupListItemView(GroupListEntry entry, View convertView, ViewGroup parent) {
@@ -179,7 +192,8 @@
*/
public static class GroupListEntry {
public final ViewType type;
- public final String title;
+ public final String accountType;
+ public final String accountName;
public final int count;
public final GroupMetaData groupData;
/**
@@ -188,24 +202,27 @@
*/
public final long id;
- private GroupListEntry(ViewType entryType, String headerTitle, int headerGroupCount,
- GroupMetaData groupMetaData, long entryId) {
+ private GroupListEntry(ViewType entryType, String groupAccountName, String groupAccountType,
+ int headerGroupCount, GroupMetaData groupMetaData, long entryId) {
type = entryType;
- title = headerTitle;
+ accountName = groupAccountName;
+ accountType = groupAccountType;
count = headerGroupCount;
groupData = groupMetaData;
id = entryId;
}
- public static GroupListEntry createEntryForHeader(String headerTitle, int groupCount) {
- return new GroupListEntry(ViewType.HEADER, headerTitle, groupCount, null, -1);
+ public static GroupListEntry createEntryForHeader(String groupAccountName,
+ String groupAccountType, int groupCount) {
+ return new GroupListEntry(ViewType.HEADER, groupAccountName, groupAccountType,
+ groupCount, null, -1);
}
public static GroupListEntry createEntryForGroup(GroupMetaData groupMetaData) {
if (groupMetaData == null) {
throw new IllegalStateException("Cannot create list entry for a null group");
}
- return new GroupListEntry(ViewType.ITEM, null, 0, groupMetaData,
+ return new GroupListEntry(ViewType.ITEM, null, null, 0, groupMetaData,
groupMetaData.getGroupId());
}
}
diff --git a/src/com/android/contacts/group/GroupBrowseListFragment.java b/src/com/android/contacts/group/GroupBrowseListFragment.java
index 0b53acf..4e6bdbc 100644
--- a/src/com/android/contacts/group/GroupBrowseListFragment.java
+++ b/src/com/android/contacts/group/GroupBrowseListFragment.java
@@ -22,6 +22,7 @@
import com.android.contacts.group.GroupBrowseListAdapter.GroupListItem;
import com.android.contacts.widget.AutoScrollListView;
+import android.accounts.Account;
import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager;
@@ -82,12 +83,11 @@
private static final String EXTRA_KEY_GROUP_URI = "groups.groupUri";
/**
- * Map of account name to a list of {@link GroupMetaData} objects
+ * Map of {@link Account} to a list of {@link GroupMetaData} objects
* representing groups within that account.
- * TODO: Change account name string into a wrapper object that has
- * account name, type, and authority.
*/
- private Map<String, List<GroupMetaData>> mGroupMap = new HashMap<String, List<GroupMetaData>>();
+ private final Map<Account, List<GroupMetaData>> mGroupMap =
+ new HashMap<Account, List<GroupMetaData>>();
private View mRootView;
private AutoScrollListView mListView;
@@ -214,14 +214,15 @@
GroupMetaData newGroup = new GroupMetaData(accountName, accountType, groupId, title,
defaultGroup, favorites);
+ Account account = new Account(accountName, accountType);
- if (mGroupMap.containsKey(accountName)) {
- List<GroupMetaData> groups = mGroupMap.get(accountName);
+ if (mGroupMap.containsKey(account)) {
+ List<GroupMetaData> groups = mGroupMap.get(account);
groups.add(newGroup);
} else {
List<GroupMetaData> groups = new ArrayList<GroupMetaData>();
groups.add(newGroup);
- mGroupMap.put(accountName, groups);
+ mGroupMap.put(account, groups);
}
}
diff --git a/src/com/android/contacts/group/GroupDetailDisplayUtils.java b/src/com/android/contacts/group/GroupDetailDisplayUtils.java
new file mode 100644
index 0000000..56413b5
--- /dev/null
+++ b/src/com/android/contacts/group/GroupDetailDisplayUtils.java
@@ -0,0 +1,50 @@
+/*
+ * 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.group;
+
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+
+public class GroupDetailDisplayUtils {
+
+ private GroupDetailDisplayUtils() {
+ // Disallow explicit creation of this class.
+ }
+
+ public static View getNewGroupSourceView(Context context) {
+ LayoutInflater inflater = (LayoutInflater)context.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ return inflater.inflate(R.layout.group_source_button, null);
+ }
+
+ public static void bindGroupSourceView(Context context, View view, String accountTypeString) {
+ ImageView accountIcon = (ImageView) view.findViewById(android.R.id.icon);
+ if (accountIcon == null) {
+ throw new IllegalStateException("Group source view must contain view with id"
+ + "android.R.id.icon");
+ }
+ AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(context);
+ AccountType accountType = accountTypeManager.getAccountType(accountTypeString);
+ accountIcon.setImageDrawable(accountType.getDisplayIcon(context));
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index a2ab23f..576f6c1 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -32,17 +32,19 @@
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.CursorLoader;
+import android.content.Intent;
import android.content.Loader;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
-import android.util.Log;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
@@ -66,6 +68,12 @@
public void onGroupSizeUpdated(String size);
/**
+ * The group source (intent action and action URI) has been determined.
+ */
+ public void onGroupSourceUpdated(String accountTypeString, String groupSourceAction,
+ String groupSourceUri);
+
+ /**
* User decided to go to Edit-Mode
*/
public void onEditRequested(Uri groupUri);
@@ -84,6 +92,8 @@
private Context mContext;
private View mRootView;
+ private ViewGroup mGroupSourceViewContainer;
+ private View mGroupSourceView;
private TextView mGroupTitle;
private TextView mGroupSize;
private ListView mMemberListView;
@@ -98,8 +108,11 @@
private long mGroupId;
private String mGroupName;
private String mAccountTypeString;
+ private boolean mIsReadOnly;
+ private boolean mShowGroupActionInActionBar;
private boolean mOptionsMenuEditable;
+ private boolean mOptionsMenuGroupPresent;
private boolean mCloseActivityAfterDelete;
public GroupDetailFragment() {
@@ -132,6 +145,8 @@
mRootView = inflater.inflate(R.layout.group_detail_fragment, container, false);
mGroupTitle = (TextView) mRootView.findViewById(R.id.group_title);
mGroupSize = (TextView) mRootView.findViewById(R.id.group_size);
+ mGroupSourceViewContainer = (ViewGroup) mRootView.findViewById(
+ R.id.group_source_view_container);
mMemberListView = (ListView) mRootView.findViewById(android.R.id.list);
return mRootView;
@@ -169,6 +184,10 @@
mListener = value;
}
+ public void setShowGroupSourceInActionBar(boolean show) {
+ mShowGroupActionInActionBar = show;
+ }
+
/**
* Start the loader to retrieve the metadata for this group.
*/
@@ -254,12 +273,15 @@
mAccountTypeString = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
mGroupId = cursor.getLong(GroupMetaDataLoader.GROUP_ID);
mGroupName = cursor.getString(GroupMetaDataLoader.TITLE);
+ mIsReadOnly = cursor.getInt(GroupMetaDataLoader.IS_READ_ONLY) == 1;
updateTitle(mGroupName);
+ // Must call invalidate so that the option menu will get updated
+ getActivity().invalidateOptionsMenu ();
- // TODO: Replace by real button
- final String action = cursor.getString(GroupMetaDataLoader.ACTION);
- final String actionUri = cursor.getString(GroupMetaDataLoader.ACTION_URI);
- Log.d(TAG, "Group open action: " + action + ", uri: " + actionUri);
+ final String accountTypeString = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+ final String groupSourceAction = cursor.getString(GroupMetaDataLoader.ACTION);
+ final String groupSourceUri = cursor.getString(GroupMetaDataLoader.ACTION_URI);
+ updateGroupSouce(accountTypeString, groupSourceAction, groupSourceUri);
}
}
@@ -294,6 +316,56 @@
}
}
+ /**
+ * Once the account type, group source action, and group source URI have been determined
+ * (based on the result from the {@link Loader}), then we can display this to the user in 1 of
+ * 3 ways depending on screen size and orientation: either as a button in the action bar,
+ * a button in a static header on the page, or as a header that scrolls with the
+ * {@link ListView}.
+ */
+ private void updateGroupSouce(final String accountTypeString, final String groupSourceAction,
+ final String groupSourceUri) {
+
+ // If the group action should be shown in the action bar, then pass the data to the
+ // listener who will take care of setting up the view and click listener. There is nothing
+ // else to be done by this {@link Fragment}.
+ if (mShowGroupActionInActionBar) {
+ mListener.onGroupSourceUpdated(accountTypeString, groupSourceAction, groupSourceUri);
+ return;
+ }
+
+ // Otherwise, if the {@link Fragment} needs to create and setup the button, then first
+ // verify that there is a valid action.
+ if (!TextUtils.isEmpty(groupSourceAction) && !TextUtils.isEmpty(groupSourceUri)) {
+ if (mGroupSourceView == null) {
+ mGroupSourceView = GroupDetailDisplayUtils.getNewGroupSourceView(mContext);
+ // Figure out how to add the view to the fragment.
+ // If there is a static header with a container for the group source view, insert
+ // the view there.
+ if (mGroupSourceViewContainer != null) {
+ mGroupSourceViewContainer.addView(mGroupSourceView);
+ } else {
+ // Otherwise, display the group source as a scrolling header within the
+ // {@link ListView} of group members.
+ mMemberListView.addHeaderView(mGroupSourceView);
+ }
+ }
+
+ // Rebind the data since this action can change if the loader returns updated data
+ mGroupSourceView.setVisibility(View.VISIBLE);
+ GroupDetailDisplayUtils.bindGroupSourceView(mContext, mGroupSourceView,
+ accountTypeString);
+ mGroupSourceView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startActivity(new Intent(groupSourceAction, Uri.parse(groupSourceUri)));
+ }
+ });
+ } else if (mGroupSourceView != null) {
+ mGroupSourceView.setVisibility(View.GONE);
+ }
+ }
+
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
@@ -314,21 +386,27 @@
}
public boolean isOptionsMenuChanged() {
- return mOptionsMenuEditable != isGroupEditable();
+ return mOptionsMenuEditable != isGroupEditable() &&
+ mOptionsMenuGroupPresent != isGroupPresent();
}
public boolean isGroupEditable() {
- // TODO: This should check the group_is_read_only flag. Modify GroupMetaDataLoader.
- // Bug: 4601729.
+ return mGroupUri != null && !mIsReadOnly;
+ }
+
+ public boolean isGroupPresent() {
return mGroupUri != null;
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
mOptionsMenuEditable = isGroupEditable();
+ mOptionsMenuGroupPresent = isGroupPresent();
+ // Editing a group is always possible if a group is selected
+ // TODO: check for external group (member editable) buganizer #5049046
final MenuItem editMenu = menu.findItem(R.id.menu_edit_group);
- editMenu.setVisible(mOptionsMenuEditable);
+ editMenu.setVisible(mOptionsMenuGroupPresent);
final MenuItem deleteMenu = menu.findItem(R.id.menu_delete_group);
deleteMenu.setVisible(mOptionsMenuEditable);
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index 65b99cc..596cceb 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -61,7 +61,6 @@
private int mStarredIndex;
private boolean mIsQuickContactEnabled = false;
- private boolean mIsSecondaryTargetEnabled = false;
/**
* Configures the adapter to filter and display contacts using different view types.
@@ -69,11 +68,17 @@
*/
public enum DisplayType {
/**
- * Displays a mixed view type of starred without secondary target and frequent contacts
+ * Displays a mixed view type of starred and frequent contacts
*/
STREQUENT,
/**
+ * Displays a mixed view type of starred and frequent contacts based on phone data.
+ * Also includes secondary touch target.
+ */
+ STREQUENT_PHONE_ONLY,
+
+ /**
* Display only starred contacts
*/
STARRED_ONLY,
@@ -117,10 +122,6 @@
mIsQuickContactEnabled = enableQuickContact;
}
- public void enableSecondaryTarget(boolean enableSecondaryTarget) {
- mIsSecondaryTargetEnabled = enableSecondaryTarget;
- }
-
/**
* Sets the column indices for expected {@link Cursor}
* based on {@link DisplayType}.
@@ -161,18 +162,22 @@
/**
* Iterates over the {@link Cursor}
* Returns position of the first NON Starred Contact
- * Returns -1 if not {@link DisplayType#STREQUENT}
+ * Returns -1 if not {@link DisplayType#}
*/
private int getDividerPosition(Cursor cursor) {
- if (cursor == null || cursor.isClosed() || mDisplayType != DisplayType.STREQUENT) {
- return -1;
- }
- while (cursor.moveToNext()) {
- if (cursor.getInt(mStarredIndex) == 0) {
- return cursor.getPosition();
- }
- }
- return -1;
+ if (cursor == null || cursor.isClosed() || (mDisplayType != DisplayType.STREQUENT
+ && mDisplayType != DisplayType.STREQUENT_PHONE_ONLY)) {
+ return -1;
+ }
+ while (cursor.moveToNext()) {
+ if (cursor.getInt(mStarredIndex) == 0) {
+ return cursor.getPosition();
+ }
+ }
+
+ // There are not NON Starred contacts in cursor
+ // Set divider positon to end and add 1 to make sure it doesn't get drawn
+ return cursor.getCount() + 1;
}
private ContactEntry createContactEntryFromCursor(Cursor cursor, int position) {
@@ -205,6 +210,7 @@
case GROUP_MEMBERS:
return getRowCount(mContactCursor.getCount());
case STREQUENT:
+ case STREQUENT_PHONE_ONLY:
/*
* Takes numbers of rows the Starred Contacts Occupy
* Calculates the number of frequent contacts
@@ -248,6 +254,7 @@
}
break;
case STREQUENT:
+ case STREQUENT_PHONE_ONLY:
if (position < getRowCount(mDividerPosition)) {
for (int columnCounter = 0; columnCounter < mColumnCount &&
contactIndex != mDividerPosition; columnCounter++) {
@@ -283,7 +290,8 @@
@Override
public boolean areAllItemsEnabled() {
- return mDisplayType != DisplayType.STREQUENT;
+ return (mDisplayType != DisplayType.STREQUENT &&
+ mDisplayType != DisplayType.STREQUENT_PHONE_ONLY);
}
@Override
@@ -337,7 +345,8 @@
}
@Override
public int getViewTypeCount() {
- return mDisplayType == DisplayType.STREQUENT ? ViewTypes.COUNT : 1;
+ return (mDisplayType == DisplayType.STREQUENT ||
+ mDisplayType == DisplayType.STREQUENT_PHONE_ONLY) ? ViewTypes.COUNT : 1;
}
/**
@@ -353,13 +362,20 @@
switch (mDisplayType) {
case STREQUENT:
if (position < getRowCount(mDividerPosition)) {
- return (mIsSecondaryTargetEnabled ?
- ViewTypes.STARRED_WITH_SECONDARY_ACTION : ViewTypes.STARRED);
+ return ViewTypes.STARRED;
} else if (position == getRowCount(mDividerPosition)) {
return ViewTypes.DIVIDER;
} else {
return ViewTypes.FREQUENT;
}
+ case STREQUENT_PHONE_ONLY:
+ if (position < getRowCount(mDividerPosition)) {
+ return ViewTypes.STARRED_WITH_SECONDARY_ACTION;
+ } else if (position == getRowCount(mDividerPosition)) {
+ return ViewTypes.DIVIDER;
+ } else {
+ return ViewTypes.FREQUENT;
+ }
case STARRED_ONLY:
case GROUP_MEMBERS:
return ViewTypes.STARRED;
diff --git a/src/com/android/contacts/list/ContactTileListFragment.java b/src/com/android/contacts/list/ContactTileListFragment.java
index a36f358..f4bd1b5 100644
--- a/src/com/android/contacts/list/ContactTileListFragment.java
+++ b/src/com/android/contacts/list/ContactTileListFragment.java
@@ -50,7 +50,7 @@
private Listener mListener;
private ContactTileAdapter mAdapter;
private ListView mListView;
- private DisplayType mDisplayType = DisplayType.STREQUENT;
+ private DisplayType mDisplayType = DisplayType.STREQUENT_PHONE_ONLY;
@Override
public void onAttach(Activity activity) {
@@ -94,10 +94,6 @@
mAdapter.enableQuickContact(enableQuickContact);
}
- public void enableSecondaryTarget(boolean enableSecondaryTarget) {
- mAdapter.enableSecondaryTarget(enableSecondaryTarget);
- }
-
private final LoaderManager.LoaderCallbacks<Cursor> mContactTileLoaderListener =
new LoaderCallbacks<Cursor>() {
@@ -108,6 +104,8 @@
return ContactTileLoaderFactory.createStarredLoader(getActivity());
case STREQUENT:
return ContactTileLoaderFactory.createStrequentLoader(getActivity());
+ case STREQUENT_PHONE_ONLY:
+ return ContactTileLoaderFactory.createStrequentPhoneOnlyLoader(getActivity());
case FREQUENT_ONLY:
return ContactTileLoaderFactory.createFrequentLoader(getActivity());
default:
diff --git a/src/com/android/contacts/vcard/ImportProcessor.java b/src/com/android/contacts/vcard/ImportProcessor.java
index 1f5779a..2a5583d 100644
--- a/src/com/android/contacts/vcard/ImportProcessor.java
+++ b/src/com/android/contacts/vcard/ImportProcessor.java
@@ -249,7 +249,7 @@
// ImportVCardActivity and ImportVCardService).
Log.e(LOG_TAG, "Nested Exception is found.");
} catch (VCardNotSupportedException e) {
- Log.e(LOG_TAG, e.getMessage());
+ Log.e(LOG_TAG, e.toString());
} catch (VCardVersionException e) {
if (i == length - 1) {
Log.e(LOG_TAG, "Appropriate version for this vCard is not found.");
@@ -257,7 +257,7 @@
// We'll try the other (v30) version.
}
} catch (VCardException e) {
- Log.e(LOG_TAG, e.getMessage());
+ Log.e(LOG_TAG, e.toString());
} finally {
if (is != null) {
try {
diff --git a/tests/src/com/android/contacts/calllog/CallLogGroupBuilderTest.java b/tests/src/com/android/contacts/calllog/CallLogGroupBuilderTest.java
new file mode 100644
index 0000000..f8da9c8
--- /dev/null
+++ b/tests/src/com/android/contacts/calllog/CallLogGroupBuilderTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.calllog;
+
+import static com.google.android.collect.Lists.newArrayList;
+
+import com.android.contacts.calllog.CallLogFragment.CallLogQuery;
+
+import android.database.MatrixCursor;
+import android.provider.CallLog.Calls;
+import android.test.AndroidTestCase;
+
+import java.util.List;
+
+/**
+ * Unit tests for {@link CallLogGroupBuilder}
+ */
+public class CallLogGroupBuilderTest extends AndroidTestCase {
+ /** A phone number for testing. */
+ private static final String TEST_NUMBER1 = "14125551234";
+ /** A phone number for testing. */
+ private static final String TEST_NUMBER2 = "14125555555";
+
+ /** The object under test. */
+ private CallLogGroupBuilder mBuilder;
+ /** Records the created groups. */
+ private FakeGroupCreator mFakeGroupCreator;
+ /** Cursor to store the values. */
+ private MatrixCursor mCursor;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mFakeGroupCreator = new FakeGroupCreator();
+ mBuilder = new CallLogGroupBuilder(mFakeGroupCreator);
+ mCursor = new MatrixCursor(CallLogFragment.CallLogQuery.EXTENDED_PROJECTION);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mCursor = null;
+ mBuilder = null;
+ mFakeGroupCreator = null;
+ super.tearDown();
+ }
+
+ public void testAddGroups_NoCalls() {
+ mBuilder.addGroups(mCursor);
+ assertEquals(0, mFakeGroupCreator.groups.size());
+ }
+
+ public void testAddGroups_OneCall() {
+ addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
+ mBuilder.addGroups(mCursor);
+ assertEquals(0, mFakeGroupCreator.groups.size());
+ }
+
+ public void testAddGroups_TwoCallsNotMatching() {
+ addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
+ addOldCallLogEntry(TEST_NUMBER2, Calls.INCOMING_TYPE);
+ mBuilder.addGroups(mCursor);
+ assertEquals(0, mFakeGroupCreator.groups.size());
+ }
+
+ public void testAddGroups_ThreeCallsMatching() {
+ addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
+ addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
+ addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
+ mBuilder.addGroups(mCursor);
+ assertEquals(1, mFakeGroupCreator.groups.size());
+ assertGroupIs(0, 3, false, mFakeGroupCreator.groups.get(0));
+ }
+
+ public void testAddGroups_MatchingIncomingAndOutgoing() {
+ addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
+ addOldCallLogEntry(TEST_NUMBER1, Calls.OUTGOING_TYPE);
+ addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
+ mBuilder.addGroups(mCursor);
+ assertEquals(1, mFakeGroupCreator.groups.size());
+ assertGroupIs(0, 3, false, mFakeGroupCreator.groups.get(0));
+ }
+
+ public void testAddGroups_HeaderSplitsGroups() {
+ addNewCallLogHeader();
+ addNewCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
+ addNewCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
+ addOldCallLogHeader();
+ addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
+ addOldCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
+ mBuilder.addGroups(mCursor);
+ assertEquals(2, mFakeGroupCreator.groups.size());
+ assertGroupIs(1, 2, false, mFakeGroupCreator.groups.get(0));
+ assertGroupIs(4, 2, false, mFakeGroupCreator.groups.get(1));
+ }
+
+ private void addOldCallLogEntry(String number, int type) {
+ addCallLogEntry(number, type, CallLogQuery.SECTION_OLD_ITEM);
+ }
+
+ private void addNewCallLogEntry(String number, int type) {
+ addCallLogEntry(number, type, CallLogQuery.SECTION_NEW_ITEM);
+ }
+
+ /** Adds a call log entry with the given number and type to the cursor. */
+ private void addCallLogEntry(String number, int type, int section) {
+ if (section != CallLogQuery.SECTION_NEW_ITEM
+ && section != CallLogQuery.SECTION_OLD_ITEM) {
+ throw new IllegalArgumentException("not an item section: " + section);
+ }
+ mCursor.moveToNext();
+ mCursor.addRow(new Object[]{
+ mCursor.getPosition(), number, 0L, 0L, type, "", "", section
+ });
+ }
+
+ private void addOldCallLogHeader() {
+ addCallLogHeader(CallLogQuery.SECTION_OLD_HEADER);
+ }
+
+ private void addNewCallLogHeader() {
+ addCallLogHeader(CallLogQuery.SECTION_NEW_HEADER);
+ }
+
+ /** Adds a call log entry with a header to the cursor. */
+ private void addCallLogHeader(int section) {
+ if (section != CallLogQuery.SECTION_NEW_HEADER
+ && section != CallLogQuery.SECTION_OLD_HEADER) {
+ throw new IllegalArgumentException("not a header section: " + section);
+ }
+ mCursor.moveToNext();
+ mCursor.addRow(new Object[]{ mCursor.getPosition(), "", 0L, 0L, 0, "", "", section });
+ }
+
+ /** Asserts that the group matches the given values. */
+ private void assertGroupIs(int cursorPosition, int size, boolean expanded, GroupSpec group) {
+ assertEquals(cursorPosition, group.cursorPosition);
+ assertEquals(size, group.size);
+ assertEquals(expanded, group.expanded);
+ }
+
+ private static class GroupSpec {
+ public final int cursorPosition;
+ public final int size;
+ public final boolean expanded;
+
+ public GroupSpec(int cursorPosition, int size, boolean expanded) {
+ this.cursorPosition = cursorPosition;
+ this.size = size;
+ this.expanded = expanded;
+ }
+ }
+
+ private static class FakeGroupCreator implements CallLogFragment.GroupCreator {
+ public final List<GroupSpec> groups = newArrayList();
+ @Override
+ public void addGroup(int cursorPosition, int size, boolean expanded) {
+ groups.add(new GroupSpec(cursorPosition, size, expanded));
+ }
+ }
+}