Merge "Enhanced performance in ContactTileView due to alpha layer."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 56bf4e5..7fb7f37 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -298,6 +298,24 @@
android:targetActivity=".activities.PeopleActivity"
/>
+ <!-- Backwards compatibility: "Contacts" from Honeycomb -->
+ <activity-alias android:name=".activities.ContactsFrontDoor"
+ android:targetActivity=".activities.PeopleActivity"
+ android:exported="true"
+ />
+
+ <!-- Backwards compatibility: "Contacts" from Gingerbread and earlier -->
+ <activity-alias android:name="DialtactsContactsEntryActivity"
+ android:targetActivity=".activities.PeopleActivity"
+ android:exported="true"
+ />
+
+ <!-- Backwards compatibility: "Phone" from Gingerbread and earlier -->
+ <activity-alias android:name="DialtactsActivity"
+ android:targetActivity=".activities.DialtactsActivity"
+ android:exported="true"
+ />
+
<!-- An activity for joining contacts -->
<activity android:name=".activities.JoinContactActivity"
android:theme="@style/JoinContactActivityTheme"
@@ -315,11 +333,17 @@
android:label="@string/activity_title_settings"
android:theme="@style/ContactsPreferencesTheme" />
+ <!-- Used to filter contacts list by account -->
+ <activity
+ android:name=".list.AccountFilterActivity"
+ android:label="@string/activity_title_contacts_filter"
+ android:theme="@style/ContactListFilterTheme" />
+
<!-- Used to select display and sync groups -->
<activity
android:name=".list.CustomContactListFilterActivity"
android:label="@string/custom_list_filter"
- android:theme="@style/CustomContactListFilterTheme" />
+ android:theme="@style/ContactListFilterTheme" />
<activity
android:name=".activities.ShowOrCreateActivity"
diff --git a/res/drawable-hdpi/call_log_action_bar_bg.9.png b/res/drawable-hdpi/call_log_action_bar_bg.9.png
new file mode 100644
index 0000000..2e4d0ca
--- /dev/null
+++ b/res/drawable-hdpi/call_log_action_bar_bg.9.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_call_log_home.png b/res/drawable-hdpi/ic_call_log_home.png
new file mode 100644
index 0000000..c7c8f16
--- /dev/null
+++ b/res/drawable-hdpi/ic_call_log_home.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_call_log_list_incoming_call.png b/res/drawable-hdpi/ic_call_log_list_incoming_call.png
index 15b5e44..624a0c5 100644
--- a/res/drawable-hdpi/ic_call_log_list_incoming_call.png
+++ b/res/drawable-hdpi/ic_call_log_list_incoming_call.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_call_log_list_missed_call.png b/res/drawable-hdpi/ic_call_log_list_missed_call.png
index 8dcb279..bd572cd 100644
--- a/res/drawable-hdpi/ic_call_log_list_missed_call.png
+++ b/res/drawable-hdpi/ic_call_log_list_missed_call.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_call_log_list_outgoing_call.png b/res/drawable-hdpi/ic_call_log_list_outgoing_call.png
index 160c707..1a93a78 100644
--- a/res/drawable-hdpi/ic_call_log_list_outgoing_call.png
+++ b/res/drawable-hdpi/ic_call_log_list_outgoing_call.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_call_log_list_voicemail.png b/res/drawable-hdpi/ic_call_log_list_voicemail.png
index d2ee44f..0c2a6f4 100644
--- a/res/drawable-hdpi/ic_call_log_list_voicemail.png
+++ b/res/drawable-hdpi/ic_call_log_list_voicemail.png
Binary files differ
diff --git a/res/drawable-mdpi/call_log_action_bar_bg.9.png b/res/drawable-mdpi/call_log_action_bar_bg.9.png
new file mode 100644
index 0000000..2e4d0ca
--- /dev/null
+++ b/res/drawable-mdpi/call_log_action_bar_bg.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_call_log_home.png b/res/drawable-mdpi/ic_call_log_home.png
new file mode 100644
index 0000000..c7c8f16
--- /dev/null
+++ b/res/drawable-mdpi/ic_call_log_home.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_call_log_list_incoming_call.png b/res/drawable-mdpi/ic_call_log_list_incoming_call.png
index c7eec52..77be776 100644
--- a/res/drawable-mdpi/ic_call_log_list_incoming_call.png
+++ b/res/drawable-mdpi/ic_call_log_list_incoming_call.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_call_log_list_missed_call.png b/res/drawable-mdpi/ic_call_log_list_missed_call.png
index 1907a62..66c8d85 100644
--- a/res/drawable-mdpi/ic_call_log_list_missed_call.png
+++ b/res/drawable-mdpi/ic_call_log_list_missed_call.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_call_log_list_outgoing_call.png b/res/drawable-mdpi/ic_call_log_list_outgoing_call.png
index 041d086..a7f85b2 100644
--- a/res/drawable-mdpi/ic_call_log_list_outgoing_call.png
+++ b/res/drawable-mdpi/ic_call_log_list_outgoing_call.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_call_log_list_voicemail.png b/res/drawable-mdpi/ic_call_log_list_voicemail.png
index d2ee44f..6aec9ed 100644
--- a/res/drawable-mdpi/ic_call_log_list_voicemail.png
+++ b/res/drawable-mdpi/ic_call_log_list_voicemail.png
Binary files differ
diff --git a/res/layout-sw580dp-w1000dp/contact_detail_container.xml b/res/layout-sw580dp-w1000dp/contact_detail_container.xml
new file mode 100644
index 0000000..b6a6319
--- /dev/null
+++ b/res/layout-sw580dp-w1000dp/contact_detail_container.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.
+-->
+
+<!--
+ Two-column layout for a contact with social updates. If the contact does not
+ have social updates, then the second fragment view will just be hidden.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scrollbars="none"
+ android:orientation="horizontal">
+
+ <fragment class="com.android.contacts.detail.ContactDetailFragment"
+ android:id="@+id/about_fragment"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="3" />
+
+ <fragment class="com.android.contacts.detail.ContactDetailUpdatesFragment"
+ android:id="@+id/updates_fragment"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="2" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw580dp/item_kind_section.xml b/res/layout-sw580dp/item_kind_section.xml
index f0ae1dc..dd38e26 100644
--- a/res/layout-sw580dp/item_kind_section.xml
+++ b/res/layout-sw580dp/item_kind_section.xml
@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,11 +22,11 @@
android:layout_height="wrap_content"
android:paddingBottom="@dimen/editor_field_bottom_padding"
android:orientation="vertical">
-
- <View
+ <ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@drawable/divider_horizontal_light" />
+ android:scaleType="fitXY"
+ android:src="@drawable/divider_horizontal_light" />
<LinearLayout
android:id="@+id/kind_editors"
diff --git a/res/layout-sw580dp/people_activity.xml b/res/layout-sw580dp/people_activity.xml
index 6d702e7..a6d9a37 100644
--- a/res/layout-sw580dp/people_activity.xml
+++ b/res/layout-sw580dp/people_activity.xml
@@ -45,9 +45,9 @@
android:layout_marginLeft="40dip"
android:layout_marginTop="24dip" />
- <!-- Contacts -->
+ <!-- All -->
<fragment
- android:id="@+id/contacts_fragment"
+ android:id="@+id/all_fragment"
class="com.android.contacts.list.DefaultContactBrowseListFragment"
android:layout_height="0dip"
android:layout_width="match_parent"
@@ -79,16 +79,27 @@
ex:animationDuration="200"
android:visibility="gone">
- <fragment
- android:id="@+id/contact_detail_fragment"
- class="com.android.contacts.detail.ContactDetailFragment"
+ <!-- This layout includes all possible views needed for a contact detail page -->
+ <include
+ layout="@layout/contact_detail_container"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"/>
+
+ <!-- This invisible worker fragment loads the contact's details -->
+ <fragment
+ android:id="@+id/contact_detail_loader_fragment"
+ class="com.android.contacts.detail.ContactLoaderFragment"
+ android:layout_height="0dip"
+ android:layout_width="0dip"
+ android:visibility="gone"/>
+
+ <!-- This is the group detail page -->
<fragment
android:id="@+id/group_detail_fragment"
class="com.android.contacts.group.GroupDetailFragment"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ android:visibility="gone" />
</view>
<view
diff --git a/res/layout/call_detail.xml b/res/layout/call_detail.xml
index 83c8f1b..6a03493 100644
--- a/res/layout/call_detail.xml
+++ b/res/layout/call_detail.xml
@@ -19,100 +19,91 @@
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="top"
- android:paddingTop="?attr/call_detail_top_gap"
- android:background="?attr/call_detail_transparent_background"
>
+ <LinearLayout
+ android:id="@+id/action_bar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/call_detail_action_bar_height"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:orientation="horizontal"
+ android:background="@drawable/call_log_action_bar_bg"
+ >
+ <ImageView
+ android:id="@+id/action_bar_home"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:src="@drawable/ic_call_log_home"
+ />
+ </LinearLayout>
<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_alignParentTop="true"
+ android:layout_below="@id/action_bar"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:background="@drawable/ic_contact_picture"
/>
- <View
- android:id="@+id/contact_background_overlay"
+ <ListView
+ android:id="@android:id/list"
android:layout_width="match_parent"
- android:layout_height="?attr/call_detail_contact_background_height"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:background="?attr/call_detail_primary_background_color"
- android:alpha="?attr/call_detail_contact_background_overlay_alpha"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/contact_background"
+ android:background="?attr/call_log_primary_background_color"
/>
-
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="?attr/call_detail_contact_background_overlay_height"
+ android:background="#3F000000"
+ android:layout_alignParentLeft="true"
+ android:layout_above="@android:id/list"
+ />
<RelativeLayout
android:id="@+id/photo_panel"
android:layout_width="match_parent"
- android:layout_height="?attr/call_detail_contact_background_height"
+ android:layout_height="?attr/call_detail_contact_background_overlay_height"
android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:paddingBottom="10dp"
+ android:layout_above="@android:id/list"
+ android:paddingLeft="5dip"
>
- <ImageView
- android:id="@+id/contact_photo"
- android:layout_width="?attr/call_detail_contact_photo_size"
- android:layout_height="?attr/call_detail_contact_photo_size"
- android:layout_alignParentLeft="true"
- android:layout_alignParentBottom="true"
-
- android:background="@drawable/ic_contact_picture"
- />
<RelativeLayout
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
- android:layout_toRightOf="@id/contact_photo"
- android:layout_marginLeft="10dp"
>
<include layout="@layout/call_log_phone_call_details" />
</RelativeLayout>
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="?attr/call_detail_action_icon_size"
+ <ImageView
+ android:id="@+id/main_action"
+ android:layout_width="?attr/call_detail_action_icon_size"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:scaleType="center"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
- android:layout_marginBottom="10dp"
- >
- <ImageView
- android:id="@+id/call"
- android:layout_width="?attr/call_detail_action_icon_size"
- android:layout_height="?attr/call_detail_action_icon_size"
- android:layout_alignParentRight="true"
- android:gravity="center_vertical"
- android:src="@drawable/ic_call_log_list_action_call"
- android:scaleType="center"
- />
- <View
- android:id="@+id/divider"
- android:layout_width="1px"
- android:layout_height="wrap_content"
- android:layout_marginTop="5dip"
- android:layout_marginBottom="5dip"
- android:layout_toLeftOf="@id/call"
- android:layout_marginLeft="11dip"
- android:background="#FFFFFF"
- />
- </RelativeLayout>
+ />
</RelativeLayout>
<RelativeLayout
android:id="@+id/call_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
- android:layout_below="@id/photo_panel"
+ android:layout_below="@android:id/list"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:paddingLeft="80dp"
- android:background="#FFFFFF"
+ android:background="?attr/call_log_secondary_background_color"
>
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:textColor="?attr/call_log_secondary_text_color"
/>
<TextView
android:id="@+id/duration"
@@ -121,6 +112,7 @@
android:layout_alignParentLeft="true"
android:layout_below="@id/time"
android:layout_alignLeft="@id/time"
+ android:textColor="?attr/call_log_secondary_text_color"
/>
<ImageView
android:id="@+id/delete"
@@ -133,11 +125,4 @@
android:visibility="gone"
/>
</RelativeLayout>
- <ListView
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/call_panel"
- android:background="?attr/call_detail_secondary_background_color"
- />
</RelativeLayout>
diff --git a/res/layout/call_log_contact_photo.xml b/res/layout/call_log_contact_photo.xml
index 178c45b..552e9ca 100644
--- a/res/layout/call_log_contact_photo.xml
+++ b/res/layout/call_log_contact_photo.xml
@@ -2,9 +2,9 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<QuickContactBadge
android:id="@+id/contact_photo"
- android:layout_width="?attr/call_log_contact_photo_size"
- android:layout_height="?attr/call_log_contact_photo_size"
- android:layout_margin="?attr/call_log_contact_photo_margin"
+ android:layout_width="?attr/call_log_list_contact_photo_size"
+ android:layout_height="?attr/call_log_list_contact_photo_size"
+ android:layout_margin="?attr/call_log_list_contact_photo_margin"
android:layout_alignParentLeft="true"
android:layout_gravity="center_vertical"
/>
diff --git a/res/layout/call_log_list_group_item.xml b/res/layout/call_log_list_group_item.xml
index 352d7ec..57465a6 100644
--- a/res/layout/call_log_list_group_item.xml
+++ b/res/layout/call_log_list_group_item.xml
@@ -21,75 +21,5 @@
<include layout="@layout/call_log_contact_photo"/>
<include layout="@layout/call_log_action_group"/>
-
- <TextView android:id="@+id/date"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toLeftOf="@id/divider"
- android:layout_alignParentBottom="true"
- android:layout_marginBottom="8dip"
- android:layout_marginLeft="10dip"
-
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:singleLine="true"
- />
-
- <TextView android:id="@+id/label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentBottom="true"
- android:layout_marginLeft="86dip"
- android:layout_marginRight="5dip"
- android:layout_alignBaseline="@id/date"
-
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textStyle="bold"
- />
-
- <TextView android:id="@+id/number"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/label"
- android:layout_toLeftOf="@id/date"
- android:layout_alignBaseline="@id/label"
- android:layout_alignWithParentIfMissing="true"
-
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceSmall"
- />
-
- <TextView android:id="@+id/groupSize"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_toLeftOf="@id/divider"
- android:layout_above="@id/date"
- android:layout_alignWithParentIfMissing="true"
- android:layout_marginBottom="-10dip"
-
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:singleLine="true"
- android:gravity="center_vertical"
- />
-
- <TextView android:id="@+id/line1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:layout_toLeftOf="@+id/groupSize"
- android:layout_above="@id/date"
- android:layout_alignWithParentIfMissing="true"
- android:layout_marginLeft="86dip"
- android:layout_marginBottom="-10dip"
-
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:gravity="center_vertical"
- />
+ <include layout="@layout/call_log_list_item_layout" />
</RelativeLayout>
diff --git a/res/layout/call_log_list_item_layout.xml b/res/layout/call_log_list_item_layout.xml
index 2128a79..8ea2bf7 100644
--- a/res/layout/call_log_list_item_layout.xml
+++ b/res/layout/call_log_list_item_layout.xml
@@ -15,69 +15,12 @@
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
-
- <ImageView android:id="@+id/call_type_icon"
+ <RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_marginLeft="54dip"
- />
-
- <TextView android:id="@+id/date"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toLeftOf="@id/divider"
- android:layout_alignParentBottom="true"
- android:layout_marginBottom="8dip"
- android:layout_marginLeft="10dip"
-
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:singleLine="true"
- />
-
- <TextView android:id="@+id/label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentBottom="true"
- android:layout_marginLeft="86dip"
- android:layout_marginRight="5dip"
- android:layout_alignBaseline="@id/date"
-
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textStyle="bold"
- />
-
- <TextView android:id="@+id/number"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/label"
- android:layout_toLeftOf="@id/date"
- android:layout_alignBaseline="@id/label"
- android:layout_alignWithParentIfMissing="true"
-
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceSmall"
- />
-
- <TextView android:id="@+id/line1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
+ android:layout_toRightOf="@id/contact_photo"
android:layout_alignParentTop="true"
- android:layout_toLeftOf="@id/divider"
- android:layout_above="@id/date"
- android:layout_alignWithParentIfMissing="true"
- android:layout_marginLeft="86dip"
- android:layout_marginBottom="-10dip"
-
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:gravity="center_vertical"
- />
+ >
+ <include layout="@layout/call_log_phone_call_details"/>
+ </RelativeLayout>
</merge>
diff --git a/res/layout/call_log_phone_call_details.xml b/res/layout/call_log_phone_call_details.xml
index e58de21..f22efba 100644
--- a/res/layout/call_log_phone_call_details.xml
+++ b/res/layout/call_log_phone_call_details.xml
@@ -15,42 +15,62 @@
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <TextView
- android:id="@+id/number"
+ <LinearLayout
+ android:id="@+id/call_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?attr/call_detail_primary_text_color"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
- />
- <LinearLayout
- android:id="@+id/call_types"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?attr/call_detail_primary_text_color"
- android:layout_alignParentLeft="true"
- android:layout_above="@id/number"
- />
+ >
+ <LinearLayout
+ android:id="@+id/call_type_icons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+ <TextView
+ android:id="@+id/call_type_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?attr/call_log_primary_text_color"
+ />
+ <TextView
+ android:id="@+id/call_type_separator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="?attr/call_log_date_margin"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?attr/call_log_primary_text_color"
+ android:text="@string/call_log_type_date_separator"
+ />
+ </LinearLayout>
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?attr/call_detail_primary_text_color"
- android:layout_toRightOf="@id/call_types"
- android:layout_above="@id/number"
- android:layout_marginLeft="?attr/call_detail_date_margin"
+ android:textColor="?attr/call_log_primary_text_color"
+ android:layout_toRightOf="@id/call_type"
+ android:layout_marginLeft="?attr/call_log_date_margin"
+ android:layout_alignParentBottom="true"
+ />
+ <TextView
+ android:id="@+id/number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?attr/call_log_primary_text_color"
+ android:layout_alignParentLeft="true"
+ android:layout_above="@id/call_type"
/>
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?attr/call_detail_primary_text_color"
+ android:textColor="?attr/call_log_primary_text_color"
android:layout_alignParentLeft="true"
- android:layout_above="@id/call_types"
+ android:layout_above="@id/number"
android:paddingBottom="2dp"
/>
</merge>
diff --git a/res/layout/contact_detail_container.xml b/res/layout/contact_detail_container.xml
new file mode 100644
index 0000000..0b5b85a
--- /dev/null
+++ b/res/layout/contact_detail_container.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!--
+ Layout for the contact card page. If the contact has social updates, then
+ the ViewPager and ContactDetailTabCarousel are shown together. If there
+ aren't any social updates, then just the ContactDetailFragment will be
+ shown. We include all 3 views even though they are never shown
+ simultaneously because this layout is reused but set with different data
+ each time (i.e. on a tablet).
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/pager"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:paddingTop="20dip"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <include
+ android:id="@+id/tab_carousel"
+ layout="@layout/contact_detail_tab_carousel"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="20dip"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"/>
+
+ <fragment
+ android:id="@+id/contact_detail_fragment"
+ class="com.android.contacts.detail.ContactDetailFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/contact_list_filter.xml b/res/layout/contact_list_filter.xml
new file mode 100644
index 0000000..de05cf8
--- /dev/null
+++ b/res/layout/contact_list_filter.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:fillViewport="true">
+
+ <ListView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:background="?android:attr/dividerHorizontal" />
+</LinearLayout>
diff --git a/res/layout/contact_list_filter_phones_only.xml b/res/layout/contact_list_filter_phones_only.xml
deleted file mode 100644
index 35965da..0000000
--- a/res/layout/contact_list_filter_phones_only.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical"
- android:paddingRight="?android:attr/scrollbarSize"
->
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="14dip"
- android:layout_marginRight="6dip"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="6dip"
- android:layout_weight="1"
- >
-
- <TextView
- android:id="@android:id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceMedium"
- />
-
- <TextView
- android:id="@android:id/text2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/text1"
- android:layout_alignLeft="@android:id/text1"
- android:maxLines="2"
- android:textAppearance="?android:attr/textAppearanceSmall"
- />
-
- </RelativeLayout>
-
- <CheckBox
- android:id="@android:id/checkbox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="4dip"
- android:focusable="false"
- android:clickable="false"
- android:gravity="center_vertical"
- android:orientation="vertical" />
-
-</LinearLayout>
diff --git a/res/layout/contact_tile_single.xml b/res/layout/contact_tile_frequent.xml
similarity index 95%
rename from res/layout/contact_tile_single.xml
rename to res/layout/contact_tile_frequent.xml
index 7147f3c..4ba7396 100644
--- a/res/layout/contact_tile_single.xml
+++ b/res/layout/contact_tile_frequent.xml
@@ -36,8 +36,8 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:paddingTop="24dip" />
- <ImageView
- android:id="@+id/contact_tile_image"
+ <QuickContactBadge
+ android:id="@+id/contact_tile_quick"
android:layout_width="64dip"
android:layout_height="64dip"
android:scaleType="centerCrop" />
diff --git a/res/layout/contact_tile_square.xml b/res/layout/contact_tile_square.xml
deleted file mode 100644
index ca79cef..0000000
--- a/res/layout/contact_tile_square.xml
+++ /dev/null
@@ -1,57 +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.
--->
-<view
- xmlns:android="http://schemas.android.com/apk/res/android"
- class="com.android.contacts.list.ContactTileSquareView"
- android:focusable="true"
- android:padding="1px"
- android:background="@drawable/list_selector" >
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <ImageView
- android:id="@+id/contact_tile_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop" />
-
- <View
- android:id="@+id/contact_tile_background"
- android:layout_width="match_parent"
- android:layout_height="48dip"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:alpha="0.5"
- android:background="@android:color/black" />
-
- <TextView
- android:id="@+id/contact_tile_name"
- android:layout_width="match_parent"
- android:layout_height="48dip"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:gravity="center_vertical"
- android:paddingLeft="8dip"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@android:color/white"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- </RelativeLayout>
-
-</view>
diff --git a/res/layout/contact_tile_single.xml b/res/layout/contact_tile_starred.xml
similarity index 60%
copy from res/layout/contact_tile_single.xml
copy to res/layout/contact_tile_starred.xml
index 7147f3c..6cdf213 100644
--- a/res/layout/contact_tile_single.xml
+++ b/res/layout/contact_tile_starred.xml
@@ -15,33 +15,27 @@
-->
<view
xmlns:android="http://schemas.android.com/apk/res/android"
- class="com.android.contacts.list.ContactTileView"
- android:focusable="true"
- android:background="@drawable/list_selector"
- android:paddingRight="16dip"
- android:paddingLeft="16dip" >
+ class="com.android.contacts.list.ContactTileStarredView"
+ style="@style/ContactTileStarred" >
- <LinearLayout
+ <RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
- <TextView
- android:id="@+id/contact_tile_name"
- android:layout_width="0dip"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- android:textSize="18sp"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:paddingTop="24dip" />
-
<ImageView
android:id="@+id/contact_tile_image"
- android:layout_width="64dip"
- android:layout_height="64dip"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:scaleType="centerCrop" />
- </LinearLayout>
+ <View
+ android:id="@+id/contact_tile_background"
+ style="@style/ContactTileStarredTextBackground" />
+
+ <TextView
+ android:id="@+id/contact_tile_name"
+ style="@style/ContactTileStarredText" />
+
+ </RelativeLayout>
</view>
diff --git a/res/layout/contact_tile_starred_quick_contact.xml b/res/layout/contact_tile_starred_quick_contact.xml
new file mode 100644
index 0000000..241ab54
--- /dev/null
+++ b/res/layout/contact_tile_starred_quick_contact.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.
+-->
+<view
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.contacts.list.ContactTileStarredView"
+ style="@style/ContactTileStarred" >
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <QuickContactBadge
+ android:id="@+id/contact_tile_quick"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerCrop" />
+
+ <View
+ android:id="@+id/contact_tile_background"
+ style="@style/ContactTileStarredTextBackground" />
+
+ <TextView
+ android:id="@+id/contact_tile_name"
+ style="@style/ContactTileStarredText" />
+
+ </RelativeLayout>
+
+</view>
diff --git a/res/layout/contacts_list_content.xml b/res/layout/contacts_list_content.xml
index 6792f9c..b92929b 100644
--- a/res/layout/contacts_list_content.xml
+++ b/res/layout/contacts_list_content.xml
@@ -21,6 +21,19 @@
android:layout_height="match_parent"
android:orientation="vertical">
+ <TextView
+ android:id="@+id/account_filter_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:gravity="center"
+ android:padding="5dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@android:color/white"
+ android:background="@android:color/black"
+ android:visibility="gone" />
+
<view
class="com.android.contacts.list.ContactEntryListView"
android:id="@android:id/list"
diff --git a/res/layout/custom_action_bar.xml b/res/layout/custom_action_bar.xml
index 2357756..f749586 100644
--- a/res/layout/custom_action_bar.xml
+++ b/res/layout/custom_action_bar.xml
@@ -14,11 +14,20 @@
limitations under the License.
-->
+<!-- Dimensions are set at runtime in ActionBarAdapter -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_width="0dip"
+ android:layout_height="0dip" >
+ <!-- To prevent the search view from getting the initial focus. -->
+ <View
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:layout_width="1px"
+ android:layout_height="1px" >
+ <requestFocus />
+ </View>
<SearchView
android:id="@+id/search_view"
android:layout_width="match_parent"
diff --git a/res/layout/dialpad_fragment.xml b/res/layout/dialpad_fragment.xml
index aee2c25..9f8c0cf 100644
--- a/res/layout/dialpad_fragment.xml
+++ b/res/layout/dialpad_fragment.xml
@@ -22,20 +22,35 @@
android:layout_marginLeft="8dip"
android:layout_marginRight="8dip" >
- <!-- Text field above the keypad where the digits are displayed.
- It's type is set to NULL (to disable the IME keyboard) in the
- java code.
-
- Background drawable can be controlled programatically.
- -->
- <EditText android:id="@+id/digits"
+ <!-- Text field and possibly soft menu button above the keypad where
+ the digits are displayed. -->
+ <RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dialpad_digits_height"
android:layout_marginTop="4dip"
android:gravity="center"
- android:textAppearance="@style/DialtactsDigitsTextAppearance"
- android:background="@drawable/dialpad_background"
- android:textColor="?android:attr/textColorPrimary" />
+ android:background="@drawable/dialpad_background" >
+
+ <!-- Type of this EditText is set to NULL (to disable the IME keyboard)
+ in the java code.
+
+ Background drawable can be controlled programatically. -->
+ <EditText android:id="@+id/digits"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentLeft="true"
+ android:layout_toLeftOf="@+id/moreoverflow"
+ android:gravity="center"
+ android:textAppearance="@style/DialtactsDigitsTextAppearance"
+ android:textColor="?android:attr/textColorPrimary" />
+
+ <ImageButton android:id="@+id/overflow_menu"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentRight="true"
+ android:src="@drawable/ic_menu_overflow" />
+
+ </RelativeLayout>
<!-- Keypad section -->
<include layout="@layout/dialpad" />
diff --git a/res/layout/people_activity.xml b/res/layout/people_activity.xml
index bcf218b..11431b3 100644
--- a/res/layout/people_activity.xml
+++ b/res/layout/people_activity.xml
@@ -26,9 +26,9 @@
android:layout_height="match_parent"
android:layout_width="match_parent" />
- <!-- Contacts -->
+ <!-- All -->
<fragment
- android:id="@+id/contacts_fragment"
+ android:id="@+id/all_fragment"
class="com.android.contacts.list.DefaultContactBrowseListFragment"
android:layout_height="match_parent"
android:layout_width="match_parent"
diff --git a/res/menu-sw580dp-w720dp/actions.xml b/res/menu-sw580dp-w720dp/actions.xml
index 081065c..d87b091 100644
--- a/res/menu-sw580dp-w720dp/actions.xml
+++ b/res/menu-sw580dp-w720dp/actions.xml
@@ -15,11 +15,6 @@
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:id="@+id/menu_search"
- android:showAsAction="always"
- android:actionViewClass="android.widget.SearchView" />
-
- <item
android:id="@+id/menu_add_contact"
android:icon="@drawable/ic_menu_add_contact_holo_light"
android:title="@string/menu_new_contact_action_bar"
diff --git a/res/menu-sw580dp/actions.xml b/res/menu-sw580dp/actions.xml
index 1d955c1..fea883e 100644
--- a/res/menu-sw580dp/actions.xml
+++ b/res/menu-sw580dp/actions.xml
@@ -15,11 +15,6 @@
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:id="@+id/menu_search"
- android:showAsAction="always"
- android:actionViewClass="android.widget.SearchView" />
-
- <item
android:id="@+id/menu_add_contact"
android:icon="@drawable/ic_menu_add_contact_holo_light"
android:title="@string/menu_new_contact_action_bar"
diff --git a/res/menu/call_log_options.xml b/res/menu/call_log_options.xml
index 57d637e..1f8de57 100644
--- a/res/menu/call_log_options.xml
+++ b/res/menu/call_log_options.xml
@@ -19,4 +19,9 @@
android:icon="@android:drawable/ic_menu_close_clear_cancel"
android:title="@string/recentCalls_deleteAll"
android:showAsAction="withText" />
+
+ <item
+ android:id="@+id/menu_call_settings_call_log"
+ android:title="@string/call_settings"
+ android:showAsAction="withText" />
</menu>
diff --git a/res/menu/dialpad_options.xml b/res/menu/dialpad_options.xml
index 77da9cb..c736e34 100644
--- a/res/menu/dialpad_options.xml
+++ b/res/menu/dialpad_options.xml
@@ -30,4 +30,9 @@
android:icon="@drawable/ic_menu_wait"
android:title="@string/add_wait"
android:showAsAction="withText" />
+
+ <item
+ android:id="@+id/menu_call_settings_dialpad"
+ android:title="@string/call_settings"
+ android:showAsAction="withText" />
</menu>
diff --git a/res/menu/dialtacts_options.xml b/res/menu/dialtacts_options.xml
index a5ed153..aa3af3f 100644
--- a/res/menu/dialtacts_options.xml
+++ b/res/menu/dialtacts_options.xml
@@ -15,11 +15,6 @@
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:id="@+id/menu_call_settings"
- android:title="@string/call_settings"
- android:showAsAction="withText" />
-
- <item
android:id="@+id/search_on_action_bar"
android:icon="@android:drawable/ic_menu_search"
android:showAsAction="always" />
diff --git a/res/values-sw580dp-w1000dp/dimens.xml b/res/values-sw580dp-w1000dp/dimens.xml
index 45da0a8..dfa1b81 100644
--- a/res/values-sw580dp-w1000dp/dimens.xml
+++ b/res/values-sw580dp-w1000dp/dimens.xml
@@ -21,4 +21,5 @@
<dimen name="action_bar_search_spacing">32dip</dimen>
<dimen name="detail_header_view_margin">16dip</dimen>
<dimen name="detail_header_attribution_height">56dip</dimen>
+ <dimen name="detail_tab_carousel_height">0dip</dimen>
</resources>
diff --git a/res/values-sw580dp/dimens.xml b/res/values-sw580dp/dimens.xml
index f1e5736..bcaf1d2 100644
--- a/res/values-sw580dp/dimens.xml
+++ b/res/values-sw580dp/dimens.xml
@@ -32,5 +32,6 @@
<dimen name="shortcut_icon_size">64dip</dimen>
<dimen name="list_section_height">37dip</dimen>
<dimen name="directory_header_height">56dip</dimen>
- <dimen name="detail_tab_carousel_height">150dip</dimen>
+ <dimen name="detail_tab_carousel_height">256dip</dimen>
+ <dimen name="search_view_width">400dip</dimen>
</resources>
diff --git a/res/values-sw580dp/donottranslate_config.xml b/res/values-sw580dp/donottranslate_config.xml
index fcb7da9..5ca1af6 100644
--- a/res/values-sw580dp/donottranslate_config.xml
+++ b/res/values-sw580dp/donottranslate_config.xml
@@ -19,4 +19,5 @@
<resources>
<bool name="config_use_two_panes">true</bool>
+ <bool name="always_show_search_view">true</bool>
</resources>
diff --git a/res/values-sw580dp/styles.xml b/res/values-sw580dp/styles.xml
index 32d02be..4f2b073 100644
--- a/res/values-sw580dp/styles.xml
+++ b/res/values-sw580dp/styles.xml
@@ -60,10 +60,11 @@
<item name="list_item_header_text_size">14sp</item>
</style>
- <style name="CustomContactListFilterTheme" parent="@android:Theme.Holo.Light.Dialog">
+ <style name="ContactListFilterTheme" parent="@android:Theme.Holo.Light.Dialog">
+ <item name="android:windowCloseOnTouchOutside">true</item>
</style>
- <style name="CustomContactListFilterView" parent="CustomContactListFilterTheme">
+ <style name="CustomContactListFilterView" parent="ContactListFilterTheme">
<item name="android:layout_width">400dip</item>
<item name="android:layout_height">400dip</item>
</style>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a0c4845..948d357 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -148,7 +148,7 @@
<!-- Spacing on the left the search field in the action bar -->
<dimen name="action_bar_search_spacing">12dip</dimen>
- <!-- Size of the shortcut icon. 0dip means: use the system default -->
+ <!-- Size of the shortcut icon. 0dip means: use the system default -->
<dimen name="shortcut_icon_size">0dip</dimen>
<!-- Height of list sections (A, B, C) that show the first character of the contacts -->
@@ -181,4 +181,7 @@
<dimen name="dialpad_digits_margin_top">1dip</dimen>
<!-- Just used in landscape mode -->
<dimen name="dialpad_digits_margin_bottom">50dip</dimen>
+
+ <!-- Width of search view in action bar. Use 0dip for MATCH_PARENT -->
+ <dimen name="search_view_width">0dip</dimen>
</resources>
diff --git a/res/values/donottranslate_config.xml b/res/values/donottranslate_config.xml
index e310953..c778212 100644
--- a/res/values/donottranslate_config.xml
+++ b/res/values/donottranslate_config.xml
@@ -103,4 +103,8 @@
<!-- If true, Contacts uses two panes: List and Detail. If false, Details are
shown in their own screens. This flag must be in sync with the layout definitions. -->
<bool name="config_use_two_panes">false</bool>
+
+ <!-- If true, search view on action bar will always be visible. If false, it'll only be
+ visible in search mode. -->
+ <bool name="always_show_search_view">false</bool>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6e88440..9e2e085 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -296,12 +296,6 @@
<!-- 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 title of the filter to only show contacts with phone numbers -->
- <string name="showFilterPhones">Only contacts with phones</string>
-
- <!-- The description of the filter to only show contacts with phone numbers -->
- <string name="showFilterPhonesDescrip">Only display contacts that have phone numbers</string>
-
<!-- The header over the list of all contacts groups -->
<string name="headerContactGroups">Choose contacts to display</string>
@@ -366,6 +360,9 @@
<!-- Displayed at the top of the contacts showing the zero total number of contacts visible when a group or account is selected [CHAR LIMIT=64]-->
<string name="listTotalAllContactsZeroGroup">No contacts in <xliff:g id="name" example="Friends">%s</xliff:g></string>
+ <!-- Displayed at the top of the contacts showing the account filter selected [CHAR LIMIT=64] -->
+ <string name="listAllContactsInAccount">Contacts in <xliff:g id="name" example="abc@gmail.com">%s</xliff:g></string>
+
<!-- Displayed at the top of the contacts showing the total number of contacts found when "Only contacts with phones" not selected -->
<plurals name="listFoundAllContacts">
<item quantity="one">1 found</item>
@@ -384,8 +381,8 @@
<item quantity="other"><xliff:g id="count">%d</xliff:g> found</item>
</plurals>
- <!-- The description text for the contacts tab. Space is limited for this string, so the shorter the better -->
- <string name="contactsIconLabel">Contacts</string>
+ <!-- The description text for the "all contacts" tab. Space is limited for this string, so the shorter the better -->
+ <string name="contactsAllLabel">All</string>
<!-- The description text for the groups tab. Space is limited for this string, so the shorter the better -->
<string name="contactsGroupsLabel">Groups</string>
@@ -574,6 +571,9 @@
<!-- Title for missed call details screen -->
<string name="type_missed">Missed call</string>
+ <!-- Title for voicemail details screen -->
+ <string name="type_voicemail">Voicemail</string>
+
<!-- Description for incoming calls going to voice mail vs. not -->
<string name="actionIncomingCall">Incoming calls</string>
@@ -1490,6 +1490,9 @@
<!-- Title of the settings activity [CHAR LIMIT=64] -->
<string name="activity_title_settings">Settings</string>
+ <!-- Title of the activity that allows the uesr to filter the list of contacts displayed according to account [CHAR LIMIT=25] -->
+ <string name="activity_title_contacts_filter">Contacts to display</string>
+
<!-- Menu item for the settings activity [CHAR LIMIT=64] -->
<string name="menu_settings">Settings</string>
@@ -1601,4 +1604,7 @@
<!-- Title of the notification of new voicemail. -->
<string name="notification_voicemail_title">New voicemail</string>
+
+ <!-- The separator between the call type text and the date in the call log [CHAR LIMIT=3] -->
+ <string name="call_log_type_date_separator">/</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 0cf8e25..01f0e62 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -36,28 +36,33 @@
<item name="list_item_header_text_color">#ffcccccc</item>
<item name="list_item_header_text_size">14sp</item>
<item name="contact_filter_popup_width">320dip</item>
- <!-- CallLogActivity -->
- <item name="call_log_contact_photo_size">50dip</item>
- <item name="call_log_contact_photo_margin">5dip</item>
+ <!-- CallLogList -->
+ <item name="call_log_list_contact_photo_size">50dip</item>
+ <item name="call_log_list_contact_photo_margin">5dip</item>
<item name="call_log_list_item_height">60dip</item>
+ <!-- CallLog -->
+ <item name="call_log_date_margin">5dip</item>
+ <item name="call_log_primary_text_color">#FFFFFF</item>
+ <item name="call_log_primary_background_color">#000000</item>
+ <item name="call_log_secondary_text_color">#FFFFFF</item>
+ <item name="call_log_secondary_background_color">#333333</item>
</style>
- <style name="CallDetailActivityTheme" parent="android:Theme.Holo.Light">
- <item name="android:windowContentOverlay">@null</item>
- <item name="android:windowIsTranslucent">true</item>
+ <style name="CallDetailActivityTheme" parent="android:Theme.Holo">
<item name="android:windowNoTitle">true</item>
- <item name="android:windowBackground">@android:color/transparent</item>
<item name="android:gravity">top</item>
- <item name="call_detail_top_gap">49dip</item>
<item name="call_detail_transparent_background">#CC000000</item>
<item name="call_detail_contact_background_height">150dip</item>
+ <item name="call_detail_contact_background_overlay_height">70dip</item>
+ <item name="call_detail_action_bar_height">60dip</item>
+ <item name="call_detail_action_icon_size">60dip</item>
<item name="call_detail_contact_background_overlay_alpha">0.25</item>
- <item name="call_detail_primary_background_color">#000000</item>
- <item name="call_detail_contact_photo_size">60dip</item>
- <item name="call_detail_primary_text_color">#FFFFFF</item>
- <item name="call_detail_action_icon_size">50dip</item>
- <item name="call_detail_secondary_background_color">#FFFFFF</item>
- <item name="call_detail_date_margin">5dip</item>
+ <!-- CallLog -->
+ <item name="call_log_date_margin">5dip</item>
+ <item name="call_log_primary_text_color">#FFFFFF</item>
+ <item name="call_log_primary_background_color">#000000</item>
+ <item name="call_log_secondary_text_color">#FFFFFF</item>
+ <item name="call_log_secondary_background_color">#333333</item>
</style>
<style name="ContactDetailActivityTheme" parent="android:Theme.Holo.Light">
@@ -133,21 +138,26 @@
</declare-styleable>
<declare-styleable name="CallDetailActivity">
- <attr name="call_detail_top_gap" format="dimension" />
<attr name="call_detail_transparent_background" format="color" />
<attr name="call_detail_contact_background_height" format="dimension" />
+ <attr name="call_detail_contact_background_overlay_height" format="dimension" />
<attr name="call_detail_contact_background_overlay_alpha" format="float" />
<attr name="call_detail_contact_photo_size" format="dimension" />
<attr name="call_detail_action_icon_size" format="dimension" />
- <attr name="call_detail_primary_text_color" format="color" />
- <attr name="call_detail_primary_background_color" format="color" />
- <attr name="call_detail_secondary_background_color" format="color" />
- <attr name="call_detail_date_margin" format="dimension" />
+ <attr name="call_detail_action_bar_height" format="dimension" />
</declare-styleable>
- <declare-styleable name="CallLogActivity">
- <attr name="call_log_contact_photo_size" format="dimension" />
- <attr name="call_log_contact_photo_margin" format="dimension" />
+ <declare-styleable name="CallLog">
+ <attr name="call_log_primary_text_color" format="color" />
+ <attr name="call_log_primary_background_color" format="color" />
+ <attr name="call_log_secondary_text_color" format="color" />
+ <attr name="call_log_secondary_background_color" format="color" />
+ <attr name="call_log_date_margin" format="dimension" />
+ </declare-styleable>
+
+ <declare-styleable name="CallLogList">
+ <attr name="call_log_list_contact_photo_size" format="dimension" />
+ <attr name="call_log_list_contact_photo_margin" format="dimension" />
<attr name="call_log_list_item_height" format="dimension" />
</declare-styleable>
@@ -227,7 +237,7 @@
<style name="ContactsPreferencesTheme" parent="@android:Theme.Holo.Light">
</style>
- <style name="CustomContactListFilterTheme" parent="@android:Theme">
+ <style name="ContactListFilterTheme" parent="@android:Theme.Holo.Light">
</style>
<style name="ContactPickerLayout" parent="ContactPickerTheme">
@@ -235,7 +245,7 @@
<item name="android:layout_height">match_parent</item>
</style>
- <style name="CustomContactListFilterView" parent="CustomContactListFilterTheme">
+ <style name="CustomContactListFilterView" parent="ContactListFilterTheme">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
</style>
@@ -318,4 +328,32 @@
<item name="android:background">@drawable/btn_dial</item>
<item name="android:soundEffectsEnabled">false</item>
</style>
+
+ <style name="ContactTileStarred">
+ <item name="android:focusable">true</item>
+ <item name="android:padding">1px</item>
+ <item name="android:background">@drawable/list_selector</item>
+ </style>
+
+ <style name="ContactTileStarredText">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">48dip</item>
+ <item name="android:layout_alignParentBottom">true</item>
+ <item name="android:layout_alignParentLeft">true</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:paddingLeft">8dip</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:ellipsize">end</item>
+ <item name="android:textColor">@android:color/white</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
+ </style>
+
+ <style name="ContactTileStarredTextBackground">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">48dip</item>
+ <item name="android:layout_alignParentBottom">true</item>
+ <item name="android:layout_alignParentLeft">true</item>
+ <item name="android:alpha">0.5</item>
+ <item name="android:background">@android:color/black</item>
+ </style>
</resources>
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index f87f8a4..d08b76a 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -61,8 +61,8 @@
private PhoneCallDetailsHelper mPhoneCallDetailsHelper;
private TextView mCallTimeView;
private TextView mCallDurationView;
- private View mCallActionView;
- private ImageView mContactPhotoView;
+ private View mHomeActionView;
+ private ImageView mMainActionView;
private ImageView mContactBackgroundView;
private String mNumber = null;
@@ -121,14 +121,22 @@
getResources().getDrawable(R.drawable.ic_call_log_list_outgoing_call),
getResources().getDrawable(R.drawable.ic_call_log_list_missed_call),
getResources().getDrawable(R.drawable.ic_call_log_list_voicemail));
- mCallActionView = findViewById(R.id.call);
- mContactPhotoView = (ImageView) findViewById(R.id.contact_photo);
+ mHomeActionView = findViewById(R.id.action_bar_home);
+ mMainActionView = (ImageView) findViewById(R.id.main_action);
mContactBackgroundView = (ImageView) findViewById(R.id.contact_background);
mCallTimeView = (TextView) findViewById(R.id.time);
mCallDurationView = (TextView) findViewById(R.id.duration);
mDefaultCountryIso = ContactsUtils.getCurrentCountryIso(this);
mContactPhotoManager = ContactPhotoManager.getInstance(this);
getListView().setOnItemClickListener(this);
+ mHomeActionView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // We want this to start the call log if this activity was not started from the
+ // call log itself.
+ CallDetailActivity.this.finish();
+ }
+ });
}
@Override
@@ -161,7 +169,7 @@
*
* @param callUri Uri into {@link CallLog.Calls}
*/
- private void updateData(Uri callUri) {
+ private void updateData(final Uri callUri) {
ContentResolver resolver = getContentResolver();
Cursor callCursor = resolver.query(callUri, CALL_LOG_PROJECTION, null, null, null);
try {
@@ -198,7 +206,7 @@
mNumber.equals(CallerInfo.PRIVATE_NUMBER)) {
numberText = getString(mNumber.equals(CallerInfo.PRIVATE_NUMBER)
? R.string.private_num : R.string.unknown);
- mCallActionView.setVisibility(View.GONE);
+ mMainActionView.setVisibility(View.GONE);
} else {
// Perform a reverse-phonebook lookup to find the PERSON_ID
Uri personUri = null;
@@ -206,6 +214,7 @@
Uri.encode(mNumber));
Cursor phonesCursor = resolver.query(
phoneUri, PHONES_PROJECTION, null, null, null);
+ String candidateNumberText = mNumber;
try {
if (phonesCursor != null && phonesCursor.moveToFirst()) {
long personId = phonesCursor.getLong(COLUMN_INDEX_ID);
@@ -213,57 +222,90 @@
Contacts.CONTENT_URI, personId);
nameText = phonesCursor.getString(COLUMN_INDEX_NAME);
photoId = phonesCursor.getLong(COLUMN_INDEX_PHOTO_ID);
- mNumber = PhoneNumberUtils.formatNumber(
+ candidateNumberText = PhoneNumberUtils.formatNumber(
phonesCursor.getString(COLUMN_INDEX_NUMBER),
phonesCursor.getString(COLUMN_INDEX_NORMALIZED_NUMBER),
countryIso);
numberType = phonesCursor.getInt(COLUMN_INDEX_TYPE);
numberLabel = phonesCursor.getString(COLUMN_INDEX_LABEL);
} else {
- mNumber = PhoneNumberUtils.formatNumber(mNumber, countryIso);
+ candidateNumberText =
+ PhoneNumberUtils.formatNumber(mNumber, countryIso);
}
} finally {
- if (phonesCursor != null) phonesCursor.close();
+ if (phonesCursor != null) phonesCursor.close();
+ numberText = candidateNumberText;
}
- numberText = mNumber;
- mCallActionView.setVisibility(View.VISIBLE);
- mCallActionView.setOnClickListener(new View.OnClickListener() {
+ // Let user view contact details if they exist, otherwise add option
+ // to create new contact from this number.
+ final Intent mainActionIntent;
+ final int mainActionIcon;
+ if (personUri != null) {
+ mainActionIntent = new Intent(Intent.ACTION_VIEW, personUri);
+ mainActionIcon = R.drawable.sym_action_view_contact;
+ } else {
+ mainActionIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ mainActionIntent.setType(Contacts.CONTENT_ITEM_TYPE);
+ mainActionIntent.putExtra(Insert.PHONE, mNumber);
+ mainActionIcon = R.drawable.sym_action_add;
+ }
+
+ mMainActionView.setVisibility(View.VISIBLE);
+ mMainActionView.setImageResource(mainActionIcon);
+ mMainActionView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- Intent callIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
- Uri.fromParts("tel", mNumber, null));
- startActivity(callIntent);
+ startActivity(mainActionIntent);
}
});
// Build list of various available actions
List<ViewEntry> actions = new ArrayList<ViewEntry>();
- Intent smsIntent = new Intent(Intent.ACTION_SENDTO,
- Uri.fromParts("sms", mNumber, null));
- actions.add(new ViewEntry(R.drawable.sym_action_sms,
- getString(R.string.menu_sendTextMessage), smsIntent));
-
- // Let user view contact details if they exist, otherwise add option
- // to create new contact from this number.
- if (personUri != null) {
- Intent viewIntent = new Intent(Intent.ACTION_VIEW, personUri);
- actions.add(new ViewEntry(R.drawable.sym_action_view_contact,
- getString(R.string.menu_viewContact), viewIntent));
+ final boolean isSipNumber = PhoneNumberUtils.isUriNumber(mNumber);
+ final Uri numberCallUri;
+ if (isSipNumber) {
+ numberCallUri = Uri.fromParts("sip", mNumber, null);
} else {
- Intent createIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
- createIntent.setType(Contacts.CONTENT_ITEM_TYPE);
- createIntent.putExtra(Insert.PHONE, mNumber);
- actions.add(new ViewEntry(R.drawable.sym_action_add,
- getString(R.string.recentCalls_addToContact), createIntent));
+ numberCallUri = Uri.fromParts("tel", mNumber, null);
+ }
+
+ actions.add(new ViewEntry(android.R.drawable.sym_action_call,
+ getString(R.string.menu_callNumber, mNumber),
+ new Intent(Intent.ACTION_CALL_PRIVILEGED, numberCallUri)));
+
+ if (!isSipNumber) {
+ Intent smsIntent = new Intent(Intent.ACTION_SENDTO,
+ Uri.fromParts("sms", mNumber, null));
+ actions.add(new ViewEntry(R.drawable.sym_action_sms,
+ getString(R.string.menu_sendTextMessage), smsIntent));
+ }
+
+ actions.add(new ViewEntry(android.R.drawable.ic_menu_close_clear_cancel,
+ getString(R.string.recentCalls_removeFromRecentList),
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ long id = ContentUris.parseId(callUri);
+ getContentResolver().delete(Calls.CONTENT_URI_WITH_VOICEMAIL,
+ Calls._ID + " = ?", new String[]{Long.toString(id)});
+ finish();
+ }
+ }));
+
+ if (!isSipNumber) {
+ actions.add(new ViewEntry(android.R.drawable.sym_action_call,
+ getString(R.string.recentCalls_editNumberBeforeCall),
+ new Intent(Intent.ACTION_DIAL, numberCallUri)));
}
ViewAdapter adapter = new ViewAdapter(this, actions);
setListAdapter(adapter);
}
- mPhoneCallDetailsHelper.setPhoneCallDetails(mPhoneCallDetailsViews, date, callType,
- nameText, numberText, numberType, numberLabel);
+ mPhoneCallDetailsHelper.setPhoneCallDetails(mPhoneCallDetailsViews,
+ new PhoneCallDetails(mNumber, numberText, new int[]{ callType }, date,
+ nameText, numberType, numberLabel), false);
loadContactPhotos(photoId);
} else {
@@ -282,21 +324,7 @@
/** Load the contact photos and places them in the corresponding views. */
private void loadContactPhotos(final long photoId) {
- // There seem to be a limitation in the ContactPhotoManager that does not allow requesting
- // two photos at once.
- // TODO: Figure out the problem with ContactPhotoManager and remove this nonsense.
- mContactPhotoView.post(new Runnable() {
- @Override
- public void run() {
- mContactPhotoManager.loadPhoto(mContactPhotoView, photoId);
- mContactPhotoView.postDelayed(new Runnable() {
- @Override
- public void run() {
- mContactPhotoManager.loadPhoto(mContactBackgroundView, photoId);
- }
- }, 100);
- }
- });
+ mContactPhotoManager.loadPhoto(mContactBackgroundView, photoId);
}
private String formatDuration(long elapsedSeconds) {
@@ -319,9 +347,11 @@
}
static final class ViewEntry {
- public int icon = -1;
- public String text = null;
- public Intent intent = null;
+ public final int icon;
+ public final String text;
+ public final Intent intent;
+ public final View.OnClickListener action;
+
public String label = null;
public String number = null;
@@ -329,6 +359,14 @@
this.icon = icon;
this.text = text;
this.intent = intent;
+ this.action = null;
+ }
+
+ public ViewEntry(int icon, String text, View.OnClickListener listener) {
+ this.icon = icon;
+ this.text = text;
+ this.intent = null;
+ this.action = listener;
}
}
@@ -406,6 +444,8 @@
ViewEntry entry = (ViewEntry) view.getTag();
if (entry.intent != null) {
startActivity(entry.intent);
+ } else if (entry.action != null) {
+ entry.action.onClick(view);
}
}
}
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
index 2ec1cbf..7ed9d86 100644
--- a/src/com/android/contacts/ContactLoader.java
+++ b/src/com/android/contacts/ContactLoader.java
@@ -413,7 +413,6 @@
RawContacts.SYNC3,
RawContacts.SYNC4,
RawContacts.DELETED,
- RawContacts.IS_RESTRICTED,
RawContacts.NAME_VERIFIED,
Contacts.Entity.DATA_ID,
@@ -481,46 +480,45 @@
public final static int SYNC3 = 22;
public final static int SYNC4 = 23;
public final static int DELETED = 24;
- public final static int IS_RESTRICTED = 25;
- public final static int NAME_VERIFIED = 26;
+ public final static int NAME_VERIFIED = 25;
- public final static int DATA_ID = 27;
- public final static int DATA1 = 28;
- public final static int DATA2 = 29;
- public final static int DATA3 = 30;
- public final static int DATA4 = 31;
- public final static int DATA5 = 32;
- public final static int DATA6 = 33;
- public final static int DATA7 = 34;
- public final static int DATA8 = 35;
- public final static int DATA9 = 36;
- public final static int DATA10 = 37;
- public final static int DATA11 = 38;
- public final static int DATA12 = 39;
- public final static int DATA13 = 40;
- public final static int DATA14 = 41;
- public final static int DATA15 = 42;
- public final static int DATA_SYNC1 = 43;
- public final static int DATA_SYNC2 = 44;
- public final static int DATA_SYNC3 = 45;
- public final static int DATA_SYNC4 = 46;
- public final static int DATA_VERSION = 47;
- public final static int IS_PRIMARY = 48;
- public final static int IS_SUPERPRIMARY = 49;
- public final static int MIMETYPE = 50;
- public final static int RES_PACKAGE = 51;
+ public final static int DATA_ID = 26;
+ public final static int DATA1 = 27;
+ public final static int DATA2 = 28;
+ public final static int DATA3 = 29;
+ public final static int DATA4 = 30;
+ public final static int DATA5 = 31;
+ public final static int DATA6 = 32;
+ public final static int DATA7 = 33;
+ public final static int DATA8 = 34;
+ public final static int DATA9 = 35;
+ public final static int DATA10 = 36;
+ public final static int DATA11 = 37;
+ public final static int DATA12 = 38;
+ public final static int DATA13 = 39;
+ public final static int DATA14 = 40;
+ public final static int DATA15 = 41;
+ public final static int DATA_SYNC1 = 42;
+ public final static int DATA_SYNC2 = 43;
+ public final static int DATA_SYNC3 = 44;
+ public final static int DATA_SYNC4 = 45;
+ public final static int DATA_VERSION = 46;
+ public final static int IS_PRIMARY = 47;
+ public final static int IS_SUPERPRIMARY = 48;
+ public final static int MIMETYPE = 49;
+ public final static int RES_PACKAGE = 50;
- public final static int GROUP_SOURCE_ID = 52;
+ public final static int GROUP_SOURCE_ID = 51;
- public final static int PRESENCE = 53;
- public final static int CHAT_CAPABILITY = 54;
- public final static int STATUS = 55;
- public final static int STATUS_RES_PACKAGE = 56;
- public final static int STATUS_ICON = 57;
- public final static int STATUS_LABEL = 58;
- public final static int STATUS_TIMESTAMP = 59;
+ public final static int PRESENCE = 52;
+ public final static int CHAT_CAPABILITY = 53;
+ public final static int STATUS = 54;
+ public final static int STATUS_RES_PACKAGE = 55;
+ public final static int STATUS_ICON = 56;
+ public final static int STATUS_LABEL = 57;
+ public final static int STATUS_TIMESTAMP = 58;
- public final static int PHOTO_URI = 60;
+ public final static int PHOTO_URI = 59;
}
private static class DirectoryQuery {
@@ -764,7 +762,6 @@
cursorColumnToContentValues(cursor, cv, ContactQuery.DELETED);
cursorColumnToContentValues(cursor, cv, ContactQuery.CONTACT_ID);
cursorColumnToContentValues(cursor, cv, ContactQuery.STARRED);
- cursorColumnToContentValues(cursor, cv, ContactQuery.IS_RESTRICTED);
cursorColumnToContentValues(cursor, cv, ContactQuery.NAME_VERIFIED);
return cv;
diff --git a/src/com/android/contacts/ContactPhotoManager.java b/src/com/android/contacts/ContactPhotoManager.java
index e61d379..fe73fbe 100644
--- a/src/com/android/contacts/ContactPhotoManager.java
+++ b/src/com/android/contacts/ContactPhotoManager.java
@@ -66,11 +66,12 @@
* the available authenticators. This method can safely be called from the UI thread.
*/
public static ContactPhotoManager getInstance(Context context) {
+ Context applicationContext = context.getApplicationContext();
ContactPhotoManager service =
- (ContactPhotoManager) context.getSystemService(CONTACT_PHOTO_SERVICE);
+ (ContactPhotoManager) applicationContext.getSystemService(CONTACT_PHOTO_SERVICE);
if (service == null) {
- service = createContactPhotoManager(context);
- Log.e(TAG, "No contact photo service in context: " + context);
+ service = createContactPhotoManager(applicationContext);
+ Log.e(TAG, "No contact photo service in context: " + applicationContext);
}
return service;
}
diff --git a/src/com/android/contacts/GroupMemberLoader.java b/src/com/android/contacts/GroupMemberLoader.java
index 0633bf8..8f08ccd 100644
--- a/src/com/android/contacts/GroupMemberLoader.java
+++ b/src/com/android/contacts/GroupMemberLoader.java
@@ -82,8 +82,6 @@
Uri uri = Data.CONTENT_URI;
uri = uri.buildUpon().appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
String.valueOf(Directory.DEFAULT)).build();
- // TODO: Bring back dataRestriction
- // uri = applyDataRestriction(uri);
return uri;
}
diff --git a/src/com/android/contacts/GroupMetaDataLoader.java b/src/com/android/contacts/GroupMetaDataLoader.java
index d900825..8533bb6 100644
--- a/src/com/android/contacts/GroupMetaDataLoader.java
+++ b/src/com/android/contacts/GroupMetaDataLoader.java
@@ -34,6 +34,7 @@
Groups.AUTO_ADD,
Groups.FAVORITES,
Groups.GROUP_IS_READ_ONLY,
+ Groups.DELETED,
};
public final static int ACCOUNT_NAME = 0;
@@ -43,6 +44,7 @@
public final static int AUTO_ADD = 4;
public final static int FAVORITES = 5;
public final static int IS_READ_ONLY = 6;
+ public final static int DELETED = 7;
public GroupMetaDataLoader(Context context, Uri groupUri) {
super(context, ensureIsGroupUri(groupUri), COLUMNS, Groups.ACCOUNT_TYPE + " NOT NULL AND "
diff --git a/src/com/android/contacts/PhoneCallDetails.java b/src/com/android/contacts/PhoneCallDetails.java
new file mode 100644
index 0000000..7b02a88
--- /dev/null
+++ b/src/com/android/contacts/PhoneCallDetails.java
@@ -0,0 +1,62 @@
+/*
+ * 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;
+
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+
+/**
+ * The details of a phone call to be shown in the UI.
+ */
+public class PhoneCallDetails {
+ /** The number of the other party involved in the call. */
+ public final CharSequence number;
+ /** The formatted version of {@link #number}. */
+ public final CharSequence formattedNumber;
+ /**
+ * The type of calls, as defined in the call log table, e.g., {@link Calls#INCOMING_TYPE}.
+ * <p>
+ * There might be multiple types if this represents a set of entries grouped together.
+ */
+ public final int[] callTypes;
+ /** The date of the call, in milliseconds since the epoch. */
+ public final long date;
+ /** The name of the contact, or the empty string. */
+ public final CharSequence name;
+ /** The type of phone, e.g., {@link Phone#TYPE_HOME}, 0 if not available. */
+ public final int numberType;
+ /** The custom label associated with the phone number in the contact, or the empty string. */
+ public final CharSequence numberLabel;
+
+ /** Create the details for a call with a number not associated with a contact. */
+ public PhoneCallDetails(CharSequence number, CharSequence formattedNumber, int[] callTypes,
+ long date) {
+ this(number, formattedNumber, callTypes, date, "", 0, "");
+ }
+
+ /** Create the details for a call with a number associated with a contact. */
+ public PhoneCallDetails(CharSequence number, CharSequence formattedNumber, int[] callTypes,
+ long date, CharSequence name, int numberType, CharSequence numberLabel) {
+ this.number = number;
+ this.formattedNumber = formattedNumber;
+ this.callTypes = callTypes;
+ this.date = date;
+ this.name = name;
+ this.numberType = numberType;
+ this.numberLabel = numberLabel;
+ }
+}
diff --git a/src/com/android/contacts/PhoneCallDetailsHelper.java b/src/com/android/contacts/PhoneCallDetailsHelper.java
index 78ca252..7f73b04 100644
--- a/src/com/android/contacts/PhoneCallDetailsHelper.java
+++ b/src/com/android/contacts/PhoneCallDetailsHelper.java
@@ -47,6 +47,14 @@
private final Drawable mMissedDrawable;
/** Icon for voicemails. */
private final Drawable mVoicemailDrawable;
+ /** Name used to identify incoming calls. */
+ private final String mIncomingName;
+ /** Name used to identify outgoing calls. */
+ private final String mOutgoingName;
+ /** Name used to identify missed calls. */
+ private final String mMissedName;
+ /** Name used to identify voicemail calls. */
+ private final String mVoicemailName;
/** The injected current time in milliseconds since the epoch. Used only by tests. */
private Long mCurrentTimeMillisForTest;
@@ -67,72 +75,72 @@
mOutgoingDrawable = outgoingDrawable;
mMissedDrawable = missedDrawable;
mVoicemailDrawable = voicemailDrawable;
+ // Cache these values so that we do not need to look them up each time.
+ mIncomingName = mResources.getString(R.string.type_incoming);
+ mOutgoingName = mResources.getString(R.string.type_outgoing);
+ mMissedName = mResources.getString(R.string.type_missed);
+ mVoicemailName = mResources.getString(R.string.type_voicemail);
}
- /**
- * Fills the call details views with content.
- *
- * @param date the date of the call, in milliseconds since the epoch
- * @param callType the type of call, as defined in the call log table
- * @param name the name of the contact, if available
- * @param number the number of the other party involved in the call
- * @param numberType the type of phone, e.g., {@link Phone#TYPE_HOME}, 0 if not available
- * @param numberLabel the custom label associated with the phone number in the contact
- */
- public void setPhoneCallDetails(PhoneCallDetailsViews views, long date,
- int callType, CharSequence name, CharSequence number, int numberType,
- CharSequence numberLabel) {
- Drawable callTypeDrawable = null;
- switch (callType) {
- case Calls.INCOMING_TYPE:
- callTypeDrawable = mIncomingDrawable;
- break;
+ /** Fills the call details views with content. */
+ public void setPhoneCallDetails(PhoneCallDetailsViews views, PhoneCallDetails details,
+ boolean useIcons) {
+ if (useIcons) {
+ views.callTypeIcons.removeAllViews();
+ int count = details.callTypes.length;
+ for (int callType : details.callTypes) {
+ ImageView callTypeImage = new ImageView(mContext);
+ callTypeImage.setImageDrawable(getCallTypeDrawable(callType));
+ views.callTypeIcons.addView(callTypeImage);
+ }
+ views.callTypeIcons.setVisibility(View.VISIBLE);
+ views.callTypeText.setVisibility(View.GONE);
+ views.callTypeSeparator.setVisibility(View.GONE);
+ } else {
+ String callTypeName;
+ // Use the name of the first call type.
+ // TODO: We should update this to handle the text for multiple calls as well.
+ int callType = details.callTypes[0];
+ views.callTypeText.setText(getCallTypeText(callType));
+ views.callTypeIcons.removeAllViews();
- case Calls.OUTGOING_TYPE:
- callTypeDrawable = mOutgoingDrawable;
- break;
-
- case Calls.MISSED_TYPE:
- callTypeDrawable = mMissedDrawable;
- break;
-
- case Calls.VOICEMAIL_TYPE:
- callTypeDrawable = mVoicemailDrawable;
- break;
+ views.callTypeText.setVisibility(View.VISIBLE);
+ views.callTypeSeparator.setVisibility(View.VISIBLE);
+ views.callTypeIcons.setVisibility(View.GONE);
}
+
CharSequence shortDateText =
- DateUtils.getRelativeTimeSpanString(date,
+ DateUtils.getRelativeTimeSpanString(details.date,
getCurrentTimeMillis(),
DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE);
CharSequence numberFormattedLabel = null;
// Only show a label if the number is shown and it is not a SIP address.
- if (!TextUtils.isEmpty(number) && !PhoneNumberUtils.isUriNumber(number.toString())) {
- numberFormattedLabel = Phone.getTypeLabel(mResources, numberType, numberLabel);
+ if (!TextUtils.isEmpty(details.number)
+ && !PhoneNumberUtils.isUriNumber(details.number.toString())) {
+ numberFormattedLabel = Phone.getTypeLabel(mResources, details.numberType,
+ details.numberLabel);
}
- CharSequence nameText;
- CharSequence numberText;
- if (TextUtils.isEmpty(name)) {
- nameText = getDisplayNumber(number);
+ final CharSequence nameText;
+ final CharSequence numberText;
+ if (TextUtils.isEmpty(details.name)) {
+ nameText = getDisplayNumber(details.number, details.formattedNumber);
numberText = "";
} else {
- nameText = name;
- numberText = getDisplayNumber(number);
- if (callType != 0 && numberFormattedLabel != null) {
+ nameText = details.name;
+ CharSequence displayNumber = getDisplayNumber(details.number, details.formattedNumber);
+ if (numberFormattedLabel != null) {
numberText = FormatUtils.applyStyleToSpan(Typeface.BOLD,
- numberFormattedLabel + " " + number, 0,
+ numberFormattedLabel + " " + displayNumber, 0,
numberFormattedLabel.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else {
+ numberText = displayNumber;
}
}
- ImageView callTypeImage = new ImageView(mContext);
- callTypeImage.setImageDrawable(callTypeDrawable);
- views.callTypesLayout.removeAllViews();
- views.callTypesLayout.addView(callTypeImage);
-
views.dateView.setText(shortDateText);
views.dateView.setVisibility(View.VISIBLE);
views.nameView.setText(nameText);
@@ -147,7 +155,47 @@
}
}
- private CharSequence getDisplayNumber(CharSequence number) {
+ /** Returns the text used to represent the given call type. */
+ private String getCallTypeText(int callType) {
+ switch (callType) {
+ case Calls.INCOMING_TYPE:
+ return mIncomingName;
+
+ case Calls.OUTGOING_TYPE:
+ return mOutgoingName;
+
+ case Calls.MISSED_TYPE:
+ return mMissedName;
+
+ case Calls.VOICEMAIL_TYPE:
+ return mVoicemailName;
+
+ default:
+ throw new IllegalArgumentException("invalid call type: " + callType);
+ }
+ }
+
+ /** Returns the drawable of the icon associated with the given call type. */
+ private Drawable getCallTypeDrawable(int callType) {
+ switch (callType) {
+ case Calls.INCOMING_TYPE:
+ return mIncomingDrawable;
+
+ case Calls.OUTGOING_TYPE:
+ return mOutgoingDrawable;
+
+ case Calls.MISSED_TYPE:
+ return mMissedDrawable;
+
+ case Calls.VOICEMAIL_TYPE:
+ return mVoicemailDrawable;
+
+ default:
+ throw new IllegalArgumentException("invalid call type: " + callType);
+ }
+ }
+
+ private CharSequence getDisplayNumber(CharSequence number, CharSequence formattedNumber) {
if (TextUtils.isEmpty(number)) {
return "";
}
@@ -163,7 +211,11 @@
if (PhoneNumberUtils.extractNetworkPortion(number.toString()).equals(mVoicemailNumber)) {
return mResources.getString(R.string.voicemail);
}
- return number;
+ if (TextUtils.isEmpty(formattedNumber)) {
+ return number;
+ } else {
+ return formattedNumber;
+ }
}
public void setCurrentTimeForTest(long currentTimeMillis) {
diff --git a/src/com/android/contacts/PhoneCallDetailsViews.java b/src/com/android/contacts/PhoneCallDetailsViews.java
index 483ec65..7453af0 100644
--- a/src/com/android/contacts/PhoneCallDetailsViews.java
+++ b/src/com/android/contacts/PhoneCallDetailsViews.java
@@ -25,14 +25,18 @@
*/
public final class PhoneCallDetailsViews {
public final TextView nameView;
- public final LinearLayout callTypesLayout;
+ public final LinearLayout callTypeIcons;
+ public final TextView callTypeText;
+ public final View callTypeSeparator;
public final TextView dateView;
public final TextView numberView;
- private PhoneCallDetailsViews(TextView nameView, LinearLayout callTypesLayout,
- TextView dateView, TextView numberView) {
+ private PhoneCallDetailsViews(TextView nameView, LinearLayout callTypeIcons,
+ TextView callTypeText, View callTypeSeparator, TextView dateView, TextView numberView) {
this.nameView = nameView;
- this.callTypesLayout = callTypesLayout;
+ this.callTypeIcons = callTypeIcons;
+ this.callTypeText = callTypeText;
+ this.callTypeSeparator = callTypeSeparator;
this.dateView = dateView;
this.numberView = numberView;
}
@@ -46,13 +50,17 @@
*/
public static PhoneCallDetailsViews fromView(View view) {
return new PhoneCallDetailsViews((TextView) view.findViewById(R.id.name),
- (LinearLayout) view.findViewById(R.id.call_types),
+ (LinearLayout) view.findViewById(R.id.call_type_icons),
+ (TextView) view.findViewById(R.id.call_type_name),
+ view.findViewById(R.id.call_type_separator),
(TextView) view.findViewById(R.id.date),
(TextView) view.findViewById(R.id.number));
}
public static PhoneCallDetailsViews createForTest(TextView nameView,
- LinearLayout callTypesLayout, TextView dateView, TextView numberView) {
- return new PhoneCallDetailsViews(nameView, callTypesLayout, dateView, numberView);
+ LinearLayout callTypeIcons, TextView callTypeText, View callTypeSeparator,
+ TextView dateView, TextView numberView) {
+ return new PhoneCallDetailsViews(nameView, callTypeIcons, callTypeText, callTypeSeparator,
+ dateView, numberView);
}
}
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index 073f665..3a183d2 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -18,8 +18,6 @@
import com.android.contacts.R;
import com.android.contacts.activities.ActionBarAdapter.Listener.Action;
-import com.android.contacts.list.ContactListFilterController;
-import com.android.contacts.list.ContactListFilterController.ContactListFilterListener;
import com.android.contacts.list.ContactsRequest;
import android.app.ActionBar;
@@ -36,8 +34,7 @@
/**
* Adapter for the action bar at the top of the Contacts activity.
*/
-public class ActionBarAdapter
- implements OnQueryTextListener, OnCloseListener, ContactListFilterListener {
+public class ActionBarAdapter implements OnQueryTextListener, OnCloseListener {
public interface Listener {
public enum Action {
@@ -58,27 +55,23 @@
private SearchView mSearchView;
private final Context mContext;
+ private final boolean mAlwaysShowSearchView;
private Listener mListener;
- private ContactListFilterController mFilterController;
private ActionBar mActionBar;
- private View mCustomSearchView;
- private LayoutParams mLayoutParams;
- private boolean mIsSearchInOverflowMenu;
public ActionBarAdapter(Context context, Listener listener) {
mContext = context;
mListener = listener;
mSearchLabelText = mContext.getString(R.string.search_label);
+ mAlwaysShowSearchView = mContext.getResources().getBoolean(R.bool.always_show_search_view);
}
- public void onCreate(Bundle savedState, ContactsRequest request, ActionBar actionBar,
- boolean searchInOverflowMenu) {
+ public void onCreate(Bundle savedState, ContactsRequest request, ActionBar actionBar) {
mActionBar = actionBar;
mQueryString = null;
- mIsSearchInOverflowMenu = searchInOverflowMenu;
if (savedState != null) {
mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE);
@@ -88,33 +81,29 @@
mQueryString = request.getQueryString();
}
- if (mSearchView != null) {
- mSearchView.setQuery(mQueryString, false);
+ // Set up search view.
+ View customSearchView = LayoutInflater.from(mContext).inflate(R.layout.custom_action_bar,
+ null);
+ int searchViewWidth = mContext.getResources().getDimensionPixelSize(
+ R.dimen.search_view_width);
+ if (searchViewWidth == 0) {
+ searchViewWidth = LayoutParams.MATCH_PARENT;
}
+ LayoutParams layoutParams = new LayoutParams(searchViewWidth, LayoutParams.WRAP_CONTENT);
+ mSearchView = (SearchView) customSearchView.findViewById(R.id.search_view);
+ mSearchView.setQueryHint(mContext.getString(R.string.hint_findContacts));
+ mSearchView.setOnQueryTextListener(this);
+ mSearchView.setOnCloseListener(this);
+ mSearchView.setQuery(mQueryString, false);
+ mActionBar.setCustomView(customSearchView, layoutParams);
update();
}
- public void setSearchView(SearchView searchView) {
- mSearchView = searchView;
- mSearchView.setOnQueryTextListener(this);
- mSearchView.setOnCloseListener(this);
- mSearchView.setQuery(mQueryString, false);
- }
-
public void setListener(Listener listener) {
mListener = listener;
}
- public void setContactListFilterController(ContactListFilterController controller) {
- mFilterController = controller;
- mFilterController.addListener(this);
- }
-
- public boolean isSearchInOverflowMenu() {
- return mIsSearchInOverflowMenu;
- }
-
public boolean isSearchMode() {
return mSearchMode;
}
@@ -127,7 +116,7 @@
return;
}
if (mSearchMode) {
- mSearchView.requestFocus();
+ setFocusOnSearchView();
} else {
mSearchView.setQuery(null, false);
}
@@ -147,31 +136,20 @@
public void update() {
if (mSearchMode) {
- // If the search icon was in the overflow menu, then inflate a custom view containing
- // a search view for the action bar (and hide the tabs).
- if (mIsSearchInOverflowMenu) {
- if (mCustomSearchView == null) {
- mCustomSearchView = LayoutInflater.from(mContext).inflate(
- R.layout.custom_action_bar, null);
- mLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.WRAP_CONTENT);
- SearchView searchView = (SearchView) mCustomSearchView.
- findViewById(R.id.search_view);
- searchView.setQueryHint(mContext.getString(R.string.hint_findContacts));
- setSearchView(searchView);
- }
- mActionBar.setDisplayShowCustomEnabled(true);
- mActionBar.setCustomView(mCustomSearchView, mLayoutParams);
- mSearchView.requestFocus();
- } else {
+ mActionBar.setDisplayShowCustomEnabled(true);
+ if (mAlwaysShowSearchView) {
+ // Tablet -- change the app title for the search mode
mActionBar.setTitle(mSearchLabelText);
+ } else {
+ // Phone -- search view gets focus
+ setFocusOnSearchView();
}
mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
if (mListener != null) {
mListener.onAction(Action.START_SEARCH_MODE);
}
} else {
- mActionBar.setDisplayShowCustomEnabled(false);
+ mActionBar.setDisplayShowCustomEnabled(mAlwaysShowSearchView);
mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mActionBar.setTitle(null);
if (mListener != null) {
@@ -220,20 +198,14 @@
public void onRestoreInstanceState(Bundle savedState) {
mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE);
mQueryString = savedState.getString(EXTRA_KEY_QUERY);
- mActionBar.setSelectedNavigationItem(savedState.getInt(EXTRA_KEY_SELECTED_TAB));
+ int selectedTab = savedState.getInt(EXTRA_KEY_SELECTED_TAB);
+ if (selectedTab >= 0) {
+ mActionBar.setSelectedNavigationItem(selectedTab);
+ }
}
- @Override
- public void onContactListFiltersLoaded() {
- update();
- }
-
- @Override
- public void onContactListFilterChanged() {
- update();
- }
-
- @Override
- public void onContactListFilterCustomizationRequest() {
+ private void setFocusOnSearchView() {
+ mSearchView.requestFocus();
+ mSearchView.setIconified(false); // Workaround for the "IME not popping up" issue.
}
}
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index b45ba39..7456967 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -62,6 +62,7 @@
import java.util.ArrayList;
+// TODO: Use {@link ContactDetailLayoutController} so there isn't duplicated code
public class ContactDetailActivity extends ContactsActivity {
private static final String TAG = "ContactDetailActivity";
@@ -216,19 +217,24 @@
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
+ // First check if the {@link ContactLoaderFragment} can handle the key
+ if (mLoaderFragment != null && mLoaderFragment.handleKeyDown(keyCode)) return true;
+
+ // Otherwise find the correct fragment to handle the event
FragmentKeyListener mCurrentFragment;
switch (getCurrentPage()) {
case 0:
- mCurrentFragment = (FragmentKeyListener) mDetailFragment;
+ mCurrentFragment = mDetailFragment;
break;
case 1:
- mCurrentFragment = (FragmentKeyListener) mUpdatesFragment;
+ mCurrentFragment = mUpdatesFragment;
break;
default:
throw new IllegalStateException("Invalid current item for ViewPager");
}
- if (mCurrentFragment.handleKeyDown(keyCode)) return true;
+ if (mCurrentFragment != null && mCurrentFragment.handleKeyDown(keyCode)) return true;
+ // In the last case, give the key event to the superclass.
return super.onKeyDown(keyCode, event);
}
@@ -260,6 +266,11 @@
private final ContactLoaderFragmentListener mLoaderFragmentListener =
new ContactLoaderFragmentListener() {
@Override
+ public void onContactNotFound() {
+ finish();
+ }
+
+ @Override
public void onDetailsLoaded(final ContactLoader.Result result) {
if (result == null) {
return;
@@ -283,6 +294,16 @@
}
});
}
+
+ @Override
+ public void onEditRequested(Uri contactLookupUri) {
+ startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri));
+ }
+
+ @Override
+ public void onDeleteRequested(Uri contactUri) {
+ ContactDeletionInteraction.start(ContactDetailActivity.this, contactUri, true);
+ }
};
/**
@@ -302,28 +323,40 @@
mContentView = (ViewGroup) mInflater.inflate(
R.layout.contact_detail_container_with_updates, mRootView, false);
mRootView.addView(mContentView);
+
+ // Make sure all needed views are retrieved. Note that narrow width screens have a
+ // {@link ViewPager} and {@link ContactDetailTabCarousel}, while wide width screens have
+ // a {@link ContactDetailFragmentCarousel}.
+ mViewPager = (ViewPager) findViewById(R.id.pager);
+ if (mViewPager != null) {
+ mViewPager.removeAllViews();
+ mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
+ mViewPager.setOnPageChangeListener(mOnPageChangeListener);
+ }
+
+ mTabCarousel = (ContactDetailTabCarousel) findViewById(R.id.tab_carousel);
+ if (mTabCarousel != null) {
+ mTabCarousel.setListener(mTabCarouselListener);
+ }
+
+ mFragmentCarousel = (ContactDetailFragmentCarousel)
+ findViewById(R.id.fragment_carousel);
}
- // Narrow width screens have a {@link ViewPager} and {@link ContactDetailTabCarousel}
- mViewPager = (ViewPager) findViewById(R.id.pager);
- if (mViewPager != null) {
- mViewPager.removeAllViews();
- mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
- mViewPager.setOnPageChangeListener(mOnPageChangeListener);
- }
-
- mTabCarousel = (ContactDetailTabCarousel) findViewById(R.id.tab_carousel);
+ // Then reset the contact data to the appropriate views
if (mTabCarousel != null) {
- mTabCarousel.setListener(mTabCarouselListener);
mTabCarousel.loadData(mContactData);
}
-
- // Otherwise, wide width screens have a {@link ContactDetailFragmentCarousel}
- mFragmentCarousel = (ContactDetailFragmentCarousel) findViewById(R.id.fragment_carousel);
if (mFragmentCarousel != null) {
if (mDetailFragment != null) mFragmentCarousel.setAboutFragment(mDetailFragment);
if (mUpdatesFragment != null) mFragmentCarousel.setUpdatesFragment(mUpdatesFragment);
}
+ if (mDetailFragment != null) {
+ mDetailFragment.setData(mLookupUri, mContactData);
+ }
+ if (mUpdatesFragment != null) {
+ mUpdatesFragment.setData(mLookupUri, mContactData);
+ }
}
private void setupContactWithoutUpdates() {
@@ -332,21 +365,15 @@
R.layout.contact_detail_container_without_updates, mRootView, false);
mRootView.addView(mContentView);
}
+ // Reset contact data
+ if (mDetailFragment != null) {
+ mDetailFragment.setData(mLookupUri, mContactData);
+ }
}
private final ContactDetailFragment.Listener mFragmentListener =
new ContactDetailFragment.Listener() {
@Override
- public void onContactNotFound() {
- finish();
- }
-
- @Override
- public void onEditRequested(Uri contactLookupUri) {
- startActivity(new Intent(Intent.ACTION_EDIT, contactLookupUri));
- }
-
- @Override
public void onItemClicked(Intent intent) {
try {
startActivity(intent);
@@ -356,11 +383,6 @@
}
@Override
- public void onDeleteRequested(Uri contactUri) {
- ContactDeletionInteraction.start(ContactDetailActivity.this, contactUri, true);
- }
-
- @Override
public void onCreateRawContactRequested(
ArrayList<ContentValues> values, Account account) {
Toast.makeText(ContactDetailActivity.this, R.string.toast_making_personal_copy,
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 5dbe87b..0fa745d 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -333,6 +333,7 @@
mCallLogFragment.onVisibilityChanged(currentPosition == TAB_INDEX_CALL_LOG);
} else if (fragment instanceof StrequentContactListFragment) {
mStrequentFragment = (StrequentContactListFragment) fragment;
+ mStrequentFragment.setQuickContact(false);
mStrequentFragment.setListener(mStrequentListener);
} else if (fragment instanceof PhoneNumberPickerFragment) {
mSearchFragment = (PhoneNumberPickerFragment) fragment;
@@ -592,16 +593,6 @@
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings);
- if (!mInSearchUi) {
- callSettingsMenuItem.setVisible(!mInSearchUi);
- Intent settingsIntent = new Intent(Intent.ACTION_MAIN);
- settingsIntent.setClassName(PHONE_PACKAGE, CALL_SETTINGS_CLASS_NAME);
- callSettingsMenuItem.setIntent(settingsIntent);
- } else {
- callSettingsMenuItem.setVisible(false);
- }
-
final MenuItem searchMenuItem = menu.findItem(R.id.search_on_action_bar);
if (mInSearchUi || getActionBar().getSelectedTab().getPosition() == TAB_INDEX_DIALER) {
searchMenuItem.setVisible(false);
@@ -753,4 +744,11 @@
((ViewPagerVisibilityListener) fragment).onVisibilityChanged(visibility);
}
}
+
+ /** Returns an Intent to launch Call Settings screen */
+ public static Intent getCallSettingsIntent() {
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName(PHONE_PACKAGE, CALL_SETTINGS_CLASS_NAME);
+ return intent;
+ }
}
diff --git a/src/com/android/contacts/activities/GroupDetailActivity.java b/src/com/android/contacts/activities/GroupDetailActivity.java
index 21900c6..c8f511b 100644
--- a/src/com/android/contacts/activities/GroupDetailActivity.java
+++ b/src/com/android/contacts/activities/GroupDetailActivity.java
@@ -43,6 +43,7 @@
R.id.group_detail_fragment);
fragment.setListener(mFragmentListener);
fragment.loadGroup(getIntent().getData());
+ fragment.closeActivityAfterDelete(true);
ActionBar actionBar = getActionBar();
if (actionBar != null) {
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index eecb375..12a17f2 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -16,16 +16,23 @@
package com.android.contacts.activities;
+import com.android.contacts.ContactLoader;
import com.android.contacts.ContactSaveService;
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
import com.android.contacts.detail.ContactDetailFragment;
+import com.android.contacts.detail.ContactDetailLayoutController;
+import com.android.contacts.detail.ContactDetailTabCarousel;
+import com.android.contacts.detail.ContactDetailUpdatesFragment;
+import com.android.contacts.detail.ContactLoaderFragment;
+import com.android.contacts.detail.ContactLoaderFragment.ContactLoaderFragmentListener;
import com.android.contacts.group.GroupBrowseListFragment;
import com.android.contacts.group.GroupBrowseListFragment.OnGroupBrowserActionListener;
import com.android.contacts.group.GroupDetailFragment;
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.ContactBrowseListContextMenuAdapter;
import com.android.contacts.list.ContactBrowseListFragment;
import com.android.contacts.list.ContactEntryListFragment;
@@ -48,6 +55,7 @@
import com.android.contacts.util.AccountSelectionUtil;
import com.android.contacts.util.AccountsListAdapter;
import com.android.contacts.util.DialogManager;
+import com.android.contacts.util.PhoneCapabilityTester;
import com.android.contacts.widget.ContextMenuAdapter;
import android.accounts.Account;
@@ -64,11 +72,13 @@
import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Intents;
import android.provider.ContactsContract.ProviderStatus;
import android.provider.Settings;
+import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
@@ -100,7 +110,8 @@
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_CUSTOMIZE_FILTER = 6;
+ private static final int SUBACTIVITY_ACCOUNT_FILTER = 6;
+ private static final int SUBACTIVITY_CUSTOMIZE_FILTER = 7;
private static final String KEY_SEARCH_MODE = "searchMode";
@@ -113,21 +124,18 @@
private boolean mSearchMode;
- private ContactBrowseListFragment mListFragment;
-
- /**
- * Whether we have a right-side contact or group detail pane for displaying info on that
- * contact or group while browsing. Generally means "this is a tablet".
- */
- private boolean mContentPaneDisplayed;
-
private ContactDetailFragment mContactDetailFragment;
+ private ContactDetailUpdatesFragment mContactDetailUpdatesFragment;
private final ContactDetailFragmentListener mContactDetailFragmentListener =
new ContactDetailFragmentListener();
- private final GroupDetailFragmentListener mGroupDetailFragmentListener =
- new GroupDetailFragmentListener();
+
+ private ContactLoaderFragment mContactDetailLoaderFragment;
+ private final ContactDetailLoaderFragmentListener mContactDetailLoaderFragmentListener =
+ new ContactDetailLoaderFragmentListener();
private GroupDetailFragment mGroupDetailFragment;
+ private final GroupDetailFragmentListener mGroupDetailFragmentListener =
+ new GroupDetailFragmentListener();
private StrequentContactListFragment.Listener mFavoritesFragmentListener =
new StrequentContactListFragmentListener();
@@ -142,7 +150,10 @@
private boolean mOptionsMenuContactsAvailable;
- private DefaultContactBrowseListFragment mContactsFragment;
+ /**
+ * Showing a list of Contacts. Also used for showing search results in search mode.
+ */
+ private DefaultContactBrowseListFragment mAllFragment;
private StrequentContactListFragment mFavoritesFragment;
private StrequentContactListFragment mFrequentFragment;
private GroupBrowseListFragment mGroupsFragment;
@@ -153,16 +164,41 @@
private View mAddGroupImageView;
+ private ContactDetailLayoutController mContactDetailLayoutController;
+
+ private final Handler mHandler = new Handler();
+
+ /**
+ * TODO: Use ViewPager so that tabs can be swiped left and right. Figure out how to use the
+ * support library in our app.
+ */
+ private final TabListener mTabListener = new TabListener() {
+ @Override
+ public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+ hideFragmentOnTabUnselect((TabState) tab.getTag(), ft);
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ final TabState tabState = (TabState) tab.getTag();
+ setSelectedTab(tabState);
+ showFragmentOnTabSelect(tabState, ft);
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ }
+ };
+
private enum TabState {
- FAVORITES, CONTACTS, GROUPS
+ FAVORITES, ALL, GROUPS
}
private TabState mSelectedTab;
public PeopleActivity() {
mIntentResolver = new ContactsIntentResolver(this);
- // TODO: Get rid of the ContactListFilterController class because there aren't any
- // dropdown filters anymore. Just store the selected filter as a member variable.
mContactListFilterController = new ContactListFilterController(this);
mContactListFilterController.addListener(this);
mProviderStatusLoader = new ProviderStatusLoader(this);
@@ -174,12 +210,12 @@
@Override
public void onAttachFragment(Fragment fragment) {
- if (fragment instanceof ContactBrowseListFragment) {
- mListFragment = (ContactBrowseListFragment)fragment;
- mListFragment.setOnContactListActionListener(new ContactBrowserActionListener());
+ if (fragment instanceof DefaultContactBrowseListFragment) {
+ mAllFragment = (DefaultContactBrowseListFragment)fragment;
+ mAllFragment.setOnContactListActionListener(new ContactBrowserActionListener());
if (!getWindow().hasFeature(Window.FEATURE_ACTION_BAR)) {
- mListFragment.setContextMenuAdapter(
- new ContactBrowseListContextMenuAdapter(mListFragment));
+ mAllFragment.setContextMenuAdapter(
+ new ContactBrowseListContextMenuAdapter(mAllFragment));
}
} else if (fragment instanceof GroupBrowseListFragment) {
mGroupsFragment = (GroupBrowseListFragment) fragment;
@@ -187,20 +223,25 @@
} else if (fragment instanceof ContactDetailFragment) {
mContactDetailFragment = (ContactDetailFragment) fragment;
mContactDetailFragment.setListener(mContactDetailFragmentListener);
- mContentPaneDisplayed = true;
+ } else if (fragment instanceof ContactDetailUpdatesFragment) {
+ mContactDetailUpdatesFragment = (ContactDetailUpdatesFragment) fragment;
} else if (fragment instanceof ContactsUnavailableFragment) {
mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment;
mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader);
mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
new ContactsUnavailableFragmentListener());
+ } else if (fragment instanceof ContactLoaderFragment) {
+ mContactDetailLoaderFragment = (ContactLoaderFragment) fragment;
+ mContactDetailLoaderFragment.setListener(mContactDetailLoaderFragmentListener);
} else if (fragment instanceof GroupDetailFragment) {
mGroupDetailFragment = (GroupDetailFragment) fragment;
mGroupDetailFragment.setListener(mGroupDetailFragmentListener);
- mContentPaneDisplayed = true;
+ mGroupDetailFragment.setQuickContact(PhoneCapabilityTester.isUsingTwoPanes(this));
} else if (fragment instanceof StrequentContactListFragment) {
mFavoritesFragment = (StrequentContactListFragment) fragment;
mFavoritesFragment.setListener(mFavoritesFragmentListener);
mFavoritesFragment.setDisplayType(DisplayType.STARRED_ONLY);
+ mFavoritesFragment.setQuickContact(PhoneCapabilityTester.isUsingTwoPanes(this));
}
}
@@ -246,13 +287,13 @@
.findFragmentById(R.id.favorites_fragment);
mFrequentFragment = (StrequentContactListFragment) fragmentManager
.findFragmentById(R.id.frequent_fragment);
- mContactsFragment = (DefaultContactBrowseListFragment) fragmentManager
- .findFragmentById(R.id.contacts_fragment);
+ mAllFragment = (DefaultContactBrowseListFragment) fragmentManager
+ .findFragmentById(R.id.all_fragment);
mGroupsFragment = (GroupBrowseListFragment) fragmentManager
.findFragmentById(R.id.groups_fragment);
// Hide all tabs (the current tab will later be reshown once a tab is selected)
final FragmentTransaction transaction = fragmentManager.beginTransaction();
- transaction.hide(mContactsFragment);
+ transaction.hide(mAllFragment);
transaction.hide(mGroupsFragment);
if (mFrequentFragment != null) {
@@ -268,7 +309,7 @@
}
if (mRequest.getActionCode() == ContactsRequest.ACTION_VIEW_CONTACT
- && !mContentPaneDisplayed) {
+ && !PhoneCapabilityTester.isUsingTwoPanes(this)) {
redirect = new Intent(this, ContactDetailActivity.class);
redirect.setAction(Intent.ACTION_VIEW);
redirect.setData(mRequest.getContactUri());
@@ -280,27 +321,33 @@
setTitle(mRequest.getActivityTitle());
ActionBar actionBar = getActionBar();
mActionBarAdapter = new ActionBarAdapter(this, this);
- mActionBarAdapter.onCreate(savedState, mRequest, getActionBar(), !mContentPaneDisplayed);
- mActionBarAdapter.setContactListFilterController(mContactListFilterController);
+ mActionBarAdapter.onCreate(savedState, mRequest, getActionBar());
+
+ ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
+ ContactDetailTabCarousel tabCarousel = (ContactDetailTabCarousel)
+ findViewById(R.id.tab_carousel);
+ mContactDetailLayoutController = new ContactDetailLayoutController(
+ getFragmentManager(), viewPager, tabCarousel,
+ mContactDetailFragmentListener);
if (createContentView) {
actionBar.removeAllTabs();
Tab favoritesTab = actionBar.newTab();
- favoritesTab.setText(getString(R.string.strequentList));
- favoritesTab.setTabListener(new TabChangeListener(mFavoritesFragment,
- mFrequentFragment, TabState.FAVORITES));
+ favoritesTab.setTag(TabState.FAVORITES);
+ favoritesTab.setText(getString(R.string.contactsFavoritesLabel));
+ favoritesTab.setTabListener(mTabListener);
actionBar.addTab(favoritesTab);
- Tab peopleTab = actionBar.newTab();
- peopleTab.setText(getString(R.string.people));
- peopleTab.setTabListener(new TabChangeListener(mContactsFragment,
- mContactDetailFragment, TabState.CONTACTS));
- actionBar.addTab(peopleTab);
+ Tab allTab = actionBar.newTab();
+ allTab.setTag(TabState.ALL);
+ allTab.setText(getString(R.string.contactsAllLabel));
+ allTab.setTabListener(mTabListener);
+ actionBar.addTab(allTab);
Tab groupsTab = actionBar.newTab();
+ groupsTab.setTag(TabState.GROUPS);
groupsTab.setText(getString(R.string.contactsGroupsLabel));
- groupsTab.setTabListener(new TabChangeListener(mGroupsFragment,
- mGroupDetailFragment, TabState.GROUPS));
+ groupsTab.setTabListener(mTabListener);
actionBar.addTab(groupsTab);
actionBar.setDisplayShowTitleEnabled(true);
@@ -314,54 +361,68 @@
configureFragments(savedState == null);
}
- /**
- * Tab change listener that is instantiated once for each tab. Handles showing/hiding fragments.
- * TODO: Use ViewPager so that tabs can be swiped left and right. Figure out how to use the
- * support library in our app.
- */
- private class TabChangeListener implements TabListener {
- private final Fragment mBrowseListFragment;
-
- /**
- * Right pane fragment that is present on larger screen sizes (can be
- * null for smaller screen sizes).
- */
- private final Fragment mDetailFragment;
- private final TabState mTabState;
-
- public TabChangeListener(Fragment listFragment, Fragment detailFragment, TabState state) {
- mBrowseListFragment = listFragment;
- mDetailFragment = detailFragment;
- mTabState = state;
- }
-
- @Override
- public void onTabUnselected(Tab tab, FragmentTransaction ft) {
- ft.hide(mBrowseListFragment);
- if (mDetailFragment != null) {
- ft.hide(mDetailFragment);
+ private void hideFragmentOnTabUnselect(TabState newTabState, FragmentTransaction ft) {
+ switch (newTabState) {
+ case FAVORITES: {
+ ft.hide(mFavoritesFragment);
+ if (mFrequentFragment != null) {
+ ft.hide(mFrequentFragment);
+ }
+ break;
+ }
+ case ALL: {
+ ft.hide(mAllFragment);
+ if (mContactDetailFragment != null) {
+ ft.hide(mContactDetailFragment);
+ }
+ break;
+ }
+ case GROUPS: {
+ ft.hide(mGroupsFragment);
+ if (mGroupDetailFragment != null) {
+ ft.hide(mGroupDetailFragment);
+ }
+ break;
+ }
+ default: {
+ throw new IllegalStateException("Unexpected tab state: " + newTabState);
}
}
+ }
- @Override
- public void onTabSelected(Tab tab, FragmentTransaction ft) {
- ft.show(mBrowseListFragment);
- if (mDetailFragment != null) {
- ft.show(mDetailFragment);
+ private void showFragmentOnTabSelect(TabState newTabState, FragmentTransaction ft) {
+ switch (newTabState) {
+ case FAVORITES: {
+ ft.show(mFavoritesFragment);
+ if (mFrequentFragment != null) {
+ ft.show(mFrequentFragment);
+ }
+ break;
}
- setSelectedTab(mTabState);
- invalidateOptionsMenu();
- }
-
- @Override
- public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ case ALL: {
+ ft.show(mAllFragment);
+ if (mContactDetailFragment != null) {
+ ft.show(mContactDetailFragment);
+ }
+ break;
+ }
+ case GROUPS: {
+ ft.show(mGroupsFragment);
+ if (mGroupDetailFragment != null) {
+ ft.show(mGroupDetailFragment);
+ }
+ break;
+ }
+ default: {
+ throw new IllegalStateException("Unexpected tab state: " + newTabState);
+ }
}
}
private void setSelectedTab(TabState tab) {
mSelectedTab = tab;
- if (mContentPaneDisplayed) {
+ if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
switch (mSelectedTab) {
case FAVORITES:
mFavoritesView.setVisibility(View.VISIBLE);
@@ -369,7 +430,7 @@
mDetailsView.setVisibility(View.GONE);
break;
case GROUPS:
- case CONTACTS:
+ case ALL:
mFavoritesView.setVisibility(View.GONE);
mBrowserView.setVisibility(View.VISIBLE);
mDetailsView.setVisibility(View.VISIBLE);
@@ -429,6 +490,10 @@
filter = ContactListFilter.createFilterWithType(
ContactListFilter.FILTER_TYPE_STARRED);
break;
+ case ContactsRequest.ACTION_VIEW_CONTACT:
+ if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
+ getActionBar().setSelectedNavigationItem(TabState.ALL.ordinal());
+ }
}
mSearchMode = mRequest.isSearchMode();
@@ -445,7 +510,7 @@
mSearchMode = false;
}
- mListFragment.setContactsRequest(mRequest);
+ mAllFragment.setContactsRequest(mRequest);
configureContactListFragmentForRequest();
} else {
@@ -459,35 +524,18 @@
}
@Override
- public void onContactListFiltersLoaded() {
- if (mListFragment == null || !mListFragment.isAdded()) {
- return;
- }
-
- mListFragment.setFilter(mContactListFilterController.getFilter());
-
- invalidateOptionsMenuIfNeeded();
- }
-
- @Override
public void onContactListFilterChanged() {
- if (mListFragment == null || !mListFragment.isAdded()) {
+ if (mAllFragment == null || !mAllFragment.isAdded()) {
return;
}
- mListFragment.setFilter(mContactListFilterController.getFilter());
+ mAllFragment.setFilter(mContactListFilterController.getFilter());
invalidateOptionsMenuIfNeeded();
}
- @Override
- public void onContactListFilterCustomizationRequest() {
- startActivityForResult(new Intent(this, CustomContactListFilterActivity.class),
- SUBACTIVITY_CUSTOMIZE_FILTER);
- }
-
private void setupContactDetailFragment(final Uri contactLookupUri) {
- mContactDetailFragment.loadUri(contactLookupUri);
+ mContactDetailLoaderFragment.loadUri(contactLookupUri);
invalidateOptionsMenuIfNeeded();
}
@@ -504,14 +552,14 @@
switch (action) {
case START_SEARCH_MODE:
// Checking if multi fragments are being displayed
- if (mContentPaneDisplayed) {
+ if (PhoneCapabilityTester.isUsingTwoPanes(this)) {
mFavoritesView.setVisibility(View.GONE);
mBrowserView.setVisibility(View.VISIBLE);
mDetailsView.setVisibility(View.VISIBLE);
}
// Bring the contact list fragment (and detail fragment if applicable) to the front
FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.show(mContactsFragment);
+ ft.show(mAllFragment);
if (mContactDetailFragment != null) ft.show(mContactDetailFragment);
ft.commit();
clearSearch();
@@ -523,9 +571,9 @@
// If the last selected tab was not the "All contacts" tab, then hide these
// fragments because we need to show favorites or groups.
- if (mSelectedTab != null && !mSelectedTab.equals(TabState.CONTACTS)) {
+ if (mSelectedTab != null && !mSelectedTab.equals(TabState.ALL)) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.hide(mContactsFragment);
+ transaction.hide(mAllFragment);
if (mContactDetailFragment != null) transaction.hide(mContactDetailFragment);
transaction.commit();
}
@@ -545,47 +593,49 @@
private void loadSearch(String query) {
configureFragments(false /* from request */);
- mListFragment.setQueryString(query, true);
+ mAllFragment.setQueryString(query, true);
}
private void configureContactListFragmentForRequest() {
Uri contactUri = mRequest.getContactUri();
if (contactUri != null) {
- mListFragment.setSelectedContactUri(contactUri);
+ mAllFragment.setSelectedContactUri(contactUri);
}
- mListFragment.setSearchMode(mRequest.isSearchMode());
- mListFragment.setQueryString(mRequest.getQueryString(), false);
+ mAllFragment.setSearchMode(mRequest.isSearchMode());
+ mAllFragment.setQueryString(mRequest.getQueryString(), false);
if (mRequest.isDirectorySearchEnabled()) {
- mListFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT);
+ mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_DEFAULT);
} else {
- mListFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
+ mAllFragment.setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
}
- if (mContactListFilterController.isLoaded()) {
- mListFragment.setFilter(mContactListFilterController.getFilter());
+ if (mContactListFilterController.isInitialized()) {
+ mAllFragment.setFilter(mContactListFilterController.getFilter());
}
}
private void configureContactListFragment() {
- mListFragment.setSearchMode(mSearchMode);
+ mAllFragment.setSearchMode(mSearchMode);
- mListFragment.setVisibleScrollbarEnabled(!mSearchMode);
- mListFragment.setVerticalScrollbarPosition(
- mContentPaneDisplayed
+ final boolean useTwoPane = PhoneCapabilityTester.isUsingTwoPanes(this);
+ mAllFragment.setVisibleScrollbarEnabled(!mSearchMode);
+ mAllFragment.setVerticalScrollbarPosition(
+ useTwoPane
? View.SCROLLBAR_POSITION_LEFT
: View.SCROLLBAR_POSITION_RIGHT);
- mListFragment.setSelectionVisible(mContentPaneDisplayed);
- mListFragment.setQuickContactEnabled(!mContentPaneDisplayed);
+ mAllFragment.setSelectionVisible(useTwoPane);
+ mAllFragment.setQuickContactEnabled(!useTwoPane);
}
private void configureGroupListFragment() {
+ final boolean useTwoPane = PhoneCapabilityTester.isUsingTwoPanes(this);
mGroupsFragment.setVerticalScrollbarPosition(
- mContentPaneDisplayed
+ useTwoPane
? View.SCROLLBAR_POSITION_LEFT
: View.SCROLLBAR_POSITION_RIGHT);
- mGroupsFragment.setSelectionVisible(mContentPaneDisplayed);
+ mGroupsFragment.setSelectionVisible(useTwoPane);
}
@Override
@@ -609,12 +659,12 @@
if (mainView != null) {
mainView.setVisibility(View.VISIBLE);
}
- if (mListFragment != null) {
- mListFragment.setEnabled(true);
+ if (mAllFragment != null) {
+ mAllFragment.setEnabled(true);
}
} else {
- if (mListFragment != null) {
- mListFragment.setEnabled(false);
+ if (mAllFragment != null) {
+ mAllFragment.setEnabled(false);
}
if (mContactsUnavailableFragment == null) {
mContactsUnavailableFragment = new ContactsUnavailableFragment();
@@ -640,14 +690,14 @@
@Override
public void onSelectionChange() {
- if (mContentPaneDisplayed) {
- setupContactDetailFragment(mListFragment.getSelectedContactUri());
+ if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) {
+ setupContactDetailFragment(mAllFragment.getSelectedContactUri());
}
}
@Override
public void onViewContactAction(Uri contactLookupUri) {
- if (mContentPaneDisplayed) {
+ if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) {
setupContactDetailFragment(contactLookupUri);
} else {
startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri));
@@ -711,34 +761,64 @@
@Override
public void onInvalidSelection() {
ContactListFilter filter;
- ContactListFilter currentFilter = mListFragment.getFilter();
+ ContactListFilter currentFilter = mAllFragment.getFilter();
if (currentFilter != null
&& currentFilter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) {
filter = ContactListFilter.createFilterWithType(
ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
- mListFragment.setFilter(filter);
+ mAllFragment.setFilter(filter);
} else {
filter = ContactListFilter.createFilterWithType(
ContactListFilter.FILTER_TYPE_SINGLE_CONTACT);
- mListFragment.setFilter(filter, false);
+ mAllFragment.setFilter(filter, false);
}
mContactListFilterController.setContactListFilter(filter, true);
}
}
- private class ContactDetailFragmentListener implements ContactDetailFragment.Listener {
+ private class ContactDetailLoaderFragmentListener implements ContactLoaderFragmentListener {
@Override
public void onContactNotFound() {
// Nothing needs to be done here
}
@Override
+ public void onDetailsLoaded(final ContactLoader.Result result) {
+ if (result == null) {
+ return;
+ }
+ // Since {@link FragmentTransaction}s cannot be done in the onLoadFinished() of the
+ // {@link LoaderCallbacks}, then post this {@link Runnable} to the {@link Handler}
+ // on the main thread to execute later.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (!mContactDetailLayoutController.isInitialized()) {
+ mContactDetailLayoutController.setContactDetailFragment(
+ mContactDetailFragment);
+ mContactDetailLayoutController.setContactDetailUpdatesFragment(
+ mContactDetailUpdatesFragment);
+ mContactDetailLayoutController.initialize();
+ }
+ mContactDetailLayoutController.setContactData(result);
+ }
+ });
+ }
+
+ @Override
public void onEditRequested(Uri contactLookupUri) {
startActivityForResult(
new Intent(Intent.ACTION_EDIT, contactLookupUri), SUBACTIVITY_EDIT_CONTACT);
}
@Override
+ public void onDeleteRequested(Uri contactUri) {
+ ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false);
+ }
+ }
+
+ public class ContactDetailFragmentListener implements ContactDetailFragment.Listener {
+ @Override
public void onItemClicked(Intent intent) {
try {
startActivity(intent);
@@ -748,11 +828,6 @@
}
@Override
- public void onDeleteRequested(Uri contactUri) {
- ContactDeletionInteraction.start(PeopleActivity.this, contactUri, false);
- }
-
- @Override
public void onCreateRawContactRequested(ArrayList<ContentValues> values, Account account) {
Toast.makeText(PeopleActivity.this, R.string.toast_making_personal_copy,
Toast.LENGTH_LONG).show();
@@ -795,7 +870,7 @@
implements StrequentContactListFragment.Listener {
@Override
public void onContactSelected(Uri contactUri) {
- if (mContentPaneDisplayed) {
+ if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) {
setupContactDetailFragment(contactUri);
} else {
startActivity(new Intent(Intent.ACTION_VIEW, contactUri));
@@ -807,7 +882,7 @@
@Override
public void onViewGroupAction(Uri groupUri) {
- if (mContentPaneDisplayed) {
+ if (PhoneCapabilityTester.isUsingTwoPanes(PeopleActivity.this)) {
setupGroupDetailFragment(groupUri);
} else {
Intent intent = new Intent(PeopleActivity.this, GroupDetailActivity.class);
@@ -880,16 +955,6 @@
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.actions, menu);
- MenuItem searchMenuItem = menu.findItem(R.id.menu_search);
- if (searchMenuItem != null && searchMenuItem.getActionView() instanceof SearchView) {
- SearchView searchView = (SearchView) searchMenuItem.getActionView();
- searchView.setQueryHint(getString(R.string.hint_findContacts));
- searchView.setIconifiedByDefault(false);
-
- if (mActionBarAdapter != null) {
- mActionBarAdapter.setSearchView(searchView);
- }
- }
// On narrow screens we specify a NEW group button in the {@link ActionBar}, so that
// it can be in the overflow menu. On wide screens, we use a custom view because we need
@@ -921,11 +986,12 @@
return true;
}
- if (mListFragment != null && mListFragment.isOptionsMenuChanged()) {
+ if (mAllFragment != null && mAllFragment.isOptionsMenuChanged()) {
return true;
}
- if (mContactDetailFragment != null && mContactDetailFragment.isOptionsMenuChanged()) {
+ if (mContactDetailLoaderFragment != null &&
+ mContactDetailLoaderFragment.isOptionsMenuChanged()) {
return true;
}
@@ -953,17 +1019,15 @@
if (mActionBarAdapter.isSearchMode()) {
addContactMenu.setVisible(false);
addGroupMenu.setVisible(false);
- // If search is normally in the overflow menu, when we are in search
- // mode, hide this option.
- if (mActionBarAdapter.isSearchInOverflowMenu()) {
- searchMenu.setVisible(false);
+ if (searchMenu != null) {
+ searchMenu.setVisible(false); // Don't show the search menu in search mode.
}
} else {
switch (mSelectedTab) {
case FAVORITES:
// TODO: Fall through until we determine what the menu items should be for
// this tab
- case CONTACTS:
+ case ALL:
addContactMenu.setVisible(true);
addGroupMenu.setVisible(false);
break;
@@ -991,8 +1055,8 @@
return true;
}
case R.id.menu_contacts_filter: {
- final Intent intent = new Intent(this, CustomContactListFilterActivity.class);
- startActivityForResult(intent, SUBACTIVITY_CUSTOMIZE_FILTER);
+ final Intent intent = new Intent(this, AccountFilterActivity.class);
+ startActivityForResult(intent, SUBACTIVITY_ACCOUNT_FILTER);
return true;
}
case R.id.menu_search: {
@@ -1061,8 +1125,8 @@
@Override
public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
boolean globalSearch) {
- if (mListFragment != null && mListFragment.isAdded() && !globalSearch) {
- mListFragment.startSearch(initialQuery);
+ if (mAllFragment != null && mAllFragment.isAdded() && !globalSearch) {
+ mAllFragment.startSearch(initialQuery);
} else {
super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
}
@@ -1071,25 +1135,42 @@
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
+ case SUBACTIVITY_ACCOUNT_FILTER: {
+ if (resultCode == Activity.RESULT_OK) {
+ ContactListFilter filter = (ContactListFilter) data.getParcelableExtra(
+ AccountFilterActivity.KEY_EXTRA_CONTACT_LIST_FILTER);
+ if (filter == null) {
+ return;
+ }
+ // If this is a custom filter, launch the activity to customize the display list
+ if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
+ final Intent intent = new Intent(this,
+ CustomContactListFilterActivity.class);
+ startActivityForResult(intent, SUBACTIVITY_CUSTOMIZE_FILTER);
+ } else {
+ mContactListFilterController.setContactListFilter(filter, true);
+ }
+ }
+ break;
+ }
case SUBACTIVITY_CUSTOMIZE_FILTER: {
if (resultCode == Activity.RESULT_OK) {
mContactListFilterController.selectCustomFilter();
}
break;
}
-
case SUBACTIVITY_EDIT_CONTACT:
case SUBACTIVITY_NEW_CONTACT: {
- if (resultCode == RESULT_OK && mContentPaneDisplayed) {
+ if (resultCode == RESULT_OK && PhoneCapabilityTester.isUsingTwoPanes(this)) {
mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
- mListFragment.reloadDataAndSetSelectedUri(data.getData());
+ mAllFragment.reloadDataAndSetSelectedUri(data.getData());
}
break;
}
case SUBACTIVITY_NEW_GROUP:
case SUBACTIVITY_EDIT_GROUP: {
- if (resultCode == RESULT_OK && mContentPaneDisplayed) {
+ if (resultCode == RESULT_OK && PhoneCapabilityTester.isUsingTwoPanes(this)) {
mRequest.setActionCode(ContactsRequest.ACTION_GROUP);
mGroupsFragment.setSelectedUri(data.getData());
}
@@ -1100,7 +1181,7 @@
// anymore
case ContactEntryListFragment.ACTIVITY_REQUEST_CODE_PICKER:
if (resultCode == RESULT_OK) {
- mListFragment.onPickerResult(data);
+ mAllFragment.onPickerResult(data);
}
// TODO fix or remove multipicker code
@@ -1115,7 +1196,7 @@
@Override
public boolean onContextItemSelected(MenuItem item) {
- ContextMenuAdapter menuAdapter = mListFragment.getContextMenuAdapter();
+ ContextMenuAdapter menuAdapter = mAllFragment.getContextMenuAdapter();
if (menuAdapter != null) {
return menuAdapter.onContextItemSelected(item);
}
@@ -1194,6 +1275,9 @@
if (mActionBarAdapter != null) {
mActionBarAdapter.onSaveInstanceState(outState);
}
+ if (mContactDetailLayoutController != null) {
+ mContactDetailLayoutController.onSaveInstanceState(outState);
+ }
}
@Override
@@ -1203,6 +1287,9 @@
if (mActionBarAdapter != null) {
mActionBarAdapter.onRestoreInstanceState(inState);
}
+ if (mContactDetailLayoutController != null) {
+ mContactDetailLayoutController.onRestoreInstanceState(inState);
+ }
}
@Override
@@ -1212,7 +1299,7 @@
// Visible for testing
public ContactBrowseListFragment getListFragment() {
- return mListFragment;
+ return mAllFragment;
}
// Visible for testing
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index 9dc50f2..11f8965 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -20,7 +20,10 @@
import com.android.contacts.CallDetailActivity;
import com.android.contacts.ContactPhotoManager;
import com.android.contacts.ContactsUtils;
+import com.android.contacts.PhoneCallDetails;
+import com.android.contacts.PhoneCallDetailsHelper;
import com.android.contacts.R;
+import com.android.contacts.activities.DialtactsActivity;
import com.android.contacts.activities.DialtactsActivity.ViewPagerVisibilityListener;
import com.android.contacts.util.ExpirableCache;
import com.android.internal.telephony.CallerInfo;
@@ -61,13 +64,12 @@
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.AdapterView;
-import android.widget.ImageView;
import android.widget.ListView;
import android.widget.QuickContactBadge;
-import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.LinkedList;
@@ -92,9 +94,6 @@
Calls.DATE,
Calls.DURATION,
Calls.TYPE,
- Calls.CACHED_NAME,
- Calls.CACHED_NUMBER_TYPE,
- Calls.CACHED_NUMBER_LABEL,
Calls.COUNTRY_ISO};
public static final int ID = 0;
@@ -102,10 +101,7 @@
public static final int DATE = 2;
public static final int DURATION = 3;
public static final int CALL_TYPE = 4;
- public static final int CALLER_NAME = 5;
- public static final int CALLER_NUMBERTYPE = 6;
- public static final int CALLER_NUMBERLABEL = 7;
- public static final int COUNTRY_ISO = 8;
+ public static final int COUNTRY_ISO = 5;
}
/** The query to use for the phones table */
@@ -147,7 +143,7 @@
private String mCurrentCountryIso;
private boolean mScrollToTop;
- private boolean mShowMenu;
+ private boolean mShowOptionsMenu;
public static final class ContactInfo {
public long personId;
@@ -176,8 +172,25 @@
/** Adapter class to fill in data for the Call Log */
public final class CallLogAdapter extends GroupingListAdapter
implements Runnable, ViewTreeObserver.OnPreDrawListener, View.OnClickListener {
+ /** The time in millis to delay starting the thread processing requests. */
+ private static final int START_PROCESSING_REQUESTS_DELAY_MILLIS = 1000;
+
+ /**
+ * A cache of the contact details for the phone numbers in the call log.
+ * <p>
+ * The content of the cache is expired (but not purged) whenever the application comes to
+ * the foreground.
+ */
private ExpirableCache<String, ContactInfo> mContactInfoCache;
+
+ /**
+ * List of requests to update contact details.
+ * <p>
+ * The requests are added when displaying the contacts and are processed by a background
+ * thread.
+ */
private final LinkedList<CallerInfoQuery> mRequests;
+
private volatile boolean mDone;
private boolean mLoading = true;
private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
@@ -220,7 +233,8 @@
@Override
public boolean onPreDraw() {
if (mFirst) {
- mHandler.sendEmptyMessageDelayed(START_THREAD, 1000);
+ mHandler.sendEmptyMessageDelayed(START_THREAD,
+ START_PROCESSING_REQUESTS_DELAY_MILLIS);
mFirst = false;
}
return true;
@@ -234,9 +248,7 @@
notifyDataSetChanged();
break;
case START_THREAD:
- if (!mRequestProcessingDisabled) {
- startRequestProcessing();
- }
+ startRequestProcessing();
break;
}
}
@@ -249,23 +261,25 @@
mRequests = new LinkedList<CallerInfoQuery>();
mPreDrawListener = null;
- Drawable drawableIncoming = getResources().getDrawable(
+ Drawable incomingDrawable = getResources().getDrawable(
R.drawable.ic_call_log_list_incoming_call);
- Drawable drawableOutgoing = getResources().getDrawable(
+ Drawable outgoingDrawable = getResources().getDrawable(
R.drawable.ic_call_log_list_outgoing_call);
- Drawable drawableMissed = getResources().getDrawable(
+ Drawable missedDrawable = getResources().getDrawable(
R.drawable.ic_call_log_list_missed_call);
- Drawable drawableVoicemail = getResources().getDrawable(
+ Drawable voicemailDrawable = getResources().getDrawable(
R.drawable.ic_call_log_list_voicemail);
- Drawable drawableCall = getResources().getDrawable(
+ Drawable callDrawable = getResources().getDrawable(
R.drawable.ic_call_log_list_action_call);
- Drawable drawablePlay = getResources().getDrawable(
+ Drawable playDrawable = getResources().getDrawable(
R.drawable.ic_call_log_list_action_play);
mContactPhotoManager = ContactPhotoManager.getInstance(getActivity());
- mCallLogViewsHelper = new CallLogListItemHelper(getResources(), mVoiceMailNumber,
- drawableIncoming, drawableOutgoing, drawableMissed, drawableVoicemail,
- drawableCall, drawablePlay);
+ PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
+ getActivity(), getResources(), mVoiceMailNumber, incomingDrawable,
+ outgoingDrawable, missedDrawable, voicemailDrawable);
+ mCallLogViewsHelper = new CallLogListItemHelper(phoneCallDetailsHelper, callDrawable,
+ playDrawable);
}
/**
@@ -296,6 +310,10 @@
}
public void startRequestProcessing() {
+ if (mRequestProcessingDisabled) {
+ return;
+ }
+
mDone = false;
mCallerIdThread = new Thread(this);
mCallerIdThread.setPriority(Thread.MIN_PRIORITY);
@@ -320,33 +338,7 @@
mContactInfoCache.expireAll();
}
- private void updateCallLog(CallerInfoQuery ciq, ContactInfo ci) {
- // Check if they are different. If not, don't update.
- if (TextUtils.equals(ciq.name, ci.name)
- && TextUtils.equals(ciq.numberLabel, ci.label)
- && ciq.numberType == ci.type
- && ciq.photoId == ci.photoId
- && ciq.lookupKey == ci.lookupKey) {
- return;
- }
- ContentValues values = new ContentValues(3);
- values.put(Calls.CACHED_NAME, ci.name);
- values.put(Calls.CACHED_NUMBER_TYPE, ci.type);
- values.put(Calls.CACHED_NUMBER_LABEL, ci.label);
-
- try {
- getActivity().getContentResolver().update(Calls.CONTENT_URI_WITH_VOICEMAIL, values,
- Calls.NUMBER + "='" + ciq.number + "'", null);
- } catch (SQLiteDiskIOException e) {
- Log.w(TAG, "Exception while updating call info", e);
- } catch (SQLiteFullException e) {
- Log.w(TAG, "Exception while updating call info", e);
- } catch (SQLiteDatabaseCorruptException e) {
- Log.w(TAG, "Exception while updating call info", e);
- }
- }
-
- private void enqueueRequest(String number, int position,
+ private void enqueueRequest(String number, boolean immediate, int position,
String name, int numberType, String numberLabel, long photoId, String lookupKey) {
CallerInfoQuery ciq = new CallerInfoQuery();
ciq.number = number;
@@ -360,6 +352,10 @@
mRequests.add(ciq);
mRequests.notifyAll();
}
+ if (mFirst && immediate) {
+ startRequestProcessing();
+ mFirst = false;
+ }
}
private boolean queryContactInfo(CallerInfoQuery ciq) {
@@ -479,9 +475,6 @@
needNotify = true;
}
}
- if (info != null) {
- updateCallLog(ciq, info);
- }
return needNotify;
}
@@ -590,7 +583,7 @@
@VisibleForTesting
@Override
public void bindStandAloneView(View view, Context context, Cursor cursor) {
- bindView(context, view, cursor);
+ bindView(view, cursor, 1);
}
@VisibleForTesting
@@ -606,7 +599,7 @@
@VisibleForTesting
@Override
public void bindChildView(View view, Context context, Cursor cursor) {
- bindView(context, view, cursor);
+ bindView(view, cursor, 1);
}
@VisibleForTesting
@@ -623,42 +616,31 @@
@Override
public void bindGroupView(View view, Context context, Cursor cursor, int groupSize,
boolean expanded) {
- final CallLogListItemViews views = (CallLogListItemViews) view.getTag();
- int groupIndicator = expanded
- ? com.android.internal.R.drawable.expander_ic_maximized
- : com.android.internal.R.drawable.expander_ic_minimized;
- views.groupIndicator.setImageResource(groupIndicator);
- views.groupSize.setText("(" + groupSize + ")");
- bindView(context, view, cursor);
+ bindView(view, cursor, groupSize);
}
private void findAndCacheViews(View view) {
-
- // Get the views to bind to
- CallLogListItemViews views = new CallLogListItemViews();
- views.line1View = (TextView) view.findViewById(R.id.line1);
- views.labelView = (TextView) view.findViewById(R.id.label);
- views.numberView = (TextView) view.findViewById(R.id.number);
- views.dateView = (TextView) view.findViewById(R.id.date);
- views.iconView = (ImageView) view.findViewById(R.id.call_type_icon);
- views.callView = (ImageView) view.findViewById(R.id.call_icon);
+ // Get the views to bind to.
+ CallLogListItemViews views = CallLogListItemViews.fromView(view);
if (views.callView != null) {
views.callView.setOnClickListener(this);
}
- views.groupIndicator = (ImageView) view.findViewById(R.id.groupIndicator);
- views.groupSize = (TextView) view.findViewById(R.id.groupSize);
- views.photoView = (QuickContactBadge) view.findViewById(R.id.contact_photo);
view.setTag(views);
}
- public void bindView(Context context, View view, Cursor c) {
+ /**
+ * Binds the views in the entry to the data in the call log.
+ *
+ * @param view the view corresponding to this entry
+ * @param c the cursor pointing to the entry in the call log
+ * @param count the number of entries in the current item, greater than 1 if it is a group
+ */
+ private void bindView(View view, Cursor c, int count) {
final CallLogListItemViews views = (CallLogListItemViews) view.getTag();
String number = c.getString(CallLogQuery.NUMBER);
- String formattedNumber = null;
- String callerName = c.getString(CallLogQuery.CALLER_NAME);
- int callerNumberType = c.getInt(CallLogQuery.CALLER_NUMBERTYPE);
- String callerNumberLabel = c.getString(CallLogQuery.CALLER_NUMBERLABEL);
+ long date = c.getLong(CallLogQuery.DATE);
+ final String formattedNumber;
String countryIso = c.getString(CallLogQuery.COUNTRY_ISO);
// Store away the number so we can call it directly if you click on the call icon
if (views.callView != null) {
@@ -673,29 +655,21 @@
// Mark it as empty and queue up a request to find the name
// The db request should happen on a non-UI thread
info = ContactInfo.EMPTY;
+ // Format the cached call_log phone number
+ formattedNumber = formatPhoneNumber(number, null, countryIso);
mContactInfoCache.put(number, info);
Log.d(TAG, "Contact info missing: " + number);
- enqueueRequest(number, c.getPosition(),
- callerName, callerNumberType, callerNumberLabel, 0L, "");
+ // Request the contact details immediately since they are currently missing.
+ enqueueRequest(number, true, c.getPosition(), "", 0, "", 0L, "");
} else if (info != ContactInfo.EMPTY) { // Has been queried
- // Check if any data is different from the data cached in the
- // calls db. If so, queue the request so that we can update
- // the calls db.
- if (!TextUtils.equals(info.name, callerName)
- || info.type != callerNumberType
- || !TextUtils.equals(info.label, callerNumberLabel)) {
- // Something is amiss, so sync up.
- Log.w(TAG, "Contact info inconsistent: " + number);
- enqueueRequest(number, c.getPosition(),
- callerName, callerNumberType, callerNumberLabel, info.photoId,
- info.lookupKey);
- } else if (cachedInfo.isExpired()) {
+ if (cachedInfo.isExpired()) {
Log.d(TAG, "Contact info expired: " + number);
// Put it back in the cache, therefore marking it as not expired, so that other
// entries with the same number will not re-request it.
mContactInfoCache.put(number, info);
- // The contact info is no longer up to date, we should request it.
- enqueueRequest(number, c.getPosition(), info.name, info.type, info.label,
+ // The contact info is no longer up to date, we should request it. However, we
+ // do not need to request them immediately.
+ enqueueRequest(number, false, c.getPosition(), info.name, info.type, info.label,
info.photoId, info.lookupKey);
}
@@ -705,6 +679,9 @@
formatPhoneNumber(info.number, info.normalizedNumber, countryIso);
}
formattedNumber = info.formattedNumber;
+ } else {
+ // Format the cached call_log phone number
+ formattedNumber = formatPhoneNumber(number, null, countryIso);
}
long contactId = info.personId;
@@ -713,36 +690,21 @@
String label = info.label;
long photoId = info.photoId;
String lookupKey = info.lookupKey;
- // If there's no name cached in our hashmap, but there's one in the
- // calls db, use the one in the calls db. Otherwise the name in our
- // hashmap is more recent, so it has precedence.
- if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(callerName)) {
- name = callerName;
- ntype = callerNumberType;
- label = callerNumberLabel;
-
- // Format the cached call_log phone number
- formattedNumber = formatPhoneNumber(number, null, countryIso);
- }
-
// Assumes the call back feature is on most of the
// time. For private and unknown numbers: hide it.
if (views.callView != null) {
views.callView.setVisibility(View.VISIBLE);
}
- if (!TextUtils.isEmpty(name)) {
- mCallLogViewsHelper.setContactNameLabelAndNumber(views, name, number, ntype, label,
- formattedNumber);
+ int[] callTypes = getCallTypes(c, count);
+ final PhoneCallDetails details;
+ if (TextUtils.isEmpty(name)) {
+ details = new PhoneCallDetails(number, formattedNumber, callTypes, date);
} else {
- // TODO: Do we need to format the number again? Is formattedNumber already storing
- // this value?
- mCallLogViewsHelper.setContactNumberOnly(views, number,
- formatPhoneNumber(number, null, countryIso));
+ details = new PhoneCallDetails(number, formattedNumber, callTypes, date, name,
+ ntype, label);
}
- mCallLogViewsHelper.setDate(views, c.getLong(CallLogQuery.DATE),
- System.currentTimeMillis());
- mCallLogViewsHelper.setCallType(views, c.getInt(CallLogQuery.CALL_TYPE));
+ mCallLogViewsHelper.setPhoneCallDetails(views, details , true);
if (views.photoView != null) {
bindQuickContact(views.photoView, photoId, contactId, lookupKey);
}
@@ -756,6 +718,24 @@
}
}
+ /**
+ * Returns the call types for the given number of items in the cursor.
+ * <p>
+ * It uses the next {@code count} rows in the cursor to extract the types.
+ * <p>
+ * It position in the cursor is unchanged by this function.
+ */
+ private int[] getCallTypes(Cursor cursor, int count) {
+ int position = cursor.getPosition();
+ int[] callTypes = new int[count];
+ for (int index = 0; index < count; ++index) {
+ callTypes[index] = cursor.getInt(CallLogQuery.CALL_TYPE);
+ cursor.moveToNext();
+ }
+ cursor.moveToPosition(position);
+ return callTypes;
+ }
+
private void bindQuickContact(QuickContactBadge view, long photoId, long contactId,
String lookupKey) {
view.assignContactUri(getContactUri(contactId, lookupKey));
@@ -775,6 +755,10 @@
public void disableRequestProcessingForTest() {
mRequestProcessingDisabled = true;
}
+
+ public void injectContactInfoForTest(String number, ContactInfo contactInfo) {
+ mContactInfoCache.put(number, contactInfo);
+ }
}
private static final class QueryHandler extends AsyncQueryHandler {
@@ -954,7 +938,14 @@
@Override
public void onPrepareOptionsMenu(Menu menu) {
- menu.findItem(R.id.delete_all).setVisible(mShowMenu);
+ menu.findItem(R.id.delete_all).setVisible(mShowOptionsMenu);
+ final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings_call_log);
+ if (mShowOptionsMenu) {
+ callSettingsMenuItem.setVisible(true);
+ callSettingsMenuItem.setIntent(DialtactsActivity.getCallSettingsIntent());
+ } else {
+ callSettingsMenuItem.setVisible(false);
+ }
}
@Override
@@ -1189,6 +1180,6 @@
@Override
public void onVisibilityChanged(boolean visible) {
- mShowMenu = visible;
+ mShowOptionsMenu = visible;
}
}
diff --git a/src/com/android/contacts/calllog/CallLogListItemHelper.java b/src/com/android/contacts/calllog/CallLogListItemHelper.java
index 56399c0..e4630e9 100644
--- a/src/com/android/contacts/calllog/CallLogListItemHelper.java
+++ b/src/com/android/contacts/calllog/CallLogListItemHelper.java
@@ -16,220 +16,64 @@
package com.android.contacts.calllog;
-import com.android.contacts.R;
+import com.android.contacts.PhoneCallDetails;
+import com.android.contacts.PhoneCallDetailsHelper;
import com.android.internal.telephony.CallerInfo;
-import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.view.View;
-import android.view.ViewGroup;
/**
* Helper class to fill in the views of a call log entry.
*/
/*package*/ class CallLogListItemHelper {
- /** The resources used to look up strings. */
- private final Resources mResources;
- /** The voicemail number. */
- private final String mVoiceMailNumber;
- /** Icon for incoming calls. */
- private final Drawable mDrawableIncoming;
- /** Icon for outgoing calls. */
- private final Drawable mDrawableOutgoing;
- /** Icon for missed calls. */
- private final Drawable mDrawableMissed;
- /** Icon for voicemails. */
- private final Drawable mDrawableVoicemail;
+ /** Helper for populating the details of a phone call. */
+ private final PhoneCallDetailsHelper mPhoneCallDetailsHelper;
/** Icon for the call action. */
- private final Drawable mDrawableCall;
+ private final Drawable mCallDrawable;
/** Icon for the play action. */
- private final Drawable mDrawablePlay;
+ private final Drawable mPlayDrawable;
/**
* Creates a new helper instance.
*
- * @param resources used to look up strings
- * @param voicemailNumber the voicemail number, used to determine if a call was to voicemail
- * @param drawableIncoming the icon drawn besides an incoming call entry
- * @param drawableOutgoing the icon drawn besides an outgoing call entry
- * @param drawableMissed the icon drawn besides a missed call entry
+ * @param phoneCallDetailsHelper used to set the details of a phone call
+ * @param callDrawable used to render the call button, for calling back a person
+ * @param playDrawable used to render the play button, for playing a voicemail
*/
- public CallLogListItemHelper(Resources resources, String voicemailNumber,
- Drawable drawableIncoming, Drawable drawableOutgoing, Drawable drawableMissed,
- Drawable drawableVoicemail, Drawable drawableCall, Drawable drawablePlay) {
- mResources = resources;
- mVoiceMailNumber = voicemailNumber;
- mDrawableIncoming = drawableIncoming;
- mDrawableOutgoing = drawableOutgoing;
- mDrawableMissed = drawableMissed;
- mDrawableVoicemail = drawableVoicemail;
- mDrawableCall = drawableCall;
- mDrawablePlay = drawablePlay;
+ public CallLogListItemHelper(PhoneCallDetailsHelper phoneCallDetailsHelper,
+ Drawable callDrawable, Drawable playDrawable) {
+ mPhoneCallDetailsHelper = phoneCallDetailsHelper;
+ mCallDrawable = callDrawable;
+ mPlayDrawable = playDrawable;
}
/**
* Sets the name, label, and number for a contact.
*
* @param views the views to populate
- * @param name the name of the contact
- * @param number the number of the contact
- * @param numberType the type of the number as it appears in the contact, e.g.,
- * {@link Phone#TYPE_HOME}
- * @param label the label of the number, only used if numberType is {@link Phone#TYPE_CUSTOM}
- * @param formattedNumber the formatted version of the number above
+ * @param details the details of a phone call needed to fill in the data
+ * @param useIcons whether to use icons to show the type of the call
*/
- public void setContactNameLabelAndNumber(CallLogListItemViews views, String name, String number,
- int numberType, String label, String formattedNumber) {
- views.line1View.setText(name);
- views.labelView.setVisibility(View.VISIBLE);
-
- // "type" and "label" are currently unused for SIP addresses.
- CharSequence numberLabel = null;
- if (!PhoneNumberUtils.isUriNumber(number)) {
- numberLabel = Phone.getTypeLabel(mResources, numberType, label);
- }
- views.numberView.setVisibility(View.VISIBLE);
- views.numberView.setText(formattedNumber);
- if (!TextUtils.isEmpty(numberLabel)) {
- views.labelView.setText(numberLabel);
- views.labelView.setVisibility(View.VISIBLE);
-
- // Zero out the numberView's left margin (see below)
- ViewGroup.MarginLayoutParams numberLP =
- (ViewGroup.MarginLayoutParams) views.numberView.getLayoutParams();
- numberLP.leftMargin = 0;
- views.numberView.setLayoutParams(numberLP);
- } else {
- // There's nothing to display in views.labelView, so hide it.
- // We can't set it to View.GONE, since it's the anchor for
- // numberView in the RelativeLayout, so make it INVISIBLE.
- // Also, we need to manually *subtract* some left margin from
- // numberView to compensate for the right margin built in to
- // labelView (otherwise the number will be indented by a very
- // slight amount).
- // TODO: a cleaner fix would be to contain both the label and
- // number inside a LinearLayout, and then set labelView *and*
- // its padding to GONE when there's no label to display.
- views.labelView.setText(null);
- views.labelView.setVisibility(View.INVISIBLE);
-
- ViewGroup.MarginLayoutParams labelLP =
- (ViewGroup.MarginLayoutParams) views.labelView.getLayoutParams();
- ViewGroup.MarginLayoutParams numberLP =
- (ViewGroup.MarginLayoutParams) views.numberView.getLayoutParams();
- // Equivalent to setting android:layout_marginLeft in XML
- numberLP.leftMargin = -labelLP.rightMargin;
- views.numberView.setLayoutParams(numberLP);
- }
- }
-
- /**
- * Sets the number in a call log entry.
- * <p>
- * To be used if we do not have a contact with this number.
- *
- * @param views the views to populate
- * @param number the number of the contact
- * @param formattedNumber the formatted version of the number above
- */
- public void setContactNumberOnly(final CallLogListItemViews views, String number,
- String formattedNumber) {
- if (number.equals(CallerInfo.UNKNOWN_NUMBER)) {
- number = mResources.getString(R.string.unknown);
- if (views.callView != null) {
- views.callView.setVisibility(View.INVISIBLE);
- }
- } else if (number.equals(CallerInfo.PRIVATE_NUMBER)) {
- number = mResources.getString(R.string.private_num);
- if (views.callView != null) {
- views.callView.setVisibility(View.INVISIBLE);
- }
- } else if (number.equals(CallerInfo.PAYPHONE_NUMBER)) {
- number = mResources.getString(R.string.payphone);
- if (views.callView != null) {
- views.callView.setVisibility(View.INVISIBLE);
- }
- } else if (PhoneNumberUtils.extractNetworkPortion(number)
- .equals(mVoiceMailNumber)) {
- number = mResources.getString(R.string.voicemail);
- } else {
- // Just a phone number, so use the formatted version of the number.
- number = formattedNumber;
- }
-
- views.line1View.setText(number);
- views.numberView.setVisibility(View.GONE);
- views.labelView.setVisibility(View.GONE);
- }
-
- /**
- * Sets the date in the views.
- *
- * @param views the views to populate
- * @param date the date of the call log entry
- * @param now the current time relative to which the date should be formatted
- */
- public void setDate(final CallLogListItemViews views, long date, long now) {
- views.dateView.setText(
- DateUtils.getRelativeTimeSpanString(
- date, now, DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE));
- }
-
- /**
- * Sets the type of the call in the views.
- *
- * @param views the views to populate
- * @param type the type of call log entry, e.g., {@link Calls#INCOMING_TYPE}
- */
- public void setCallType(final CallLogListItemViews views, int type) {
- if (views.iconView != null) {
- // Set the call type icon.
- Drawable drawable = null;
- switch (type) {
- case Calls.INCOMING_TYPE:
- drawable = mDrawableIncoming;
- break;
-
- case Calls.OUTGOING_TYPE:
- drawable = mDrawableOutgoing;
- break;
-
- case Calls.MISSED_TYPE:
- drawable = mDrawableMissed;
- break;
-
- case Calls.VOICEMAIL_TYPE:
- drawable = mDrawableVoicemail;
- break;
-
- default:
- throw new IllegalArgumentException("invalid call type: " + type);
- }
- views.iconView.setImageDrawable(drawable);
- }
+ public void setPhoneCallDetails(CallLogListItemViews views, PhoneCallDetails details,
+ boolean useIcons) {
+ mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details, useIcons);
if (views.callView != null) {
- // Set the action icon.
- Drawable drawable = null;
- switch (type) {
- case Calls.INCOMING_TYPE:
- case Calls.OUTGOING_TYPE:
- case Calls.MISSED_TYPE:
- drawable = mDrawableCall;
- break;
-
- case Calls.VOICEMAIL_TYPE:
- drawable = mDrawablePlay;
- break;
-
- default:
- throw new IllegalArgumentException("invalid call type: " + type);
- }
- views.callView.setImageDrawable(drawable);
+ // The type of icon, call or play, is determined by the first call in the group.
+ views.callView.setImageDrawable(
+ details.callTypes[0] == Calls.VOICEMAIL_TYPE ? mPlayDrawable : mCallDrawable);
+ views.callView.setVisibility(
+ canPlaceCallsTo(details.number) ? View.VISIBLE : View.INVISIBLE);
}
}
+
+ /** Returns true if it is possible to place a call to the given number. */
+ public boolean canPlaceCallsTo(CharSequence number) {
+ return !(TextUtils.isEmpty(number)
+ || number.equals(CallerInfo.UNKNOWN_NUMBER)
+ || number.equals(CallerInfo.PRIVATE_NUMBER)
+ || number.equals(CallerInfo.PAYPHONE_NUMBER));
+ }
}
diff --git a/src/com/android/contacts/calllog/CallLogListItemViews.java b/src/com/android/contacts/calllog/CallLogListItemViews.java
index 7264c96..0cf15cc 100644
--- a/src/com/android/contacts/calllog/CallLogListItemViews.java
+++ b/src/com/android/contacts/calllog/CallLogListItemViews.java
@@ -16,37 +16,39 @@
package com.android.contacts.calllog;
+import com.android.contacts.PhoneCallDetailsViews;
+import com.android.contacts.R;
+
+import android.view.View;
import android.widget.ImageView;
import android.widget.QuickContactBadge;
-import android.widget.TextView;
/**
* Simple value object containing the various views within a call log entry.
*/
public final class CallLogListItemViews {
- /** The first line in the call log entry, containing either the name or the number. */
- public TextView line1View;
- /** The label associated with the phone number. */
- public TextView labelView;
- /**
- * The number the call was from or to.
- * <p>
- * Only filled in if the number is not already in the first line, i.e., {@link #line1View}.
- */
- public TextView numberView;
- /** The date of the call. */
- public TextView dateView;
- /** The icon indicating the type of call. */
- public ImageView iconView;
- /** The icon used to place a call to the contact. Only present for non-group entries. */
- public ImageView callView;
- /** The icon used to expand and collapse an entry. Only present for group entries. */
- public ImageView groupIndicator;
- /**
- * The text view containing the number of items in the group. Only present for group
- * entries.
- */
- public TextView groupSize;
/** The quick contact badge for the contact. Only present for group and stand alone entries. */
- public QuickContactBadge photoView;
+ public final QuickContactBadge photoView;
+ /** The main action button on the entry. */
+ public final ImageView callView;
+ /** The details of the phone call. */
+ public final PhoneCallDetailsViews phoneCallDetailsViews;
+
+ private CallLogListItemViews(QuickContactBadge photoView, ImageView callView,
+ PhoneCallDetailsViews phoneCallDetailsViews) {
+ this.photoView = photoView;
+ this.callView = callView;
+ this.phoneCallDetailsViews = phoneCallDetailsViews;
+ }
+
+ public static CallLogListItemViews fromView(View view) {
+ return new CallLogListItemViews((QuickContactBadge) view.findViewById(R.id.contact_photo),
+ (ImageView) view.findViewById(R.id.call_icon),
+ PhoneCallDetailsViews.fromView(view));
+ }
+
+ public static CallLogListItemViews createForTest(QuickContactBadge photoView,
+ ImageView callView, PhoneCallDetailsViews phoneCallDetailsViews) {
+ return new CallLogListItemViews(photoView, callView, phoneCallDetailsViews);
+ }
}
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 1f7fac8..677b73a 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -19,7 +19,6 @@
import com.android.contacts.Collapser;
import com.android.contacts.Collapser.Collapsible;
import com.android.contacts.ContactLoader;
-import com.android.contacts.ContactOptionsActivity;
import com.android.contacts.ContactPresenceIconUtil;
import com.android.contacts.ContactSaveService;
import com.android.contacts.ContactsUtils;
@@ -41,13 +40,9 @@
import com.android.internal.telephony.ITelephony;
import android.accounts.Account;
-import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
-import android.app.LoaderManager;
-import android.app.LoaderManager.LoaderCallbacks;
import android.app.SearchManager;
-import android.content.ActivityNotFoundException;
import android.content.ClipboardManager;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -55,7 +50,6 @@
import android.content.Entity;
import android.content.Entity.NamedContentValues;
import android.content.Intent;
-import android.content.Loader;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.ParseException;
@@ -90,9 +84,6 @@
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
@@ -137,17 +128,12 @@
private Uri mPrimaryPhoneUri = null;
private Button mCopyGalToLocalButton;
- private boolean mAllRestricted;
private final ArrayList<Long> mWritableRawContactIds = new ArrayList<Long>();
private int mNumPhoneNumbers = 0;
private String mDefaultCountryIso;
private boolean mContactDataDisplayed;
private boolean mContactPhotoDisplayedInHeader = true;
- private boolean mOptionsMenuOptions;
- private boolean mOptionsMenuEditable;
- private boolean mOptionsMenuShareable;
-
/**
* Device capability: Set during buildEntries and used in the long-press context menu
*/
@@ -245,8 +231,6 @@
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
mView = inflater.inflate(R.layout.contact_detail_fragment, container, false);
- setHasOptionsMenu(true);
-
mInflater = inflater;
mPhotoView = (ImageView) mView.findViewById(R.id.photo);
@@ -336,37 +320,6 @@
return mLookupUri;
}
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- if (mLookupUri != null) {
- Bundle args = new Bundle();
- args.putParcelable(LOADER_ARG_CONTACT_URI, mLookupUri);
- getLoaderManager().initLoader(LOADER_DETAILS, args, mDetailLoaderListener);
- }
- }
-
- public void loadUri(Uri lookupUri) {
- if ((lookupUri != null && lookupUri.equals(mLookupUri))
- || (lookupUri == null && mLookupUri == null)) {
- return;
- }
-
- mLookupUri = lookupUri;
- mTransitionAnimationRequested = mContactDataDisplayed;
- mContactDataDisplayed = true;
- if (mLookupUri == null) {
- getLoaderManager().destroyLoader(LOADER_DETAILS);
- mContactData = null;
- bindData();
- } else if (getActivity() != null) {
- Bundle args = new Bundle();
- args.putParcelable(LOADER_ARG_CONTACT_URI, mLookupUri);
- getLoaderManager().restartLoader(LOADER_DETAILS, args, mDetailLoaderListener);
- }
- }
-
/**
* Sets whether or not the contact photo should be shown in the list of contact details in this
* {@link Fragment}.
@@ -458,7 +411,6 @@
mRawContactIds.clear();
- mAllRestricted = true;
mPrimaryPhoneUri = null;
mNumPhoneNumbers = 0;
@@ -477,11 +429,6 @@
final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
final long rawContactId = entValues.getAsLong(RawContacts._ID);
- // Mark when this contact has any unrestricted components
- Integer restricted = entValues.getAsInteger(RawContacts.IS_RESTRICTED);
- final boolean isRestricted = restricted != null && restricted != 0;
- if (!isRestricted) mAllRestricted = false;
-
if (!mRawContactIds.contains(rawContactId)) {
mRawContactIds.add(rawContactId);
}
@@ -1392,95 +1339,6 @@
}
}
- @Override
- public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
- inflater.inflate(R.menu.view_contact, menu);
- }
-
- public boolean isOptionsMenuChanged() {
- return mOptionsMenuOptions != isContactOptionsChangeEnabled()
- || mOptionsMenuEditable != isContactEditable()
- || mOptionsMenuShareable != isContactShareable();
- }
-
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- mOptionsMenuOptions = isContactOptionsChangeEnabled();
- mOptionsMenuEditable = isContactEditable();
- mOptionsMenuShareable = isContactShareable();
-
- // Options only shows telephony-related settings (ringtone, send to voicemail).
- // ==> Hide if we don't have a telephone
- final MenuItem optionsMenu = menu.findItem(R.id.menu_options);
- optionsMenu.setVisible(mOptionsMenuOptions);
-
- final MenuItem editMenu = menu.findItem(R.id.menu_edit);
- editMenu.setVisible(mOptionsMenuEditable);
-
- final MenuItem deleteMenu = menu.findItem(R.id.menu_delete);
- deleteMenu.setVisible(mOptionsMenuEditable);
-
- final MenuItem shareMenu = menu.findItem(R.id.menu_share);
- shareMenu.setVisible(mOptionsMenuShareable);
- }
-
- public boolean isContactOptionsChangeEnabled() {
- return mContactData != null && !mContactData.isDirectoryEntry()
- && PhoneCapabilityTester.isPhone(mContext);
- }
-
- public boolean isContactEditable() {
- return mContactData != null && !mContactData.isDirectoryEntry();
- }
-
- public boolean isContactShareable() {
- return mContactData != null && !mContactData.isDirectoryEntry() && !mAllRestricted;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_edit: {
- if (mListener != null) mListener.onEditRequested(mLookupUri);
- break;
- }
- case R.id.menu_delete: {
- if (mListener != null) mListener.onDeleteRequested(mLookupUri);
- return true;
- }
- case R.id.menu_options: {
- if (mContactData == null) return false;
- final Intent intent = new Intent(mContext, ContactOptionsActivity.class);
- intent.setData(mContactData.getLookupUri());
- mContext.startActivity(intent);
- return true;
- }
- case R.id.menu_share: {
- if (mAllRestricted) return false;
- if (mContactData == null) return false;
-
- final String lookupKey = mContactData.getLookupKey();
- final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
-
- final Intent intent = new Intent(Intent.ACTION_SEND);
- intent.setType(Contacts.CONTENT_VCARD_TYPE);
- intent.putExtra(Intent.EXTRA_STREAM, shareUri);
-
- // Launch chooser to share contact via
- final CharSequence chooseTitle = mContext.getText(R.string.share_via);
- final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
-
- try {
- mContext.startActivity(chooseIntent);
- } catch (ActivityNotFoundException ex) {
- Toast.makeText(mContext, R.string.share_error, Toast.LENGTH_SHORT).show();
- }
- return true;
- }
- }
- return false;
- }
-
private void makePersonalCopy() {
if (mListener == null) {
return;
@@ -1588,76 +1446,18 @@
}
return false;
}
-
- case KeyEvent.KEYCODE_DEL: {
- if (mListener != null) mListener.onDeleteRequested(mLookupUri);
- return true;
- }
}
return false;
}
- /**
- * The listener for the detail loader
- */
- private final LoaderManager.LoaderCallbacks<ContactLoader.Result> mDetailLoaderListener =
- new LoaderCallbacks<ContactLoader.Result>() {
- @Override
- public Loader<ContactLoader.Result> onCreateLoader(int id, Bundle args) {
- Uri lookupUri = args.getParcelable(LOADER_ARG_CONTACT_URI);
- return new ContactLoader(mContext, lookupUri, true /* loadGroupMetaData */);
- }
-
- @Override
- public void onLoadFinished(Loader<ContactLoader.Result> loader, ContactLoader.Result data) {
- if (!mLookupUri.equals(data.getUri())) {
- return;
- }
-
- if (data != ContactLoader.Result.NOT_FOUND && data != ContactLoader.Result.ERROR) {
- mContactData = data;
- } else {
- Log.i(TAG, "No contact found: " + ((ContactLoader)loader).getLookupUri());
- mContactData = null;
- }
-
- bindData();
-
- if (mContactData == null && mListener != null) {
- mListener.onContactNotFound();
- }
- }
-
- public void onLoaderReset(Loader<ContactLoader.Result> loader) {
- mContactData = null;
- bindData();
- }
- };
-
public static interface Listener {
/**
- * Contact was not found, so somehow close this fragment. This is raised after a contact
- * is removed via Menu/Delete
- */
- public void onContactNotFound();
-
- /**
- * User decided to go to Edit-Mode
- */
- public void onEditRequested(Uri lookupUri);
-
- /**
* User clicked a single item (e.g. mail)
*/
public void onItemClicked(Intent intent);
/**
- * User decided to delete the contact
- */
- public void onDeleteRequested(Uri lookupUri);
-
- /**
* User requested creation of a new contact with the specified values.
*
* @param values ContentValues containing data rows for the new contact.
diff --git a/src/com/android/contacts/detail/ContactDetailLayoutController.java b/src/com/android/contacts/detail/ContactDetailLayoutController.java
new file mode 100644
index 0000000..826d720
--- /dev/null
+++ b/src/com/android/contacts/detail/ContactDetailLayoutController.java
@@ -0,0 +1,331 @@
+/*
+ * 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.detail;
+
+import com.android.contacts.ContactLoader;
+import com.android.contacts.activities.PeopleActivity.ContactDetailFragmentListener;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.os.Bundle;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+
+/**
+ * Determines the layout of the contact card.
+ */
+public class ContactDetailLayoutController {
+
+ public static final int FRAGMENT_COUNT = 2;
+
+ private static final String KEY_DETAIL_FRAGMENT_TAG = "detailFragTag";
+ private static final String KEY_UPDATES_FRAGMENT_TAG = "updatesFragTag";
+
+ private String mDetailFragmentTag;
+ private String mUpdatesFragmentTag;
+
+ private enum LayoutMode {
+ TWO_COLUMN, VIEW_PAGER_AND_CAROUSEL,
+ }
+
+ private final FragmentManager mFragmentManager;
+
+ private ContactDetailFragment mContactDetailFragment;
+ private ContactDetailUpdatesFragment mContactDetailUpdatesFragment;
+
+ private final ViewPager mViewPager;
+ private final ContactDetailTabCarousel mTabCarousel;
+ private ContactDetailFragment mPagerContactDetailFragment;
+ private ContactDetailUpdatesFragment mPagerContactDetailUpdatesFragment;
+
+ private ContactDetailFragmentListener mContactDetailFragmentListener;
+
+ private ContactLoader.Result mContactData;
+
+ private boolean mIsInitialized;
+
+ private LayoutMode mLayoutMode;
+
+ public ContactDetailLayoutController(FragmentManager fragmentManager, ViewPager viewPager,
+ ContactDetailTabCarousel tabCarousel, ContactDetailFragmentListener
+ contactDetailFragmentListener) {
+ if (fragmentManager == null) {
+ throw new IllegalStateException("Cannot initialize a ContactDetailLayoutController "
+ + "without a non-null FragmentManager");
+ }
+
+ mFragmentManager = fragmentManager;
+ mViewPager = viewPager;
+ mTabCarousel = tabCarousel;
+ mContactDetailFragmentListener = contactDetailFragmentListener;
+
+ // Determine the layout based on whether the {@link ViewPager} is null or not. If the
+ // {@link ViewPager} is null, then this is a wide screen and the content can be displayed
+ // in 2 columns side by side. If the {@link ViewPager} is non-null, then this is a narrow
+ // screen and the user will need to swipe to see all the data.
+ mLayoutMode = (mViewPager == null) ? LayoutMode.TWO_COLUMN :
+ LayoutMode.VIEW_PAGER_AND_CAROUSEL;
+
+ }
+
+ public boolean isInitialized() {
+ return mIsInitialized;
+ }
+
+ public void initialize() {
+ mIsInitialized = true;
+ if (mDetailFragmentTag != null || mUpdatesFragmentTag != null) {
+ // Manually remove any {@link ViewPager} fragments if there was an orientation change
+ ContactDetailFragment oldDetailFragment = (ContactDetailFragment) mFragmentManager.
+ findFragmentByTag(mDetailFragmentTag);
+ ContactDetailUpdatesFragment oldUpdatesFragment = (ContactDetailUpdatesFragment)
+ mFragmentManager.findFragmentByTag(mUpdatesFragmentTag);
+
+ if (oldDetailFragment != null && oldUpdatesFragment != null) {
+ FragmentTransaction ft = mFragmentManager.beginTransaction();
+ ft.remove(oldDetailFragment);
+ ft.remove(oldUpdatesFragment);
+ ft.commit();
+ }
+ }
+ if (mViewPager != null) {
+ mViewPager.setAdapter(new ViewPagerAdapter(mFragmentManager));
+ mViewPager.setOnPageChangeListener(mOnPageChangeListener);
+ mTabCarousel.setListener(mTabCarouselListener);
+ }
+ }
+
+ public void setContactDetailFragment(ContactDetailFragment contactDetailFragment) {
+ mContactDetailFragment = contactDetailFragment;
+ }
+
+ public void setContactDetailUpdatesFragment(ContactDetailUpdatesFragment updatesFragment) {
+ mContactDetailUpdatesFragment = updatesFragment;
+ }
+
+ public void setContactData(ContactLoader.Result data) {
+ mContactData = data;
+ if (mContactData.getSocialSnippet() != null) {
+ showContactWithUpdates();
+ } else {
+ showContactWithoutUpdates();
+ }
+ }
+
+ private void showContactWithUpdates() {
+ FragmentTransaction ft = mFragmentManager.beginTransaction();
+
+ switch (mLayoutMode) {
+ case TWO_COLUMN: {
+ // Set the contact data
+ mContactDetailFragment.setData(mContactData.getLookupUri(), mContactData);
+ mContactDetailUpdatesFragment.setData(mContactData.getLookupUri(), mContactData);
+
+ // Update fragment visibility
+ ft.show(mContactDetailUpdatesFragment);
+ break;
+ }
+ case VIEW_PAGER_AND_CAROUSEL: {
+ // Set the contact data
+ mTabCarousel.loadData(mContactData);
+ mPagerContactDetailFragment.setData(mContactData.getLookupUri(), mContactData);
+ mPagerContactDetailUpdatesFragment.setData(mContactData.getLookupUri(),
+ mContactData);
+
+ // Update fragment and view visibility
+ mViewPager.setVisibility(View.VISIBLE);
+ mTabCarousel.setVisibility(View.VISIBLE);
+ ft.hide(mContactDetailFragment);
+ break;
+ }
+ default:
+ throw new IllegalStateException("Invalid LayoutMode " + mLayoutMode);
+ }
+
+ ft.commit();
+ }
+
+ private void showContactWithoutUpdates() {
+ FragmentTransaction ft = mFragmentManager.beginTransaction();
+
+ switch (mLayoutMode) {
+ case TWO_COLUMN:
+ mContactDetailFragment.setData(mContactData.getLookupUri(), mContactData);
+ ft.hide(mContactDetailUpdatesFragment);
+ break;
+ case VIEW_PAGER_AND_CAROUSEL:
+ mContactDetailFragment.setData(mContactData.getLookupUri(), mContactData);
+ ft.show(mContactDetailFragment);
+ mViewPager.setVisibility(View.GONE);
+ mTabCarousel.setVisibility(View.GONE);
+ break;
+ default:
+ throw new IllegalStateException("Invalid LayoutMode " + mLayoutMode);
+ }
+
+ ft.commit();
+ }
+
+ public void onSaveInstanceState(Bundle outState) {
+ if (mPagerContactDetailFragment != null) {
+ outState.putString(KEY_DETAIL_FRAGMENT_TAG,
+ mPagerContactDetailFragment.getTag());
+ outState.putString(KEY_UPDATES_FRAGMENT_TAG,
+ mPagerContactDetailUpdatesFragment.getTag());
+ }
+ }
+
+ public void onRestoreInstanceState(Bundle savedState) {
+ mDetailFragmentTag = savedState.getString(KEY_DETAIL_FRAGMENT_TAG);
+ mUpdatesFragmentTag = savedState.getString(KEY_UPDATES_FRAGMENT_TAG);
+ }
+
+ public class ViewPagerAdapter extends FragmentPagerAdapter{
+
+ public ViewPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ switch (position) {
+ case 0:
+ mPagerContactDetailFragment = new ContactDetailFragment();
+ if (mContactData != null) {
+ mPagerContactDetailFragment.setData(mContactData.getLookupUri(),
+ mContactData);
+ }
+ mPagerContactDetailFragment.setListener(mContactDetailFragmentListener);
+ mPagerContactDetailFragment.setVerticalScrollListener(mVerticalScrollListener);
+ mPagerContactDetailFragment.setShowPhotoInHeader(false);
+ return mPagerContactDetailFragment;
+ case 1:
+ mPagerContactDetailUpdatesFragment = new ContactDetailUpdatesFragment();
+ if (mContactData != null) {
+ mPagerContactDetailUpdatesFragment.setData(mContactData.getLookupUri(),
+ mContactData);
+ }
+ return mPagerContactDetailUpdatesFragment;
+ }
+ throw new IllegalStateException("No fragment at position " + position);
+ }
+
+ @Override
+ public int getCount() {
+ return FRAGMENT_COUNT;
+ }
+ }
+
+ private OnPageChangeListener mOnPageChangeListener = new OnPageChangeListener() {
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ // The user is horizontally dragging the {@link ViewPager}, so send
+ // these scroll changes to the tab carousel. Ignore these events though if the carousel
+ // is actually controlling the {@link ViewPager} scrolls because it will already be
+ // in the correct position.
+ if (mViewPager.isFakeDragging()) {
+ return;
+ }
+ int x = (int) ((position + positionOffset) *
+ mTabCarousel.getAllowedHorizontalScrollLength());
+ mTabCarousel.scrollTo(x, 0);
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ // Since a new page has been selected by the {@link ViewPager},
+ // update the tab selection in the carousel.
+ mTabCarousel.setCurrentTab(position);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {}
+
+ };
+
+ private ContactDetailTabCarousel.Listener mTabCarouselListener =
+ new ContactDetailTabCarousel.Listener() {
+
+ @Override
+ public void onTouchDown() {
+ // The user just started scrolling the carousel, so begin "fake dragging" the
+ // {@link ViewPager} if it's not already doing so.
+ if (mViewPager.isFakeDragging()) {
+ return;
+ }
+ mViewPager.beginFakeDrag();
+ }
+
+ @Override
+ public void onTouchUp() {
+ // The user just stopped scrolling the carousel, so stop "fake dragging" the
+ // {@link ViewPager} if was doing so before.
+ if (mViewPager.isFakeDragging()) {
+ mViewPager.endFakeDrag();
+ }
+ }
+
+ @Override
+ public void onScrollChanged(int l, int t, int oldl, int oldt) {
+ // The user is scrolling the carousel, so send the scroll deltas to the
+ // {@link ViewPager} so it can move in sync.
+ if (mViewPager.isFakeDragging()) {
+ mViewPager.fakeDragBy(oldl-l);
+ }
+ }
+
+ @Override
+ public void onTabSelected(int position) {
+ // The user selected a tab, so update the {@link ViewPager}
+ mViewPager.setCurrentItem(position);
+ }
+ };
+
+ private OnScrollListener mVerticalScrollListener = new OnScrollListener() {
+
+ @Override
+ public void onScroll(
+ AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+ if (mTabCarousel == null) {
+ return;
+ }
+ // If the FIRST item is not visible on the screen, then the carousel must be pinned
+ // at the top of the screen.
+ if (firstVisibleItem != 0) {
+ mTabCarousel.setY(-mTabCarousel.getAllowedVerticalScrollLength());
+ return;
+ }
+ View topView = view.getChildAt(firstVisibleItem);
+ if (topView == null) {
+ return;
+ }
+ int amtToScroll = Math.max((int) view.getChildAt(firstVisibleItem).getY(),
+ -mTabCarousel.getAllowedVerticalScrollLength());
+ mTabCarousel.setY(amtToScroll);
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {}
+
+ };
+}
diff --git a/src/com/android/contacts/detail/ContactLoaderFragment.java b/src/com/android/contacts/detail/ContactLoaderFragment.java
index 3ece0ce..b7cb2b7 100644
--- a/src/com/android/contacts/detail/ContactLoaderFragment.java
+++ b/src/com/android/contacts/detail/ContactLoaderFragment.java
@@ -17,36 +17,74 @@
package com.android.contacts.detail;
import com.android.contacts.ContactLoader;
+import com.android.contacts.ContactOptionsActivity;
import com.android.contacts.R;
+import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
+import com.android.contacts.util.PhoneCapabilityTester;
import com.android.internal.util.Objects;
import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ActivityNotFoundException;
+import android.content.ContentValues;
import android.content.Context;
+import android.content.Entity;
+import android.content.Intent;
import android.content.Loader;
import android.net.Uri;
import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.Toast;
/**
* This is an invisible worker {@link Fragment} that loads the contact details for the contact card.
* The data is then passed to the listener, who can then pass the data to other {@link View}s.
*/
-public class ContactLoaderFragment extends Fragment {
+public class ContactLoaderFragment extends Fragment implements FragmentKeyListener {
private static final String TAG = ContactLoaderFragment.class.getSimpleName();
+ private boolean mOptionsMenuOptions;
+ private boolean mOptionsMenuEditable;
+ private boolean mOptionsMenuShareable;
+
/**
* This is a listener to the {@link ContactLoaderFragment} and will be notified when the
- * contact details have finished loading.
+ * contact details have finished loading or if the user selects any menu options.
*/
public static interface ContactLoaderFragmentListener {
+ /**
+ * Contact was not found, so somehow close this fragment. This is raised after a contact
+ * is removed via Menu/Delete
+ */
+ public void onContactNotFound();
+
+ /**
+ * Contact details have finished loading.
+ */
public void onDetailsLoaded(ContactLoader.Result result);
+
+ /**
+ * User decided to go to Edit-Mode
+ */
+ public void onEditRequested(Uri lookupUri);
+
+ /**
+ * User decided to delete the contact
+ */
+ public void onDeleteRequested(Uri lookupUri);
+
}
private static final int LOADER_DETAILS = 1;
@@ -85,6 +123,7 @@
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ setHasOptionsMenu(true);
// This is an empty view that is set to visibility gone.
return inflater.inflate(R.layout.contact_detail_loader_fragment, container, false);
}
@@ -149,10 +188,15 @@
}
if (mListener != null) {
- mListener.onDetailsLoaded(mContactData);
+ if (mContactData == null) {
+ mListener.onContactNotFound();
+ } else {
+ mListener.onDetailsLoaded(mContactData);
+ }
}
}
+ @Override
public void onLoaderReset(Loader<ContactLoader.Result> loader) {
mContactData = null;
if (mListener != null) {
@@ -160,4 +204,104 @@
}
}
};
+
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
+ inflater.inflate(R.menu.view_contact, menu);
+ }
+
+ public boolean isOptionsMenuChanged() {
+ return mOptionsMenuOptions != isContactOptionsChangeEnabled()
+ || mOptionsMenuEditable != isContactEditable()
+ || mOptionsMenuShareable != isContactShareable();
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ mOptionsMenuOptions = isContactOptionsChangeEnabled();
+ mOptionsMenuEditable = isContactEditable();
+ mOptionsMenuShareable = isContactShareable();
+
+ // Options only shows telephony-related settings (ringtone, send to voicemail).
+ // ==> Hide if we don't have a telephone
+ final MenuItem optionsMenu = menu.findItem(R.id.menu_options);
+ optionsMenu.setVisible(mOptionsMenuOptions);
+
+ final MenuItem editMenu = menu.findItem(R.id.menu_edit);
+ editMenu.setVisible(mOptionsMenuEditable);
+
+ final MenuItem deleteMenu = menu.findItem(R.id.menu_delete);
+ deleteMenu.setVisible(mOptionsMenuEditable);
+
+ final MenuItem shareMenu = menu.findItem(R.id.menu_share);
+ shareMenu.setVisible(mOptionsMenuShareable);
+ }
+
+ public boolean isContactOptionsChangeEnabled() {
+ return mContactData != null && !mContactData.isDirectoryEntry()
+ && PhoneCapabilityTester.isPhone(mContext);
+ }
+
+ public boolean isContactEditable() {
+ return mContactData != null && !mContactData.isDirectoryEntry();
+ }
+
+ public boolean isContactShareable() {
+ return mContactData != null && !mContactData.isDirectoryEntry();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_edit: {
+ if (mListener != null) mListener.onEditRequested(mLookupUri);
+ break;
+ }
+ case R.id.menu_delete: {
+ if (mListener != null) mListener.onDeleteRequested(mLookupUri);
+ return true;
+ }
+ case R.id.menu_options: {
+ if (mContactData == null) return false;
+ final Intent intent = new Intent(mContext, ContactOptionsActivity.class);
+ intent.setData(mContactData.getLookupUri());
+ mContext.startActivity(intent);
+ return true;
+ }
+ case R.id.menu_share: {
+ if (mContactData == null) return false;
+
+ final String lookupKey = mContactData.getLookupKey();
+ final Uri shareUri = Uri.withAppendedPath(Contacts.CONTENT_VCARD_URI, lookupKey);
+
+ final Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType(Contacts.CONTENT_VCARD_TYPE);
+ intent.putExtra(Intent.EXTRA_STREAM, shareUri);
+
+ // Launch chooser to share contact via
+ final CharSequence chooseTitle = mContext.getText(R.string.share_via);
+ final Intent chooseIntent = Intent.createChooser(intent, chooseTitle);
+
+ try {
+ mContext.startActivity(chooseIntent);
+ } catch (ActivityNotFoundException ex) {
+ Toast.makeText(mContext, R.string.share_error, Toast.LENGTH_SHORT).show();
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean handleKeyDown(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DEL: {
+ if (mListener != null) mListener.onDeleteRequested(mLookupUri);
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index af706b8..be873c6 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -24,7 +24,6 @@
import com.android.internal.telephony.ITelephony;
import com.android.phone.CallLogAsync;
import com.android.phone.HapticFeedback;
-import com.google.i18n.phonenumbers.PhoneNumberUtil;
import android.app.Activity;
import android.app.Fragment;
@@ -62,12 +61,14 @@
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
+import android.widget.PopupMenu;
import android.widget.TextView;
/**
@@ -77,6 +78,7 @@
implements View.OnClickListener,
View.OnLongClickListener, View.OnKeyListener,
AdapterView.OnItemClickListener, TextWatcher,
+ PopupMenu.OnMenuItemClickListener,
ViewPagerVisibilityListener {
private static final String TAG = DialpadFragment.class.getSimpleName();
@@ -111,7 +113,7 @@
private ListView mDialpadChooser;
private DialpadChooserAdapter mDialpadChooserAdapter;
- private boolean mShowMenu;
+ private boolean mShowOptionsMenu;
private boolean mHasVoicemail = false;
@@ -266,6 +268,16 @@
mDigits.addTextChangedListener(mTextWatcher);
}
+ // Soft menu button should appear only when there's no hardware menu button.
+ final View overflowMenuButton = fragmentView.findViewById(R.id.overflow_menu);
+ if (overflowMenuButton != null) {
+ if (ViewConfiguration.get(getActivity()).hasPermanentMenuKey()) {
+ overflowMenuButton.setVisibility(View.GONE);
+ } else {
+ overflowMenuButton.setOnClickListener(this);
+ }
+ }
+
// Check for the presence of the keypad
View oneButton = fragmentView.findViewById(R.id.one);
if (oneButton != null) {
@@ -535,35 +547,51 @@
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.dialpad_options, menu);
+
+ // If the hardware doesn't have a hardware menu key, we'll show soft menu button on the
+ // right side of digits EditText.
+ if (ViewConfiguration.get(getActivity()).hasPermanentMenuKey()) {
+ inflater.inflate(R.menu.dialpad_options, menu);
+ }
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
- if (mDialpadChooser == null || mDigits == null) {
- // The layout itself isn't ready yet. Let's ignore this call.
- return;
+ // Hardware menu key should be available and Views should already be ready.
+ if (ViewConfiguration.get(getActivity()).hasPermanentMenuKey() &&
+ mDialpadChooser != null && mDigits != null) {
+ if (mShowOptionsMenu) {
+ setupMenuItems(menu);
+ } else {
+ menu.findItem(R.id.menu_call_settings_dialpad).setVisible(false);
+ menu.findItem(R.id.menu_add_contacts).setVisible(false);
+ menu.findItem(R.id.menu_2s_pause).setVisible(false);
+ menu.findItem(R.id.menu_add_wait).setVisible(false);
+ }
}
+ }
+ private void setupMenuItems(Menu menu) {
+ final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings_dialpad);
final MenuItem addToContactMenuItem = menu.findItem(R.id.menu_add_contacts);
- final MenuItem m2SecPauseMenuItem = menu.findItem(R.id.menu_2s_pause);
- final MenuItem mWaitMenuItem = menu.findItem(R.id.menu_add_wait);
+ final MenuItem twoSecPauseMenuItem = menu.findItem(R.id.menu_2s_pause);
+ final MenuItem waitMenuItem = menu.findItem(R.id.menu_add_wait);
+
+ callSettingsMenuItem.setVisible(true);
+ callSettingsMenuItem.setIntent(DialtactsActivity.getCallSettingsIntent());
// We show "add to contacts", "2sec pause", and "add wait" menus only when the user is
// seeing usual dialpads and has typed at least one digit.
// We never show a menu if the "choose dialpad" UI is up.
- if (!mShowMenu || dialpadChooserVisible() || isDigitsEmpty()) {
+ if (dialpadChooserVisible() || isDigitsEmpty()) {
addToContactMenuItem.setVisible(false);
- m2SecPauseMenuItem.setVisible(false);
- mWaitMenuItem.setVisible(false);
+ twoSecPauseMenuItem.setVisible(false);
+ waitMenuItem.setVisible(false);
} else {
- CharSequence digits = mDigits.getText();
+ final CharSequence digits = mDigits.getText();
// Put the current digits string into an intent
- Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
- intent.putExtra(Insert.PHONE, digits);
- intent.setType(People.CONTENT_ITEM_TYPE);
- addToContactMenuItem.setIntent(intent);
+ addToContactMenuItem.setIntent(getAddToContactIntent(digits));
addToContactMenuItem.setVisible(true);
// Check out whether to show Pause & Wait option menu items
@@ -584,25 +612,32 @@
if (selectionStart != 0) {
// Pause can be visible if cursor is not in the begining
- m2SecPauseMenuItem.setVisible(true);
+ twoSecPauseMenuItem.setVisible(true);
// For Wait to be visible set of condition to meet
- mWaitMenuItem.setVisible(showWait(selectionStart,
- selectionEnd, strDigits));
+ waitMenuItem.setVisible(showWait(selectionStart, selectionEnd, strDigits));
} else {
// cursor in the beginning both pause and wait to be invisible
- m2SecPauseMenuItem.setVisible(false);
- mWaitMenuItem.setVisible(false);
+ twoSecPauseMenuItem.setVisible(false);
+ waitMenuItem.setVisible(false);
}
} else {
+ twoSecPauseMenuItem.setVisible(true);
+
// cursor is not selected so assume new digit is added to the end
int strLength = strDigits.length();
- mWaitMenuItem.setVisible(showWait(strLength,
- strLength, strDigits));
+ waitMenuItem.setVisible(showWait(strLength, strLength, strDigits));
}
}
}
+ private static Intent getAddToContactIntent(CharSequence digits) {
+ final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ intent.putExtra(Insert.PHONE, digits);
+ intent.setType(People.CONTENT_ITEM_TYPE);
+ return intent;
+ }
+
private void keyPressed(int keyCode) {
mHaptic.vibrate();
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
@@ -706,9 +741,28 @@
}
return;
}
+ case R.id.overflow_menu: {
+ PopupMenu popup = constructPopupMenu(view);
+ if (popup != null) {
+ popup.show();
+ }
+ }
}
}
+ private PopupMenu constructPopupMenu(View anchorView) {
+ final Context context = getActivity();
+ if (context == null) {
+ return null;
+ }
+ final PopupMenu popupMenu = new PopupMenu(context, anchorView);
+ final Menu menu = popupMenu.getMenu();
+ popupMenu.inflate(R.menu.dialpad_options);
+ popupMenu.setOnMenuItemClickListener(this);
+ setupMenuItems(menu);
+ return popupMenu;
+ }
+
public boolean onLongClick(View view) {
final Editable digits = mDigits.getText();
int id = view.getId();
@@ -1091,6 +1145,11 @@
return false;
}
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ return onOptionsItemSelected(item);
+ }
+
/**
* Updates the dial string (mDigits) after inserting a Pause character (,)
* or Wait character (;).
@@ -1163,7 +1222,7 @@
* otherwise returns false. Assumes the passed string is non-empty
* and the 0th index check is not required.
*/
- private boolean showWait(int start, int end, String digits) {
+ private static boolean showWait(int start, int end, String digits) {
if (start == end) {
// visible false in this case
if (start > digits.length()) return false;
@@ -1241,6 +1300,6 @@
@Override
public void onVisibilityChanged(boolean visible) {
- mShowMenu = visible;
+ mShowOptionsMenu = visible;
}
}
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 2cd7b3c..ece2a29 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -110,6 +110,7 @@
private static final String KEY_VIEW_ID_GENERATOR = "viewidgenerator";
private static final String KEY_CURRENT_PHOTO_FILE = "currentphotofile";
private static final String KEY_CONTACT_ID_FOR_JOIN = "contactidforjoin";
+ private static final String KEY_CONTACT_WRITABLE_FOR_JOIN = "contactwritableforjoin";
private static final String KEY_SHOW_JOIN_SUGGESTIONS = "showJoinSuggestions";
private static final String KEY_ENABLED = "enabled";
private static final String KEY_STATUS = "status";
@@ -222,6 +223,7 @@
private Listener mListener;
private long mContactIdForJoin;
+ private boolean mContactWritableForJoin;
private LinearLayout mContent;
private EntityDeltaList mState;
@@ -274,9 +276,7 @@
// If anything was left unsaved, save it now but keep the editor open.
if (!getActivity().isChangingConfigurations() && mStatus == Status.EDITING) {
- if (mStatus != SaveMode.JOIN) {
- save(SaveMode.RELOAD);
- }
+ save(SaveMode.RELOAD);
}
}
@@ -373,6 +373,7 @@
mCurrentPhotoFile = new File(fileName);
}
mContactIdForJoin = savedState.getLong(KEY_CONTACT_ID_FOR_JOIN);
+ mContactWritableForJoin = savedState.getBoolean(KEY_CONTACT_WRITABLE_FOR_JOIN);
mAggregationSuggestionsRawContactId = savedState.getLong(KEY_SHOW_JOIN_SUGGESTIONS);
mEnabled = savedState.getBoolean(KEY_ENABLED);
mStatus = savedState.getInt(KEY_STATUS);
@@ -981,17 +982,17 @@
case SaveMode.RELOAD:
case SaveMode.JOIN:
if (success && contactLookupUri != null) {
+ // If it was a JOIN, we are now ready to bring up the join activity.
+ if (saveMode == SaveMode.JOIN) {
+ showJoinAggregateActivity(contactLookupUri);
+ }
+
// If this was in INSERT, we are changing into an EDIT now.
// If it already was an EDIT, we are changing to the new Uri now
mState = null;
load(Intent.ACTION_EDIT, contactLookupUri, null);
mStatus = Status.LOADING;
getLoaderManager().restartLoader(LOADER_DATA, null, mDataLoaderListener);
-
- // If it was a JOIN, we are now ready to bring up the join activity.
- if (saveMode == SaveMode.JOIN) {
- showJoinAggregateActivity(contactLookupUri);
- }
}
break;
@@ -1017,6 +1018,7 @@
}
mContactIdForJoin = ContentUris.parseId(contactLookupUri);
+ mContactWritableForJoin = isContactWritable();
final Intent intent = new Intent(JoinContactActivity.JOIN_CONTACT);
intent.putExtra(JoinContactActivity.EXTRA_TARGET_CONTACT_ID, mContactIdForJoin);
startActivityForResult(intent, REQUEST_CODE_JOIN);
@@ -1027,7 +1029,7 @@
*/
private void joinAggregate(final long contactId) {
Intent intent = ContactSaveService.createJoinContactsIntent(mContext, mContactIdForJoin,
- contactId, isContactWritable(),
+ contactId, mContactWritableForJoin,
ContactEditorActivity.class, ContactEditorActivity.ACTION_JOIN_COMPLETED);
mContext.startService(intent);
}
@@ -1460,6 +1462,7 @@
outState.putString(KEY_CURRENT_PHOTO_FILE, mCurrentPhotoFile.toString());
}
outState.putLong(KEY_CONTACT_ID_FOR_JOIN, mContactIdForJoin);
+ outState.putBoolean(KEY_CONTACT_WRITABLE_FOR_JOIN, mContactWritableForJoin);
outState.putLong(KEY_SHOW_JOIN_SUGGESTIONS, mAggregationSuggestionsRawContactId);
outState.putBoolean(KEY_ENABLED, mEnabled);
outState.putInt(KEY_STATUS, mStatus);
@@ -1586,6 +1589,7 @@
Log.v(TAG, "Time needed for setting UI: " + (setDataEndTime-setDataStartTime));
}
+ @Override
public void onLoaderReset(Loader<ContactLoader.Result> loader) {
}
};
diff --git a/src/com/android/contacts/group/GroupBrowseListFragment.java b/src/com/android/contacts/group/GroupBrowseListFragment.java
index 59b0cec..0b53acf 100644
--- a/src/com/android/contacts/group/GroupBrowseListFragment.java
+++ b/src/com/android/contacts/group/GroupBrowseListFragment.java
@@ -32,6 +32,7 @@
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Parcelable;
import android.provider.ContactsContract.Groups;
import android.text.TextUtils;
import android.view.LayoutInflater;
@@ -196,6 +197,8 @@
String accountType = mGroupListCursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
long groupId = mGroupListCursor.getLong(GroupMetaDataLoader.GROUP_ID);
String title = mGroupListCursor.getString(GroupMetaDataLoader.TITLE);
+ boolean deleted =
+ (mGroupListCursor.getInt(GroupMetaDataLoader.DELETED) == 1);
boolean defaultGroup = mGroupListCursor.isNull(GroupMetaDataLoader.AUTO_ADD)
? false
: mGroupListCursor.getInt(GroupMetaDataLoader.AUTO_ADD) != 0;
@@ -204,8 +207,8 @@
: mGroupListCursor.getInt(GroupMetaDataLoader.FAVORITES) != 0;
// Don't show the "auto-added" (i.e. My Contacts) or "favorites" groups because
- // they show up elsewhere in the app
- if (defaultGroup || favorites) {
+ // they show up elsewhere in the app. Also skip groups that are marked as "deleted"
+ if (defaultGroup || favorites || deleted) {
continue;
}
@@ -227,6 +230,7 @@
mAdapter.setSelectionVisible(mSelectionVisible);
mAdapter.setSelectedGroup(mSelectedGroupUri);
+ Parcelable listState = mListView.onSaveInstanceState();
mListView.setAdapter(mAdapter);
mListView.setEmptyView(mEmptyView);
mListView.setOnItemClickListener(new OnItemClickListener() {
@@ -239,6 +243,9 @@
if (mSelectionToScreenRequested) {
requestSelectionToScreen();
+ } else {
+ // Restore the scroll position.
+ mListView.onRestoreInstanceState(listState);
}
if (mSelectionVisible && mSelectedGroupUri != null) {
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index c4824ae..9e668bb 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -90,6 +90,7 @@
private String mGroupName;
private boolean mOptionsMenuEditable;
+ private boolean mCloseActivityAfterDelete;
public GroupDetailFragment() {
}
@@ -104,6 +105,7 @@
mAdapter = new ContactTileAdapter(activity, mContactTileListener, columnCount,
DisplayType.GROUP_MEMBERS);
+
configurePhotoLoader();
}
@@ -129,6 +131,10 @@
startGroupMetadataLoader();
}
+ public void setQuickContact(boolean enableQuickContact) {
+ mAdapter.setQuickContact(enableQuickContact);
+ }
+
private void configureAdapter(long groupId) {
mGroupId = groupId;
mMemberListView.setAdapter(mAdapter);
@@ -190,11 +196,20 @@
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- bindGroupMetaData(data);
+ data.moveToPosition(-1);
+ if (data.moveToNext()) {
+ boolean deleted = data.getInt(GroupMetaDataLoader.DELETED) == 1;
+ if (!deleted) {
+ bindGroupMetaData(data);
- // Retrieve the list of members
- configureAdapter(mGroupId);
- startGroupMembersLoader();
+ // Retrieve the list of members
+ configureAdapter(mGroupId);
+ startGroupMembersLoader();
+ return;
+ }
+ }
+ updateSize(null);
+ updateTitle(null);
}
@Override
@@ -295,10 +310,15 @@
break;
}
case R.id.menu_delete_group: {
- GroupDeletionDialogFragment.show(getFragmentManager(), mGroupId, mGroupName);
+ GroupDeletionDialogFragment.show(getFragmentManager(), mGroupId, mGroupName,
+ mCloseActivityAfterDelete);
return true;
}
}
return false;
}
+
+ public void closeActivityAfterDelete(boolean closeActivity) {
+ mCloseActivityAfterDelete = closeActivity;
+ }
}
diff --git a/src/com/android/contacts/interactions/GroupDeletionDialogFragment.java b/src/com/android/contacts/interactions/GroupDeletionDialogFragment.java
index 44d31be..c9c1342 100644
--- a/src/com/android/contacts/interactions/GroupDeletionDialogFragment.java
+++ b/src/com/android/contacts/interactions/GroupDeletionDialogFragment.java
@@ -33,8 +33,15 @@
private static final String ARG_GROUP_ID = "groupId";
private static final String ARG_LABEL = "label";
- public static void show(FragmentManager fragmentManager, long groupId, String label) {
- GroupDeletionDialogFragment dialog = new GroupDeletionDialogFragment();
+ private boolean mEndActivity;
+
+ public GroupDeletionDialogFragment(boolean endActivity) {
+ mEndActivity = endActivity;
+ }
+
+ public static void show(FragmentManager fragmentManager, long groupId, String label,
+ boolean endActivity) {
+ GroupDeletionDialogFragment dialog = new GroupDeletionDialogFragment(endActivity);
Bundle args = new Bundle();
args.putLong(ARG_GROUP_ID, groupId);
args.putString(ARG_LABEL, label);
@@ -69,5 +76,8 @@
getActivity().startService(ContactSaveService.createGroupDeletionIntent(
getActivity(), groupId));
+ if (mEndActivity) {
+ getActivity().finish();
+ }
}
}
diff --git a/src/com/android/contacts/list/AccountFilterActivity.java b/src/com/android/contacts/list/AccountFilterActivity.java
new file mode 100644
index 0000000..c38599e
--- /dev/null
+++ b/src/com/android/contacts/list/AccountFilterActivity.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.list;
+
+import com.android.contacts.ContactsActivity;
+import com.android.contacts.ContactsSearchManager;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+
+import android.accounts.Account;
+import android.app.ActionBar;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Shows a list of all available accounts, letting the user select under which account to view
+ * contacts.
+ */
+public class AccountFilterActivity extends ContactsActivity
+ implements AdapterView.OnItemClickListener {
+
+ private static final String TAG = AccountFilterActivity.class.getSimpleName();
+
+ public static final String KEY_EXTRA_CONTACT_LIST_FILTER = "contactListFilter";
+
+ private ListView mListView;
+
+ private List<ContactListFilter> mFilters = new ArrayList<ContactListFilter>();
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.contact_list_filter);
+
+ mListView = (ListView) findViewById(com.android.internal.R.id.list);
+ mListView.setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ finishAndSetResult(mFilters.get(position));
+ }
+ });
+
+ ActionBar actionBar = getActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ loadAccountFilters();
+ }
+
+ private void loadAccountFilters() {
+ ArrayList<ContactListFilter> accountFilters = new ArrayList<ContactListFilter>();
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this);
+ ArrayList<Account> accounts = accountTypes.getAccounts(false);
+ for (Account account : accounts) {
+ AccountType accountType = accountTypes.getAccountType(account.type);
+ Drawable icon = accountType != null ? accountType.getDisplayIcon(this) : null;
+ accountFilters.add(ContactListFilter.createAccountFilter(account.type, account.name,
+ icon, account.name));
+ }
+ int count = accountFilters.size();
+
+ if (count >= 1) {
+ // If we only have one account, don't show it as "account", instead show it as "all"
+ mFilters.add(ContactListFilter.createFilterWithType(
+ ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
+ if (count > 1) {
+ mFilters.addAll(accountFilters);
+ mFilters.add(ContactListFilter.createFilterWithType(
+ ContactListFilter.FILTER_TYPE_CUSTOM));
+ }
+ }
+
+ mListView.setAdapter(new FilterListAdapter(this));
+ }
+
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ finishAndSetResult(mFilters.get(position));
+ }
+
+ private void finishAndSetResult(ContactListFilter filter) {
+ final Intent intent = new Intent();
+ intent.putExtra(KEY_EXTRA_CONTACT_LIST_FILTER, filter);
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ }
+
+ @Override
+ public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
+ boolean globalSearch) {
+ if (globalSearch) {
+ super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
+ } else {
+ ContactsSearchManager.startSearch(this, initialQuery);
+ }
+ }
+
+ private class FilterListAdapter extends BaseAdapter {
+ private LayoutInflater mLayoutInflater;
+
+ public FilterListAdapter(Context context) {
+ mLayoutInflater = (LayoutInflater) context.getSystemService
+ (Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ @Override
+ public int getCount() {
+ return mFilters.size();
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public ContactListFilter getItem(int position) {
+ return mFilters.get(position);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ContactListFilterView view;
+ if (convertView != null) {
+ view = (ContactListFilterView) convertView;
+ } else {
+ view = (ContactListFilterView) mLayoutInflater.inflate(
+ R.layout.filter_spinner_item, parent, false);
+ }
+ view.setSingleAccount(mFilters.size() == 1);
+ ContactListFilter filter = mFilters.get(position);
+ view.setContactListFilter(filter);
+ view.bindView(true);
+ return view;
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ default:
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index 06e31e3..a03ddee 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -20,7 +20,6 @@
import com.android.contacts.widget.IndexerListAdapter;
import com.android.contacts.widget.TextWithHighlightingFactory;
-import android.app.Activity;
import android.content.Context;
import android.content.CursorLoader;
import android.database.Cursor;
@@ -61,7 +60,6 @@
private int mDisplayOrder;
private int mSortOrder;
private boolean mNameHighlightingEnabled;
- private boolean mDataRestrictedByCallingPackage;
private boolean mDisplayPhotos;
private boolean mQuickContactEnabled;
@@ -278,43 +276,6 @@
mIncludeProfile = includeProfile;
}
- public boolean isDataRestrictedByCallingPackage() {
- return mDataRestrictedByCallingPackage;
- }
-
- public void setDataRestrictedByCallingPackage(boolean flag) {
- mDataRestrictedByCallingPackage = flag;
- }
-
- /**
- * Adds a parameter to the URI that ensures that only unrestricted data
- * is included in the list, if {@link #isDataRestrictedByCallingPackage()} is true.
- */
- protected Uri applyDataRestriction(Uri uri) {
- if (!mDataRestrictedByCallingPackage) {
- return uri;
- }
-
- return applyDataRestriction(uri.buildUpon()).build();
- }
-
- /**
- * See {@link #applyDataRestriction(Uri)}.
- */
- protected Uri.Builder applyDataRestriction(Uri.Builder builder) {
- if (!mDataRestrictedByCallingPackage) {
- return builder;
- }
-
- String callingPackage = ((Activity)getContext()).getCallingPackage();
- if (!TextUtils.isEmpty(callingPackage)) {
- return builder.appendQueryParameter(
- ContactsContract.REQUESTING_PACKAGE_PARAM_KEY, callingPackage);
- }
-
- return builder;
- }
-
public void configureDirectoryLoader(DirectoryListLoader loader) {
loader.setDirectorySearchMode(mDirectorySearchMode);
loader.setLocalInvisibleDirectoryEnabled(LOCAL_INVISIBLE_DIRECTORY_ENABLED);
diff --git a/src/com/android/contacts/list/ContactListFilterController.java b/src/com/android/contacts/list/ContactListFilterController.java
index 533f24a..28b62ca 100644
--- a/src/com/android/contacts/list/ContactListFilterController.java
+++ b/src/com/android/contacts/list/ContactListFilterController.java
@@ -15,67 +15,43 @@
*/
package com.android.contacts.list;
-import com.android.contacts.R;
-
import android.app.Activity;
-import android.app.LoaderManager;
-import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
-import android.content.Loader;
import android.content.SharedPreferences;
-import android.content.res.TypedArray;
-import android.os.Bundle;
import android.preference.PreferenceManager;
-import android.text.TextUtils;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.BaseAdapter;
-import android.widget.ListPopupWindow;
import java.util.ArrayList;
import java.util.List;
/**
- * Controls a list of {@link ContactListFilter}'s.
+ * Stores the {@link ContactListFilter} selected by the user and saves it to
+ * {@link SharedPreferences} if necessary.
*/
-// TODO: Remove the extra functionality dealing with loading and displaying a list of filters in
-// the action bar.
-public class ContactListFilterController
- implements LoaderCallbacks<List<ContactListFilter>>, OnClickListener, OnItemClickListener {
+public class ContactListFilterController {
public interface ContactListFilterListener {
- void onContactListFiltersLoaded();
void onContactListFilterChanged();
- void onContactListFilterCustomizationRequest();
}
private Context mContext;
- private LoaderManager mLoaderManager;
- private boolean mEnabled = true;
private List<ContactListFilterListener> mListeners = new ArrayList<ContactListFilterListener>();
- private ListPopupWindow mPopup;
- private int mPopupWidth = -1;
- private List<ContactListFilter> mCachedFilters;
- private SparseArray<ContactListFilter> mFilters;
- private int mNextFilterId = 1;
- private View mAnchor;
- private FilterListAdapter mFilterListAdapter;
private ContactListFilter mFilter;
- private boolean mFiltersLoaded;
- private int mAccountCount;
+
+ private boolean mIsInitialized;
public ContactListFilterController(Activity activity) {
mContext = activity;
- mLoaderManager = activity.getLoaderManager();
}
- public void setEnabled(boolean flag) {
- mEnabled = flag;
+ public void onStart() {
+ if (mFilter == null) {
+ mFilter = ContactListFilter.restoreFromPreferences(getSharedPreferences());
+ mIsInitialized = true;
+ }
+ }
+
+ public boolean isInitialized() {
+ return mIsInitialized;
}
public void addListener(ContactListFilterListener listener) {
@@ -90,157 +66,10 @@
return mFilter;
}
- public int getFilterCount() {
- return mFilters != null ? mFilters.size() : 0;
- }
-
- public boolean isLoaded() {
- return mFiltersLoaded;
- }
-
- public void onStart() {
- if (mFilter == null) {
- mFilter = ContactListFilter.restoreFromPreferences(getSharedPreferences());
- }
- }
-
private SharedPreferences getSharedPreferences() {
return PreferenceManager.getDefaultSharedPreferences(mContext);
}
- @Override
- public ContactListFilterLoader onCreateLoader(int id, Bundle args) {
- return new ContactListFilterLoader(mContext);
- }
-
- @Override
- public void onLoadFinished(
- Loader<List<ContactListFilter>> loader, List<ContactListFilter> filters) {
- int count = filters.size();
- if (mCachedFilters != null && mCachedFilters.size() == count) {
- boolean changed = false;
- for (int i = 0; i < filters.size(); i++) {
- ContactListFilter filter1 = mCachedFilters.get(i);
- ContactListFilter filter2 = filters.get(i);
- if (!filter1.equals(filter2)) {
- changed = true;
- break;
- }
-
- // Group title is intentionally not included in the "equals" algorithm for
- // ContactListFilter, because we want stability of filter identity
- // across label changes. However, here we do care about the label changes.
- if (filter1.filterType == ContactListFilter.FILTER_TYPE_GROUP &&
- !TextUtils.equals(filter1.title, filter2.title)) {
- changed = true;
- break;
- }
- }
-
- if (!changed) {
- return;
- }
- }
-
- mCachedFilters = filters;
-
- if (mFilters == null) {
- mFilters = new SparseArray<ContactListFilter>(filters.size());
- } else {
- mFilters.clear();
- }
-
- boolean filterValid = mFilter != null && !mFilter.isValidationRequired();
-
- mAccountCount = 0;
- for (int index = 0; index < count; index++) {
- if (filters.get(index).filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
- mAccountCount++;
- }
- }
-
- if (mAccountCount != 1) {
- mFilters.append(mNextFilterId++, ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS));
- mFilters.append(mNextFilterId++, ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_STARRED));
- }
-
- for (int index = 0; index < count; index++) {
- ContactListFilter filter = filters.get(index);
-
- boolean firstAndOnly = mAccountCount == 1
- && filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT;
-
- // If we only have one account, don't show it as "account", instead show it as "all"
- if (firstAndOnly) {
- filter = ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
- }
-
- mFilters.append(mNextFilterId++, filter);
-
- if (filter.equals(mFilter)) {
- // Refresh the filter in case the title got changed
- mFilter = filter;
- filterValid = true;
- }
-
- if (firstAndOnly) {
- mFilters.append(mNextFilterId++, ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_STARRED));
- }
- }
-
- if (mAccountCount > 0) {
- mFilters.append(mNextFilterId++, ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_CUSTOM));
- }
-
- boolean filterChanged = false;
- if (mFilter == null || !filterValid) {
- filterChanged = mFilter != null;
- mFilter = getDefaultFilter();
- }
-
- if (mFilterListAdapter == null) {
- mFilterListAdapter = new FilterListAdapter();
- } else {
- mFilterListAdapter.notifyDataSetChanged();
- }
-
- mFiltersLoaded = true;
- notifyContacListFiltersLoaded();
-
- if (filterChanged) {
- notifyContactListFilterChanged();
- }
- }
-
- public void onLoaderReset(Loader<List<ContactListFilter>> loader) {
- }
-
- private void setContactListFilter(int filterId) {
- ContactListFilter filter;
- if (filterId == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
- filter = ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
- } else if (filterId == ContactListFilter.FILTER_TYPE_CUSTOM) {
- filter = ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_CUSTOM);
- } else if (filterId == ContactListFilter.FILTER_TYPE_STARRED) {
- filter = ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_STARRED);
- } else {
- filter = mFilters.get(filterId);
- if (filter == null) {
- filter = getDefaultFilter();
- }
- }
-
- setContactListFilter(filter, true);
- }
-
public void setContactListFilter(ContactListFilter filter, boolean persistent) {
if (!filter.equals(mFilter)) {
mFilter = filter;
@@ -253,59 +82,9 @@
}
}
- @Override
- public void onClick(View v) {
- if (!mFiltersLoaded || !mEnabled) {
- return;
- }
-
- if (mPopupWidth == -1) {
- TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ContactBrowser);
- mPopupWidth = a.getDimensionPixelSize(
- R.styleable.ContactBrowser_contact_filter_popup_width, -1);
- a.recycle();
-
- if (mPopupWidth == -1) {
- mPopupWidth = mAnchor.getWidth();
- }
- }
-
- mPopup = new ListPopupWindow(mContext, null);
- mPopup.setWidth(mPopupWidth);
- mPopup.setAdapter(mFilterListAdapter);
- mPopup.setAnchorView(mAnchor);
- mPopup.setOnItemClickListener(this);
- mPopup.setModal(true);
- mPopup.show();
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- mPopup.dismiss();
- if (mFilters.get((int) id).filterType == ContactListFilter.FILTER_TYPE_CUSTOM) {
- notifyContactListFilterCustomizationRequest();
- } else {
- setContactListFilter((int) id);
- }
- }
-
public void selectCustomFilter() {
- mFilter = ContactListFilter.createFilterWithType(ContactListFilter.FILTER_TYPE_CUSTOM);
- notifyContactListFilterChanged();
- }
-
- public int getAccountCount() {
- return mAccountCount;
- }
-
- private ContactListFilter getDefaultFilter() {
- return mFilters.size() > 0 ? mFilters.valueAt(0) : null;
- }
-
- private void notifyContacListFiltersLoaded() {
- for (ContactListFilterListener listener : mListeners) {
- listener.onContactListFiltersLoaded();
- }
+ setContactListFilter(ContactListFilter.createFilterWithType(
+ ContactListFilter.FILTER_TYPE_CUSTOM), true);
}
private void notifyContactListFilterChanged() {
@@ -314,48 +93,4 @@
}
}
- private void notifyContactListFilterCustomizationRequest() {
- for (ContactListFilterListener listener : mListeners) {
- listener.onContactListFilterCustomizationRequest();
- }
- }
-
- private class FilterListAdapter extends BaseAdapter {
- private LayoutInflater mLayoutInflater;
-
- public FilterListAdapter() {
- mLayoutInflater = LayoutInflater.from(mContext);
- }
-
- @Override
- public int getCount() {
- return mFilters.size();
- }
-
- @Override
- public long getItemId(int position) {
- return mFilters.keyAt(position);
- }
-
- @Override
- public Object getItem(int position) {
- return mFilters.valueAt(position);
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- ContactListFilterView view;
- if (convertView != null) {
- view = (ContactListFilterView) convertView;
- } else {
- view = (ContactListFilterView) mLayoutInflater.inflate(
- R.layout.filter_spinner_item, parent, false);
- }
- view.setSingleAccount(mAccountCount == 1);
- ContactListFilter filter = mFilters.valueAt(position);
- view.setContactListFilter(filter);
- view.setActivated(filter.equals(mFilter));
- view.bindView(true);
- return view;
- }
- }
}
diff --git a/src/com/android/contacts/list/ContactListFilterLoader.java b/src/com/android/contacts/list/ContactListFilterLoader.java
deleted file mode 100644
index 07c11bc..0000000
--- a/src/com/android/contacts/list/ContactListFilterLoader.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts.list;
-
-import com.android.contacts.model.AccountType;
-import com.android.contacts.model.AccountTypeManager;
-
-import android.accounts.Account;
-import android.content.AsyncTaskLoader;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.Cursor;
-import android.graphics.drawable.Drawable;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Groups;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A loader for the data needed for the group selector.
- */
-public class ContactListFilterLoader extends AsyncTaskLoader<List<ContactListFilter>> {
-
- private static final class GroupQuery {
- public static final String[] COLUMNS = {
- Groups._ID,
- Groups.ACCOUNT_TYPE,
- Groups.ACCOUNT_NAME,
- Groups.TITLE,
- Groups.AUTO_ADD,
- Groups.SOURCE_ID,
- Groups.GROUP_IS_READ_ONLY,
- };
-
- public static final int ID = 0;
- public static final int ACCOUNT_TYPE = 1;
- public static final int ACCOUNT_NAME = 2;
- public static final int TITLE = 3;
- public static final int IS_DEFAULT_GROUP = 4; // Using the AUTO_ADD group as default
- public static final int SOURCE_ID = 5;
- public static final int GROUP_IS_READ_ONLY = 6;
-
- public static final String SELECTION =
- Groups.DELETED + "=0" +
- " AND " + Groups.FAVORITES + "=0" +
- " AND " + Groups.ACCOUNT_TYPE + " NOT NULL" +
- " AND " + Groups.ACCOUNT_NAME + " NOT NULL";
- }
-
- private boolean mStopped;
- private ForceLoadContentObserver mObserver;
- private ArrayList<ContactListFilter> mResults;
-
- public ContactListFilterLoader(Context context) {
- super(context);
- }
-
- @Override
- public List<ContactListFilter> loadInBackground() {
-
- ArrayList<ContactListFilter> results = new ArrayList<ContactListFilter>();
- Context context = getContext();
- final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
- ArrayList<Account> accounts = accountTypes.getAccounts(false);
- for (Account account : accounts) {
- AccountType accountType = accountTypes.getAccountType(account.type);
- Drawable icon = accountType != null ? accountType.getDisplayIcon(getContext()) : null;
- results.add(ContactListFilter.createAccountFilter(account.type, account.name, icon,
- account.name));
- }
-
- ContentResolver resolver = context.getContentResolver();
-
- Cursor cursor = resolver.query(
- Groups.CONTENT_URI, GroupQuery.COLUMNS, GroupQuery.SELECTION, null, null);
- try {
- while (cursor.moveToNext()) {
- long groupId = cursor.getLong(GroupQuery.ID);
- String groupSourceId = cursor.getString(GroupQuery.SOURCE_ID);
- boolean groupReadOnly = cursor.getInt(GroupQuery.GROUP_IS_READ_ONLY) != 0;
- String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
- String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
- boolean defaultGroup = false;
- if (!cursor.isNull(GroupQuery.IS_DEFAULT_GROUP)) {
- defaultGroup = cursor.getInt(GroupQuery.IS_DEFAULT_GROUP) != 0;
- }
- if (defaultGroup) {
- // Find the filter for this account and set the default group ID
- for (ContactListFilter filter : results) {
- if (filter.accountName.equals(accountName)
- && filter.accountType.equals(accountType)) {
- filter.groupId = groupId;
- filter.groupSourceId = groupSourceId;
- break;
- }
- }
- } else {
- String title = cursor.getString(GroupQuery.TITLE);
- results.add(ContactListFilter.createGroupFilter(accountType, accountName,
- groupId, groupSourceId, groupReadOnly, title));
- }
- }
- } finally {
- cursor.close();
- }
-
- Collections.sort(results);
-
- mResults = results;
- return results;
- }
-
- /* Runs on the UI thread */
- @Override
- public void deliverResult(List<ContactListFilter> results) {
- if (!mStopped) {
- super.deliverResult(results);
- }
- }
-
- @Override
- protected void onStartLoading() {
- if (mObserver == null) {
- mObserver = new ForceLoadContentObserver();
- getContext().getContentResolver().registerContentObserver(
- Contacts.CONTENT_URI, true, mObserver);
- }
-
- mStopped = false;
-
- if (mResults != null) {
- deliverResult(mResults);
- } else {
- forceLoad();
- }
- }
-
- @Override
- protected void onStopLoading() {
- if (mObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
-
- mResults = null;
-
- // Attempt to cancel the current load task if possible.
- cancelLoad();
-
- // Make sure that any outstanding loads clean themselves up properly
- mStopped = true;
- }
-
- @Override
- protected void onReset() {
- stopLoading();
- }
-}
diff --git a/src/com/android/contacts/list/ContactPickerFragment.java b/src/com/android/contacts/list/ContactPickerFragment.java
index 6bfca57..5d0f692 100644
--- a/src/com/android/contacts/list/ContactPickerFragment.java
+++ b/src/com/android/contacts/list/ContactPickerFragment.java
@@ -170,7 +170,6 @@
super.configureAdapter();
ContactEntryListAdapter adapter = getAdapter();
- adapter.setDataRestrictedByCallingPackage(true);
// If "Create new contact" is shown, don't display the empty list UI
adapter.setEmptyListEnabled(!isCreateContactEnabled());
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index 5d5e6cb..20c639e 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -65,6 +65,7 @@
private int mPhotoUriIndex;
private int mNameIndex;
private int mStarredIndex;
+ private boolean mIsQuickContact = false;
/**
* Configures the adapter to filter and display contacts using different view types.
@@ -72,27 +73,23 @@
*/
public enum DisplayType {
/**
- * Displays a mixed view type where Starred Contacts
- * are in a regular {@link ContactTileView} layout and
- * frequent contacts are in a small {@link ContactTileView} layout.
+ * Displays a mixed view type of starred and frequent contacts
*/
STREQUENT,
/**
- * Display only starred contacts in
- * regular {@link ContactTileView} layout.
+ * Display only starred contacts
*/
STARRED_ONLY,
/**
- * Display only most frequently contacted in a
- * single {@link ContactTileView} layout.
+ * Display only most frequently contacted
*/
FREQUENT_ONLY,
/**
- * Display all contacts from a group in the cursor in a
- * regular {@link ContactTileView} layout. Use {@link GroupMemberLoader}
+ * Display all contacts from a group in the cursor
+ * Use {@link GroupMemberLoader}
* when passing {@link Cursor} into loadFromCusor method.
*/
GROUP_MEMBERS
@@ -120,6 +117,10 @@
mDisplayType = displayType;
}
+ public void setQuickContact(boolean enableQuickContact) {
+ mIsQuickContact = enableQuickContact;
+ }
+
/**
* Sets the column indices for expected {@link Cursor}
* based on {@link DisplayType}.
@@ -292,7 +293,7 @@
int columnCount = -1;
switch (itemViewType) {
- case ViewTypes.SQUARE:
+ case ViewTypes.STARRED:
if (contactTileRowView == null) {
// Creating new row if needed
contactTileRowView = new ContactTileRow(mContext, layoutResId, true);
@@ -300,7 +301,7 @@
columnCount = mColumnCount;
break;
- case ViewTypes.SINGLE_ROW:
+ case ViewTypes.FREQUENT:
if (contactTileRowView == null) {
// Creating new row if needed
contactTileRowView = new ContactTileRow(mContext, layoutResId, false);
@@ -327,10 +328,14 @@
private int getLayoutResourceId(int viewType) {
switch (viewType) {
- case ViewTypes.SQUARE:
- return R.layout.contact_tile_square;
- case ViewTypes.SINGLE_ROW:
- return R.layout.contact_tile_single;
+ case ViewTypes.STARRED:
+ if (mIsQuickContact) {
+ return R.layout.contact_tile_starred_quick_contact;
+ } else {
+ return R.layout.contact_tile_starred;
+ }
+ case ViewTypes.FREQUENT:
+ return R.layout.contact_tile_frequent;
default:
throw new IllegalArgumentException("Received unrecognized viewType " + viewType);
}
@@ -343,8 +348,8 @@
/**
* Returns view type based on {@link DisplayType}.
* {@link DisplayType#STARRED_ONLY} and {@link DisplayType#GROUP_MEMBERS}
- * are {@link ViewTypes#SQUARE}.
- * {@link DisplayType#FREQUENT_ONLY} is {@link ViewTypes#SINGLE_ROW}.
+ * are {@link ViewTypes#STARRED}.
+ * {@link DisplayType#FREQUENT_ONLY} is {@link ViewTypes#FREQUENT}.
* {@link DisplayType#STREQUENT} mixes both {@link ViewTypes}
* and also adds in {@link ViewTypes#DIVIDER}.
*/
@@ -353,17 +358,17 @@
switch (mDisplayType) {
case STREQUENT:
if (position < mDividerRowIndex) {
- return ViewTypes.SQUARE;
+ return ViewTypes.STARRED;
} else if (position == mDividerRowIndex) {
return ViewTypes.DIVIDER;
} else {
- return ViewTypes.SINGLE_ROW;
+ return ViewTypes.FREQUENT;
}
case STARRED_ONLY:
case GROUP_MEMBERS:
- return ViewTypes.SQUARE;
+ return ViewTypes.STARRED;
case FREQUENT_ONLY:
- return ViewTypes.SINGLE_ROW;
+ return ViewTypes.FREQUENT;
default:
throw new IllegalStateException(
"Received unrecognized DisplayType " + mDisplayType);
@@ -400,7 +405,7 @@
if (getChildCount() <= tileIndex) {
if (mIsContactTileSquare) {
- contactTile = (ContactTileSquareView) inflate(mContext, mLayoutResId, null);
+ contactTile = (ContactTileStarredView) inflate(mContext, mLayoutResId, null);
} else {
contactTile = (ContactTileView) inflate(mContext, mLayoutResId, null);
}
@@ -433,9 +438,9 @@
private static class ViewTypes {
public static final int COUNT = 3;
- public static final int SQUARE = 0;
+ public static final int STARRED = 0;
public static final int DIVIDER = 1;
- public static final int SINGLE_ROW = 2;
+ public static final int FREQUENT = 2;
}
public interface Listener {
diff --git a/src/com/android/contacts/list/ContactTileSquareView.java b/src/com/android/contacts/list/ContactTileStarredView.java
similarity index 80%
rename from src/com/android/contacts/list/ContactTileSquareView.java
rename to src/com/android/contacts/list/ContactTileStarredView.java
index 7716481..3c0ba42 100644
--- a/src/com/android/contacts/list/ContactTileSquareView.java
+++ b/src/com/android/contacts/list/ContactTileStarredView.java
@@ -19,13 +19,13 @@
import android.util.AttributeSet;
/**
- * A ContactTileSquare displays the contact's picture overlayed with their name
+ * A {@link ContactTileStarredView} displays the contact's picture overlayed with their name
* in a perfect square.
*/
-public class ContactTileSquareView extends ContactTileView {
- private final static String TAG = ContactTileSquareView.class.getSimpleName();
+public class ContactTileStarredView extends ContactTileView {
+ private final static String TAG = ContactTileStarredView.class.getSimpleName();
- public ContactTileSquareView(Context context, AttributeSet attrs) {
+ public ContactTileStarredView(Context context, AttributeSet attrs) {
super(context, attrs);
}
diff --git a/src/com/android/contacts/list/ContactTileView.java b/src/com/android/contacts/list/ContactTileView.java
index e4abff3..7981bd1 100644
--- a/src/com/android/contacts/list/ContactTileView.java
+++ b/src/com/android/contacts/list/ContactTileView.java
@@ -26,6 +26,7 @@
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.QuickContactBadge;
import android.widget.TextView;
/**
@@ -36,6 +37,7 @@
private Uri mLookupUri;
private ImageView mPhoto;
+ private QuickContactBadge mQuickContact;
private TextView mName;
private ContactPhotoManager mPhotoManager = null;
@@ -48,6 +50,8 @@
protected void onFinishInflate() {
super.onFinishInflate();
mName = (TextView) findViewById(R.id.contact_tile_name);
+
+ mQuickContact = (QuickContactBadge) findViewById(R.id.contact_tile_quick);
mPhoto = (ImageView) findViewById(R.id.contact_tile_image);
}
@@ -63,11 +67,23 @@
if (entry != null) {
mName.setText(entry.name);
mLookupUri = entry.lookupKey;
- mPhoto.setImageBitmap(null);
+
+ if (mQuickContact != null) {
+ mQuickContact.assignContactUri(mLookupUri);
+ mQuickContact.setImageBitmap(null);
+ } else {
+ mPhoto.setImageBitmap(null);
+ }
+
setVisibility(View.VISIBLE);
if (mPhotoManager != null) {
- mPhotoManager.loadPhoto(mPhoto, entry.photoUri);
+ if (mQuickContact != null){
+ mPhotoManager.loadPhoto(mQuickContact, entry.photoUri);
+ } else {
+ mPhotoManager.loadPhoto(mPhoto, entry.photoUri);
+ }
+
} else {
Log.w(TAG, "contactPhotoManager not set");
}
diff --git a/src/com/android/contacts/list/CustomContactListFilterActivity.java b/src/com/android/contacts/list/CustomContactListFilterActivity.java
index dae7233..0f6aaad 100644
--- a/src/com/android/contacts/list/CustomContactListFilterActivity.java
+++ b/src/com/android/contacts/list/CustomContactListFilterActivity.java
@@ -23,10 +23,8 @@
import com.android.contacts.model.AccountTypeManager;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.model.GoogleAccountType;
-import com.android.contacts.preference.ContactsPreferences;
import com.android.contacts.util.EmptyService;
import com.android.contacts.util.LocalizedNameResolver;
-import com.android.contacts.util.PhoneCapabilityTester;
import com.android.contacts.util.WeakAsyncTask;
import com.google.android.collect.Lists;
@@ -46,7 +44,6 @@
import android.content.Loader;
import android.content.OperationApplicationException;
import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
@@ -62,7 +59,6 @@
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
import android.widget.BaseExpandableListAdapter;
import android.widget.CheckBox;
import android.widget.ExpandableListAdapter;
@@ -80,8 +76,7 @@
* select which ones they want to be visible.
*/
public class CustomContactListFilterActivity extends ContactsActivity
- implements AdapterView.OnItemClickListener, View.OnClickListener,
- ExpandableListView.OnChildClickListener,
+ implements View.OnClickListener, ExpandableListView.OnChildClickListener,
LoaderCallbacks<CustomContactListFilterActivity.AccountSet>
{
private static final String TAG = "CustomContactListFilterActivity";
@@ -93,11 +88,6 @@
private SharedPreferences mPrefs;
- private CheckBox mDisplayPhones;
-
- private View mHeaderPhones;
- private View mHeaderSeparator;
-
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -111,50 +101,14 @@
final LayoutInflater inflater = getLayoutInflater();
- createWithPhonesOnlyPreferenceView(inflater);
- createDisplayGroupHeader(inflater);
-
- if (mHeaderPhones != null) {
- mList.addHeaderView(mHeaderPhones, null, true);
- mList.addHeaderView(mHeaderSeparator, null, false);
- }
-
findViewById(R.id.btn_done).setOnClickListener(this);
findViewById(R.id.btn_discard).setOnClickListener(this);
- // Catch clicks on the header views
- mList.setOnItemClickListener(this);
mList.setOnCreateContextMenuListener(this);
mList.setAdapter(mAdapter);
}
- private void createWithPhonesOnlyPreferenceView(LayoutInflater inflater) {
- boolean optionSelected = mPrefs.getBoolean(ContactsPreferences.PREF_DISPLAY_ONLY_PHONES,
- ContactsPreferences.PREF_DISPLAY_ONLY_PHONES_DEFAULT);
-
- if (!optionSelected && !PhoneCapabilityTester.isPhone(this)) {
- return;
- }
-
- // Add the "Only contacts with phones" header modifier.
- mHeaderPhones = inflater.inflate(R.layout.contact_list_filter_phones_only, mList, false);
- mHeaderPhones.setId(R.id.header_phones);
- mDisplayPhones = (CheckBox) mHeaderPhones.findViewById(android.R.id.checkbox);
- mDisplayPhones.setChecked(optionSelected);
- final TextView text1 = (TextView) mHeaderPhones.findViewById(android.R.id.text1);
- text1.setText(R.string.showFilterPhones);
- final TextView text2 = (TextView) mHeaderPhones.findViewById(android.R.id.text2);
- text2.setText(R.string.showFilterPhonesDescrip);
- }
-
- private void createDisplayGroupHeader(LayoutInflater inflater) {
- // Add the separator before showing the detailed group list.
- mHeaderSeparator = inflater.inflate(R.layout.list_separator, mList, false);
- final TextView text1 = (TextView) mHeaderSeparator;
- text1.setText(R.string.headerContactGroups);
- }
-
public static class CustomFilterConfigurationLoader extends AsyncTaskLoader<AccountSet> {
private AccountSet mAccountSet;
@@ -696,18 +650,6 @@
}
}
- /**
- * Handle any clicks on header views added to our {@link #mAdapter}, which
- * are usually the global modifier checkboxes.
- */
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Log.d(TAG, "OnItemClick, position=" + position + ", id=" + id);
- if (view == mHeaderPhones) {
- mDisplayPhones.toggle();
- return;
- }
- }
-
/** {@inheritDoc} */
public void onClick(View view) {
switch (view.getId()) {
@@ -723,21 +665,6 @@
}
/**
- * Assign a specific value to {@link ContactsPreferences#PREF_DISPLAY_ONLY_PHONES}, refreshing
- * the visible list as needed.
- */
- protected void setDisplayOnlyPhones(boolean displayOnlyPhones) {
- mDisplayPhones.setChecked(displayOnlyPhones);
-
- Editor editor = mPrefs.edit();
- editor.putBoolean(ContactsPreferences.PREF_DISPLAY_ONLY_PHONES, displayOnlyPhones);
- editor.apply();
-
- mAdapter.setChildDescripWithPhones(displayOnlyPhones);
- mAdapter.notifyDataSetChanged();
- }
-
- /**
* Handle any clicks on {@link ExpandableListAdapter} children, which
* usually mean toggling its visible state.
*/
@@ -880,10 +807,6 @@
return;
}
- if (mDisplayPhones != null) {
- setDisplayOnlyPhones(mDisplayPhones.isChecked());
- }
-
setResult(RESULT_OK);
final ArrayList<ContentProviderOperation> diff = mAdapter.mAccounts.buildDiff();
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index f95c4af..88531e1 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -34,6 +34,7 @@
private View mCounterHeaderView;
private View mSearchHeaderView;
+ private TextView mAccountFilterHeaderView;
public DefaultContactBrowseListFragment() {
setPhotoLoaderEnabled(true);
@@ -63,6 +64,8 @@
protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
super.onCreateView(inflater, container);
+ mAccountFilterHeaderView = (TextView) getView().findViewById(R.id.account_filter_header);
+
// Putting the header view inside a container will allow us to make
// it invisible later. See checkHeaderViewVisibility()
FrameLayout headerContainer = new FrameLayout(inflater.getContext());
@@ -92,6 +95,19 @@
}
@Override
+ public void setFilter(ContactListFilter filter) {
+ super.setFilter(filter);
+ if (filter != null && filter.filterType != ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS &&
+ filter.filterType != ContactListFilter.FILTER_TYPE_CUSTOM) {
+ mAccountFilterHeaderView.setText(getContext().getString(
+ R.string.listAllContactsInAccount, filter.accountName));
+ mAccountFilterHeaderView.setVisibility(View.VISIBLE);
+ } else {
+ mAccountFilterHeaderView.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
protected void showCount(int partitionIndex, Cursor data) {
if (!isSearchMode() && data != null) {
int count = data.getCount();
diff --git a/src/com/android/contacts/list/DefaultContactListAdapter.java b/src/com/android/contacts/list/DefaultContactListAdapter.java
index 7f96906..30ba3b0 100644
--- a/src/com/android/contacts/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/list/DefaultContactListAdapter.java
@@ -82,7 +82,6 @@
}
builder.appendQueryParameter(SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY,
SNIPPET_ARGS);
- applyDataRestriction(builder);
loader.setUri(builder.build());
loader.setProjection(FILTER_PROJECTION);
}
@@ -129,7 +128,6 @@
ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
.build();
}
- uri = applyDataRestriction(uri);
// Include the user's personal profile.
if (shouldIncludeProfile()) {
diff --git a/src/com/android/contacts/list/EmailAddressListAdapter.java b/src/com/android/contacts/list/EmailAddressListAdapter.java
index eae02b5..e1c8ea2 100644
--- a/src/com/android/contacts/list/EmailAddressListAdapter.java
+++ b/src/com/android/contacts/list/EmailAddressListAdapter.java
@@ -22,10 +22,9 @@
import android.net.Uri;
import android.net.Uri.Builder;
import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.ContactCounts;
import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
@@ -75,7 +74,6 @@
}
builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
String.valueOf(directoryId));
- applyDataRestriction(builder);
loader.setUri(builder.build());
loader.setProjection(EMAILS_PROJECTION);
diff --git a/src/com/android/contacts/list/PhoneNumberListAdapter.java b/src/com/android/contacts/list/PhoneNumberListAdapter.java
index aabeebc..21a7846 100644
--- a/src/com/android/contacts/list/PhoneNumberListAdapter.java
+++ b/src/com/android/contacts/list/PhoneNumberListAdapter.java
@@ -90,7 +90,6 @@
builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
String.valueOf(directoryId));
- applyDataRestriction(builder);
uri = builder.build();
// TODO a projection that includes the search snippet
loader.setProjection(PHONES_PROJECTION);
@@ -101,7 +100,6 @@
if (isSectionHeaderDisplayEnabled()) {
uri = buildSectionIndexerUri(uri);
}
- uri = applyDataRestriction(uri);
loader.setProjection(PHONES_PROJECTION);
}
diff --git a/src/com/android/contacts/list/PostalAddressListAdapter.java b/src/com/android/contacts/list/PostalAddressListAdapter.java
index 86d465a..5642045 100644
--- a/src/com/android/contacts/list/PostalAddressListAdapter.java
+++ b/src/com/android/contacts/list/PostalAddressListAdapter.java
@@ -63,7 +63,6 @@
@Override
public void configureLoader(CursorLoader loader, long directoryId) {
Uri uri = buildSectionIndexerUri(StructuredPostal.CONTENT_URI);
- uri = applyDataRestriction(uri);
loader.setUri(uri);
loader.setProjection(POSTALS_PROJECTION);
diff --git a/src/com/android/contacts/list/StrequentContactListFragment.java b/src/com/android/contacts/list/StrequentContactListFragment.java
index 18edaea..00b3072 100644
--- a/src/com/android/contacts/list/StrequentContactListFragment.java
+++ b/src/com/android/contacts/list/StrequentContactListFragment.java
@@ -85,6 +85,10 @@
mAdapter.setDisplayType(displayType);
}
+ public void setQuickContact(boolean enableQuickContact) {
+ mAdapter.setQuickContact(enableQuickContact);
+ }
+
/**
* The listener for the strequent meta data loader.
*/
diff --git a/tests/src/com/android/contacts/ContactLoaderTest.java b/tests/src/com/android/contacts/ContactLoaderTest.java
index 0e0e2b0..1d3fb20 100644
--- a/tests/src/com/android/contacts/ContactLoaderTest.java
+++ b/tests/src/com/android/contacts/ContactLoaderTest.java
@@ -279,8 +279,7 @@
RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE, RawContacts.DIRTY,
RawContacts.VERSION, RawContacts.SOURCE_ID, RawContacts.SYNC1,
RawContacts.SYNC2, RawContacts.SYNC3, RawContacts.SYNC4,
- RawContacts.DELETED, RawContacts.IS_RESTRICTED,
- RawContacts.NAME_VERIFIED,
+ RawContacts.DELETED, RawContacts.NAME_VERIFIED,
Contacts.Entity.DATA_ID,
@@ -313,7 +312,7 @@
"mockAccountName", "mockAccountType", 0,
1, 0, "sync1",
"sync2", "sync3", "sync4",
- 0, 0, 0,
+ 0, 0,
dataId,
diff --git a/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java b/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java
index 5c02959..353c2ec 100644
--- a/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java
+++ b/tests/src/com/android/contacts/PhoneCallDetailsHelperTest.java
@@ -25,6 +25,7 @@
import android.graphics.drawable.Drawable;
import android.provider.CallLog.Calls;
import android.test.AndroidTestCase;
+import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -41,7 +42,9 @@
/** The date of the call log entry. */
private static final long TEST_DATE = 1300000000;
/** The number of the caller/callee in the log entry. */
- private static final String TEST_NUMBER = "1-412-555-5555";
+ private static final String TEST_NUMBER = "14125555555";
+ /** The formatted version of {@link #TEST_NUMBER}. */
+ private static final String TEST_FORMATTED_NUMBER = "1-412-255-5555";
/** A drawable to be used for incoming calls. */
private static final Drawable TEST_INCOMING_DRAWABLE = new ColorDrawable(Color.BLACK);
/** A drawable to be used for outgoing calls. */
@@ -64,7 +67,8 @@
TEST_VOICEMAIL_NUMBER, TEST_INCOMING_DRAWABLE, TEST_OUTGOING_DRAWABLE,
TEST_MISSED_DRAWABLE, TEST_VOICEMAIL_DRAWABLE);
mViews = PhoneCallDetailsViews.createForTest(new TextView(context),
- new LinearLayout(context), new TextView(context), new TextView(context));
+ new LinearLayout(context), new TextView(context), new View(context),
+ new TextView(context), new TextView(context));
}
@Override
@@ -75,27 +79,27 @@
}
public void testSetPhoneCallDetails_Unknown() {
- setPhoneCallDetailsWithNumber(CallerInfo.UNKNOWN_NUMBER);
+ setPhoneCallDetailsWithNumber(CallerInfo.UNKNOWN_NUMBER, CallerInfo.UNKNOWN_NUMBER);
assertNameEqualsResource(R.string.unknown);
}
public void testSetPhoneCallDetails_Private() {
- setPhoneCallDetailsWithNumber(CallerInfo.PRIVATE_NUMBER);
+ setPhoneCallDetailsWithNumber(CallerInfo.PRIVATE_NUMBER, CallerInfo.PRIVATE_NUMBER);
assertNameEqualsResource(R.string.private_num);
}
public void testSetPhoneCallDetails_Payphone() {
- setPhoneCallDetailsWithNumber(CallerInfo.PAYPHONE_NUMBER);
+ setPhoneCallDetailsWithNumber(CallerInfo.PAYPHONE_NUMBER, CallerInfo.PAYPHONE_NUMBER);
assertNameEqualsResource(R.string.payphone);
}
public void testSetPhoneCallDetails_Voicemail() {
- setPhoneCallDetailsWithNumber(TEST_VOICEMAIL_NUMBER);
+ setPhoneCallDetailsWithNumber(TEST_VOICEMAIL_NUMBER, TEST_VOICEMAIL_NUMBER);
assertNameEqualsResource(R.string.voicemail);
}
public void testSetPhoneCallDetails_Normal() {
- setPhoneCallDetailsWithNumber("1-412-555-1212");
+ setPhoneCallDetailsWithNumber("14125551212", "1-412-555-1212");
assertNameEquals("1-412-555-1212");
}
@@ -126,20 +130,40 @@
}
}
- public void testSetPhoneCallDetails_CallType() {
- setPhoneCallDetailsWithCallType(Calls.INCOMING_TYPE);
+ public void testSetPhoneCallDetails_CallTypeIcons() {
+ setPhoneCallDetailsWithCallTypeIcons(Calls.INCOMING_TYPE);
assertCallTypeIconsEquals(TEST_INCOMING_DRAWABLE);
- setPhoneCallDetailsWithCallType(Calls.OUTGOING_TYPE);
+ setPhoneCallDetailsWithCallTypeIcons(Calls.OUTGOING_TYPE);
assertCallTypeIconsEquals(TEST_OUTGOING_DRAWABLE);
- setPhoneCallDetailsWithCallType(Calls.MISSED_TYPE);
+ setPhoneCallDetailsWithCallTypeIcons(Calls.MISSED_TYPE);
assertCallTypeIconsEquals(TEST_MISSED_DRAWABLE);
- setPhoneCallDetailsWithCallType(Calls.VOICEMAIL_TYPE);
+ setPhoneCallDetailsWithCallTypeIcons(Calls.VOICEMAIL_TYPE);
assertCallTypeIconsEquals(TEST_VOICEMAIL_DRAWABLE);
}
+ public void testSetPhoneCallDetails_CallTypeText() {
+ LocaleTestUtils localeTestUtils = new LocaleTestUtils(getContext());
+ localeTestUtils.setLocale(Locale.US);
+ try {
+ setPhoneCallDetailsWithCallTypeText(Calls.INCOMING_TYPE);
+ assertCallTypeTextEquals("Incoming call");
+
+ setPhoneCallDetailsWithCallTypeText(Calls.OUTGOING_TYPE);
+ assertCallTypeTextEquals("Outgoing call");
+
+ setPhoneCallDetailsWithCallTypeText(Calls.MISSED_TYPE);
+ assertCallTypeTextEquals("Missed call");
+
+ setPhoneCallDetailsWithCallTypeText(Calls.VOICEMAIL_TYPE);
+ assertCallTypeTextEquals("Voicemail");
+ } finally {
+ localeTestUtils.restoreLocale();
+ }
+ }
+
/** Asserts that the name text field contains the value of the given string resource. */
private void assertNameEqualsResource(int resId) {
assertNameEquals(getContext().getString(resId));
@@ -155,28 +179,56 @@
assertEquals(text, mViews.dateView.getText().toString());
}
- /** Asserts that the call type linear layout contains the images with the given drawables. */
+ /** Asserts that the call type contains the images with the given drawables. */
private void assertCallTypeIconsEquals(Drawable... drawables) {
- assertEquals(drawables.length, mViews.callTypesLayout.getChildCount());
+ assertEquals(drawables.length, mViews.callTypeIcons.getChildCount());
for (int index = 0; index < drawables.length; ++index) {
Drawable drawable = drawables[index];
- ImageView imageView = (ImageView) mViews.callTypesLayout.getChildAt(index);
+ ImageView imageView = (ImageView) mViews.callTypeIcons.getChildAt(index);
assertEquals(drawable, imageView.getDrawable());
}
+ assertEquals(View.VISIBLE, mViews.callTypeIcons.getVisibility());
+ assertEquals(View.GONE, mViews.callTypeText.getVisibility());
+ assertEquals(View.GONE, mViews.callTypeSeparator.getVisibility());
+ }
+
+ /** Asserts that the call type contains the given text. */
+ private void assertCallTypeTextEquals(String text) {
+ assertEquals(text, mViews.callTypeText.getText().toString());
+ assertEquals(View.GONE, mViews.callTypeIcons.getVisibility());
+ assertEquals(View.VISIBLE, mViews.callTypeText.getVisibility());
+ assertEquals(View.VISIBLE, mViews.callTypeSeparator.getVisibility());
}
/** Sets the phone call details with default values and the given number. */
- private void setPhoneCallDetailsWithNumber(String number) {
- mHelper.setPhoneCallDetails(mViews, TEST_DATE, Calls.INCOMING_TYPE, "", number, 0, "");
+ private void setPhoneCallDetailsWithNumber(String number, String formattedNumber) {
+ mHelper.setPhoneCallDetails(mViews,
+ new PhoneCallDetails(number, formattedNumber, new int[]{ Calls.INCOMING_TYPE },
+ TEST_DATE),
+ false);
}
/** Sets the phone call details with default values and the given date. */
private void setPhoneCallDetailsWithDate(long date) {
- mHelper.setPhoneCallDetails(mViews, date, Calls.INCOMING_TYPE, "", TEST_NUMBER, 0, "");
+ mHelper.setPhoneCallDetails(mViews,
+ new PhoneCallDetails(TEST_NUMBER, TEST_FORMATTED_NUMBER,
+ new int[]{ Calls.INCOMING_TYPE }, date),
+ false);
}
- /** Sets the phone call details with default values and the given call type. */
- private void setPhoneCallDetailsWithCallType(int callType) {
- mHelper.setPhoneCallDetails(mViews, TEST_DATE, callType, "", TEST_NUMBER, 0, "");
+ /** Sets the phone call details with default values and the given call types using icons. */
+ private void setPhoneCallDetailsWithCallTypeIcons(int... callTypes) {
+ setPhoneCallDetailsWithCallTypes(true, callTypes);
+ }
+
+ /** Sets the phone call details with default values and the given call types using text. */
+ private void setPhoneCallDetailsWithCallTypeText(int... callTypes) {
+ setPhoneCallDetailsWithCallTypes(false, callTypes);
+ }
+
+ private void setPhoneCallDetailsWithCallTypes(boolean useIcons, int... callTypes) {
+ mHelper.setPhoneCallDetails(mViews,
+ new PhoneCallDetails(TEST_NUMBER, TEST_FORMATTED_NUMBER, callTypes, TEST_DATE),
+ useIcons);
}
}
diff --git a/tests/src/com/android/contacts/activities/CallLogActivityTests.java b/tests/src/com/android/contacts/activities/CallLogActivityTests.java
index adc1838..8db291d 100644
--- a/tests/src/com/android/contacts/activities/CallLogActivityTests.java
+++ b/tests/src/com/android/contacts/activities/CallLogActivityTests.java
@@ -18,6 +18,7 @@
import com.android.contacts.R;
import com.android.contacts.calllog.CallLogFragment;
+import com.android.contacts.calllog.CallLogFragment.ContactInfo;
import com.android.contacts.calllog.CallLogListItemViews;
import com.android.internal.telephony.CallerInfo;
@@ -27,6 +28,7 @@
import android.graphics.drawable.BitmapDrawable;
import android.provider.CallLog.Calls;
import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.telephony.PhoneNumberUtils;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
@@ -51,25 +53,31 @@
@LargeTest
public class CallLogActivityTests
extends ActivityInstrumentationTestCase2<CallLogActivity> {
- static private final String TAG = "CallLogActivityTests";
- static private final String[] CALL_LOG_PROJECTION = new String[] {
+ private static final String TAG = "CallLogActivityTests";
+
+ private static final String[] CALL_LOG_PROJECTION = new String[] {
Calls._ID,
Calls.NUMBER,
Calls.DATE,
Calls.DURATION,
Calls.TYPE,
- Calls.CACHED_NAME,
- Calls.CACHED_NUMBER_TYPE,
- Calls.CACHED_NUMBER_LABEL,
Calls.COUNTRY_ISO,
};
- static private final int RAND_DURATION = -1;
- static private final long NOW = -1L;
+ private static final int RAND_DURATION = -1;
+ private static final long NOW = -1L;
+ /** A test value for the person id of a contact. */
+ private static final long TEST_PERSON_ID = 1;
+ /** A test value for the photo id of a contact. */
+ private static final long TEST_PHOTO_ID = 2;
+ /** A test value for the lookup key for contacts. */
+ private static final String TEST_LOOKUP_KEY = "contact_id";
+ /** A test value for the country ISO of the phone number in the call log. */
+ private static final String TEST_COUNTRY_ISO = "US";
/** A phone number to be used in tests. */
- private static final String TEST_PHONE_NUMBER = "12125551000";
- /** The formatted version of {@link #TEST_PHONE_NUMBER}. */
- private static final String TEST_FORMATTED_PHONE_NUMBER = "1 212-555-1000";
+ private static final String TEST_NUMBER = "12125551000";
+ /** The formatted version of {@link #TEST_NUMBER}. */
+ private static final String TEST_FORMATTED_NUMBER = "1 212-555-1000";
// We get the call list activity and assign is a frame to build
// its list. mAdapter is an inner class of
@@ -113,6 +121,7 @@
// Do not process requests for details during tests. This would start a background thread,
// which makes the tests flaky.
mAdapter.disableRequestProcessingForTest();
+ mAdapter.stopRequestProcessing();
mParentView = new FrameLayout(mActivity);
mCursor = new MatrixCursor(CALL_LOG_PROJECTION);
buildIconMap();
@@ -184,28 +193,26 @@
@MediumTest
public void testBindView_NumberOnly() {
mCursor.moveToFirst();
- insert(TEST_PHONE_NUMBER, NOW, 0, Calls.INCOMING_TYPE);
+ insert(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE);
View view = mAdapter.newStandAloneView(getActivity(), mParentView);
mAdapter.bindStandAloneView(view, getActivity(), mCursor);
CallLogListItemViews views = (CallLogListItemViews) view.getTag();
- assertNameIs(views, TEST_FORMATTED_PHONE_NUMBER);
- assertNumberLabelIsGone(views);
- assertNumberIsGone(views);
+ assertNameIs(views, TEST_FORMATTED_NUMBER);
+ assertNumberAndLabelAreGone(views);
}
@MediumTest
public void testBindView_WithCachedName() {
mCursor.moveToFirst();
- insertWithCachedValues(TEST_PHONE_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
+ insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
"John Doe", Phone.TYPE_HOME, "");
View view = mAdapter.newStandAloneView(getActivity(), mParentView);
mAdapter.bindStandAloneView(view, getActivity(), mCursor);
CallLogListItemViews views = (CallLogListItemViews) view.getTag();
assertNameIs(views, "John Doe");
- assertNumberLabelIsVisible(views);
- assertNumberIs(views, TEST_FORMATTED_PHONE_NUMBER);
+ assertNumberAndLabelAre(views, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME));
}
@MediumTest
@@ -218,51 +225,47 @@
CallLogListItemViews views = (CallLogListItemViews) view.getTag();
assertNameIs(views, "John Doe");
- assertNumberLabelIsInvisible(views);
- assertNumberIs(views, "sip:johndoe@gmail.com");
+ assertNumberAndLabelAre(views, "sip:johndoe@gmail.com", null);
}
@MediumTest
public void testBindView_HomeLabel() {
mCursor.moveToFirst();
- insertWithCachedValues(TEST_PHONE_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
+ insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
"John Doe", Phone.TYPE_HOME, "");
View view = mAdapter.newStandAloneView(getActivity(), mParentView);
mAdapter.bindStandAloneView(view, getActivity(), mCursor);
CallLogListItemViews views = (CallLogListItemViews) view.getTag();
assertNameIs(views, "John Doe");
- assertNumberLabelIs(views, getTypeLabel(Phone.TYPE_HOME));
- assertNumberIsVisible(views);
+ assertNumberAndLabelAre(views, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME));
}
@MediumTest
public void testBindView_WorkLabel() {
mCursor.moveToFirst();
- insertWithCachedValues(TEST_PHONE_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
+ insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
"John Doe", Phone.TYPE_WORK, "");
View view = mAdapter.newStandAloneView(getActivity(), mParentView);
mAdapter.bindStandAloneView(view, getActivity(), mCursor);
CallLogListItemViews views = (CallLogListItemViews) view.getTag();
assertNameIs(views, "John Doe");
- assertNumberLabelIs(views, getTypeLabel(Phone.TYPE_WORK));
- assertNumberIsVisible(views);
+ assertNumberAndLabelAre(views, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_WORK));
}
@MediumTest
public void testBindView_CustomLabel() {
mCursor.moveToFirst();
String numberLabel = "My label";
- insertWithCachedValues(TEST_PHONE_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
+ insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
"John Doe", Phone.TYPE_CUSTOM, numberLabel);
View view = mAdapter.newStandAloneView(getActivity(), mParentView);
mAdapter.bindStandAloneView(view, getActivity(), mCursor);
CallLogListItemViews views = (CallLogListItemViews) view.getTag();
assertNameIs(views, "John Doe");
- assertNumberLabelIs(views, numberLabel);
- assertNumberIsVisible(views);
+ assertNumberAndLabelAre(views, TEST_FORMATTED_NUMBER, numberLabel);
}
/** Returns the label associated with a given phone type. */
@@ -274,27 +277,6 @@
// HELPERS to check conditions on the DB/views
//
/**
- * Check the date of the current list item.
- * @param date That should be present in the call log list
- * item. Only NOW is supported.
- */
- private void checkDate(long date) {
- if (NOW == date) {
- assertEquals("0 mins ago", mItem.dateView.getText());
- }
- throw new UnsupportedOperationException();
- }
-
- /**
- * Checks the right icon is used to represent the call type
- * (missed, incoming, outgoing.) in the current item.
- */
- private void checkCallType(int type) {
- Bitmap icon = ((BitmapDrawable) mItem.iconView.getDrawable()).getBitmap();
- assertEquals(mCallTypeIcons.get(type), icon);
- }
-
- /**
* Go over all the views in the list and check that the Call
* icon's visibility matches the nature of the number.
*/
@@ -406,6 +388,32 @@
*/
private void insertWithCachedValues(String number, long date, int duration, int type,
String cachedName, int cachedNumberType, String cachedNumberLabel) {
+ insert(number, date, duration, type);
+ ContactInfo contactInfo = new ContactInfo();
+ contactInfo.personId = TEST_PERSON_ID;
+ contactInfo.name = cachedName;
+ contactInfo.type = cachedNumberType;
+ contactInfo.label = cachedNumberLabel;
+ String formattedNumber = PhoneNumberUtils.formatNumber(number, TEST_COUNTRY_ISO);
+ if (formattedNumber == null) {
+ formattedNumber = number;
+ }
+ contactInfo.formattedNumber = formattedNumber;
+ contactInfo.normalizedNumber = number;
+ contactInfo.photoId = TEST_PHOTO_ID;
+ contactInfo.lookupKey = TEST_LOOKUP_KEY;
+ mAdapter.injectContactInfoForTest(number, contactInfo);
+ }
+
+ /**
+ * Insert a new call entry in the test DB.
+ * @param number The phone number. For unknown and private numbers,
+ * use CallerInfo.UNKNOWN_NUMBER or CallerInfo.PRIVATE_NUMBER.
+ * @param date In millisec since epoch. Use NOW to use the current time.
+ * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
+ * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE.
+ */
+ private void insert(String number, long date, int duration, int type) {
MatrixCursor.RowBuilder row = mCursor.newRow();
row.add(mIndex);
mIndex ++;
@@ -423,22 +431,7 @@
assertEquals(Calls.OUTGOING_TYPE, type);
}
row.add(type); // type
- row.add(cachedName); // cached name
- row.add(cachedNumberType); // cached number type
- row.add(cachedNumberLabel); // cached number label
- row.add("US"); // country ISO
- }
-
- /**
- * Insert a new call entry in the test DB.
- * @param number The phone number. For unknown and private numbers,
- * use CallerInfo.UNKNOWN_NUMBER or CallerInfo.PRIVATE_NUMBER.
- * @param date In millisec since epoch. Use NOW to use the current time.
- * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
- * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE.
- */
- private void insert(String number, long date, int duration, int type) {
- insertWithCachedValues(number, date, duration, type, "", Phone.TYPE_HOME, "");
+ row.add(TEST_COUNTRY_ISO); // country ISO
}
/**
@@ -512,44 +505,25 @@
/** Asserts that the name text view is shown and contains the given text. */
private void assertNameIs(CallLogListItemViews views, String name) {
- assertEquals(View.VISIBLE, views.line1View.getVisibility());
- assertEquals(name, views.line1View.getText());
+ assertEquals(View.VISIBLE, views.phoneCallDetailsViews.nameView.getVisibility());
+ assertEquals(name, views.phoneCallDetailsViews.nameView.getText());
}
- /** Asserts that the number label text view is shown and contains the given text. */
- private void assertNumberLabelIs(CallLogListItemViews views, CharSequence numberLabel) {
- assertNumberLabelIsVisible(views);
- assertEquals(numberLabel, views.labelView.getText());
+ /** Asserts that the number and label text view contains the given text. */
+ private void assertNumberAndLabelAre(CallLogListItemViews views, CharSequence number,
+ CharSequence numberLabel) {
+ assertEquals(View.VISIBLE, views.phoneCallDetailsViews.numberView.getVisibility());
+ final CharSequence expectedText;
+ if (numberLabel == null) {
+ expectedText = number;
+ } else {
+ expectedText = numberLabel + " " + number;
+ }
+ assertEquals(expectedText, views.phoneCallDetailsViews.numberView.getText().toString());
}
- /** Asserts that the number label text view is shown. */
- private void assertNumberLabelIsVisible(CallLogListItemViews views) {
- assertEquals(View.VISIBLE, views.labelView.getVisibility());
- }
-
- /** Asserts that the number label text view is invisible. */
- private void assertNumberLabelIsInvisible(CallLogListItemViews views) {
- assertEquals(View.INVISIBLE, views.labelView.getVisibility());
- }
-
- /** Asserts that the number label text view is gone. */
- private void assertNumberLabelIsGone(CallLogListItemViews views) {
- assertEquals(View.GONE, views.labelView.getVisibility());
- }
-
- /** Asserts that the number text view is shown and contains the given text. */
- private void assertNumberIs(CallLogListItemViews views, String number) {
- assertNumberIsVisible(views);
- assertEquals(number, views.numberView.getText());
- }
-
- /** Asserts that the number text view is shown. */
- private void assertNumberIsVisible(CallLogListItemViews views) {
- assertEquals(View.VISIBLE, views.numberView.getVisibility());
- }
-
- /** Asserts that the number text view is gone. */
- private void assertNumberIsGone(CallLogListItemViews views) {
- assertEquals(View.GONE, views.numberView.getVisibility());
+ /** Asserts that the number and label text view is gone. */
+ private void assertNumberAndLabelAreGone(CallLogListItemViews views) {
+ assertEquals(View.GONE, views.phoneCallDetailsViews.numberView.getVisibility());
}
}
diff --git a/tests/src/com/android/contacts/calllog/CallLogListItemHelperTest.java b/tests/src/com/android/contacts/calllog/CallLogListItemHelperTest.java
index a33b710..a8714b4 100644
--- a/tests/src/com/android/contacts/calllog/CallLogListItemHelperTest.java
+++ b/tests/src/com/android/contacts/calllog/CallLogListItemHelperTest.java
@@ -16,8 +16,9 @@
package com.android.contacts.calllog;
-import com.android.contacts.R;
-import com.android.contacts.util.LocaleTestUtils;
+import com.android.contacts.PhoneCallDetails;
+import com.android.contacts.PhoneCallDetailsHelper;
+import com.android.contacts.PhoneCallDetailsViews;
import com.android.internal.telephony.CallerInfo;
import android.content.Context;
@@ -28,15 +29,20 @@
import android.test.AndroidTestCase;
import android.view.View;
import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.QuickContactBadge;
import android.widget.TextView;
-import java.util.GregorianCalendar;
-import java.util.Locale;
-
/**
* Unit tests for {@link CallLogListItemHelper}.
*/
public class CallLogListItemHelperTest extends AndroidTestCase {
+ /** A test phone number for phone calls. */
+ private static final String TEST_NUMBER = "14125555555";
+ /** The formatted version of {@link #TEST_NUMBER}. */
+ private static final String TEST_FORMATTED_NUMBER = "1-412-255-5555";
+ /** A test date value for phone calls. */
+ private static final long TEST_DATE = 1300000000;
/** A test voicemail number. */
private static final String TEST_VOICEMAIL_NUMBER = "123";
/** A drawable to be used for incoming calls. */
@@ -62,17 +68,15 @@
protected void setUp() throws Exception {
super.setUp();
Context context = getContext();
- mHelper = new CallLogListItemHelper(context.getResources(), TEST_VOICEMAIL_NUMBER,
- TEST_INCOMING_DRAWABLE, TEST_OUTGOING_DRAWABLE, TEST_MISSED_DRAWABLE,
- TEST_VOICEMAIL_DRAWABLE, TEST_CALL_DRAWABLE, TEST_PLAY_DRAWABLE);
- mViews = new CallLogListItemViews();
- // Only set the views that are needed in the tests.
- mViews.iconView = new ImageView(context);
- mViews.dateView = new TextView(context);
- mViews.callView = new ImageView(context);
- mViews.line1View = new TextView(context);
- mViews.labelView = new TextView(context);
- mViews.numberView = new TextView(context);
+ PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(context,
+ context.getResources(), TEST_VOICEMAIL_NUMBER, TEST_INCOMING_DRAWABLE,
+ TEST_OUTGOING_DRAWABLE, TEST_MISSED_DRAWABLE, TEST_VOICEMAIL_DRAWABLE);
+ mHelper = new CallLogListItemHelper(phoneCallDetailsHelper, TEST_CALL_DRAWABLE,
+ TEST_PLAY_DRAWABLE);
+ mViews = CallLogListItemViews.createForTest(new QuickContactBadge(context),
+ new ImageView(context), PhoneCallDetailsViews.createForTest(new TextView(context),
+ new LinearLayout(context), new TextView(context), new TextView(context),
+ new TextView(context), new TextView(context)));
}
@Override
@@ -82,70 +86,50 @@
super.tearDown();
}
- public void testSetContactNumberOnly() {
- mHelper.setContactNumberOnly(mViews, "12125551234", "1-212-555-1234");
- assertEquals("1-212-555-1234", mViews.line1View.getText());
- assertEquals(View.GONE, mViews.labelView.getVisibility());
- assertEquals(View.GONE, mViews.numberView.getVisibility());
+ public void testSetPhoneCallDetails() {
+ setPhoneCallDetailsWithNumber("12125551234", "1-212-555-1234");
assertEquals(View.VISIBLE, mViews.callView.getVisibility());
+ assertEquals(TEST_CALL_DRAWABLE, mViews.callView.getDrawable());
}
- public void testSetContactNumberOnly_Unknown() {
- mHelper.setContactNumberOnly(mViews, CallerInfo.UNKNOWN_NUMBER, "");
- assertEquals(getContext().getString(R.string.unknown), mViews.line1View.getText());
- assertEquals(View.GONE, mViews.labelView.getVisibility());
- assertEquals(View.GONE, mViews.numberView.getVisibility());
+ public void testSetPhoneCallDetails_Unknown() {
+ setPhoneCallDetailsWithNumber(CallerInfo.UNKNOWN_NUMBER, CallerInfo.UNKNOWN_NUMBER);
assertEquals(View.INVISIBLE, mViews.callView.getVisibility());
}
- public void testSetContactNumberOnly_Private() {
- mHelper.setContactNumberOnly(mViews, CallerInfo.PRIVATE_NUMBER, "");
- assertEquals(getContext().getString(R.string.private_num), mViews.line1View.getText());
- assertEquals(View.GONE, mViews.labelView.getVisibility());
- assertEquals(View.GONE, mViews.numberView.getVisibility());
+ public void testSetPhoneCallDetails_Private() {
+ setPhoneCallDetailsWithNumber(CallerInfo.PRIVATE_NUMBER, CallerInfo.PRIVATE_NUMBER);
assertEquals(View.INVISIBLE, mViews.callView.getVisibility());
}
- public void testSetContactNumberOnly_Payphone() {
- mHelper.setContactNumberOnly(mViews, CallerInfo.PAYPHONE_NUMBER, "");
- assertEquals(getContext().getString(R.string.payphone), mViews.line1View.getText());
- assertEquals(View.GONE, mViews.labelView.getVisibility());
- assertEquals(View.GONE, mViews.numberView.getVisibility());
+ public void testSetPhoneCallDetails_Payphone() {
+ setPhoneCallDetailsWithNumber(CallerInfo.PAYPHONE_NUMBER, CallerInfo.PAYPHONE_NUMBER);
assertEquals(View.INVISIBLE, mViews.callView.getVisibility());
}
- public void testSetContactNumberOnly_Voicemail() {
- mHelper.setContactNumberOnly(mViews, TEST_VOICEMAIL_NUMBER, "");
- assertEquals(getContext().getString(R.string.voicemail), mViews.line1View.getText());
- assertEquals(View.GONE, mViews.labelView.getVisibility());
- assertEquals(View.GONE, mViews.numberView.getVisibility());
+ public void testSetPhoneCallDetails_VoicemailNumber() {
+ setPhoneCallDetailsWithNumber(TEST_VOICEMAIL_NUMBER, TEST_VOICEMAIL_NUMBER);
assertEquals(View.VISIBLE, mViews.callView.getVisibility());
+ assertEquals(TEST_CALL_DRAWABLE, mViews.callView.getDrawable());
}
- public void testSetDate() {
- // This test requires the locale to be set to US.
- LocaleTestUtils localeTestUtils = new LocaleTestUtils(getContext());
- localeTestUtils.setLocale(Locale.US);
- try {
- mHelper.setDate(mViews,
- new GregorianCalendar(2011, 5, 1, 12, 0, 0).getTimeInMillis(),
- new GregorianCalendar(2011, 5, 1, 13, 0, 0).getTimeInMillis());
- assertEquals("1 hour ago", mViews.dateView.getText());
- mHelper.setDate(mViews,
- new GregorianCalendar(2010, 5, 1, 12, 0, 0).getTimeInMillis(),
- new GregorianCalendar(2011, 5, 1, 13, 0, 0).getTimeInMillis());
- assertEquals("June 1, 2010", mViews.dateView.getText());
- } finally {
- localeTestUtils.restoreLocale();
- }
+ public void testSetPhoneCallDetails_Voicemail() {
+ setPhoneCallDetailsWithTypes(Calls.VOICEMAIL_TYPE);
+ assertEquals(View.VISIBLE, mViews.callView.getVisibility());
+ assertEquals(TEST_PLAY_DRAWABLE, mViews.callView.getDrawable());
}
- public void testSetCallType_Icon() {
- mHelper.setCallType(mViews, Calls.INCOMING_TYPE);
- assertEquals(TEST_INCOMING_DRAWABLE, mViews.iconView.getDrawable());
- mHelper.setCallType(mViews, Calls.OUTGOING_TYPE);
- assertEquals(TEST_OUTGOING_DRAWABLE, mViews.iconView.getDrawable());
- mHelper.setCallType(mViews, Calls.MISSED_TYPE);
- assertEquals(TEST_MISSED_DRAWABLE, mViews.iconView.getDrawable());
+ /** Sets the details of a phone call using the specified phone number. */
+ private void setPhoneCallDetailsWithNumber(String number, String formattedNumber) {
+ mHelper.setPhoneCallDetails(mViews,
+ new PhoneCallDetails(number, formattedNumber, new int[]{ Calls.INCOMING_TYPE },
+ TEST_DATE),
+ true);
+ }
+
+ /** Sets the details of a phone call using the specified call type. */
+ private void setPhoneCallDetailsWithTypes(int... types) {
+ mHelper.setPhoneCallDetails(mViews,
+ new PhoneCallDetails(TEST_NUMBER, TEST_FORMATTED_NUMBER, types, TEST_DATE), true);
}
}