Merge "Fix VVM playback control buttons don’t support TALKBACK"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0f3285e..509960c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -15,7 +15,8 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.dialer">
+ package="com.android.dialer"
+ coreApp="true">
<uses-permission android:name="android.permission.CALL_PRIVILEGED" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
@@ -67,7 +68,7 @@
dialpad screen. -->
<activity android:name=".DialtactsActivity"
android:label="@string/launcherDialer"
- android:theme="@style/DialtactsTheme"
+ android:theme="@style/DialtactsThemeHiddenActionBar"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
android:icon="@mipmap/ic_launcher_phone"
diff --git a/res/drawable-hdpi/dialer_recent_card_bg.9.png b/res/drawable-hdpi/dialer_recent_card_bg.9.png
deleted file mode 100644
index 2bfdbcb..0000000
--- a/res/drawable-hdpi/dialer_recent_card_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/divider.9.png b/res/drawable-hdpi/divider.9.png
new file mode 100644
index 0000000..88e5d91
--- /dev/null
+++ b/res/drawable-hdpi/divider.9.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_back_arrow.png b/res/drawable-hdpi/ic_back_arrow.png
new file mode 100644
index 0000000..aad4f36
--- /dev/null
+++ b/res/drawable-hdpi/ic_back_arrow.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_contact_info.png b/res/drawable-hdpi/ic_contact_info.png
deleted file mode 100644
index 9c23000..0000000
--- a/res/drawable-hdpi/ic_contact_info.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_contacts_tiles.9.png b/res/drawable-hdpi/ic_contacts_tiles.9.png
new file mode 100644
index 0000000..572fdb8
--- /dev/null
+++ b/res/drawable-hdpi/ic_contacts_tiles.9.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_dial_action_delete.png b/res/drawable-hdpi/ic_dial_action_delete.png
index 0bf8563..ebf692a 100644
--- a/res/drawable-hdpi/ic_dial_action_delete.png
+++ b/res/drawable-hdpi/ic_dial_action_delete.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_menu_dialpad_lt.png b/res/drawable-hdpi/ic_menu_dialpad_lt.png
new file mode 100644
index 0000000..bd0fe5d
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_dialpad_lt.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_remove.png b/res/drawable-hdpi/ic_remove.png
new file mode 100644
index 0000000..1ee6adf
--- /dev/null
+++ b/res/drawable-hdpi/ic_remove.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_remove_highlight.png b/res/drawable-hdpi/ic_remove_highlight.png
new file mode 100644
index 0000000..435ee36
--- /dev/null
+++ b/res/drawable-hdpi/ic_remove_highlight.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_star_marked_as_fav.png b/res/drawable-hdpi/ic_star_marked_as_fav.png
deleted file mode 100644
index 8a138c4..0000000
--- a/res/drawable-hdpi/ic_star_marked_as_fav.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_tile_for_recents_and_contact_tile.9.png b/res/drawable-hdpi/ic_tile_for_recents_and_contact_tile.9.png
new file mode 100644
index 0000000..b5f50de
--- /dev/null
+++ b/res/drawable-hdpi/ic_tile_for_recents_and_contact_tile.9.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_voice_search.png b/res/drawable-hdpi/ic_voice_search.png
index 6caf4a4..9631d3e 100644
--- a/res/drawable-hdpi/ic_voice_search.png
+++ b/res/drawable-hdpi/ic_voice_search.png
Binary files differ
diff --git a/res/drawable-hdpi/overflow_thumbnail.png b/res/drawable-hdpi/overflow_thumbnail.png
new file mode 100644
index 0000000..57db353
--- /dev/null
+++ b/res/drawable-hdpi/overflow_thumbnail.png
Binary files differ
diff --git a/res/drawable-hdpi/search_bg.9.png b/res/drawable-hdpi/search_bg.9.png
new file mode 100644
index 0000000..81c47be
--- /dev/null
+++ b/res/drawable-hdpi/search_bg.9.png
Binary files differ
diff --git a/res/drawable-hdpi/star_thumbnail.png b/res/drawable-hdpi/star_thumbnail.png
new file mode 100644
index 0000000..1d4d5e1
--- /dev/null
+++ b/res/drawable-hdpi/star_thumbnail.png
Binary files differ
diff --git a/res/drawable-mdpi/dialer_recent_card_bg.9.png b/res/drawable-mdpi/dialer_recent_card_bg.9.png
deleted file mode 100644
index dff6a0b..0000000
--- a/res/drawable-mdpi/dialer_recent_card_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/divider.9.png b/res/drawable-mdpi/divider.9.png
new file mode 100644
index 0000000..88e5d91
--- /dev/null
+++ b/res/drawable-mdpi/divider.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_back_arrow.png b/res/drawable-mdpi/ic_back_arrow.png
new file mode 100644
index 0000000..56eb887
--- /dev/null
+++ b/res/drawable-mdpi/ic_back_arrow.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_contact_info.png b/res/drawable-mdpi/ic_contact_info.png
deleted file mode 100644
index 5d35ec5..0000000
--- a/res/drawable-mdpi/ic_contact_info.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_contacts_tiles.9.png b/res/drawable-mdpi/ic_contacts_tiles.9.png
new file mode 100644
index 0000000..343053a
--- /dev/null
+++ b/res/drawable-mdpi/ic_contacts_tiles.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_dial_action_delete.png b/res/drawable-mdpi/ic_dial_action_delete.png
index 98341e9..e1394c5 100644
--- a/res/drawable-mdpi/ic_dial_action_delete.png
+++ b/res/drawable-mdpi/ic_dial_action_delete.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_dialpad_lt.png b/res/drawable-mdpi/ic_menu_dialpad_lt.png
new file mode 100644
index 0000000..14674ed
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_dialpad_lt.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_remove.png b/res/drawable-mdpi/ic_remove.png
new file mode 100644
index 0000000..2c134ea
--- /dev/null
+++ b/res/drawable-mdpi/ic_remove.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_remove_highlight.png b/res/drawable-mdpi/ic_remove_highlight.png
new file mode 100644
index 0000000..6a961cb
--- /dev/null
+++ b/res/drawable-mdpi/ic_remove_highlight.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_star_marked_as_fav.png b/res/drawable-mdpi/ic_star_marked_as_fav.png
deleted file mode 100644
index ee1b5ec..0000000
--- a/res/drawable-mdpi/ic_star_marked_as_fav.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_tile_for_recents_and_contact_tile.9.png b/res/drawable-mdpi/ic_tile_for_recents_and_contact_tile.9.png
new file mode 100644
index 0000000..e43b3ef
--- /dev/null
+++ b/res/drawable-mdpi/ic_tile_for_recents_and_contact_tile.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_voice_search.png b/res/drawable-mdpi/ic_voice_search.png
index e290f92..af58538 100644
--- a/res/drawable-mdpi/ic_voice_search.png
+++ b/res/drawable-mdpi/ic_voice_search.png
Binary files differ
diff --git a/res/drawable-mdpi/overflow_thumbnail.png b/res/drawable-mdpi/overflow_thumbnail.png
new file mode 100644
index 0000000..c699374
--- /dev/null
+++ b/res/drawable-mdpi/overflow_thumbnail.png
Binary files differ
diff --git a/res/drawable-mdpi/search_bg.9.png b/res/drawable-mdpi/search_bg.9.png
new file mode 100644
index 0000000..879aeaa
--- /dev/null
+++ b/res/drawable-mdpi/search_bg.9.png
Binary files differ
diff --git a/res/drawable-mdpi/star_thumbnail.png b/res/drawable-mdpi/star_thumbnail.png
new file mode 100644
index 0000000..7b96272
--- /dev/null
+++ b/res/drawable-mdpi/star_thumbnail.png
Binary files differ
diff --git a/res/drawable-xhdpi/dialer_recent_card_bg.9.png b/res/drawable-xhdpi/dialer_recent_card_bg.9.png
deleted file mode 100644
index 05d254d..0000000
--- a/res/drawable-xhdpi/dialer_recent_card_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/divider.9.png b/res/drawable-xhdpi/divider.9.png
new file mode 100644
index 0000000..88e5d91
--- /dev/null
+++ b/res/drawable-xhdpi/divider.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_back_arrow.png b/res/drawable-xhdpi/ic_back_arrow.png
new file mode 100644
index 0000000..9d46e3d
--- /dev/null
+++ b/res/drawable-xhdpi/ic_back_arrow.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contact_info.png b/res/drawable-xhdpi/ic_contact_info.png
deleted file mode 100644
index 88d367b..0000000
--- a/res/drawable-xhdpi/ic_contact_info.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_contacts_tiles.9.png b/res/drawable-xhdpi/ic_contacts_tiles.9.png
new file mode 100644
index 0000000..5a56587
--- /dev/null
+++ b/res/drawable-xhdpi/ic_contacts_tiles.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_dial_action_delete.png b/res/drawable-xhdpi/ic_dial_action_delete.png
index 989e8b1..6788669 100644
--- a/res/drawable-xhdpi/ic_dial_action_delete.png
+++ b/res/drawable-xhdpi/ic_dial_action_delete.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_dialpad_lt.png b/res/drawable-xhdpi/ic_menu_dialpad_lt.png
new file mode 100644
index 0000000..345cf1a
--- /dev/null
+++ b/res/drawable-xhdpi/ic_menu_dialpad_lt.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_remove.png b/res/drawable-xhdpi/ic_remove.png
new file mode 100644
index 0000000..be81592
--- /dev/null
+++ b/res/drawable-xhdpi/ic_remove.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_remove_highlight.png b/res/drawable-xhdpi/ic_remove_highlight.png
new file mode 100644
index 0000000..57949e3
--- /dev/null
+++ b/res/drawable-xhdpi/ic_remove_highlight.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_star_marked_as_fav.png b/res/drawable-xhdpi/ic_star_marked_as_fav.png
deleted file mode 100644
index 372747a..0000000
--- a/res/drawable-xhdpi/ic_star_marked_as_fav.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_tile_for_recents_and_contact_tile.9.png b/res/drawable-xhdpi/ic_tile_for_recents_and_contact_tile.9.png
new file mode 100644
index 0000000..ebfe897
--- /dev/null
+++ b/res/drawable-xhdpi/ic_tile_for_recents_and_contact_tile.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_voice_search.png b/res/drawable-xhdpi/ic_voice_search.png
index 6147581..6e5d811 100644
--- a/res/drawable-xhdpi/ic_voice_search.png
+++ b/res/drawable-xhdpi/ic_voice_search.png
Binary files differ
diff --git a/res/drawable-xhdpi/overflow_thumbnail.png b/res/drawable-xhdpi/overflow_thumbnail.png
new file mode 100644
index 0000000..e538b98
--- /dev/null
+++ b/res/drawable-xhdpi/overflow_thumbnail.png
Binary files differ
diff --git a/res/drawable-xhdpi/search_bg.9.png b/res/drawable-xhdpi/search_bg.9.png
new file mode 100644
index 0000000..9d2d9ae
--- /dev/null
+++ b/res/drawable-xhdpi/search_bg.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/star_thumbnail.png b/res/drawable-xhdpi/star_thumbnail.png
new file mode 100644
index 0000000..a71262f
--- /dev/null
+++ b/res/drawable-xhdpi/star_thumbnail.png
Binary files differ
diff --git a/res/drawable-xxhdpi/dialer_recent_card_bg.9.png b/res/drawable-xxhdpi/dialer_recent_card_bg.9.png
deleted file mode 100644
index 87d95f0..0000000
--- a/res/drawable-xxhdpi/dialer_recent_card_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/divider.9.png b/res/drawable-xxhdpi/divider.9.png
new file mode 100644
index 0000000..88e5d91
--- /dev/null
+++ b/res/drawable-xxhdpi/divider.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_back_arrow.png b/res/drawable-xxhdpi/ic_back_arrow.png
new file mode 100644
index 0000000..66b6e35
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_back_arrow.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_contact_info.png b/res/drawable-xxhdpi/ic_contact_info.png
deleted file mode 100644
index e5d2939..0000000
--- a/res/drawable-xxhdpi/ic_contact_info.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_contacts_tiles.9.png b/res/drawable-xxhdpi/ic_contacts_tiles.9.png
new file mode 100644
index 0000000..4e6872d
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_contacts_tiles.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_dial_action_delete.png b/res/drawable-xxhdpi/ic_dial_action_delete.png
index a7ff1b1..ca91a72 100644
--- a/res/drawable-xxhdpi/ic_dial_action_delete.png
+++ b/res/drawable-xxhdpi/ic_dial_action_delete.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_dialpad_lt.png b/res/drawable-xxhdpi/ic_menu_dialpad_lt.png
new file mode 100644
index 0000000..45c1ab2
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_menu_dialpad_lt.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_remove.png b/res/drawable-xxhdpi/ic_remove.png
new file mode 100644
index 0000000..2722f23
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_remove.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_remove_highlight.png b/res/drawable-xxhdpi/ic_remove_highlight.png
new file mode 100644
index 0000000..23ee8f6
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_remove_highlight.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_star_marked_as_fav.png b/res/drawable-xxhdpi/ic_star_marked_as_fav.png
deleted file mode 100644
index 3eeff4c..0000000
--- a/res/drawable-xxhdpi/ic_star_marked_as_fav.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_tile_for_recents_and_contact_tile.9.png b/res/drawable-xxhdpi/ic_tile_for_recents_and_contact_tile.9.png
new file mode 100644
index 0000000..0a99cdb
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_tile_for_recents_and_contact_tile.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_voice_search.png b/res/drawable-xxhdpi/ic_voice_search.png
index 4c17d2b..4e72f69 100644
--- a/res/drawable-xxhdpi/ic_voice_search.png
+++ b/res/drawable-xxhdpi/ic_voice_search.png
Binary files differ
diff --git a/res/drawable-xxhdpi/overflow_thumbnail.png b/res/drawable-xxhdpi/overflow_thumbnail.png
new file mode 100644
index 0000000..7f3f733
--- /dev/null
+++ b/res/drawable-xxhdpi/overflow_thumbnail.png
Binary files differ
diff --git a/res/drawable-xxhdpi/search_bg.9.png b/res/drawable-xxhdpi/search_bg.9.png
new file mode 100644
index 0000000..2f4f4b6
--- /dev/null
+++ b/res/drawable-xxhdpi/search_bg.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/star_thumbnail.png b/res/drawable-xxhdpi/star_thumbnail.png
new file mode 100644
index 0000000..5f13fec
--- /dev/null
+++ b/res/drawable-xxhdpi/star_thumbnail.png
Binary files differ
diff --git a/res/drawable/action_bar_tab.xml b/res/drawable/action_bar_tab.xml
index 35df053..3f31dba 100644
--- a/res/drawable/action_bar_tab.xml
+++ b/res/drawable/action_bar_tab.xml
@@ -16,11 +16,10 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
<item android:drawable="@drawable/tab_selected_pressed" android:state_pressed="true" android:state_selected="true"/>
<item android:drawable="@drawable/tab_selected_focused" android:state_focused="true" android:state_selected="true"/>
<item android:drawable="@drawable/tab_selected" android:state_selected="true"/>
<item android:drawable="@drawable/tab_unselected_pressed" android:state_pressed="true"/>
<item android:drawable="@drawable/tab_unselected_focused" android:state_focused="true"/>
-
+ <item android:drawable="@drawable/tab_unselected" android:state_selected="false"/>
</selector>
\ No newline at end of file
diff --git a/res/drawable/background_all_contacts.xml b/res/drawable/background_all_contacts.xml
new file mode 100644
index 0000000..0d3703f
--- /dev/null
+++ b/res/drawable/background_all_contacts.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="false">
+ <shape android:shape="rectangle" >
+ <solid android:color="@color/all_contacts_button_color" />
+ </shape>
+ </item>
+ <item android:state_pressed="true">
+ <shape android:shape="rectangle" >
+ <solid android:color="@color/all_contacts_button_pressed_color" />
+ </shape>
+ </item>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/call_history_actionbar_background.xml b/res/drawable/call_history_actionbar_background.xml
index f781f27..eabceac 100644
--- a/res/drawable/call_history_actionbar_background.xml
+++ b/res/drawable/call_history_actionbar_background.xml
@@ -16,10 +16,10 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="rectangle">
- <solid android:color="#c6c6c6" />
+ <solid android:color="@color/actionbar_underline" />
</shape>
</item>
- <item android:bottom="1dip">
+ <item android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="@color/actionbar_background_color" />
</shape>
diff --git a/res/drawable/dialpad_key_colors.xml b/res/drawable/dialpad_key_colors.xml
new file mode 100644
index 0000000..27b4d4f
--- /dev/null
+++ b/res/drawable/dialpad_key_colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_pressed="true"
+ android:drawable="@color/background_dialpad_pressed" />
+ <item android:drawable="@color/background_dialpad" />
+</selector>
diff --git a/res/drawable/shadow_fade_up.xml b/res/drawable/shadow_fade_up.xml
new file mode 100644
index 0000000..e2d9934
--- /dev/null
+++ b/res/drawable/shadow_fade_up.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle" >
+ <gradient
+ android:type="linear"
+ android:startColor="#66999999"
+ android:endColor="#00ffffff"
+ android:angle="90"/>
+</shape>
\ No newline at end of file
diff --git a/res/drawable/tab_selected.xml b/res/drawable/tab_selected.xml
index 25d76fc..821f047 100644
--- a/res/drawable/tab_selected.xml
+++ b/res/drawable/tab_selected.xml
@@ -14,18 +14,26 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
+ <!-- Tab underline -->
<item>
- <shape android:shape="rectangle" >
- <solid android:color="#7d7d7d" />
+ <shape android:shape="rectangle">
+ <solid android:color="@color/tab_underline_selected_color"/>
</shape>
</item>
- <item android:bottom="6dp">
+ <!-- Tab selection indicator -->
+ <item android:bottom="@dimen/tab_underline_height">
<shape android:shape="rectangle" >
- <solid android:color="@color/actionbar_background_color" />
+ <solid android:color="@color/tab_selected_color" />
</shape>
</item>
-
+ <!-- Tab background -->
+ <item android:bottom="@dimen/tab_selection_height">
+ <shape android:shape="rectangle" >
+ <gradient
+ android:startColor="@color/tab_background_gradient_start_color"
+ android:endColor="@color/tab_background_gradient_end_color"
+ android:angle="270" />
+ </shape>
+ </item>
</layer-list>
\ No newline at end of file
diff --git a/res/drawable/tab_selected_focused.xml b/res/drawable/tab_selected_focused.xml
index 8efd7b3..7c0d35f 100644
--- a/res/drawable/tab_selected_focused.xml
+++ b/res/drawable/tab_selected_focused.xml
@@ -14,32 +14,33 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
+ <!-- Tab underline -->
<item>
- <shape android:shape="rectangle" >
- <solid android:color="#7d7d7d" />
+ <shape android:shape="rectangle">
+ <solid android:color="@color/tab_underline_selected_color"/>
</shape>
</item>
- <item android:bottom="6dp">
+ <!-- Tab selection indicator -->
+ <item android:bottom="@dimen/tab_underline_height">
<shape android:shape="rectangle" >
- <solid android:color="@color/actionbar_background_color" />
+ <solid android:color="@color/tab_selected_color" />
+ </shape>
+ </item>
+ <!-- Tab background -->
+ <item android:bottom="@dimen/tab_selection_height">
+ <shape android:shape="rectangle" >
+ <gradient
+ android:startColor="@color/tab_background_gradient_start_color"
+ android:endColor="@color/tab_background_gradient_end_color"
+ android:angle="270" />
</shape>
</item>
<item>
<shape android:shape="rectangle" >
- <solid android:color="#27ffffff" />
- </shape>
- </item>
- <item>
- <shape android:shape="rectangle" >
- <solid android:color="#00000000" />
-
<stroke
- android:width="2dp"
- android:color="#27ffffff" />
+ android:width="4dp"
+ android:color="#44ff0000" />
</shape>
</item>
-
</layer-list>
\ No newline at end of file
diff --git a/res/drawable/tab_selected_pressed.xml b/res/drawable/tab_selected_pressed.xml
index a38344e..32efb69 100644
--- a/res/drawable/tab_selected_pressed.xml
+++ b/res/drawable/tab_selected_pressed.xml
@@ -14,23 +14,26 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
+ <!-- Tab underline -->
<item>
- <shape android:shape="rectangle" >
- <solid android:color="#7d7d7d" />
+ <shape android:shape="rectangle">
+ <solid android:color="@color/tab_underline_selected_color"/>
</shape>
</item>
- <item android:bottom="6dp">
+ <!-- Tab selection indicator -->
+ <item android:bottom="@dimen/tab_underline_height">
<shape android:shape="rectangle" >
- <solid android:color="@color/actionbar_background_color" />
+ <solid android:color="@color/tab_selected_color" />
</shape>
</item>
- <item>
+ <!-- Tab background (in pressed state) -->
+ <item android:bottom="@dimen/tab_selection_height">
<shape android:shape="rectangle" >
- <solid android:color="#4dffffff" />
+ <gradient
+ android:startColor="@color/tab_background_gradient_start_pressed_color"
+ android:endColor="@color/tab_background_gradient_end_pressed_color"
+ android:angle="270" />
</shape>
</item>
-
</layer-list>
\ No newline at end of file
diff --git a/res/drawable/tab_unselected.xml b/res/drawable/tab_unselected.xml
new file mode 100644
index 0000000..3eee487
--- /dev/null
+++ b/res/drawable/tab_unselected.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+ <!-- Tab underline -->
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/tab_underline_color"/>
+ </shape>
+ </item>
+ <!-- Tab background -->
+ <item android:bottom="@dimen/tab_underline_height">
+ <shape android:shape="rectangle" >
+ <gradient
+ android:startColor="@color/tab_background_gradient_start_color"
+ android:endColor="@color/tab_background_gradient_end_color"
+ android:angle="270" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/tab_unselected_focused.xml b/res/drawable/tab_unselected_focused.xml
index 2aceb63..c854739 100644
--- a/res/drawable/tab_unselected_focused.xml
+++ b/res/drawable/tab_unselected_focused.xml
@@ -14,22 +14,27 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Tab underline -->
<item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/tab_underline_color"/>
+ </shape>
+ </item>
+ <!-- Tab background -->
+ <item android:bottom="@dimen/tab_underline_height">
<shape android:shape="rectangle" >
- <solid android:color="#27ffffff" />
+ <gradient
+ android:startColor="@color/tab_background_gradient_start_color"
+ android:endColor="@color/tab_background_gradient_end_color"
+ android:angle="270" />
</shape>
</item>
<item>
<shape android:shape="rectangle" >
- <solid android:color="#00000000" />
-
<stroke
- android:width="2dp"
- android:color="#27ffffff" />
+ android:width="4dp"
+ android:color="#44ff0000" />
</shape>
</item>
-
</layer-list>
\ No newline at end of file
diff --git a/res/drawable/tab_unselected_pressed.xml b/res/drawable/tab_unselected_pressed.xml
index 2362b65..5d2af32 100644
--- a/res/drawable/tab_unselected_pressed.xml
+++ b/res/drawable/tab_unselected_pressed.xml
@@ -14,13 +14,20 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
-
+ <!-- Tab underline -->
<item>
- <shape android:shape="rectangle" >
- <solid android:color="#4dffffff" />
+ <shape android:shape="rectangle">
+ <solid android:color="@color/tab_underline_color"/>
</shape>
</item>
-
+ <!-- Tab background (in pressed state) -->
+ <item android:bottom="@dimen/tab_underline_height">
+ <shape android:shape="rectangle" >
+ <gradient
+ android:startColor="@color/tab_background_gradient_start_pressed_color"
+ android:endColor="@color/tab_background_gradient_end_pressed_color"
+ android:angle="270" />
+ </shape>
+ </item>
</layer-list>
\ No newline at end of file
diff --git a/res/layout/call_log_list_item.xml b/res/layout/call_log_list_item.xml
index 1d368f6..e17dc27 100644
--- a/res/layout/call_log_list_item.xml
+++ b/res/layout/call_log_list_item.xml
@@ -34,9 +34,20 @@
@id/call_log_item gone
-->
+ <!-- Linear layout to separate the primary area containing the contact badge and caller
+ information and the secondary action (call details / play voicemail). -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ >
+
+ <!-- Primary area containing the contact badge and caller information -->
<LinearLayout
android:id="@+id/primary_action_view"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:padding="@dimen/call_log_outer_margin"
@@ -46,7 +57,7 @@
android:focusable="true"
android:nextFocusRight="@+id/secondary_action_icon"
android:nextFocusLeft="@+id/quick_contact_photo"
- >
+ >
<QuickContactBadge
android:id="@+id/quick_contact_photo"
android:layout_width="@dimen/call_log_list_contact_photo_size"
@@ -54,7 +65,7 @@
android:nextFocusRight="@id/primary_action_view"
android:layout_alignParentStart="true"
android:focusable="true"
- />
+ />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
@@ -62,7 +73,7 @@
android:orientation="vertical"
android:gravity="center_vertical"
android:layout_marginStart="@dimen/call_log_inner_margin"
- >
+ >
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
@@ -71,12 +82,12 @@
android:textColor="?attr/call_log_primary_text_color"
android:textSize="16sp"
android:singleLine="true"
- />
+ />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
- >
+ >
<TextView
android:id="@+id/label"
android:layout_width="wrap_content"
@@ -87,13 +98,13 @@
android:singleLine="true"
android:ellipsize="marquee"
/>
- </LinearLayout>
+ </LinearLayout>
<LinearLayout
android:id="@+id/call_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
- >
+ >
<view
class="com.android.dialer.calllog.CallTypeIconsView"
android:id="@+id/call_type_icons"
@@ -101,7 +112,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/call_log_icon_margin"
android:layout_gravity="center_vertical"
- />
+ />
<TextView
android:id="@+id/call_count_and_date"
android:layout_width="wrap_content"
@@ -111,23 +122,39 @@
android:textColor="?attr/call_log_secondary_text_color"
android:textSize="12sp"
android:singleLine="true"
- />
+ />
</LinearLayout>
</LinearLayout>
+ </LinearLayout>
+ <!-- Linear layout to house a vertical separator line and the secondary action button.
+ Used as a convenience to hide both the separator and action button at the same
+ time. -->
+ <LinearLayout
+ android:id="@+id/secondary_action_view"
+ android:layout_width="@dimen/call_log_call_action_width"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ >
+ <!-- Thin vertical divider to visually separate the secondary action button -->
+ <View
+ android:id="@+id/vertical_divider"
+ android:layout_width="@dimen/call_log_list_item_vertical_divider_width"
+ android:layout_height="match_parent"
+ android:layout_marginTop="@dimen/call_log_list_item_vertical_divider_margin"
+ android:layout_marginBottom="@dimen/call_log_list_item_vertical_divider_margin"
+ android:background="?android:attr/dividerVertical"/>
+ <!-- The secondary action button; either play voicemail or call details. -->
<ImageButton
android:id="@+id/secondary_action_icon"
- android:layout_width="@dimen/call_log_call_action_width"
+ android:layout_width="fill_parent"
android:layout_height="match_parent"
- android:paddingStart="@dimen/call_log_inner_margin"
- android:paddingTop="@dimen/call_log_inner_margin"
- android:paddingBottom="@dimen/call_log_inner_margin"
- android:paddingEnd="@dimen/call_log_inner_margin"
android:scaleType="center"
android:background="?android:attr/selectableItemBackground"
android:nextFocusLeft="@id/primary_action_view"
- />
+ />
</LinearLayout>
-
+ </LinearLayout>
<TextView
android:id="@+id/call_log_header"
style="@style/ContactListSeparatorTextViewStyle"
diff --git a/res/layout/dialpad.xml b/res/layout/dialpad.xml
index 4fc3e83..c13f525 100644
--- a/res/layout/dialpad.xml
+++ b/res/layout/dialpad.xml
@@ -19,42 +19,25 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialpad"
android:layout_width="match_parent"
- android:layout_height="0px"
- android:layout_weight="@integer/dialpad_layout_weight_dialpad"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="@dimen/dialpad_vertical_margin"
- android:paddingStart="5dip"
- android:paddingEnd="5dip"
- android:paddingBottom="10dip"
- android:stretchColumns="0,1,2"
+ android:layout_height="wrap_content"
+ android:paddingLeft="5dp"
+ android:paddingRight="5dp"
+ android:stretchColumns="*"
android:layoutDirection="ltr" >
- <TableRow
- android:layout_height="0px"
- android:layout_weight="1">
+ <TableRow>
<com.android.dialer.dialpad.DialpadKeyButton
- xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/one"
- style="@style/DialtactsDialpadButtonStyle"
- android:clickable="true"
- android:focusable="true" >
+ style="@style/DialpadKeyButtonStyle">
<LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center" >
+ style="@style/DialpadKeyInternalLayoutStyle">
<TextView
android:id="@+id/dialpad_key_number"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/dialpad_primary_text_color"
- android:textSize="@dimen/dialpad_key_numbers_size"
- android:fontFamily="sans-serif-light"/>
+ style="@style/DialpadKeyNumberStyle"/>
<ImageView
android:id="@+id/dialpad_key_voicemail"
- android:layout_width="@dimen/dialpad_key_letters_width"
- android:layout_height="wrap_content"
+ style="@style/DialpadKeyLettersStyle"
android:src="@drawable/ic_dial_action_vm"
- android:paddingLeft="11dp"
android:scaleType="fitStart"
android:baselineAlignBottom="true" />
</LinearLayout>
@@ -63,57 +46,45 @@
<include layout="@layout/dialpad_key" android:id="@+id/three"/>
</TableRow>
- <TableRow
- android:layout_height="0px"
- android:layout_weight="1">
+ <TableRow>
<include layout="@layout/dialpad_key" android:id="@+id/four"/>
<include layout="@layout/dialpad_key" android:id="@+id/five"/>
<include layout="@layout/dialpad_key" android:id="@+id/six"/>
</TableRow>
- <TableRow
- android:layout_height="0px"
- android:layout_weight="1">
+ <TableRow>
<include layout="@layout/dialpad_key" android:id="@+id/seven"/>
<include layout="@layout/dialpad_key" android:id="@+id/eight"/>
<include layout="@layout/dialpad_key" android:id="@+id/nine"/>
</TableRow>
- <TableRow
- android:layout_height="0px"
- android:layout_weight="1">
+ <TableRow>
<com.android.dialer.dialpad.DialpadKeyButton
- xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/star"
- style="@style/DialtactsDialpadButtonStyle"
- android:clickable="true"
- android:focusable="true" >
- <TextView
- android:id="@id/dialpad_key_number"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/dialpad_secondary_text_color"
- android:textSize="@dimen/dialpad_key_special_characters_size"
- android:fontFamily="sans-serif-light"
- android:paddingRight="@dimen/dialpad_key_letters_width"
- android:layout_gravity="center" />
+ style="@style/DialpadKeyButtonStyle">
+ <LinearLayout
+ style="@style/DialpadKeyInternalLayoutStyle">
+ <TextView
+ android:id="@id/dialpad_key_number"
+ style="@style/DialpadKeyStarPoundStyle" />
+ <View
+ android:layout_height="match_parent"
+ android:layout_width="@dimen/dialpad_key_letters_width" />
+ </LinearLayout>
</com.android.dialer.dialpad.DialpadKeyButton>
<include layout="@layout/dialpad_key" android:id="@+id/zero"/>
<com.android.dialer.dialpad.DialpadKeyButton
- xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pound"
- style="@style/DialtactsDialpadButtonStyle"
- android:clickable="true"
- android:focusable="true" >
- <TextView
- android:id="@id/dialpad_key_number"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/dialpad_secondary_text_color"
- android:textSize="@dimen/dialpad_key_special_characters_size"
- android:fontFamily="sans-serif-light"
- android:paddingRight="@dimen/dialpad_key_letters_width"
- android:layout_gravity="center" />
+ style="@style/DialpadKeyButtonStyle">
+ <LinearLayout
+ style="@style/DialpadKeyInternalLayoutStyle">
+ <TextView
+ android:id="@id/dialpad_key_number"
+ style="@style/DialpadKeyStarPoundStyle" />
+ <View
+ android:layout_height="match_parent"
+ android:layout_width="@dimen/dialpad_key_letters_width" />
+ </LinearLayout>
</com.android.dialer.dialpad.DialpadKeyButton>
</TableRow>
</TableLayout>
diff --git a/res/layout/dialpad_chooser_list_item.xml b/res/layout/dialpad_chooser_list_item.xml
index 5ba88ca..9a49036 100644
--- a/res/layout/dialpad_chooser_list_item.xml
+++ b/res/layout/dialpad_chooser_list_item.xml
@@ -27,7 +27,7 @@
<TextView android:id="@+id/text"
android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="@color/dialpad_text_color"
+ android:textColor="@color/dialpad_primary_text_color"
android:layout_gravity="center_vertical"
android:layout_width="0dip"
android:layout_weight="1"
diff --git a/res/layout/dialpad_digits.xml b/res/layout/dialpad_digits.xml
new file mode 100644
index 0000000..21638f0
--- /dev/null
+++ b/res/layout/dialpad_digits.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Text field and possibly soft menu button above the keypad where
+ the digits are displayed. -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/digits_container"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dialpad_digits_height"
+ android:orientation="horizontal">
+
+ <view class="com.android.dialer.dialpad.DigitsEditText"
+ android:id="@+id/digits"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/dialpad_digits_padding"
+ android:scrollHorizontally="true"
+ android:singleLine="true"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:background="@android:color/transparent"
+ android:maxLines="1"
+ android:textSize="@dimen/dialpad_digits_text_size"
+ android:freezesText="true"
+ android:focusableInTouchMode="true"
+ android:editable="true"
+ android:cursorVisible="false"
+ android:textColor="@color/dialpad_digits_text_color"
+ android:textCursorDrawable="@null"
+ android:fontFamily="sans-serif-light"
+ android:textStyle="normal" />
+
+ <ImageButton
+ android:id="@+id/deleteButton"
+ android:paddingLeft="@dimen/dialpad_digits_padding"
+ android:paddingRight="@dimen/dialpad_digits_padding"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:state_enabled="false"
+ android:background="@drawable/dialpad_key_colors"
+ android:contentDescription="@string/description_delete_button"
+ android:src="@drawable/ic_dial_action_delete" />
+</LinearLayout>
diff --git a/res/layout/dialpad_fragment.xml b/res/layout/dialpad_fragment.xml
index e673cea..dfe5c7c 100644
--- a/res/layout/dialpad_fragment.xml
+++ b/res/layout/dialpad_fragment.xml
@@ -16,103 +16,55 @@
<view class="com.android.dialer.dialpad.DialpadFragment$DialpadSlidingLinearLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:orientation="vertical" >
<!-- spacer view -->
<View
android:id="@+id/spacer"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_weight="1" />
+ android:layout_weight="1"
+ android:background="#00000000" />
+ <!-- Dialpad shadow -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="3dp"
+ android:background="@drawable/shadow_fade_up" />
<view class="com.android.dialer.dialpad.DialpadFragment$HoverIgnoringLinearLayout"
android:id="@+id/top"
+ android:animateLayoutChanges="true"
+ android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="2"
+ android:layout_gravity="bottom"
android:orientation="vertical"
- android:paddingStart="@dimen/dialpad_horizontal_margin"
- android:paddingEnd="@dimen/dialpad_horizontal_margin"
android:layoutDirection="ltr"
- android:background="@color/background_dialpad" >
+ android:background="@color/background_dialpad">
-
- <!-- Text field and possibly soft menu button above the keypad where
- the digits are displayed. -->
- <LinearLayout
- android:id="@+id/digits_container"
+ <Space
android:layout_width="match_parent"
- android:layout_height="0px"
- android:layout_weight="@integer/dialpad_layout_weight_digits"
- android:layout_marginTop="@dimen/dialpad_vertical_margin"
- android:gravity="center" >
+ android:layout_height="2dp"
+ android:background="@color/dialpad_separator_line_color" />
- <com.android.dialer.dialpad.DigitsEditText
- android:id="@+id/digits"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_height="match_parent"
- android:paddingLeft="10dp"
- android:gravity="center"
- android:scrollHorizontally="true"
- android:singleLine="true"
- android:textAppearance="@style/DialtactsDigitsTextAppearance"
- android:textColor="@color/dialpad_text_color"
- android:textCursorDrawable="@null"
- android:fontFamily="sans-serif-light"
- android:nextFocusRight="@+id/overflow_menu"
- android:background="@android:color/transparent" />
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="10dp" />
- <ImageButton
- android:id="@+id/deleteButton"
- android:layout_width="56dip"
- android:layout_height="match_parent"
- android:layout_gravity="center_vertical"
- android:gravity="center"
- android:state_enabled="false"
- android:background="?android:attr/selectableItemBackground"
- android:contentDescription="@string/description_delete_button"
- android:src="@drawable/ic_dial_action_delete" />
- </LinearLayout>
+ <include layout="@layout/dialpad_digits"/>
- <!-- Keypad section -->
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="8dp" />
+
<include layout="@layout/dialpad" />
- <View style="@style/DialpadHorizontalSeparator"/>
-
- <LinearLayout
- android:id="@+id/dialButtonContainer"
+ <Space
android:layout_width="match_parent"
- android:layout_height="@dimen/fake_action_bar_height"
- android:layout_gravity="center_horizontal"
- android:background="@color/dialpad_primary_text_color"
- android:layoutDirection="locale">
- <ImageButton
- android:id="@+id/call_history_on_dialpad_button"
- android:layout_height="match_parent"
- android:layout_width="@dimen/fake_menu_button_min_width"
- android:layout_gravity="bottom|start"
- android:background="@drawable/btn_call"
- android:contentDescription="@string/action_menu_call_history_description"
- android:src="@drawable/ic_menu_history_lt"/>
- <ImageButton
- android:id="@+id/dialButton"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:state_enabled="false"
- android:background="@drawable/btn_call"
- android:contentDescription="@string/description_dial_button"
- android:src="@drawable/ic_dial_action_call" />
- <ImageButton
- android:id="@+id/overflow_menu_on_dialpad"
- android:layout_height="match_parent"
- android:layout_width="@dimen/fake_menu_button_min_width"
- android:layout_gravity="bottom|end"
- android:background="@drawable/btn_call"
- android:src="@drawable/ic_menu_overflow_lt"
- android:contentDescription="@string/action_menu_overflow_description" />
- </LinearLayout>
+ android:layout_height="8dp" />
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="2dp"
+ android:background="@color/dialpad_separator_line_color" />
<!-- "Dialpad chooser" UI, shown only when the user brings up the
Dialer while a call is already in progress.
@@ -120,9 +72,9 @@
(the textfield/button and the dialpad) are hidden. -->
<ListView android:id="@+id/dialpadChooser"
android:layout_width="match_parent"
- android:layout_height="1dip"
+ android:layout_height="wrap_content"
android:layout_weight="1"
- />
+ android:visibility="gone" />
</view>
</view>
diff --git a/res/layout/dialpad_key.xml b/res/layout/dialpad_key.xml
index c104f8a..5bf858c 100644
--- a/res/layout/dialpad_key.xml
+++ b/res/layout/dialpad_key.xml
@@ -17,29 +17,19 @@
<!-- A layout representing a single key in the dialpad -->
<com.android.dialer.dialpad.DialpadKeyButton
xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/DialtactsDialpadButtonStyle"
- android:clickable="true"
- android:focusable="true" >
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center">
- <TextView
- android:id="@+id/dialpad_key_number"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="@color/dialpad_primary_text_color"
- android:textSize="@dimen/dialpad_key_numbers_size"
- android:fontFamily="sans-serif-light"
- />
- <TextView
- android:id="@+id/dialpad_key_letters"
- android:layout_width="@dimen/dialpad_key_letters_width"
- android:layout_height="wrap_content"
- android:textColor="@color/dialpad_secondary_text_color"
- android:textSize="@dimen/dialpad_key_letters_size"
- android:paddingLeft="11dp"
- android:fontFamily="sans-serif-light"
- />
+ style="@style/DialpadKeyButtonStyle">
+
+ <LinearLayout style="@style/DialpadKeyInternalLayoutStyle">
+
+ <!-- Note in the referenced styles that we assign hard widths to these components
+ because we want them to line up vertically when we arrange them in an MxN grid -->
+
+ <TextView
+ android:id="@+id/dialpad_key_number"
+ style="@style/DialpadKeyNumberStyle" />
+
+ <TextView
+ android:id="@+id/dialpad_key_letters"
+ style="@style/DialpadKeyLettersStyle" />
</LinearLayout>
</com.android.dialer.dialpad.DialpadKeyButton>
\ No newline at end of file
diff --git a/res/layout/dialtacts_activity.xml b/res/layout/dialtacts_activity.xml
index e2c3853..0f80564 100644
--- a/res/layout/dialtacts_activity.xml
+++ b/res/layout/dialtacts_activity.xml
@@ -13,113 +13,181 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<FrameLayout
+<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
+ android:orientation="vertical"
android:focusableInTouchMode="true"
android:clipChildren="false"
- android:id="@+id/dialtacts_container"
>
- <!-- Overlapping dialpad fragment is inserted here -->
- <LinearLayout
+
+ <FrameLayout
+ android:id="@+id/dialtacts_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false"
- android:orientation="vertical" >
+ android:layout_height="0dp"
+ android:layout_weight="1">
+
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/search_view_container"
- android:orientation="vertical"
- >
- <LinearLayout
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:orientation="vertical" >
+
+ <!-- Search entry box and remove view -->
+ <FrameLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:paddingLeft="16dp"
- android:paddingRight="23dp"
- android:background="@color/searchbox_background_color"
- android:gravity="center_vertical">
- <EditText
- android:id="@+id/search_view"
- android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:id="@+id/search_and_remove_view_container"
+ >
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/search_box_height"
+ android:id="@+id/search_view_container"
+ android:orientation="horizontal"
+ android:layout_marginTop="@dimen/search_top_margin"
+ android:layout_marginBottom="@dimen/search_bottom_margin"
+ android:layout_marginLeft="@dimen/search_margin_horizontal"
+ android:layout_marginRight="@dimen/search_margin_horizontal"
+ android:paddingLeft="@dimen/search_box_left_padding"
+ android:paddingRight="@dimen/search_box_right_padding"
+ android:background="@drawable/search_bg"
+ android:gravity="center_vertical"
+ >
+ <EditText
+ android:id="@+id/search_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/search_box_icon_size"
+ android:layout_weight="1"
+ android:layout_marginLeft="@dimen/search_box_text_left_margin"
+ android:textSize="@dimen/search_text_size"
+ android:fontFamily="@string/search_font_family"
+ android:textColor="@color/searchbox_text_color"
+ android:textColorHint="@color/searchbox_hint_text_color"
+ android:inputType="textFilter"/>
+ <ImageView
+ android:id="@+id/search_close_button"
+ android:layout_height="@dimen/search_box_icon_size"
+ android:layout_width="@dimen/search_box_icon_size"
+ android:padding="6dp"
+ android:src="@drawable/ic_close_dk"
+ android:clickable="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:contentDescription="@string/description_clear_search"
+ android:visibility="gone" />
+ <ImageView
+ android:id="@+id/voice_search_button"
+ android:layout_height="@dimen/search_box_icon_size"
+ android:layout_width="@dimen/search_box_icon_size"
+ android:padding="@dimen/search_box_icon_padding"
+ android:src="@drawable/ic_voice_search"
+ android:clickable="true"
+ android:contentDescription="@string/description_start_voice_search"
+ android:background="?android:attr/selectableItemBackground" />
+ </LinearLayout>
+ <com.android.dialer.list.RemoveView
+ android:layout_width="match_parent"
android:layout_height="56dp"
- android:layout_weight="1"
- android:textSize="@dimen/search_text_size"
- android:inputType="textFilter"/>
- <ImageView
- android:id="@+id/search_close_button"
- android:layout_height="40dp"
- android:layout_width="40dp"
- android:padding="6dp"
- android:src="@drawable/ic_close_dk"
- android:clickable="true"
- android:background="?android:attr/selectableItemBackground"
- android:visibility="gone" />
- <ImageView
- android:id="@+id/voice_search_button"
- android:layout_height="40dp"
- android:layout_width="40dp"
- android:padding="3dp"
- android:src="@drawable/ic_voice_search"
- android:clickable="true"
- android:contentDescription="@string/description_start_voice_search"
- android:background="?android:attr/selectableItemBackground" />
- </LinearLayout>
- <View
- android:id="@+id/searchbox_divider"
- android:layout_height="1dp"
+ android:id="@+id/remove_view_container"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:visibility="gone">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/remove_view_icon"
+ android:src="@drawable/ic_remove"
+ android:contentDescription="@string/remove_contact"
+ />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/remove_view_text"
+ android:textSize="@dimen/remove_text_size"
+ android:textColor="@color/remove_text_color"
+ android:text="@string/remove_contact"
+ />
+ </com.android.dialer.list.RemoveView>
+ </FrameLayout>
+
+ <!-- Relative Layout is used to contain the main contacts grid and the thin translucent
+ horizontal divider line at the bottom of the contacts grid above the menu bar. -->
+ <RelativeLayout android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:clipChildren="false"
+ >
+ <!-- The main contacts grid -->
+ <FrameLayout
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:id="@+id/dialtacts_frame"
+ android:clipChildren="false"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ >
+ </FrameLayout>
+ <!-- Thin translucent horizontal line at the bottom of the contacts grid. Floats
+ above the contacts grid and has a translucent color. -->
+ <View
+ android:layout_height="2dp"
+ android:layout_width="match_parent"
+ android:background="@color/contacts_grid_bottom_border_color"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ />
+ </RelativeLayout>
+ <Space
+ android:id="@+id/contact_tile_frame_spacer"
+ android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:background="@color/background_dialer_light" />
+ android:layout_alignParentBottom="true"
+ android:visibility="gone"/>
</LinearLayout>
- <FrameLayout
- android:layout_height="0dp"
- android:layout_weight="1"
- android:layout_width="match_parent"
- android:id="@+id/dialtacts_frame"
- android:clipChildren="false">
- </FrameLayout>
- <View
- android:layout_height="2dp"
- android:layout_width="match_parent"
- android:background="#33999999"
- />
- <FrameLayout
- android:layout_height="@dimen/fake_action_bar_height"
- android:layout_width="match_parent"
- android:id="@+id/fake_action_bar"
- android:background="@color/actionbar_background_color">
- <ImageButton
- android:id="@+id/call_history_button"
- android:layout_width="@dimen/fake_menu_button_min_width"
- android:layout_height="match_parent"
- android:layout_gravity="bottom|start"
- android:background="?android:attr/selectableItemBackground"
- android:contentDescription="@string/action_menu_call_history_description"
- android:src="@drawable/ic_menu_history_dk"/>
- <ImageButton
- android:id="@+id/dialpad_button"
- android:layout_width="@dimen/fake_menu_button_min_width"
- android:layout_height="match_parent"
- android:layout_gravity="bottom|center"
- android:background="?android:attr/selectableItemBackground"
- android:contentDescription="@string/action_menu_dialpad_button"
- android:src="@drawable/ic_menu_dialpad_dk"/>
- <ImageButton
- android:id="@+id/overflow_menu"
- android:layout_width="@dimen/fake_menu_button_min_width"
- android:layout_height="match_parent"
- android:layout_gravity="bottom|end"
- android:src="@drawable/ic_menu_overflow_dk"
- android:contentDescription="@string/action_menu_overflow_description"
- android:background="?android:attr/selectableItemBackground"/>
- </FrameLayout>
- <View
- android:id="@+id/dialtacts_bottom_padding"
- android:layout_width="match_parent"
- android:layout_height="?android:attr/actionBarSize"
+ </FrameLayout>
+
+ <!-- Fake action bar -->
+ <FrameLayout
+ android:layout_height="@dimen/fake_action_bar_height"
+ android:layout_width="match_parent"
+ android:id="@+id/fake_action_bar"
+ android:background="@color/actionbar_background_color">
+ <ImageButton
+ android:id="@+id/call_history_button"
+ android:layout_width="@dimen/fake_menu_button_min_width"
+ android:layout_height="match_parent"
+ android:layout_gravity="bottom|start"
+ android:background="?android:attr/selectableItemBackground"
+ android:contentDescription="@string/action_menu_call_history_description"
+ android:src="@drawable/ic_menu_history_lt"/>
+ <ImageButton
+ android:id="@+id/dialpad_button"
+ android:layout_width="@dimen/fake_menu_button_min_width"
+ android:layout_height="match_parent"
+ android:layout_gravity="bottom|center"
+ android:background="?android:attr/selectableItemBackground"
+ android:contentDescription="@string/action_menu_dialpad_button"
+ android:src="@drawable/ic_menu_dialpad_lt"/>
+ <ImageButton
+ android:id="@+id/dial_button"
+ android:layout_width="@dimen/fake_menu_button_min_width"
+ android:layout_height="match_parent"
+ android:layout_gravity="bottom|center"
+ android:background="@drawable/btn_call"
+ android:contentDescription="@string/description_dial_button"
+ android:src="@drawable/ic_dial_action_call"
android:visibility="gone" />
- </LinearLayout>
-</FrameLayout>
+ <ImageButton
+ android:id="@+id/overflow_menu"
+ android:layout_width="@dimen/fake_menu_button_min_width"
+ android:layout_height="match_parent"
+ android:layout_gravity="bottom|end"
+ android:src="@drawable/ic_menu_overflow_lt"
+ android:contentDescription="@string/action_menu_overflow_description"
+ android:background="?android:attr/selectableItemBackground"/>
+ </FrameLayout>
+</LinearLayout>
diff --git a/res/layout/phone_favorite_regular_row_view.xml b/res/layout/phone_favorite_regular_row_view.xml
index 012c9be..d046fdb 100644
--- a/res/layout/phone_favorite_regular_row_view.xml
+++ b/res/layout/phone_favorite_regular_row_view.xml
@@ -65,18 +65,6 @@
android:singleLine="true"
android:textColor="@color/dialtacts_secondary_text_color" />
</LinearLayout>
- <ImageView
- android:id="@+id/contact_favorite_star"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignRight="@id/contact_tile_quick"
- android:layout_alignEnd="@id/contact_tile_quick"
- android:layout_alignBottom="@id/contact_tile_quick"
- android:layout_marginRight="7dip"
- android:layout_marginEnd="7dip"
- android:layout_marginBottom="7dip"
- android:src="@drawable/ic_star_marked_as_fav"
- android:visibility="gone" />
</RelativeLayout>
diff --git a/res/layout/phone_favorite_tile_view.xml b/res/layout/phone_favorite_tile_view.xml
index 8806d39..952bb2a 100644
--- a/res/layout/phone_favorite_tile_view.xml
+++ b/res/layout/phone_favorite_tile_view.xml
@@ -25,7 +25,8 @@
android:id="@+id/contact_favorite_card"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:focusable="true">
+ android:focusable="true"
+ android:background="@drawable/ic_contacts_tiles">
<com.android.contacts.common.widget.LayoutSuppressingImageView
android:id="@+id/contact_tile_image"
@@ -42,11 +43,11 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingLeft="8dp"
- android:paddingRight="@dimen/contact_tile_info_button_height_and_width"
- android:paddingStart="8dp"
- android:paddingEnd="@dimen/contact_tile_info_button_height_and_width"
- android:paddingBottom="4dp"
+ android:paddingLeft="@dimen/contact_tile_text_side_padding"
+ android:paddingRight="@dimen/contact_tile_text_side_padding"
+ android:paddingStart="@dimen/contact_tile_text_side_padding"
+ android:paddingEnd="@dimen/contact_tile_text_side_padding"
+ android:paddingBottom="@dimen/contact_tile_text_bottom_padding"
android:layout_alignParentBottom="true"
android:orientation="vertical" >
<TextView
@@ -57,7 +58,7 @@
android:textColor="@color/contact_tile_name_color"
android:fontFamily="sans-serif"
android:singleLine="true"
- android:textSize="16sp"
+ android:textSize="15sp"
android:fadingEdge="horizontal"
android:fadingEdgeLength="3dip"
android:ellipsize="marquee"
@@ -70,8 +71,7 @@
android:textColor="@color/contact_tile_name_color"
android:fontFamily="sans-serif"
android:singleLine="true"
- android:textSize="12sp"
- android:paddingBottom="2dp"
+ android:textSize="11sp"
android:fadingEdge="horizontal"
android:fadingEdgeLength="3dip"
android:ellipsize="marquee"
@@ -85,40 +85,22 @@
android:nextFocusRight="@+id/contact_tile_secondary_button"
android:background="?android:attr/selectableItemBackground" />
- <ImageView
- android:id="@+id/contact_favorite_star"
- android:background="?android:attr/selectableItemBackground"
- android:layout_height="@dimen/contact_tile_info_button_height_and_width"
- android:layout_width="@dimen/contact_tile_info_button_height_and_width"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:layout_alignParentBottom="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true"
- android:src="@drawable/ic_star_marked_as_fav"
- android:scaleType="center"
- android:visibility="gone" />
-
-
<ImageButton
android:id="@id/contact_tile_secondary_button"
- android:src="@drawable/ic_contact_info"
+ android:src="@drawable/overflow_thumbnail"
android:background="?android:attr/selectableItemBackground"
android:layout_height="@dimen/contact_tile_info_button_height_and_width"
android:layout_width="@dimen/contact_tile_info_button_height_and_width"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:layout_alignParentBottom="true"
+ android:paddingLeft="4dp"
+ android:paddingRight="4dp"
+ android:paddingStart="4dp"
+ android:paddingEnd="4dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
+ android:scaleType="center"
android:contentDescription="@string/description_view_contact_detail" />
</RelativeLayout>
diff --git a/res/layout/phone_favorites_fragment.xml b/res/layout/phone_favorites_fragment.xml
index 363565a..d1ff3d7 100644
--- a/res/layout/phone_favorites_fragment.xml
+++ b/res/layout/phone_favorites_fragment.xml
@@ -22,7 +22,7 @@
android:divider="?android:attr/dividerHorizontal"
android:showDividers="end"
android:clipChildren="false"
- android:background="@color/background_dialer_light">
+ android:background="@color/background_dialer_list_items">
<FrameLayout
android:id="@+id/contact_tile_frame"
@@ -30,7 +30,7 @@
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true">
+ >
<com.android.dialer.list.PhoneFavoriteListView
android:id="@+id/contact_tile_list"
@@ -58,5 +58,4 @@
android:layout_alignParentBottom="true"
layout="@layout/phone_no_favorites"
android:visibility="gone"/>
-
</RelativeLayout>
diff --git a/res/layout/phone_favorites_menu.xml b/res/layout/phone_favorites_menu.xml
new file mode 100644
index 0000000..387ea5b
--- /dev/null
+++ b/res/layout/phone_favorites_menu.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<!-- The phone favorites menu appears on the main dialer screen above the favorite callers area,
+ and provides access to the All Contacts list. -->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/phone_favorites_menu"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="@dimen/favorites_menu_padding_horizontal"
+ android:paddingRight="@dimen/favorites_menu_padding_horizontal"
+ android:paddingTop="@dimen/favorites_menu_padding_top"
+ android:paddingBottom="@dimen/favorites_menu_padding_bottom"
+ android:background="@color/favorites_menu_background_color"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/favorites_menu_speed_dial_height"
+ android:fontFamily="@string/favorites_menu_speed_dial_font_family"
+ android:text="@string/favorites_menu_speed_dial"
+ android:textSize="@dimen/favorites_menu_speed_dial_text_size"
+ android:textColor="@color/speed_dial_text_color"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ />
+ <Button
+ android:id="@+id/all_contacts_button"
+ android:fontFamily="@string/favorites_menu_all_contacts_font_family"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/favorites_menu_all_contacts_height"
+ android:paddingLeft="@dimen/favorites_menu_padding_horizontal"
+ android:paddingRight="@dimen/favorites_menu_padding_horizontal"
+ android:text="@string/favorites_menu_all_contacts"
+ android:textSize="@dimen/favorites_menu_all_contacts_text_size"
+ android:background="@drawable/background_all_contacts"
+ android:textColor="@color/all_contacts_button_text_color"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:gravity="center"
+ />
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/layout/phone_no_favorites.xml b/res/layout/phone_no_favorites.xml
index 1a0079f..b95afd1 100644
--- a/res/layout/phone_no_favorites.xml
+++ b/res/layout/phone_no_favorites.xml
@@ -18,7 +18,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
android:minHeight="?android:attr/listPreferredItemHeight">
<include
@@ -33,23 +32,18 @@
<LinearLayout
android:id="@+id/nofavorite_frame"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:orientation="vertical"
- android:layout_gravity="center"
android:gravity="center"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_alignParentTop="true"
- android:layout_above="@id/show_all_contact_button_in_nofav">
-
+ android:layout_centerInParent="true">
<ImageView
android:id="@+id/nofavorite_image"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:gravity="center_horizontal"
android:src="@drawable/no_favorites_banner"
- android:layout_marginBottom="14dp"/>
-
+ android:layout_marginBottom="14dp"
+ android:contentDescription="@string/no_favorites"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
diff --git a/res/layout/tile_interactions_teaser_view.xml b/res/layout/tile_interactions_teaser_view.xml
index 4104446..8204cb1 100644
--- a/res/layout/tile_interactions_teaser_view.xml
+++ b/res/layout/tile_interactions_teaser_view.xml
@@ -18,7 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/actionbar_background_color"
+ android:background="@color/background_dialer_list_items"
android:paddingBottom="@dimen/favorites_row_bottom_padding"
android:paddingTop="@dimen/favorites_row_top_padding">
diff --git a/res/values/colors.xml b/res/values/colors.xml
index a8ddf2b..e9730ea 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -12,14 +12,26 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License
- -->
+-->
<resources>
<!-- Secondary text color in the Phone app -->
<color name="dialtacts_primary_text_color">#000000</color>
<color name="dialtacts_secondary_text_color">#888888</color>
- <color name="dialpad_text_color">#ffffff</color>
+
+ <!-- Background color of dialpad -->
+ <color name="background_dialpad">#ffffff</color>
+ <!-- Pressed color of dialpad buttons -->
+ <color name="background_dialpad_pressed">#ececec</color>
+ <!-- Primary color of dialpad text, including the call button -->
+ <color name="dialpad_primary_text_color">#3B77E7</color>
+ <!-- Secondary color of dialpad text (used for the letters corresponding to each digit -->
+ <color name="dialpad_secondary_text_color">#8b8b8b</color>
+ <!-- Color of dialpad digits -->
+ <color name="dialpad_digits_text_color">#000000</color>
+ <!-- Color for dialpad separator lines -->
+ <color name="dialpad_separator_line_color">#dadada</color>
<!-- Color of the text describing an unconsumed missed call. -->
<color name="call_log_missed_call_highlight_color">#FF0000</color>
@@ -27,8 +39,10 @@
<!-- Color of the text describing an unconsumed voicemail. -->
<color name="call_log_voicemail_highlight_color">#33b5e5</color>
- <!-- Colour of voicemail progress bar to the right of position indicator.
- Same as the background color of the dialer -->
+ <!--
+ Colour of voicemail progress bar to the right of position indicator.
+ Same as the background color of the dialer
+ -->
<color name="voicemail_playback_seek_bar_yet_to_play">#cecece</color>
<!-- Colour of voicemail progress bar to the left of position indicator. -->
@@ -38,36 +52,87 @@
<color name="item_selected">#660099cc</color>
<!-- Background color of new dialer activity -->
- <color name="background_dialer_light">#cecece</color>
+ <color name="background_dialer_light">#eeeeee</color>
<!-- Background color of dialer list items (contacts, call log entries) -->
<color name="background_dialer_list_items">#eeeeee</color>
- <!-- Background color of new dialpad -->
- <color name="background_dialpad">#f2020709</color>
+ <!-- Background color of action bars. Ensure this stays in sync with packages/Telephony
+ actionbar_background_color. -->
+ <color name="actionbar_background_color">#3B77E7</color>
- <!-- Primary color of dialpad text, including the call button -->
- <color name="dialpad_primary_text_color">#33b5e5</color>
- <!-- Secondary color of dialpad text (used for the letters corresponding to each digit -->
- <color name="dialpad_secondary_text_color">#aaaaaa</color>
-
- <!-- Background color of action bars -->
- <color name="actionbar_background_color">#ffffff</color>
+ <!-- Underline color of action bars. Ensure this stays in sync with packages/Telephony
+ actionbar_underline. -->
+ <color name="actionbar_underline">#3265C1</color>
<!-- Color of the 1dp divider that separates favorites -->
<color name="favorite_contacts_separator_color">#d0d0d0</color>
<!-- Background color of the search box -->
<color name="searchbox_background_color">#ffffff</color>
- <!-- Text color of the search box -->
- <color name="searchbox_text_color">#d3d3d3</color>
+ <!-- Text color of the search box text as entered by user -->
+ <color name="searchbox_text_color">#000000</color>
+ <!-- Text color of the search box hint text -->
+ <color name="searchbox_hint_text_color">#d3d3d3</color>
<!-- Color of the contact name in favorite tiles -->
- <color name="contact_tile_name_color">#f0f0f0</color>
+ <color name="contact_tile_name_color">#ffffff</color>
<!-- Undo dialogue color -->
<color name="undo_dialogue_text_color">#4d4d4d</color>
<!-- Text color for no favorites message -->
<color name="nofavorite_text_color">#777777</color>
+
+ <!-- Text color for the "Remove" text in its regular state -->
+ <color name="remove_text_color">#555555</color>
+
+ <!-- Text color for the "Remove" text when a contact is dragged on top of the remove view -->
+ <color name="remove_highlighted_text_color">#FF3F3B</color>
+
+ <!-- Text color for the "speed dial" label in the favorites menu. -->
+ <color name="speed_dial_text_color">#555555</color>
+
+ <!-- Background color for the "All Contacts" button in the favorites menu. -->
+ <color name="all_contacts_button_color">#999999</color>
+
+ <!-- Background color for the "All Contacts" button in the favorites menu when pressed. -->
+ <color name="all_contacts_button_pressed_color">#808080</color>
+
+ <!-- Background color for the favorites menu. -->
+ <color name="favorites_menu_background_color">#eeeeee</color>
+
+ <!-- Text color for the "All Contacts" button above the favorite callers -->
+ <color name="all_contacts_button_text_color">#ffffff</color>
+
+ <!-- Background gradient start color for tabs in an unpressed state. -->
+ <color name="tab_background_gradient_start_color">#d3d3d3</color>
+
+ <!-- Background gradient end color for tabs in an unpressed state. -->
+ <color name="tab_background_gradient_end_color">#ebebeb</color>
+
+ <!-- Background gradient start color for tabs in a pressed state. -->
+ <color name="tab_background_gradient_start_pressed_color">#c4c4c4</color>
+
+ <!-- Background gradient end color for tabs in a pressed state. -->
+ <color name="tab_background_gradient_end_pressed_color">#dedede</color>
+
+ <!-- Color of the underline for a tab which is selected. -->
+ <color name="tab_underline_selected_color">#3265c1</color>
+
+ <!-- Color of the underline for a tab which is not selected. -->
+ <color name="tab_underline_color">#c8c8c8</color>
+
+ <!-- Color of the selection indicator for a tab which is selected. -->
+ <color name="tab_selected_color">#3b77e7</color>
+
+ <!-- Color of tab text. -->
+ <color name="tab_text_color">#505050</color>
+
+ <!-- Color of the bottom border below the contacts grid on the main dialer screen. -->
+ <color name="contacts_grid_bottom_border_color">#16000000</color>
+
+ <!-- Color of action bar text. Ensure this stays in sync with packages/Telephony
+ phone_settings_actionbar_text_color-->
+ <color name="actionbar_text_color">#FFFFFF</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1743952..9490d8d 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -13,28 +13,21 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License
- -->
+-->
<resources>
- <!-- Height of edit text in dialpad fragment -->
- <dimen name="dialpad_horizontal_margin">0dip</dimen>
- <dimen name="dialpad_vertical_margin">2dip</dimen>
- <dimen name="dialpad_digits_text_size">35sp</dimen>
-
- <!-- Just used in landscape mode -->
- <dimen name="dialpad_digits_height">0px</dimen>
- <dimen name="dialpad_digits_margin_bottom">0px</dimen>
- <dimen name="dialpad_center_margin">3dp</dimen>
- <dimen name="dialpad_button_margin">2dp</dimen>
- <!-- Match call_button_height to Phone's dimens/in_call_end_button_height -->
- <dimen name="call_button_height">74dp</dimen>
-
<!-- Search View -->
<dimen name="search_text_size">14sp</dimen>
+ <!--
+ Drag to remove view (in dp because it is used in conjunction with a statically
+ sized icon
+ -->
+ <dimen name="remove_text_size">16dp</dimen>
+
<!-- Call Log -->
<dimen name="call_log_call_action_size">32dip</dimen>
- <dimen name="call_log_call_action_width">48dip</dimen>
+ <dimen name="call_log_call_action_width">54dp</dimen>
<dimen name="call_log_icon_margin">4dip</dimen>
<dimen name="call_log_inner_margin">8dip</dimen>
<dimen name="call_log_outer_margin">8dip</dimen>
@@ -43,30 +36,47 @@
<dimen name="call_log_list_contact_photo_size">64dip</dimen>
<dimen name="call_detail_contact_name_margin">24dip</dimen>
<dimen name="call_detail_button_spacing">2dip</dimen>
+ <!-- Defines the vertical margin for the vertical separator between
+ the main area of a call log entry and the secondary action button. -->
+ <dimen name="call_log_list_item_vertical_divider_margin">17dp</dimen>
- <!-- Layout weight values for dialpad screen. These layouts will be used in one
- LinearLayout (dialpad_fragment.xml), configuring dialpad screen's vertical
- ratio. -->
- <integer name="dialpad_layout_weight_digits">15</integer>
- <integer name="dialpad_layout_weight_dialpad">65</integer>
+ <!-- Defines the width of the vertical separator between
+ the main area of a call log entry and the secondary action button. -->
+ <dimen name="call_log_list_item_vertical_divider_width">1dp</dimen>
<!-- Text dimensions for dialpad keys -->
- <dimen name="dialpad_key_numbers_size">40dp</dimen>
- <dimen name="dialpad_key_letters_size">11dp</dimen>
- <dimen name="dialpad_key_plus_size">15dp</dimen>
- <dimen name="dialpad_key_special_characters_size">25dp</dimen>
- <dimen name="dialpad_key_letters_width">41dp</dimen>
-
+ <dimen name="dialpad_key_numbers_size">40sp</dimen>
+ <dimen name="dialpad_key_letters_size">13sp</dimen>
+ <dimen name="dialpad_key_star_pound_size">26sp</dimen>
+ <dimen name="dialpad_key_numbers_width">30dp</dimen>
+ <dimen name="dialpad_key_letters_width">50dp</dimen>
+ <dimen name="dialpad_key_height">56dp</dimen>
+ <dimen name="dialpad_key_plus_size">18sp</dimen>
+ <dimen name="dialpad_number_to_letters_padding">11dp</dimen>
+ <dimen name="dialpad_horizontal_padding">5dp</dimen>
+ <dimen name="dialpad_digits_text_size">36sp</dimen>
+ <dimen name="dialpad_digits_height">47dp</dimen>
+ <dimen name="dialpad_digits_padding">16dp</dimen>
+ <dimen name="dialpad_digits_margin_bottom">0px</dimen>
+ <dimen name="dialpad_center_margin">3dp</dimen>
+ <dimen name="dialpad_button_margin">2dp</dimen>
+ <!-- Match call_button_height to Phone's dimens/in_call_end_button_height -->
+ <dimen name="call_button_height">74dp</dimen>
<dimen name="fake_action_bar_height">60dp</dimen>
<!-- Min with of fake menu buttons, which should be same as ActionBar's one -->
<dimen name="fake_menu_button_min_width">56dp</dimen>
<!-- Favorites tile and recent call log padding -->
+ <dimen name="contact_tile_divider_width">12dp</dimen>
+ <!-- Favorites tile and recent call log padding -->
<dimen name="contact_tile_divider_padding">3dp</dimen>
<dimen name="contact_tile_info_button_height_and_width">36dp</dimen>
- <dimen name="favorites_row_top_padding">8dp</dimen>
- <dimen name="favorites_row_bottom_padding">8dp</dimen>
+ <item name="contact_tile_height_to_width_ratio" type="dimen">67%</item>
+ <dimen name="contact_tile_text_side_padding">10dp</dimen>
+ <dimen name="contact_tile_text_bottom_padding">8dp</dimen>
+ <dimen name="favorites_row_top_padding">6dp</dimen>
+ <dimen name="favorites_row_bottom_padding">6dp</dimen>
<dimen name="favorites_row_start_padding">8dp</dimen>
<dimen name="favorites_row_end_padding">8dp</dimen>
<dimen name="favorites_row_undo_text_side_padding">32dp</dimen>
@@ -75,4 +85,49 @@
<!-- Padding for the tooltip -->
<dimen name="dismiss_button_padding_start">20dip</dimen>
<dimen name="dismiss_button_padding_end">28dip</dimen>
+
+ <!-- Padding above the favorites menu. -->
+ <dimen name="favorites_menu_padding_top">6dp</dimen>
+ <!-- Padding below the favorites menu.. -->
+ <dimen name="favorites_menu_padding_bottom">3dp</dimen>
+ <!-- Horizontal padding in the favorites menu. -->
+ <dimen name="favorites_menu_padding_horizontal">8dp</dimen>
+ <!-- Text size for the "speed dial" text in the favorites menu. -->
+ <dimen name="favorites_menu_speed_dial_text_size">18sp</dimen>
+ <!-- Height of the speed dial TextView in the favorites menu. -->
+ <dimen name="favorites_menu_speed_dial_height">24dp</dimen>
+ <!-- Text size for the "All Contacts" text in the favorites menu. -->
+ <dimen name="favorites_menu_all_contacts_text_size">12sp</dimen>
+ <!-- Height of the all contacts Button in the favorites menu. -->
+ <dimen name="favorites_menu_all_contacts_height">24dp</dimen>
+
+ <!-- Margin to the left and right of the search box. -->
+ <dimen name="search_margin_horizontal">7dp</dimen>
+ <!-- Margin above the search box. -->
+ <dimen name="search_top_margin">10dp</dimen>
+ <!-- Margin below the search box. -->
+ <dimen name="search_bottom_margin">4dp</dimen>
+ <!-- Height of the search box. -->
+ <dimen name="search_box_height">41dp</dimen>
+ <!-- Search box text size -->
+ <dimen name="search_text_size">13.24sp</dimen>
+ <!-- Search box interior padding - left -->
+ <dimen name="search_box_left_padding">16dp</dimen>
+ <!-- Search box interior padding - right -->
+ <dimen name="search_box_right_padding">8dp</dimen>
+ <!-- Padding around the icon in the search box. -->
+ <dimen name="search_box_icon_padding">3dp</dimen>
+ <!-- Left margin of the text field in the search box. -->
+ <dimen name="search_box_text_left_margin">8dp</dimen>
+ <!-- Size of the icon (voice search, close search) in the search box. -->
+ <dimen name="search_box_icon_size">37dp</dimen>
+
+ <!-- Height of the underline of a tab. -->
+ <dimen name="tab_underline_height">2dp</dimen>
+ <!-- Height of the selection indicator of a tab. -->
+ <dimen name="tab_selection_height">7.7dp</dimen>
+ <!-- Padding above and below the divider line of a tab. -->
+ <dimen name="tab_divider_padding">12dp</dimen>
+ <!-- Size of text in tabs. -->
+ <dimen name="tab_text_size">12sp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5b1c293..847e0f3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -465,6 +465,86 @@
-->
<string name="description_call">Call <xliff:g id="name">%1$s</xliff:g></string>
+ <!-- String describing the button to access the contact details for a name or number.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_contact_details">Contact details for <xliff:g id="nameOrNumber">%1$s</xliff:g></string>
+
+ <!-- String describing the button to access call details in the call log.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_call_details">Call details</string>
+
+ <!-- String indicating a call log entry has an associated voicemail.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_new_voicemail">New voicemail.</string>
+
+ <!-- String indicating the number of calls to/from a caller in the call log.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_num_calls"><xliff:g id="numberOfCalls">%1$s</xliff:g> calls.</string>
+
+
+ <!-- Call history description for a missed call from a caller.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_return_missed_call">Return missed call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string>
+
+
+ <!-- Call history description for an answered call for a caller.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_return_answered_call">Return answered call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string>
+
+ <!-- Call history description for a missed call from an unknown caller.
+ Drops the "return" part of description_return_missed_call since it is not
+ possible to actually call an unknown number.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_unknown_missed_call">Missed call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string>
+
+
+ <!-- Call history description for an answered call from an unknown caller.
+ Drops the "return" part of description_return_answered_call since it is not
+ possible to actually call an unknown number.
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_unknown_answered_call">Answered call from <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g> <xliff:g id="timeOfCall">%3$s</xliff:g></string>
+
+ <!-- String describing an outgoing call entry in the call log. Used to indicate that
+ a call will be made to the specified caller. Used when there are multiple calls to/from
+ the caller.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_call_last_multiple">Call <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g>. Last called <xliff:g id="timeOfCall">%3$s</xliff:g>.</string>
+
+ <!-- String describing an outgoing call entry in the call log. Used to indicate that
+ a call will be made to the specified caller. Used when there is only a single call
+ related to/from the caller.
+
+ Note: AccessibilityServices use this attribute to announce what the view represents.
+ This is especially valuable for views without textual representation like ImageView.
+ -->
+ <string name="description_call_last">Call <xliff:g id="nameOrNumber">%1$s</xliff:g> <xliff:g id="typeOrLocation">%2$s</xliff:g>. Called <xliff:g id="timeOfCall">%3$s</xliff:g>.</string>
+
<!-- String describing the button to SMS a number or contact.
@@ -483,11 +563,15 @@
-->
<string name="description_call_log_unheard_voicemail">Unheard voicemail</string>
+ <!-- String describing the icon used to clear the search field -->
+ <string name="description_clear_search">Clear search</string>
+
<!-- String describing the icon used to start a voice search -->
<string name="description_start_voice_search">Start voice search</string>
- <!-- The string used to represent an unknown location for a phone number in the call log [CHAR LIMIT=3] -->
- <string name="call_log_empty_gecode">\u0020</string>
+ <!-- The string used to represent an unknown location for a phone number in the call log
+ Do not translate. -->
+ <string name="call_log_empty_geocode"></string>
<!-- Menu item used to call a contact, containing the number of the contact to call -->
<string name="menu_callNumber">Call <xliff:g id="number">%s</xliff:g></string>
@@ -549,6 +633,9 @@
<!-- Message displayed when there is no application available to handle the add contact menu option. [CHAR LIMIT=NONE] -->
<string name="add_contact_not_available">Re-enable the People application to use this feature.</string>
+ <!-- Message displayed when there is no application available to handle voice search. [CHAR LIMIT=NONE] -->
+ <string name="voice_search_not_available">Voice search is not available.</string>
+
<!-- Hint displayed in dialer search box when there is no query that is currently typed.
[CHAR LIMIT=30] -->
<string name="dialer_hint_find_contact">Type a name or phone number</string>
@@ -665,4 +752,33 @@
<!-- Content description for dismiss button on badge. [CHAR LIMIT=NONE] -->
<string name="description_dismiss">Dismiss</string>
+
+ <!-- Remove button that shows up when contact is long-pressed. [CHAR LIMIT=NONE] -->
+ <string name="remove_contact">Remove</string>
+
+ <!-- Header text displayed on the main dialer screen above the list of favorite phone numbers.
+ [CHAR LIMIT=21] -->
+ <string name="favorites_menu_speed_dial">Speed Dial</string>
+
+ <!-- Button text for the "all contacts" button displayed on the main dialer screen above the
+ list of favorite phone numbers. Navigates the user to the "All Contacts" list.
+ This text represents the same action as the text in string "menu_allContacts".
+ [CHAR LIMIT=21] -->
+ <string name="favorites_menu_all_contacts">ALL CONTACTS</string>
+
+ <!-- The font-family to use for the "speed dial" label on the favorites menu.
+ Do not translate. -->
+ <string name="favorites_menu_speed_dial_font_family">sans-serif-light</string>
+
+ <!-- The font-family to use for the "all contacts" label on the favorites menu.
+ Do not translate. -->
+ <string name="favorites_menu_all_contacts_font_family">sans-serif</string>
+
+ <!-- The font-family to use for the text inside the searchbox.
+ Do not translate. -->
+ <string name="search_font_family">sans-serif</string>
+
+ <!-- The font-family to use for tab text.
+ Do not translate. -->
+ <string name="tab_font_family">sans-serif</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 89678fc..1025d0f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -21,7 +21,16 @@
<item name="android:textColorSecondary">@color/dialtacts_secondary_text_color</item>
<item name="android:windowActionBarOverlay">false</item>
<item name="android:actionBarStyle">@style/DialtactsActionBarStyle</item>
+ <!-- Style for the tabs (for the tabs) -->
<item name="android:actionBarTabStyle">@style/DialtactsActionBarTabStyle</item>
+ <!-- Style for the tab bar (for the divider between tabs) -->
+ <item name="android:actionBarTabBarStyle">@style/DialtactsActionBarTabBarStyle</item>
+ <!-- Style for the tab bar text (for text on tabs) -->
+ <item name="android:actionBarTabTextStyle">@style/DialtactsActionBarTabTextStyle</item>
+ <!-- Style for the overflow button in the actionbar. -->
+ <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
+ <!-- The "Up" icon in the action bar. -->
+ <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
<item name="android:windowContentOverlay">@null</item>
<!-- Searchbox Style -->
<item name="android:editTextStyle">@style/DialtactsSearchboxStyle</item>
@@ -68,6 +77,24 @@
<item name="favorites_padding_bottom">?android:attr/actionBarSize</item>
</style>
+ <!-- An extension of the main DialtactsTheme used when the the launcher activity is shown.
+ Ensures that there is no actionbar showing during the load of the dialer app. -->
+ <style name="DialtactsThemeHiddenActionBar" parent="DialtactsTheme">
+ <item name="android:actionBarStyle">@style/DialtactsHiddenActionBarStyle</item>
+ </style>
+
+ <!-- A "hidden" action bar style. Used when loading the launcher activity so that the
+ default actionbar is effectively hidden. -->
+ <style name="DialtactsHiddenActionBarStyle" parent="DialtactsActionBarStyle">
+ <item name="android:height">0dp</item>
+ </style>
+
+ <!-- Action bar overflow menu icon. -->
+ <style name="DialtactsActionBarOverflow"
+ parent="@android:style/Widget.Holo.ActionButton.Overflow">
+ <item name="android:src">@drawable/ic_menu_overflow_lt</item>
+ </style>
+
<style name="DialpadTheme" parent="DialtactsTheme">
<item name="android:textColorPrimary">#FFFFFF</item>
</style>
@@ -80,6 +107,9 @@
<item name="android:windowBackground">@color/background_dialer_list_items</item>
<item name="android:gravity">top</item>
<item name="android:listViewStyle">@style/ListViewStyle</item>
+ <item name="android:actionBarStyle">@style/DialtactsActionBarStyle</item>
+ <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
+ <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
<!-- CallLog -->
<item name="call_log_primary_text_color">#333333</item>
<item name="call_log_primary_background_color">#FFFFFF</item>
@@ -93,44 +123,84 @@
<item name="call_log_voicemail_status_action_text_color">#33b5e5</item>
</style>
- <style name="Theme">
+ <style name="DialpadKeyNumberStyle">
+ <item name="android:textColor">@color/actionbar_background_color</item>
+ <item name="android:textSize">@dimen/dialpad_key_numbers_size</item>
+ <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:layout_width">@dimen/dialpad_key_numbers_width</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">center</item>
</style>
- <style name="DialtactsDigitsTextAppearance">
- <item name="android:maxLines">1</item>
- <item name="android:scrollHorizontally">true</item>
- <item name="android:textSize">@dimen/dialpad_digits_text_size</item>
- <item name="android:freezesText">true</item>
- <item name="android:focusableInTouchMode">true</item>
- <item name="android:editable">true</item>
- <item name="android:cursorVisible">false</item>
- <item name="android:layout_weight">0</item>
+ <style name="DialpadKeyStarPoundStyle">
+ <item name="android:textColor">@color/dialpad_secondary_text_color</item>
+ <item name="android:textSize">@dimen/dialpad_key_star_pound_size</item>
+ <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:layout_width">@dimen/dialpad_key_numbers_width</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:gravity">center</item>
</style>
- <style name="DialtactsDialpadButtonStyle">
- <item name="android:layout_width">0dip</item>
- <item name="android:layout_height">match_parent</item>
- <item name="android:layout_weight">1</item>
- <item name="android:background">?android:attr/selectableItemBackground</item>
+ <style name="DialpadKeyLettersStyle">
+ <item name="android:textColor">@color/dialpad_secondary_text_color</item>
+ <item name="android:textSize">@dimen/dialpad_key_letters_size</item>
+ <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:layout_width">@dimen/dialpad_key_letters_width</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:paddingLeft">@dimen/dialpad_number_to_letters_padding</item>
+ </style>
+
+ <style name="DialpadKeyButtonStyle">
<item name="android:soundEffectsEnabled">false</item>
+ <item name="android:clickable">true</item>
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">@dimen/dialpad_key_height</item>
+ <item name="android:background">@drawable/dialpad_key_colors</item>
+ <item name="android:focusable">true</item>
</style>
- <style name="DialpadHorizontalSeparator">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">@dimen/dialpad_vertical_margin</item>
- <item name="android:background">#66000000</item>
+ <style name="DialpadKeyInternalLayoutStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_gravity">center</item>
</style>
<style name="DialtactsActionBarStyle" parent="android:Widget.Holo.ActionBar">
<item name="android:background">@drawable/call_history_actionbar_background</item>
- <item name="android:backgroundStacked">@color/actionbar_background_color</item>
+ <item name="android:backgroundStacked">#ffffff</item>
+ <item name="android:titleTextStyle">@style/DialtactsActionBarTitleText</item>
<!-- Empty icon -->
<item name="android:icon">@android:color/transparent</item>
- <item name="android:displayOptions"></item>
</style>
+ <!-- Text in the action bar at the top of the screen -->
+ <style name="DialtactsActionBarTitleText"
+ parent="@android:style/TextAppearance.Holo.Widget.ActionBar.Title">
+ <item name="android:textColor">@color/actionbar_text_color</item>
+ </style>
+
+ <!-- Styling for the tab bar; handles styling of the divider line. -->
+ <style name="DialtactsActionBarTabBarStyle"
+ parent="@android:style/Widget.Holo.ActionBar.TabBar">
+ <item name="android:divider">@drawable/divider</item>
+ <item name="android:showDividers">middle</item>
+ <item name="android:dividerPadding">@dimen/tab_divider_padding</item>
+ <item name="android:background">@drawable/action_bar_tab</item>
+ </style>
+
+ <!-- Styling for tabs. -->
<style name="DialtactsActionBarTabStyle" parent="@android:style/Widget.Holo.ActionBar.TabView">
<item name="android:background">@drawable/action_bar_tab</item>
+ <item name="android:showDividers">none</item>
+ </style>
+
+ <!-- Text style for tabs. -->
+ <style name="DialtactsActionBarTabTextStyle"
+ parent="android:style/Widget.Holo.Light.ActionBar.TabText">
+ <item name="android:textColor">@color/tab_text_color</item>
+ <item name="android:textSize">@dimen/tab_text_size</item>
+ <item name="android:fontFamily">@string/tab_font_family</item>
+ <item name="android:textStyle">bold</item>
</style>
<style name="ListViewStyle" parent="@android:style/Widget.Holo.Light.ListView">
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
index a24940d..42f295c 100644
--- a/src/com/android/dialer/CallDetailActivity.java
+++ b/src/com/android/dialer/CallDetailActivity.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.LoaderManager.LoaderCallbacks;
+import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -31,6 +32,7 @@
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.CallLog;
+import android.provider.ContactsContract;
import android.provider.CallLog.Calls;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
@@ -56,6 +58,7 @@
import com.android.contacts.common.ContactPhotoManager;
import com.android.contacts.common.CallUtil;
import com.android.contacts.common.ClipboardUtils;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
import com.android.contacts.common.GeoUtil;
import com.android.contacts.common.model.Contact;
import com.android.contacts.common.model.ContactLoader;
@@ -65,7 +68,7 @@
import com.android.dialer.calllog.CallTypeHelper;
import com.android.dialer.calllog.ContactInfo;
import com.android.dialer.calllog.ContactInfoHelper;
-import com.android.dialer.calllog.PhoneNumberHelper;
+import com.android.dialer.calllog.PhoneNumberDisplayHelper;
import com.android.dialer.calllog.PhoneNumberUtilsWrapper;
import com.android.dialer.util.AsyncTaskExecutor;
import com.android.dialer.util.AsyncTaskExecutors;
@@ -114,7 +117,7 @@
public static final String EXTRA_FROM_NOTIFICATION = "EXTRA_FROM_NOTIFICATION";
private CallTypeHelper mCallTypeHelper;
- private PhoneNumberHelper mPhoneNumberHelper;
+ private PhoneNumberDisplayHelper mPhoneNumberHelper;
private PhoneCallDetailsHelper mPhoneCallDetailsHelper;
private TextView mHeaderTextView;
private View mHeaderOverlayView;
@@ -300,7 +303,7 @@
mResources = getResources();
mCallTypeHelper = new CallTypeHelper(getResources());
- mPhoneNumberHelper = new PhoneNumberHelper(mResources);
+ mPhoneNumberHelper = new PhoneNumberDisplayHelper(mResources);
mPhoneCallDetailsHelper = new PhoneCallDetailsHelper(mResources, mCallTypeHelper,
new PhoneNumberUtilsWrapper());
mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
@@ -544,14 +547,14 @@
mainActionDescription);
}
+ final CharSequence displayNumber =
+ mPhoneNumberHelper.getDisplayNumber(
+ firstDetails.number,
+ firstDetails.numberPresentation,
+ firstDetails.formattedNumber);
+
// This action allows to call the number that places the call.
if (canPlaceCallsTo) {
- final CharSequence displayNumber =
- mPhoneNumberHelper.getDisplayNumber(
- firstDetails.number,
- firstDetails.numberPresentation,
- firstDetails.formattedNumber);
-
ViewEntry entry = new ViewEntry(
getString(R.string.menu_callNumber,
forceLeftToRight(displayNumber)),
@@ -622,7 +625,20 @@
}
},
historyList);
- loadContactPhotos(photoUri);
+
+ final String displayNameForDefaultImage = TextUtils.isEmpty(firstDetails.name) ?
+ displayNumber.toString() : firstDetails.name.toString();
+
+ final String lookupKey = ContactInfoHelper.getLookupKeyFromUri(contactUri);
+
+ final boolean isBusiness = mContactInfoHelper.isBusiness(firstDetails.sourceType);
+
+ final int contactType =
+ isVoicemailNumber? ContactPhotoManager.TYPE_VOICEMAIL :
+ isBusiness ? ContactPhotoManager.TYPE_BUSINESS :
+ ContactPhotoManager.TYPE_DEFAULT;
+
+ loadContactPhotos(photoUri, displayNameForDefaultImage, lookupKey, contactType);
findViewById(R.id.call_detail).setVisibility(View.VISIBLE);
}
}
@@ -643,7 +659,13 @@
mMainActionPushLayerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- startActivity(actionIntent);
+ try {
+ startActivity(actionIntent);
+ } catch (ActivityNotFoundException e) {
+ final Toast toast = Toast.makeText(CallDetailActivity.this,
+ R.string.add_contact_not_available, Toast.LENGTH_SHORT);
+ toast.show();
+ }
}
});
mMainActionPushLayerView.setContentDescription(actionDescription);
@@ -683,6 +705,7 @@
final CharSequence numberLabel;
final Uri photoUri;
final Uri lookupUri;
+ int sourceType;
// If this is not a regular number, there is no point in looking it up in the contacts.
ContactInfo info =
PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)
@@ -697,6 +720,7 @@
numberLabel = "";
photoUri = null;
lookupUri = null;
+ sourceType = 0;
} else {
formattedNumber = info.formattedNumber;
nameText = info.name;
@@ -704,11 +728,12 @@
numberLabel = info.label;
photoUri = info.photoUri;
lookupUri = info.lookupUri;
+ sourceType = info.sourceType;
}
return new PhoneCallDetails(number, numberPresentation,
formattedNumber, countryIso, geocode,
new int[]{ callType }, date, duration,
- nameText, numberType, numberLabel, lookupUri, photoUri);
+ nameText, numberType, numberLabel, lookupUri, photoUri, sourceType);
} finally {
if (callCursor != null) {
callCursor.close();
@@ -717,9 +742,12 @@
}
/** Load the contact photos and places them in the corresponding views. */
- private void loadContactPhotos(Uri photoUri) {
+ private void loadContactPhotos(Uri photoUri, String displayName, String lookupKey,
+ int contactType) {
+ final DefaultImageRequest request = new DefaultImageRequest(displayName, lookupKey,
+ contactType);
mContactPhotoManager.loadPhoto(mContactBackgroundView, photoUri,
- mContactBackgroundView.getWidth(), true);
+ mContactBackgroundView.getWidth(), true, request);
}
static final class ViewEntry {
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 04934c7..b78a7bc 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -22,12 +22,12 @@
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
-import android.app.FragmentManager.BackStackEntry;
import android.app.FragmentTransaction;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
-import android.graphics.drawable.Drawable;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
@@ -39,19 +39,18 @@
import android.speech.RecognizerIntent;
import android.telephony.TelephonyManager;
import android.text.Editable;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.TextWatcher;
-import android.text.style.ImageSpan;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.View.OnFocusChangeListener;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView.OnScrollListener;
import android.widget.EditText;
+import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.Toast;
@@ -67,15 +66,20 @@
import com.android.dialer.dialpad.SmartDialPrefix;
import com.android.dialer.interactions.PhoneNumberInteraction;
import com.android.dialer.list.AllContactsActivity;
+import com.android.dialer.list.DragDropController;
+import com.android.dialer.list.OnDragDropListener;
import com.android.dialer.list.OnListFragmentScrolledListener;
import com.android.dialer.list.PhoneFavoriteFragment;
+import com.android.dialer.list.PhoneFavoriteTileView;
import com.android.dialer.list.RegularSearchFragment;
+import com.android.dialer.list.RemoveView;
import com.android.dialer.list.SearchFragment;
import com.android.dialer.list.SmartDialSearchFragment;
import com.android.dialerbind.DatabaseHelperManager;
import com.android.internal.telephony.ITelephony;
import java.util.ArrayList;
+import java.util.List;
/**
* The dialer tab's title is 'phone', a more common name (see strings.xml).
@@ -83,11 +87,13 @@
public class DialtactsActivity extends TransactionSafeActivity implements View.OnClickListener,
DialpadFragment.OnDialpadQueryChangedListener, PopupMenu.OnMenuItemClickListener,
OnListFragmentScrolledListener,
- DialpadFragment.OnDialpadFragmentStartedListener,
- PhoneFavoriteFragment.OnShowAllContactsListener {
+ DialpadFragment.HostInterface,
+ PhoneFavoriteFragment.OnShowAllContactsListener,
+ PhoneFavoriteFragment.HostInterface,
+ OnDragDropListener, View.OnLongClickListener {
private static final String TAG = "DialtactsActivity";
- public static final boolean DEBUG = false;
+ public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences";
@@ -114,10 +120,10 @@
*/
private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
- private static final int SUBACTIVITY_ACCOUNT_FILTER = 1;
-
private static final int ACTIVITY_REQUEST_CODE_VOICE_SEARCH = 1;
+ private static final int ANIMATION_DURATION = 200;
+
private String mFilterText;
/**
@@ -141,14 +147,17 @@
private SmartDialSearchFragment mSmartDialSearchFragment;
private View mMenuButton;
+ private View mFakeActionBar;
private View mCallHistoryButton;
private View mDialpadButton;
+ private View mDialButton;
private PopupMenu mOverflowMenu;
+ private PopupMenu mDialpadOverflowMenu;
- // Padding view used to shift the fragments up when the dialpad is shown.
- private View mBottomPaddingView;
+ // Padding view used to shift the fragment frame up when the dialpad is shown so that
+ // the contents of the fragment frame continue to exist in a layout of the same height
+ private View mFragmentsSpacer;
private View mFragmentsFrame;
- private View mActionBar;
private boolean mInDialpadSearch;
private boolean mInRegularSearch;
@@ -164,6 +173,10 @@
*/
private boolean mFirstLaunch;
private View mSearchViewContainer;
+ private RemoveView mRemoveViewContainer;
+ // This view points to the Framelayout that houses both the search view and remove view
+ // containers.
+ private View mSearchAndRemoveViewContainer;
private View mSearchViewCloseButton;
private View mVoiceSearchButton;
private EditText mSearchView;
@@ -292,12 +305,10 @@
// Add the favorites fragment, and the dialpad fragment, but only if savedInstanceState
// is null. Otherwise the fragment manager takes care of recreating these fragments.
if (savedInstanceState == null) {
- final PhoneFavoriteFragment phoneFavoriteFragment = new PhoneFavoriteFragment();
-
- final FragmentTransaction ft = getFragmentManager().beginTransaction();
- ft.add(R.id.dialtacts_frame, phoneFavoriteFragment, TAG_FAVORITES_FRAGMENT);
- ft.add(R.id.dialtacts_container, new DialpadFragment(), TAG_DIALPAD_FRAGMENT);
- ft.commit();
+ getFragmentManager().beginTransaction()
+ .add(R.id.dialtacts_frame, new PhoneFavoriteFragment(), TAG_FAVORITES_FRAGMENT)
+ .add(R.id.dialtacts_container, new DialpadFragment(), TAG_DIALPAD_FRAGMENT)
+ .commit();
} else {
mSearchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY);
mInRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI);
@@ -305,9 +316,27 @@
mFirstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH);
}
- mBottomPaddingView = findViewById(R.id.dialtacts_bottom_padding);
mFragmentsFrame = findViewById(R.id.dialtacts_frame);
- mActionBar = findViewById(R.id.fake_action_bar);
+ mFragmentsSpacer = findViewById(R.id.contact_tile_frame_spacer);
+
+ mRemoveViewContainer = (RemoveView) findViewById(R.id.remove_view_container);
+ mSearchAndRemoveViewContainer = findViewById(R.id.search_and_remove_view_container);
+
+ // When the first global layout pass is completed (and mSearchAndRemoveViewContainer has
+ // been assigned a valid height), assign that height to mFragmentsSpacer as well.
+ mSearchAndRemoveViewContainer.getViewTreeObserver().addOnGlobalLayoutListener(
+ new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ mSearchAndRemoveViewContainer.getViewTreeObserver()
+ .removeOnGlobalLayoutListener(this);
+ mFragmentsSpacer.setLayoutParams(
+ new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ mSearchAndRemoveViewContainer.getHeight()));
+ }
+ });
+
+
prepareSearchView();
if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
@@ -315,7 +344,7 @@
setupFilterText(intent);
}
- setupFakeActionBarItems();
+ hideDialpadFragment(false, false);
mDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this);
SmartDialPrefix.initializeNanpSettings(this);
@@ -324,13 +353,14 @@
@Override
protected void onResume() {
super.onResume();
+ setupFakeActionBarItems();
if (mFirstLaunch) {
displayFragment(getIntent());
} else if (!phoneIsInUse() && mInCallDialpadUp) {
hideDialpadFragment(false, true);
mInCallDialpadUp = false;
}
-
+ prepareVoiceSearchButton();
mFirstLaunch = false;
mDialerDatabaseHelper.startSmartDialUpdateThread();
}
@@ -364,10 +394,16 @@
mSmartDialSearchFragment = (SmartDialSearchFragment) fragment;
mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(
mPhoneNumberPickerActionListener);
+ if (mFragmentsFrame != null) {
+ mFragmentsFrame.setAlpha(1.0f);
+ }
} else if (fragment instanceof SearchFragment) {
mRegularSearchFragment = (RegularSearchFragment) fragment;
mRegularSearchFragment.setOnPhoneNumberPickerActionListener(
mPhoneNumberPickerActionListener);
+ if (mFragmentsFrame != null) {
+ mFragmentsFrame.setAlpha(1.0f);
+ }
} else if (fragment instanceof PhoneFavoriteFragment) {
mPhoneFavoriteFragment = (PhoneFavoriteFragment) fragment;
mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener);
@@ -421,7 +457,11 @@
public void onClick(View view) {
switch (view.getId()) {
case R.id.overflow_menu: {
- mOverflowMenu.show();
+ if (isDialpadShowing()) {
+ mDialpadOverflowMenu.show();
+ } else {
+ mOverflowMenu.show();
+ }
break;
}
case R.id.dialpad_button:
@@ -432,7 +472,10 @@
mInCallDialpadUp = false;
showDialpadFragment(true);
break;
- case R.id.call_history_on_dialpad_button:
+ case R.id.dial_button:
+ // Dial button was pressed; tell the Dialpad fragment
+ mDialpadFragment.dialButtonPressed();
+ break;
case R.id.call_history_button:
// Use explicit CallLogActivity intent instead of ACTION_VIEW +
// CONTENT_TYPE, so that we always open our call log from our dialer
@@ -447,8 +490,13 @@
}
break;
case R.id.voice_search_button:
- final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
- startActivityForResult(voiceIntent, ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
+ try {
+ startActivityForResult(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
+ ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(DialtactsActivity.this, R.string.voice_search_not_available,
+ Toast.LENGTH_SHORT).show();
+ }
break;
default: {
Log.wtf(TAG, "Unexpected onClick event from " + view);
@@ -458,6 +506,22 @@
}
@Override
+ public boolean onLongClick(View view) {
+ switch (view.getId()) {
+ case R.id.dial_button: {
+ // Dial button was pressed; tell the Dialpad fragment
+ mDialpadFragment.dialButtonPressed();
+ return true; // Consume the event
+ }
+ default: {
+ Log.wtf(TAG, "Unexpected onClick event from " + view);
+ break;
+ }
+ }
+ return false;
+ }
+
+ @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == ACTIVITY_REQUEST_CODE_VOICE_SEARCH) {
if (resultCode == RESULT_OK) {
@@ -486,6 +550,9 @@
}
ft.show(mDialpadFragment);
ft.commit();
+ mDialButton.setVisibility(shouldShowOnscreenDialButton() ? View.VISIBLE : View.GONE);
+ mDialpadButton.setVisibility(View.GONE);
+ mMenuButton.setOnTouchListener(mDialpadOverflowMenu.getDragToOpenListener());
}
public void hideDialpadFragment(boolean animate, boolean clearDialpad) {
@@ -501,35 +568,38 @@
}
ft.hide(mDialpadFragment);
ft.commit();
+ mDialButton.setVisibility(View.GONE);
+ mDialpadButton.setVisibility(View.VISIBLE);
+ mMenuButton.setOnTouchListener(mOverflowMenu.getDragToOpenListener());
}
private void prepareSearchView() {
mSearchViewContainer = findViewById(R.id.search_view_container);
mSearchViewCloseButton = findViewById(R.id.search_close_button);
mSearchViewCloseButton.setOnClickListener(this);
- mVoiceSearchButton = findViewById(R.id.voice_search_button);
- mVoiceSearchButton.setOnClickListener(this);
+
mSearchView = (EditText) findViewById(R.id.search_view);
mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
+ mSearchView.setHint(getString(R.string.dialer_hint_find_contact));
- final String hintText = getString(R.string.dialer_hint_find_contact);
+ prepareVoiceSearchButton();
+ }
- // The following code is used to insert an icon into a CharSequence (copied from
- // SearchView)
- final SpannableStringBuilder ssb = new SpannableStringBuilder(" "); // for the icon
- ssb.append(hintText);
- final Drawable searchIcon = getResources().getDrawable(R.drawable.ic_ab_search);
- final int textSize = (int) (mSearchView.getTextSize() * 1.20);
- searchIcon.setBounds(0, 0, textSize, textSize);
- ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-
- mSearchView.setHint(ssb);
+ private void prepareVoiceSearchButton() {
+ mVoiceSearchButton = findViewById(R.id.voice_search_button);
+ final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+ if (canIntentBeHandled(voiceIntent)) {
+ mVoiceSearchButton.setVisibility(View.VISIBLE);
+ mVoiceSearchButton.setOnClickListener(this);
+ } else {
+ mVoiceSearchButton.setVisibility(View.GONE);
+ }
}
final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- mSearchViewContainer.setVisibility(View.GONE);
+ mSearchAndRemoveViewContainer.setVisibility(View.GONE);
}
};
@@ -548,82 +618,95 @@
}
public void hideSearchBar() {
- hideSearchBar(true);
- }
+ final int height = mSearchAndRemoveViewContainer.getHeight();
- public void hideSearchBar(boolean shiftView) {
- if (shiftView) {
- mSearchViewContainer.animate().cancel();
- mSearchViewContainer.setAlpha(1);
- mSearchViewContainer.setTranslationY(0);
- mSearchViewContainer.animate().withLayer().alpha(0).translationY(-mSearchView.getHeight())
- .setDuration(200).setListener(mHideListener);
+ mSearchAndRemoveViewContainer.animate().cancel();
+ mSearchAndRemoveViewContainer.setAlpha(1);
+ mSearchAndRemoveViewContainer.setTranslationY(0);
+ mSearchAndRemoveViewContainer.animate().withLayer().alpha(0)
+ .translationY(-height).setDuration(ANIMATION_DURATION)
+ .setListener(mHideListener);
- mFragmentsFrame.animate().withLayer()
- .translationY(-mSearchViewContainer.getHeight()).setDuration(200).setListener(
- new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBottomPaddingView.setVisibility(View.VISIBLE);
- mFragmentsFrame.setTranslationY(0);
- mActionBar.setVisibility(View.INVISIBLE);
- }
- });
- } else {
- mSearchViewContainer.setTranslationY(-mSearchView.getHeight());
- mActionBar.setVisibility(View.INVISIBLE);
+ mFragmentsFrame.animate().withLayer()
+ .translationY(-height).setDuration(ANIMATION_DURATION).setListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mFragmentsFrame.setTranslationY(0);
+ // Display the fragments spacer (which has the same height as the
+ // search box) now that the search box is hidden, so that
+ // mFragmentsFrame always retains the same height
+ mFragmentsSpacer.setVisibility(View.VISIBLE);
+ }
+ });
+
+ if (!mInDialpadSearch && !mInRegularSearch) {
+ // If the favorites fragment is showing, fade to blank.
+ mFragmentsFrame.animate().alpha(0.0f);
}
}
public void showSearchBar() {
- mSearchViewContainer.animate().cancel();
- mSearchViewContainer.setAlpha(0);
- mSearchViewContainer.setTranslationY(-mSearchViewContainer.getHeight());
- mSearchViewContainer.animate().withLayer().alpha(1).translationY(0).setDuration(200)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mSearchViewContainer.setVisibility(View.VISIBLE);
- mActionBar.setVisibility(View.VISIBLE);
- }
- });
+ final int height = mSearchAndRemoveViewContainer.getHeight();
+ mSearchAndRemoveViewContainer.animate().cancel();
+ mSearchAndRemoveViewContainer.setAlpha(0);
+ mSearchAndRemoveViewContainer.setTranslationY(-height);
+ mSearchAndRemoveViewContainer.animate().withLayer().alpha(1).translationY(0)
+ .setDuration(ANIMATION_DURATION).setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mSearchAndRemoveViewContainer.setVisibility(View.VISIBLE);
+ }
+ });
- mFragmentsFrame.setTranslationY(-mSearchViewContainer.getHeight());
- mFragmentsFrame.animate().withLayer().translationY(0).setDuration(200)
+ mFragmentsFrame.setTranslationY(-height);
+ mFragmentsFrame.animate().withLayer().translationY(0).setDuration(ANIMATION_DURATION)
+ .alpha(1.0f)
.setListener(
new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- mBottomPaddingView.setVisibility(View.GONE);
+ // Hide the fragment spacer now that the search box will
+ // be displayed again
+ mFragmentsSpacer.setVisibility(View.GONE);
}
});
}
-
- public void setupFakeActionBarItems() {
+ private void setupFakeActionBarItems() {
mMenuButton = findViewById(R.id.overflow_menu);
if (mMenuButton != null) {
mMenuButton.setOnClickListener(this);
-
- mOverflowMenu = new OverflowPopupMenu(DialtactsActivity.this, mMenuButton);
- final Menu menu = mOverflowMenu.getMenu();
- mOverflowMenu.inflate(R.menu.dialtacts_options);
- mOverflowMenu.setOnMenuItemClickListener(this);
+ if (mOverflowMenu == null) {
+ mOverflowMenu = buildOptionsMenu(mMenuButton);
+ }
+ if (mDialpadOverflowMenu == null) {
+ mDialpadOverflowMenu = mDialpadFragment.buildOptionsMenu(mMenuButton);
+ }
+ // Initial state is with dialpad fragment not shown
mMenuButton.setOnTouchListener(mOverflowMenu.getDragToOpenListener());
}
+ mFakeActionBar = findViewById(R.id.fake_action_bar);
+
mCallHistoryButton = findViewById(R.id.call_history_button);
// mCallHistoryButton.setMinimumWidth(fakeMenuItemWidth);
mCallHistoryButton.setOnClickListener(this);
+ mDialButton = findViewById(R.id.dial_button);
+ mDialButton.setOnClickListener(this);
+ mDialButton.setOnLongClickListener(this);
+
mDialpadButton = findViewById(R.id.dialpad_button);
// DialpadButton.setMinimumWidth(fakeMenuItemWidth);
mDialpadButton.setOnClickListener(this);
}
- public void setupFakeActionBarItemsForDialpadFragment() {
- final View callhistoryButton = findViewById(R.id.call_history_on_dialpad_button);
- callhistoryButton.setOnClickListener(this);
+ private PopupMenu buildOptionsMenu(View invoker) {
+ PopupMenu menu = new OverflowPopupMenu(this, invoker);
+ menu.inflate(R.menu.dialtacts_options);
+ menu.setOnMenuItemClickListener(this);
+ return menu;
}
private void fixIntent(Intent intent) {
@@ -824,7 +907,6 @@
}
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
- transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
SearchFragment fragment;
if (mInDialpadSearch) {
@@ -870,6 +952,10 @@
// transitioned between search fragments
getFragmentManager().popBackStack(0, FragmentManager.POP_BACK_STACK_INCLUSIVE);
setNotInSearchUi();
+
+ if (isDialpadShowing()) {
+ mFragmentsFrame.setAlpha(0);
+ }
}
/** Returns an Intent to launch Call Settings screen */
@@ -887,12 +973,6 @@
} else if (getInSearchUi()) {
mSearchView.setText(null);
mDialpadFragment.clearDialpad();
- } else if (isTaskRoot()) {
- // Instead of stopping, simply push this to the back of the stack.
- // This is only done when running at the top of the stack;
- // otherwise, we have been launched by someone else so need to
- // allow the user to go back to the caller.
- moveTaskToBack(false);
} else {
super.onBackPressed();
}
@@ -927,8 +1007,15 @@
}
@Override
- public void onDialpadFragmentStarted() {
- setupFakeActionBarItemsForDialpadFragment();
+ public void setDialButtonEnabled(boolean enabled) {
+ if (mDialButton != null) {
+ mDialButton.setEnabled(enabled);
+ }
+ }
+
+ @Override
+ public void setDialButtonContainerVisible(boolean visible) {
+ mFakeActionBar.setVisibility(visible ? View.VISIBLE : View.GONE);
}
private boolean phoneIsInUse() {
@@ -950,9 +1037,58 @@
return intent;
}
- public static Intent getInsertContactWithNameIntent(CharSequence text) {
- final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
- intent.putExtra(Intents.Insert.NAME, text);
- return intent;
+ private boolean canIntentBeHandled(Intent intent) {
+ final PackageManager packageManager = getPackageManager();
+ final List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ return resolveInfo != null && resolveInfo.size() > 0;
+ }
+
+ @Override
+ public void onDragStarted(int itemIndex, int x, int y, PhoneFavoriteTileView view) {
+ crossfadeViews(mRemoveViewContainer, mSearchViewContainer, ANIMATION_DURATION);
+ }
+
+ @Override
+ public void onDragHovered(int itemIndex, int x, int y) {}
+
+ @Override
+ public void onDragFinished(int x, int y) {
+ crossfadeViews(mSearchViewContainer, mRemoveViewContainer, ANIMATION_DURATION);
+ }
+
+ @Override
+ public void onDroppedOnRemove() {}
+
+ /**
+ * Allows the PhoneFavoriteFragment to attach the drag controller to mRemoveViewContainer
+ * once it has been attached to the activity.
+ */
+ @Override
+ public void setDragDropController(DragDropController dragController) {
+ mRemoveViewContainer.setDragDropController(dragController);
+ }
+
+ /**
+ * Crossfades two views so that the first one appears while the other one is fading
+ * out of view.
+ */
+ private void crossfadeViews(final View fadeIn, final View fadeOut, int duration) {
+ fadeOut.animate().alpha(0).setDuration(duration)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ fadeOut.setVisibility(View.GONE);
+ }
+ });
+
+ fadeIn.setVisibility(View.VISIBLE);
+ fadeIn.setAlpha(0);
+ fadeIn.animate().alpha(1).setDuration(ANIMATION_DURATION)
+ .setListener(null);
+ }
+
+ private boolean shouldShowOnscreenDialButton() {
+ return getResources().getBoolean(R.bool.config_show_onscreen_dial_button);
}
}
diff --git a/src/com/android/dialer/PhoneCallDetails.java b/src/com/android/dialer/PhoneCallDetails.java
index c380b65..4e01ab5 100644
--- a/src/com/android/dialer/PhoneCallDetails.java
+++ b/src/com/android/dialer/PhoneCallDetails.java
@@ -59,13 +59,17 @@
* This is meant to store the high-res photo only.
*/
public final Uri photoUri;
+ /**
+ * The source type of the contact associated with this call.
+ */
+ public final int sourceType;
/** Create the details for a call with a number not associated with a contact. */
public PhoneCallDetails(CharSequence number, int numberPresentation,
CharSequence formattedNumber, String countryIso, String geocode,
int[] callTypes, long date, long duration) {
this(number, numberPresentation, formattedNumber, countryIso, geocode,
- callTypes, date, duration, "", 0, "", null, null);
+ callTypes, date, duration, "", 0, "", null, null, 0);
}
/** Create the details for a call with a number associated with a contact. */
@@ -73,7 +77,7 @@
CharSequence formattedNumber, String countryIso, String geocode,
int[] callTypes, long date, long duration, CharSequence name,
int numberType, CharSequence numberLabel, Uri contactUri,
- Uri photoUri) {
+ Uri photoUri, int sourceType) {
this.number = number;
this.numberPresentation = numberPresentation;
this.formattedNumber = formattedNumber;
@@ -87,5 +91,6 @@
this.numberLabel = numberLabel;
this.contactUri = contactUri;
this.photoUri = photoUri;
+ this.sourceType = sourceType;
}
}
diff --git a/src/com/android/dialer/PhoneCallDetailsHelper.java b/src/com/android/dialer/PhoneCallDetailsHelper.java
index 340168e..aee5052 100644
--- a/src/com/android/dialer/PhoneCallDetailsHelper.java
+++ b/src/com/android/dialer/PhoneCallDetailsHelper.java
@@ -33,7 +33,7 @@
import com.android.contacts.common.test.NeededForTesting;
import com.android.dialer.calllog.CallTypeHelper;
import com.android.dialer.calllog.ContactInfo;
-import com.android.dialer.calllog.PhoneNumberHelper;
+import com.android.dialer.calllog.PhoneNumberDisplayHelper;
import com.android.dialer.calllog.PhoneNumberUtilsWrapper;
/**
@@ -48,7 +48,7 @@
private Long mCurrentTimeMillisForTest;
// Helper classes.
private final CallTypeHelper mCallTypeHelper;
- private final PhoneNumberHelper mPhoneNumberHelper;
+ private final PhoneNumberDisplayHelper mPhoneNumberHelper;
private final PhoneNumberUtilsWrapper mPhoneNumberUtilsWrapper;
/**
@@ -62,8 +62,8 @@
PhoneNumberUtilsWrapper phoneUtils) {
mResources = resources;
mCallTypeHelper = callTypeHelper;
- mPhoneNumberHelper = new PhoneNumberHelper(resources);
mPhoneNumberUtilsWrapper = phoneUtils;
+ mPhoneNumberHelper = new PhoneNumberDisplayHelper(mPhoneNumberUtilsWrapper, resources);
}
/** Fills the call details views with content. */
@@ -90,26 +90,13 @@
isHighlighted ? mCallTypeHelper.getHighlightedColor(details.callTypes[0]) : null;
// The date of this call, relative to the current time.
- CharSequence dateText =
- DateUtils.getRelativeTimeSpanString(details.date,
- getCurrentTimeMillis(),
- DateUtils.MINUTE_IN_MILLIS,
- DateUtils.FORMAT_ABBREV_RELATIVE);
+ CharSequence dateText = getCallDate(details);
// Set the call count and date.
setCallCountAndDate(views, callCount, dateText, highlightColor);
- CharSequence numberFormattedLabel = null;
- // Only show a label if the number is shown and it is not a SIP address.
- if (!TextUtils.isEmpty(details.number)
- && !PhoneNumberUtils.isUriNumber(details.number.toString())) {
- if (details.numberLabel == ContactInfo.GEOCODE_AS_LABEL) {
- numberFormattedLabel = details.geocode;
- } else {
- numberFormattedLabel = Phone.getTypeLabel(mResources, details.numberType,
- details.numberLabel);
- }
- }
+ // Get type of call (ie mobile, home, etc) if known, or the caller's
+ CharSequence numberFormattedLabel = getCallTypeOrLocation(details);
final CharSequence nameText;
final CharSequence numberText;
@@ -121,7 +108,7 @@
nameText = displayNumber;
if (TextUtils.isEmpty(details.geocode)
|| mPhoneNumberUtilsWrapper.isVoicemailNumber(details.number)) {
- numberText = mResources.getString(R.string.call_log_empty_gecode);
+ numberText = mResources.getString(R.string.call_log_empty_geocode);
} else {
numberText = details.geocode;
}
@@ -136,11 +123,45 @@
}
views.nameView.setText(nameText);
-
views.labelView.setText(labelText);
views.labelView.setVisibility(TextUtils.isEmpty(labelText) ? View.GONE : View.VISIBLE);
}
+ /**
+ * For a call, if there is an associated contact for the caller, return the known call type
+ * (e.g. mobile, home, work). If there is no associated contact, attempt to use the caller's
+ * location if known.
+ * @param details Call details to use.
+ * @return Type of call (mobile/home) if known, or the location of the caller (if known).
+ */
+ public CharSequence getCallTypeOrLocation(PhoneCallDetails details) {
+ CharSequence numberFormattedLabel = null;
+ // Only show a label if the number is shown and it is not a SIP address.
+ if (!TextUtils.isEmpty(details.number)
+ && !PhoneNumberUtils.isUriNumber(details.number.toString())) {
+ if (details.numberLabel == ContactInfo.GEOCODE_AS_LABEL) {
+ numberFormattedLabel = details.geocode;
+ } else {
+ numberFormattedLabel = Phone.getTypeLabel(mResources, details.numberType,
+ details.numberLabel);
+ }
+ }
+ return numberFormattedLabel;
+ }
+
+ /**
+ * Get the call date/time of the call, relative to the current time.
+ * e.g. 3 minutes ago
+ * @param details Call details to use.
+ * @return String representing when the call occurred.
+ */
+ public CharSequence getCallDate(PhoneCallDetails details) {
+ return DateUtils.getRelativeTimeSpanString(details.date,
+ getCurrentTimeMillis(),
+ DateUtils.MINUTE_IN_MILLIS,
+ DateUtils.FORMAT_ABBREV_RELATIVE);
+ }
+
/** Sets the text of the header view for the details page of a phone call. */
public void setCallDetailsHeader(TextView nameView, PhoneCallDetails details) {
final CharSequence nameText;
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index e4fd0d8..aee24ba 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -27,6 +27,7 @@
import android.provider.CallLog.Calls;
import android.provider.ContactsContract.PhoneLookup;
import android.text.TextUtils;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -37,11 +38,13 @@
import com.android.common.widget.GroupingListAdapter;
import com.android.contacts.common.ContactPhotoManager;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
import com.android.contacts.common.util.UriUtils;
import com.android.dialer.PhoneCallDetails;
import com.android.dialer.PhoneCallDetailsHelper;
import com.android.dialer.R;
import com.android.dialer.util.ExpirableCache;
+
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
@@ -176,16 +179,18 @@
/** Helper to set up contact photos. */
private final ContactPhotoManager mContactPhotoManager;
/** Helper to parse and process phone numbers. */
- private PhoneNumberHelper mPhoneNumberHelper;
+ private PhoneNumberDisplayHelper mPhoneNumberHelper;
/** Helper to group call log entries. */
private final CallLogGroupBuilder mCallLogGroupBuilder;
/** Can be set to true by tests to disable processing of requests. */
private volatile boolean mRequestProcessingDisabled = false;
- /** True if CallLogAdapter is created from the PhoneFavoriteFragment, where the primary
- * action should be set to call a number instead of opening the detail page. */
- private boolean mUseCallAsPrimaryAction = false;
+ /**
+ * Whether to show the secondary action button used to play voicemail or show call details.
+ * True if created from a CallLogFragment.
+ * False if created from the PhoneFavoriteFragment. */
+ private boolean mShowSecondaryActionButton = true;
private boolean mIsCallLog = true;
private int mNumMissedCalls = 0;
@@ -246,14 +251,14 @@
};
public CallLogAdapter(Context context, CallFetcher callFetcher,
- ContactInfoHelper contactInfoHelper, boolean useCallAsPrimaryAction,
+ ContactInfoHelper contactInfoHelper, boolean showSecondaryActionButton,
boolean isCallLog) {
super(context);
mContext = context;
mCallFetcher = callFetcher;
mContactInfoHelper = contactInfoHelper;
- mUseCallAsPrimaryAction = useCallAsPrimaryAction;
+ mShowSecondaryActionButton = showSecondaryActionButton;
mIsCallLog = isCallLog;
mContactInfoCache = ExpirableCache.create(CONTACT_INFO_CACHE_SIZE);
@@ -263,7 +268,7 @@
CallTypeHelper callTypeHelper = new CallTypeHelper(resources);
mContactPhotoManager = ContactPhotoManager.getInstance(mContext);
- mPhoneNumberHelper = new PhoneNumberHelper(resources);
+ mPhoneNumberHelper = new PhoneNumberDisplayHelper(resources);
PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
resources, callTypeHelper, new PhoneNumberUtilsWrapper());
mCallLogViewsHelper =
@@ -508,7 +513,7 @@
// Get the views to bind to.
CallLogListItemViews views = CallLogListItemViews.fromView(view);
views.primaryActionView.setOnClickListener(mActionListener);
- views.secondaryActionView.setOnClickListener(mActionListener);
+ views.secondaryActionButtonView.setOnClickListener(mActionListener);
view.setTag(views);
}
@@ -535,31 +540,33 @@
final ContactInfo cachedContactInfo = getContactInfoFromCallLog(c);
- if (!mUseCallAsPrimaryAction) {
- // Sets the primary action to open call detail page.
- views.primaryActionView.setTag(
- IntentProvider.getCallDetailIntentProvider(
- getCursor(), c.getPosition(), c.getLong(CallLogQuery.ID), count));
- } else if (PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)) {
+ final boolean isVoicemailNumber =
+ PhoneNumberUtilsWrapper.INSTANCE.isVoicemailNumber(number);
+
+ // Primary action is always to call, if possible.
+ if (PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)) {
// Sets the primary action to call the number.
views.primaryActionView.setTag(IntentProvider.getReturnCallIntentProvider(number));
} else {
views.primaryActionView.setTag(null);
}
- // Store away the voicemail information so we can play it directly.
- if (callType == Calls.VOICEMAIL_TYPE) {
- String voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
- final long rowId = c.getLong(CallLogQuery.ID);
- views.secondaryActionView.setTag(
- IntentProvider.getPlayVoicemailIntentProvider(rowId, voicemailUri));
- } else if (!TextUtils.isEmpty(number)) {
- // Store away the number so we can call it directly if you click on the call icon.
- views.secondaryActionView.setTag(
- IntentProvider.getReturnCallIntentProvider(number));
+ if ( mShowSecondaryActionButton ) {
+ // Store away the voicemail information so we can play it directly.
+ if (callType == Calls.VOICEMAIL_TYPE) {
+ String voicemailUri = c.getString(CallLogQuery.VOICEMAIL_URI);
+ final long rowId = c.getLong(CallLogQuery.ID);
+ views.secondaryActionButtonView.setTag(
+ IntentProvider.getPlayVoicemailIntentProvider(rowId, voicemailUri));
+ } else {
+ // Store the call details information.
+ views.secondaryActionButtonView.setTag(
+ IntentProvider.getCallDetailIntentProvider(
+ getCursor(), c.getPosition(), c.getLong(CallLogQuery.ID), count));
+ }
} else {
// No action enabled.
- views.secondaryActionView.setTag(null);
+ views.secondaryActionButtonView.setTag(null);
}
// Lookup contacts with this number
@@ -568,7 +575,7 @@
mContactInfoCache.getCachedValue(numberCountryIso);
ContactInfo info = cachedInfo == null ? null : cachedInfo.getValue();
if (!PhoneNumberUtilsWrapper.canPlaceCallsTo(number, numberPresentation)
- || new PhoneNumberUtilsWrapper().isVoicemailNumber(number)) {
+ || isVoicemailNumber) {
// If this is a number that cannot be dialed, there is no point in looking up a contact
// for it.
info = ContactInfo.EMPTY;
@@ -608,6 +615,7 @@
CharSequence formattedNumber = info.formattedNumber;
final int[] callTypes = getCallTypes(c, count);
final String geocode = c.getString(CallLogQuery.GEOCODED_LOCATION);
+ final int sourceType = info.sourceType;
final PhoneCallDetails details;
if (TextUtils.isEmpty(name)) {
@@ -617,23 +625,39 @@
} else {
details = new PhoneCallDetails(number, numberPresentation,
formattedNumber, countryIso, geocode, callTypes, date,
- duration, name, ntype, label, lookupUri, photoUri);
+ duration, name, ntype, label, lookupUri, photoUri, sourceType);
}
final boolean isNew = c.getInt(CallLogQuery.IS_READ) == 0;
// New items also use the highlighted version of the text.
final boolean isHighlighted = isNew;
mCallLogViewsHelper.setPhoneCallDetails(views, details, isHighlighted,
- mUseCallAsPrimaryAction);
+ mShowSecondaryActionButton);
- if (photoId == 0 && photoUri != null) {
- setPhoto(views, photoUri, lookupUri);
- } else {
- setPhoto(views, photoId, lookupUri);
+ int contactType = ContactPhotoManager.TYPE_DEFAULT;
+
+ if (isVoicemailNumber) {
+ contactType = ContactPhotoManager.TYPE_VOICEMAIL;
+ } else if (mContactInfoHelper.isBusiness(info.sourceType)) {
+ contactType = ContactPhotoManager.TYPE_BUSINESS;
}
- views.quickContactView.setContentDescription(views.phoneCallDetailsViews.nameView.
- getText());
+ String lookupKey = lookupUri == null ? null
+ : ContactInfoHelper.getLookupKeyFromUri(lookupUri);
+
+ String nameForDefaultImage = null;
+ if (TextUtils.isEmpty(name)) {
+ nameForDefaultImage = mPhoneNumberHelper.getDisplayNumber(details.number,
+ details.numberPresentation, details.formattedNumber).toString();
+ } else {
+ nameForDefaultImage = name;
+ }
+
+ if (photoId == 0 && photoUri != null) {
+ setPhoto(views, photoUri, lookupUri, nameForDefaultImage, lookupKey, contactType);
+ } else {
+ setPhoto(views, photoId, lookupUri, nameForDefaultImage, lookupKey, contactType);
+ }
// Listen for the first draw
if (mViewTreeObserver == null) {
@@ -855,15 +879,22 @@
return callTypes;
}
- private void setPhoto(CallLogListItemViews views, long photoId, Uri contactUri) {
+ private void setPhoto(CallLogListItemViews views, long photoId, Uri contactUri,
+ String displayName, String identifier, int contactType) {
views.quickContactView.assignContactUri(contactUri);
- mContactPhotoManager.loadThumbnail(views.quickContactView, photoId, false /* darkTheme */);
+ DefaultImageRequest request = new DefaultImageRequest(displayName, identifier,
+ contactType);
+ mContactPhotoManager.loadThumbnail(views.quickContactView, photoId, false /* darkTheme */,
+ request);
}
- private void setPhoto(CallLogListItemViews views, Uri photoUri, Uri contactUri) {
+ private void setPhoto(CallLogListItemViews views, Uri photoUri, Uri contactUri,
+ String displayName, String identifier, int contactType) {
views.quickContactView.assignContactUri(contactUri);
+ DefaultImageRequest request = new DefaultImageRequest(displayName, identifier,
+ contactType);
mContactPhotoManager.loadDirectoryPhoto(views.quickContactView, photoUri,
- false /* darkTheme */);
+ false /* darkTheme */, request);
}
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index e8d8b27..46c02a7 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -234,7 +234,7 @@
updateEmptyMessage(mCallTypeFilter);
String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
mAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this, new ContactInfoHelper(
- getActivity(), currentCountryIso), false, true);
+ getActivity(), currentCountryIso), true, true);
setListAdapter(mAdapter);
getListView().setItemsCanFocus(true);
}
diff --git a/src/com/android/dialer/calllog/CallLogListItemHelper.java b/src/com/android/dialer/calllog/CallLogListItemHelper.java
index 576a432..a85cd01 100644
--- a/src/com/android/dialer/calllog/CallLogListItemHelper.java
+++ b/src/com/android/dialer/calllog/CallLogListItemHelper.java
@@ -17,6 +17,7 @@
package com.android.dialer.calllog;
import android.content.res.Resources;
+import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.text.TextUtils;
import android.view.View;
@@ -32,7 +33,7 @@
/** Helper for populating the details of a phone call. */
private final PhoneCallDetailsHelper mPhoneCallDetailsHelper;
/** Helper for handling phone numbers. */
- private final PhoneNumberHelper mPhoneNumberHelper;
+ private final PhoneNumberDisplayHelper mPhoneNumberHelper;
/** Resources to look up strings. */
private final Resources mResources;
@@ -43,7 +44,7 @@
* @param phoneNumberHelper used to process phone number
*/
public CallLogListItemHelper(PhoneCallDetailsHelper phoneCallDetailsHelper,
- PhoneNumberHelper phoneNumberHelper, Resources resources) {
+ PhoneNumberDisplayHelper phoneNumberHelper, Resources resources) {
mPhoneCallDetailsHelper = phoneCallDetailsHelper;
mPhoneNumberHelper = phoneNumberHelper;
mResources = resources;
@@ -55,37 +56,201 @@
* @param views the views to populate
* @param details the details of a phone call needed to fill in the data
* @param isHighlighted whether to use the highlight text for the call
+ * @param showSecondaryActionButton whether to show the secondary action button or not
*/
public void setPhoneCallDetails(CallLogListItemViews views, PhoneCallDetails details,
- boolean isHighlighted, boolean useCallAsPrimaryAction) {
+ boolean isHighlighted, boolean showSecondaryActionButton) {
mPhoneCallDetailsHelper.setPhoneCallDetails(views.phoneCallDetailsViews, details,
isHighlighted);
- boolean canCall = PhoneNumberUtilsWrapper.canPlaceCallsTo(details.number,
- details.numberPresentation);
boolean canPlay = details.callTypes[0] == Calls.VOICEMAIL_TYPE;
- if (canPlay) {
- // Playback action takes preference.
- configurePlaySecondaryAction(views, isHighlighted);
- } else if (canCall && !useCallAsPrimaryAction) {
- // Call is the secondary action.
- configureCallSecondaryAction(views, details);
+ // Set the accessibility text for the contact badge
+ views.quickContactView.setContentDescription(getContactBadgeDescription(details));
+
+ // Set the primary action accessibility description
+ views.primaryActionView.setContentDescription(getCallDescription(details));
+
+ // If secondary action is visible, either show voicemail playback icon, or
+ // show the "clock" icon corresponding to the call details screen.
+ if (showSecondaryActionButton) {
+ if (canPlay) {
+ // Playback action takes preference.
+ configurePlaySecondaryAction(views, isHighlighted);
+ } else {
+ // Call details is the secondary action.
+ configureCallDetailsSecondaryAction(views, details);
+ }
} else {
- // No action available.
+ // No secondary action is to be shown (ie this is likely a PhoneFavoriteFragment)
views.secondaryActionView.setVisibility(View.GONE);
}
}
- /** Sets the secondary action to correspond to the call button. */
- private void configureCallSecondaryAction(CallLogListItemViews views,
+ /**
+ * Sets the secondary action to invoke call details.
+ *
+ * @param views the views to populate
+ * @param details the details of a phone call needed to fill in the call details data
+ */
+ private void configureCallDetailsSecondaryAction(CallLogListItemViews views,
PhoneCallDetails details) {
views.secondaryActionView.setVisibility(View.VISIBLE);
- views.secondaryActionView.setImageResource(R.drawable.ic_phone_dk);
- views.secondaryActionView.setContentDescription(getCallActionDescription(details));
+ // Use the small dark grey clock icon.
+ views.secondaryActionButtonView.setImageResource(R.drawable.ic_menu_history_dk);
+ views.secondaryActionButtonView.setContentDescription(
+ mResources.getString(R.string.description_call_details));
}
- /** Returns the description used by the call action for this phone call. */
- private CharSequence getCallActionDescription(PhoneCallDetails details) {
+ /**
+ * Returns the accessibility description for the contact badge for a call log entry.
+ *
+ * @param details Details of call.
+ * @return Accessibility description.
+ */
+ private CharSequence getContactBadgeDescription(PhoneCallDetails details) {
+ return mResources.getString(R.string.description_contact_details, getNameOrNumber(details));
+ }
+
+ /**
+ * Returns the accessibility description of the "return call/call" action for a call log
+ * entry.
+ * Accessibility text is a combination of:
+ * {Voicemail Prefix}. {Number of Calls}. {Caller information}.
+ * If most recent call is a voicemail, {Voicemail Prefix} is "New Voicemail.", otherwise "".
+ *
+ * If more than one call for the caller, {Number of Calls} is:
+ * "{number of calls} calls.", otherwise "".
+ *
+ * The {Caller Information} references the most recent call associated with the caller.
+ * For incoming calls:
+ * If missed call: Return missed call from {Name/Number} {Call Type} {Call Time}.
+ * If answered call: Return answered call from {Name/Number} {Call Type} {Call Time}.
+ *
+ * For unknown callers, drop the "Return" part, since the call can't be returned:
+ * If answered unknown: Answered call from {Name/Number} {Call Time}.
+ * If missed unknown: Missed call from {Name/Number} {Call Time}.
+ *
+ * For outgoing calls:
+ * If outgoing: Call {Name/Number] {Call Type}. {Last} called {Call Time}.
+ * Where {Last} is dropped if the number of calls for the caller is 1.
+ *
+ * Where:
+ * {Name/Number} is the name or number of the caller (as shown in call log).
+ * {Call type} is the contact phone number type (eg mobile) or location.
+ * {Call Time} is the time since the last call for the contact occurred.
+ *
+ * Examples:
+ * 3 calls. New Voicemail. Return missed call from Joe Smith mobile 2 hours ago.
+ * 2 calls. Call John Doe mobile. Last called 1 hour ago.
+ * @param details Details of call.
+ * @return Return call action description.
+ */
+ public CharSequence getCallDescription(PhoneCallDetails details) {
+ int lastCallType = getLastCallType(details.callTypes);
+ boolean isVoiceMail = lastCallType == Calls.VOICEMAIL_TYPE;
+
+ // Get the name or number of the caller.
+ final CharSequence nameOrNumber = getNameOrNumber(details);
+
+ // Get the call type or location of the caller; null if not applicable
+ final CharSequence typeOrLocation = mPhoneCallDetailsHelper.getCallTypeOrLocation(details);
+
+ // Get the time/date of the call
+ final CharSequence timeOfCall = mPhoneCallDetailsHelper.getCallDate(details);
+
+ StringBuilder callDescription = new StringBuilder();
+
+ // Prepend the voicemail indication.
+ if (isVoiceMail) {
+ callDescription.append(mResources.getString(R.string.description_new_voicemail));
+ }
+
+ // Add number of calls if more than one.
+ if (details.callTypes.length > 1) {
+ callDescription.append(mResources.getString(R.string.description_num_calls,
+ details.callTypes.length));
+ }
+
+ int stringID = getCallDescriptionStringID(details);
+
+ // Use chosen string resource to build up the message.
+ callDescription.append(mResources.getString(stringID,
+ nameOrNumber,
+ // If no type or location can be determined, sub in empty string.
+ typeOrLocation == null ? "" : typeOrLocation,
+ timeOfCall));
+
+ return callDescription;
+ }
+
+ /**
+ * Determine the appropriate string ID to describe a call for accessibility purposes.
+ *
+ * @param details Call details.
+ * @return String resource ID to use.
+ */
+ public int getCallDescriptionStringID(PhoneCallDetails details) {
+ int lastCallType = getLastCallType(details.callTypes);
+ boolean isNumberCallable = PhoneNumberUtilsWrapper.canPlaceCallsTo(details.number,
+ details.numberPresentation);
+
+ // Default string to use is "call XYZ..." just in case we manage to fall through.
+ int stringID = R.string.description_call_last_multiple;
+
+ if (!isNumberCallable) {
+ // Number isn't callable; this is an incoming call from an unknown caller.
+ // An uncallable outgoing call wouldn't be in the call log.
+
+ // Voicemail and missed calls are both considered missed.
+ if (lastCallType == Calls.VOICEMAIL_TYPE ||
+ lastCallType == Calls.MISSED_TYPE) {
+ stringID = R.string.description_unknown_missed_call;
+ } else if (lastCallType == Calls.INCOMING_TYPE) {
+ stringID = R.string.description_unknown_answered_call;
+ }
+ } else {
+ // Known caller, so callable.
+
+ // Missed call (ie voicemail or missed)
+ if (lastCallType == Calls.VOICEMAIL_TYPE ||
+ lastCallType == Calls.MISSED_TYPE) {
+ stringID = R.string.description_return_missed_call;
+ } else if (lastCallType == Calls.INCOMING_TYPE) {
+ // Incoming answered.
+ stringID = R.string.description_return_answered_call;
+ } else {
+ // Outgoing call.
+
+ // If we have a history of multiple calls
+ if (details.callTypes.length > 1) {
+ stringID = R.string.description_call_last_multiple;
+ } else {
+ stringID = R.string.description_call_last;
+ }
+ }
+ }
+ return stringID;
+ }
+
+ /**
+ * Determine the call type for the most recent call.
+ * @param callTypes Call types to check.
+ * @return Call type.
+ */
+ private int getLastCallType(int[] callTypes) {
+ if (callTypes.length > 0) {
+ return callTypes[0];
+ } else {
+ return Calls.MISSED_TYPE;
+ }
+ }
+
+ /**
+ * Return the name or number of the caller specified by the details.
+ * @param details Call details
+ * @return the name (if known) of the caller, otherwise the formatted number.
+ */
+ private CharSequence getNameOrNumber(PhoneCallDetails details) {
final CharSequence recipient;
if (!TextUtils.isEmpty(details.name)) {
recipient = details.name;
@@ -93,15 +258,15 @@
recipient = mPhoneNumberHelper.getDisplayNumber(
details.number, details.numberPresentation, details.formattedNumber);
}
- return mResources.getString(R.string.description_call, recipient);
+ return recipient;
}
/** Sets the secondary action to correspond to the play button. */
private void configurePlaySecondaryAction(CallLogListItemViews views, boolean isHighlighted) {
views.secondaryActionView.setVisibility(View.VISIBLE);
- views.secondaryActionView.setImageResource(
+ views.secondaryActionButtonView.setImageResource(
isHighlighted ? R.drawable.ic_play_active_holo_dark : R.drawable.ic_play_holo_light);
- views.secondaryActionView.setContentDescription(
+ views.secondaryActionButtonView.setContentDescription(
mResources.getString(R.string.description_call_log_play_button));
}
}
diff --git a/src/com/android/dialer/calllog/CallLogListItemViews.java b/src/com/android/dialer/calllog/CallLogListItemViews.java
index ed6218f..a378956 100644
--- a/src/com/android/dialer/calllog/CallLogListItemViews.java
+++ b/src/com/android/dialer/calllog/CallLogListItemViews.java
@@ -34,19 +34,25 @@
public final QuickContactBadge quickContactView;
/** The primary action view of the entry. */
public final View primaryActionView;
+ /** The secondary action view, which includes both the vertical divider line and
+ * the action button itself. Used so that the button and divider line can be
+ * made visible/hidden as a whole. */
+ public final View secondaryActionView;
/** The secondary action button on the entry. */
- public final ImageView secondaryActionView;
+ public final ImageView secondaryActionButtonView;
/** The details of the phone call. */
public final PhoneCallDetailsViews phoneCallDetailsViews;
/** The text of the header of a section. */
public final TextView listHeaderTextView;
private CallLogListItemViews(QuickContactBadge quickContactView, View primaryActionView,
- ImageView secondaryActionView, PhoneCallDetailsViews phoneCallDetailsViews,
+ View secondaryActionView, ImageView secondaryActionButtonView,
+ PhoneCallDetailsViews phoneCallDetailsViews,
TextView listHeaderTextView) {
this.quickContactView = quickContactView;
this.primaryActionView = primaryActionView;
this.secondaryActionView = secondaryActionView;
+ this.secondaryActionButtonView = secondaryActionButtonView;
this.phoneCallDetailsViews = phoneCallDetailsViews;
this.listHeaderTextView = listHeaderTextView;
}
@@ -55,6 +61,7 @@
return new CallLogListItemViews(
(QuickContactBadge) view.findViewById(R.id.quick_contact_photo),
view.findViewById(R.id.primary_action_view),
+ view.findViewById(R.id.secondary_action_view),
(ImageView) view.findViewById(R.id.secondary_action_icon),
PhoneCallDetailsViews.fromView(view),
(TextView) view.findViewById(R.id.call_log_header));
@@ -65,6 +72,7 @@
return new CallLogListItemViews(
new QuickContactBadge(context),
new View(context),
+ new View(context),
new ImageView(context),
PhoneCallDetailsViews.createForTest(context),
new TextView(context));
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsService.java b/src/com/android/dialer/calllog/CallLogNotificationsService.java
index 3270963..ccd9335 100644
--- a/src/com/android/dialer/calllog/CallLogNotificationsService.java
+++ b/src/com/android/dialer/calllog/CallLogNotificationsService.java
@@ -70,6 +70,10 @@
@Override
protected void onHandleIntent(Intent intent) {
+ if (intent == null) {
+ Log.d(TAG, "onHandleIntent: could not handle null intent");
+ return;
+ }
if (ACTION_MARK_NEW_VOICEMAILS_AS_OLD.equals(intent.getAction())) {
mCallLogQueryHandler.markNewVoicemailsAsOld();
} else if (ACTION_UPDATE_NOTIFICATIONS.equals(intent.getAction())) {
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index def3c97..3992676 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -258,7 +258,7 @@
}
@Override
- protected void onNotNullableQueryComplete(int token, Object cookie, Cursor cursor) {
+ protected synchronized void onNotNullableQueryComplete(int token, Object cookie, Cursor cursor) {
if (token == QUERY_CALLLOG_TOKEN) {
int requestId = ((Integer) cookie).intValue();
if (requestId != mCallsRequestId) {
diff --git a/src/com/android/dialer/calllog/ContactInfo.java b/src/com/android/dialer/calllog/ContactInfo.java
index 601f552..41afa5a 100644
--- a/src/com/android/dialer/calllog/ContactInfo.java
+++ b/src/com/android/dialer/calllog/ContactInfo.java
@@ -27,6 +27,7 @@
*/
public class ContactInfo {
public Uri lookupUri;
+ public String lookupKey;
public String name;
public int type;
public String label;
diff --git a/src/com/android/dialer/calllog/ContactInfoHelper.java b/src/com/android/dialer/calllog/ContactInfoHelper.java
index bcb98fd..e1a01b4 100644
--- a/src/com/android/dialer/calllog/ContactInfoHelper.java
+++ b/src/com/android/dialer/calllog/ContactInfoHelper.java
@@ -1,17 +1,15 @@
/*
* 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
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT 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.dialer.calllog;
@@ -36,6 +34,8 @@
import org.json.JSONException;
import org.json.JSONObject;
+import java.util.List;
+
/**
* Utility class to look up the contact information for a given number.
*/
@@ -100,7 +100,7 @@
updatedInfo = new ContactInfo();
updatedInfo.number = number;
updatedInfo.formattedNumber = formatPhoneNumber(number, null, countryIso);
- updatedInfo.lookupUri = createTemporaryContactUri(number);
+ updatedInfo.lookupUri = createTemporaryContactUri(updatedInfo.formattedNumber);
} else {
updatedInfo = info;
}
@@ -112,23 +112,20 @@
* Creates a JSON-encoded lookup uri for a unknown number without an associated contact
*
* @param number - Unknown phone number
- * @return JSON-encoded URI that can be used to perform a lookup when clicking
- * on the quick contact card.
+ * @return JSON-encoded URI that can be used to perform a lookup when clicking on the quick
+ * contact card.
*/
private static Uri createTemporaryContactUri(String number) {
try {
- final JSONObject contactRows = new JSONObject()
- .put(Phone.CONTENT_ITEM_TYPE, new JSONObject()
- .put(Phone.NUMBER, number)
- .put(Phone.TYPE, Phone.TYPE_CUSTOM));
+ final JSONObject contactRows = new JSONObject().put(Phone.CONTENT_ITEM_TYPE,
+ new JSONObject().put(Phone.NUMBER, number).put(Phone.TYPE, Phone.TYPE_CUSTOM));
- final String jsonString = new JSONObject()
- .put(Contacts.DISPLAY_NAME, number)
- .put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.PHONE)
- .put(Contacts.CONTENT_ITEM_TYPE, contactRows)
- .toString();
+ final String jsonString = new JSONObject().put(Contacts.DISPLAY_NAME, number)
+ .put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.PHONE)
+ .put(Contacts.CONTENT_ITEM_TYPE, contactRows).toString();
- return Contacts.CONTENT_LOOKUP_URI.buildUpon()
+ return Contacts.CONTENT_LOOKUP_URI
+ .buildUpon()
.appendPath(Constants.LOOKUP_URI_ENCODED)
.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
String.valueOf(Long.MAX_VALUE))
@@ -151,8 +148,7 @@
private ContactInfo lookupContactFromUri(Uri uri) {
final ContactInfo info;
Cursor phonesCursor =
- mContext.getContentResolver().query(
- uri, PhoneQuery._PROJECTION, null, null, null);
+ mContext.getContentResolver().query(uri, PhoneQuery._PROJECTION, null, null, null);
if (phonesCursor != null) {
try {
@@ -160,6 +156,7 @@
info = new ContactInfo();
long contactId = phonesCursor.getLong(PhoneQuery.PERSON_ID);
String lookupKey = phonesCursor.getString(PhoneQuery.LOOKUP_KEY);
+ info.lookupKey = lookupKey;
info.lookupUri = Contacts.getLookupUri(contactId, lookupKey);
info.name = phonesCursor.getString(PhoneQuery.NAME);
info.type = phonesCursor.getInt(PhoneQuery.PHONE_TYPE);
@@ -229,8 +226,8 @@
if (info != null && info != ContactInfo.EMPTY) {
info.formattedNumber = formatPhoneNumber(number, null, countryIso);
} else if (mCachedNumberLookupService != null) {
- CachedContactInfo cacheInfo = mCachedNumberLookupService
- .lookupCachedContactFromNumber(mContext, number);
+ CachedContactInfo cacheInfo =
+ mCachedNumberLookupService.lookupCachedContactFromNumber(mContext, number);
info = cacheInfo != null ? cacheInfo.getContactInfo() : null;
}
return info;
@@ -241,14 +238,12 @@
*
* @param number the number to be formatted.
* @param normalizedNumber the normalized number of the given number.
- * @param countryIso the ISO 3166-1 two letters country code, the country's
- * convention will be used to format the number if the normalized
- * phone is null.
+ * @param countryIso the ISO 3166-1 two letters country code, the country's convention will be
+ * used to format the number if the normalized phone is null.
*
* @return the formatted number, or the given number if it was formatted.
*/
- private String formatPhoneNumber(String number, String normalizedNumber,
- String countryIso) {
+ private String formatPhoneNumber(String number, String normalizedNumber, String countryIso) {
if (TextUtils.isEmpty(number)) {
return "";
}
@@ -261,4 +256,33 @@
}
return PhoneNumberUtils.formatNumber(number, normalizedNumber, countryIso);
}
+
+ /**
+ * Parses the given URI to determine the original lookup key of the contact.
+ */
+ public static String getLookupKeyFromUri(Uri lookupUri) {
+ // Would be nice to be able to persist the lookup key somehow to avoid having to parse
+ // the uri entirely just to retrieve the lookup key, but every uri is already parsed
+ // once anyway to check if it is an encoded JSON uri, so this has negligible effect
+ // on performance.
+ if (lookupUri != null && !UriUtils.isEncodedContactUri(lookupUri)) {
+ final List<String> segments = lookupUri.getPathSegments();
+ // This returns the third path segment of the uri, where the lookup key is located.
+ // See {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
+ return (segments.size() < 3) ? null : segments.get(2);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Given a contact's sourceType, return true if the contact is a business
+ *
+ * @param sourceType sourceType of the contact. This is usually populated by
+ * {@link #mCachedNumberLookupService}.
+ */
+ public boolean isBusiness(int sourceType) {
+ return mCachedNumberLookupService != null
+ && mCachedNumberLookupService.isBusiness(sourceType);
+ }
}
diff --git a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
index 14e1bed..fedd012 100644
--- a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
+++ b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
@@ -57,7 +57,7 @@
private final NotificationManager mNotificationManager;
private final NewCallsQuery mNewCallsQuery;
private final NameLookupQuery mNameLookupQuery;
- private final PhoneNumberHelper mPhoneNumberHelper;
+ private final PhoneNumberDisplayHelper mPhoneNumberHelper;
/** Returns the singleton instance of the {@link DefaultVoicemailNotifier}. */
public static synchronized DefaultVoicemailNotifier getInstance(Context context) {
@@ -75,7 +75,7 @@
private DefaultVoicemailNotifier(Context context,
NotificationManager notificationManager, NewCallsQuery newCallsQuery,
- NameLookupQuery nameLookupQuery, PhoneNumberHelper phoneNumberHelper) {
+ NameLookupQuery nameLookupQuery, PhoneNumberDisplayHelper phoneNumberHelper) {
mContext = context;
mNotificationManager = notificationManager;
mNewCallsQuery = newCallsQuery;
@@ -340,7 +340,7 @@
* This will cause some Disk I/O, at least the first time it is created, so it should not be
* called from the main thread.
*/
- public static PhoneNumberHelper createPhoneNumberHelper(Context context) {
- return new PhoneNumberHelper(context.getResources());
+ public static PhoneNumberDisplayHelper createPhoneNumberHelper(Context context) {
+ return new PhoneNumberDisplayHelper(context.getResources());
}
}
diff --git a/src/com/android/dialer/calllog/PhoneNumberHelper.java b/src/com/android/dialer/calllog/PhoneNumberDisplayHelper.java
similarity index 80%
rename from src/com/android/dialer/calllog/PhoneNumberHelper.java
rename to src/com/android/dialer/calllog/PhoneNumberDisplayHelper.java
index 336facb..5d7ce7e 100644
--- a/src/com/android/dialer/calllog/PhoneNumberHelper.java
+++ b/src/com/android/dialer/calllog/PhoneNumberDisplayHelper.java
@@ -18,18 +18,25 @@
import android.content.res.Resources;
import android.provider.CallLog.Calls;
-import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
+import android.util.Log;
import com.android.dialer.R;
/**
- * Helper for formatting and managing phone numbers.
+ * Helper for formatting and managing the display of phone numbers.
*/
-public class PhoneNumberHelper {
+public class PhoneNumberDisplayHelper {
+ private final PhoneNumberUtilsWrapper mPhoneNumberUtils;
private final Resources mResources;
- public PhoneNumberHelper(Resources resources) {
+ public PhoneNumberDisplayHelper(Resources resources) {
+ mResources = resources;
+ mPhoneNumberUtils = new PhoneNumberUtilsWrapper();
+ }
+
+ public PhoneNumberDisplayHelper(PhoneNumberUtilsWrapper phoneNumberUtils, Resources resources) {
+ mPhoneNumberUtils = phoneNumberUtils;
mResources = resources;
}
@@ -43,7 +50,7 @@
if (presentation == Calls.PRESENTATION_PAYPHONE) {
return mResources.getString(R.string.payphone);
}
- if (new PhoneNumberUtilsWrapper().isVoicemailNumber(number)) {
+ if (mPhoneNumberUtils.isVoicemailNumber(number)) {
return mResources.getString(R.string.voicemail);
}
if (PhoneNumberUtilsWrapper.isLegacyUnknownNumbers(number)) {
@@ -62,7 +69,6 @@
int presentation, CharSequence formattedNumber) {
final CharSequence displayName = getDisplayName(number, presentation);
-
if (!TextUtils.isEmpty(displayName)) {
return displayName;
}
diff --git a/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java b/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java
index 9913c20..2faab75 100644
--- a/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java
+++ b/src/com/android/dialer/calllog/PhoneNumberUtilsWrapper.java
@@ -20,7 +20,7 @@
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
-import com.google.android.collect.Sets;
+import com.google.common.collect.Sets;
import java.util.Set;
@@ -28,7 +28,7 @@
*
*/
public class PhoneNumberUtilsWrapper {
-
+ public static final PhoneNumberUtilsWrapper INSTANCE = new PhoneNumberUtilsWrapper();
private static final Set<String> LEGACY_UNKNOWN_NUMBERS = Sets.newHashSet("-1", "-2", "-3");
/** Returns true if it is possible to place a call to the given number. */
@@ -50,7 +50,7 @@
* mock-out this, it is not a static method.
*/
public boolean isVoicemailNumber(CharSequence number) {
- return PhoneNumberUtils.isVoiceMailNumber(number.toString());
+ return number!= null && PhoneNumberUtils.isVoiceMailNumber(number.toString());
}
/**
@@ -58,7 +58,7 @@
* static method.
*/
public boolean isSipNumber(CharSequence number) {
- return PhoneNumberUtils.isUriNumber(number.toString());
+ return number != null && PhoneNumberUtils.isUriNumber(number.toString());
}
public static boolean isUnknownNumberThatCanBeLookedUp(CharSequence number, int presentation) {
@@ -74,16 +74,16 @@
if (TextUtils.isEmpty(number)) {
return false;
}
- if (new PhoneNumberUtilsWrapper().isVoicemailNumber(number)) {
+ if (INSTANCE.isVoicemailNumber(number)) {
return false;
}
- if (isLegacyUnknownNumbers(number.toString())) {
+ if (isLegacyUnknownNumbers(number)) {
return false;
}
return true;
}
public static boolean isLegacyUnknownNumbers(CharSequence number) {
- return LEGACY_UNKNOWN_NUMBERS.contains(number.toString());
+ return number != null && LEGACY_UNKNOWN_NUMBERS.contains(number.toString());
}
}
diff --git a/src/com/android/dialer/database/DialerDatabaseHelper.java b/src/com/android/dialer/database/DialerDatabaseHelper.java
index e9d7735..f6f5f6c 100644
--- a/src/com/android/dialer/database/DialerDatabaseHelper.java
+++ b/src/com/android/dialer/database/DialerDatabaseHelper.java
@@ -37,6 +37,7 @@
import android.util.Log;
import com.android.contacts.common.util.StopWatch;
+import com.android.dialer.R;
import com.android.dialer.dialpad.SmartDialNameMatcher;
import com.android.dialer.dialpad.SmartDialPrefix;
@@ -162,6 +163,17 @@
/** Selects only rows that have been updated after a certain time stamp.*/
static final String SELECT_UPDATED_CLAUSE =
Phone.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
+
+ /** Ignores contacts that have an unreasonably long lookup key. These are likely to be
+ * the result of multiple (> 50) merged raw contacts, and are likely to cause
+ * OutOfMemoryExceptions within SQLite, or cause memory allocation problems later on
+ * when iterating through the cursor set (see b/13133579)
+ */
+ static final String SELECT_IGNORE_LOOKUP_KEY_TOO_LONG_CLAUSE =
+ "length(" + Phone.LOOKUP_KEY + ") < 1000";
+
+ static final String SELECTION = SELECT_UPDATED_CLAUSE + " AND " +
+ SELECT_IGNORE_LOOKUP_KEY_TOO_LONG_CLAUSE;
}
/** Query options for querying the deleted contact database.*/
@@ -510,6 +522,9 @@
DeleteContactQuery.PROJECTION,
DeleteContactQuery.SELECT_UPDATED_CLAUSE,
new String[] {last_update_time}, null);
+ if (deletedContactCursor == null) {
+ return;
+ }
db.beginTransaction();
try {
@@ -627,11 +642,35 @@
updatedContactCursor.moveToPosition(-1);
while (updatedContactCursor.moveToNext()) {
+ insert.clearBindings();
+
+ // Handle string columns which can possibly be null first. In the case of certain
+ // null columns (due to malformed rows possibly inserted by third-party apps
+ // or sync adapters), skip the phone number row.
+ final String number = updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
+ if (TextUtils.isEmpty(number)) {
+ continue;
+ } else {
+ insert.bindString(2, number);
+ }
+
+ final String lookupKey = updatedContactCursor.getString(
+ PhoneQuery.PHONE_LOOKUP_KEY);
+ if (TextUtils.isEmpty(lookupKey)) {
+ continue;
+ } else {
+ insert.bindString(4, lookupKey);
+ }
+
+ final String displayName = updatedContactCursor.getString(
+ PhoneQuery.PHONE_DISPLAY_NAME);
+ if (displayName == null) {
+ insert.bindString(5, mContext.getResources().getString(R.string.missing_name));
+ } else {
+ insert.bindString(5, displayName);
+ }
insert.bindLong(1, updatedContactCursor.getLong(PhoneQuery.PHONE_ID));
- insert.bindString(2, updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER));
insert.bindLong(3, updatedContactCursor.getLong(PhoneQuery.PHONE_CONTACT_ID));
- insert.bindString(4, updatedContactCursor.getString(PhoneQuery.PHONE_LOOKUP_KEY));
- insert.bindString(5, updatedContactCursor.getString(PhoneQuery.PHONE_DISPLAY_NAME));
insert.bindLong(6, updatedContactCursor.getLong(PhoneQuery.PHONE_PHOTO_ID));
insert.bindLong(7, updatedContactCursor.getLong(PhoneQuery.PHONE_LAST_TIME_USED));
insert.bindLong(8, updatedContactCursor.getInt(PhoneQuery.PHONE_TIMES_USED));
@@ -641,8 +680,6 @@
insert.bindLong(12, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_PRIMARY));
insert.bindLong(13, currentMillis);
insert.executeInsert();
- insert.clearBindings();
-
final String contactPhoneNumber =
updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
final ArrayList<String> numberPrefixes =
@@ -731,7 +768,7 @@
* update time.
*/
final Cursor updatedContactCursor = mContext.getContentResolver().query(PhoneQuery.URI,
- PhoneQuery.PROJECTION, PhoneQuery.SELECT_UPDATED_CLAUSE,
+ PhoneQuery.PROJECTION, PhoneQuery.SELECTION,
new String[]{lastUpdateMillis}, null);
/** Sets the time after querying the database as the current update time. */
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index 00b8281..83cfbd8 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -36,11 +36,9 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.provider.ContactsContract.Contacts;
import android.provider.Contacts.People;
import android.provider.Contacts.Phones;
import android.provider.Contacts.PhonesColumns;
-import android.provider.ContactsContract.Intents;
import android.provider.Settings;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
@@ -51,17 +49,13 @@
import android.text.TextWatcher;
import android.text.style.RelativeSizeSpan;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnPreDrawListener;
@@ -77,17 +71,12 @@
import com.android.contacts.common.CallUtil;
import com.android.contacts.common.GeoUtil;
-import com.android.contacts.common.activity.TransactionSafeActivity;
-import com.android.contacts.common.preference.ContactsPreferences;
import com.android.contacts.common.util.PhoneNumberFormatter;
import com.android.contacts.common.util.StopWatch;
import com.android.dialer.NeededForReflection;
import com.android.dialer.DialtactsActivity;
import com.android.dialer.R;
import com.android.dialer.SpecialCharSequenceMgr;
-import com.android.dialer.database.DialerDatabaseHelper;
-import com.android.dialer.interactions.PhoneNumberInteraction;
-import com.android.dialer.util.OrientationUtil;
import com.android.internal.telephony.ITelephony;
import com.android.phone.common.CallLogAsync;
import com.android.phone.common.HapticFeedback;
@@ -106,8 +95,19 @@
DialpadKeyButton.OnPressedListener {
private static final String TAG = DialpadFragment.class.getSimpleName();
- public interface OnDialpadFragmentStartedListener {
- public void onDialpadFragmentStarted();
+ /**
+ * This interface allows the DialpadFragment to tell its hosting Activity when and when not
+ * to display the "dial" button. While this is logically part of the DialpadFragment, the
+ * need to have a particular kind of slick animation puts the "dial" button in the parent.
+ *
+ * The parent calls dialButtonPressed() and optionsMenuInvoked() on the dialpad fragment
+ * when appropriate.
+ *
+ * TODO: Refactor the app so this interchange is a bit cleaner.
+ */
+ public interface HostInterface {
+ void setDialButtonEnabled(boolean enabled);
+ void setDialButtonContainerVisible(boolean visible);
}
/**
@@ -188,8 +188,6 @@
/** Stream type used to play the DTMF tones off call, and mapped to the volume control keys */
private static final int DIAL_TONE_STREAM_TYPE = AudioManager.STREAM_DTMF;
- private ContactsPreferences mContactsPrefs;
-
private OnDialpadQueryChangedListener mDialpadQueryListener;
/**
@@ -213,8 +211,6 @@
*/
private final HashSet<View> mPressedDialpadKeys = new HashSet<View>(12);
- private View mDialButtonContainer;
- private View mDialButton;
private ListView mDialpadChooser;
private DialpadChooserAdapter mDialpadChooserAdapter;
@@ -350,7 +346,6 @@
public void onCreate(Bundle state) {
super.onCreate(state);
mFirstLaunch = true;
- mContactsPrefs = new ContactsPreferences(getActivity());
mCurrentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
try {
@@ -400,7 +395,6 @@
// Load up the resources for the text field.
Resources r = getResources();
- mDialButtonContainer = fragmentView.findViewById(R.id.dialButtonContainer);
mDigitsContainer = fragmentView.findViewById(R.id.digits_container);
mDigits = (EditText) fragmentView.findViewById(R.id.digits);
mDigits.setKeyListener(UnicodeDialerKeyListener.INSTANCE);
@@ -415,15 +409,6 @@
setupKeypad(fragmentView);
}
- mDialButton = fragmentView.findViewById(R.id.dialButton);
- if (r.getBoolean(R.bool.config_show_onscreen_dial_button)) {
- mDialButton.setOnClickListener(this);
- mDialButton.setOnLongClickListener(this);
- } else {
- mDialButton.setVisibility(View.GONE); // It's VISIBLE by default
- mDialButton = null;
- }
-
mDelete = fragmentView.findViewById(R.id.deleteButton);
if (mDelete != null) {
mDelete.setOnClickListener(this);
@@ -435,7 +420,7 @@
@Override
public boolean onTouch(View v, MotionEvent event) {
if (isDigitsEmpty()) {
- hideAndClearDialpad();
+ hideAndClearDialpad(true);
return true;
}
return false;
@@ -461,18 +446,6 @@
@Override
public void onStart() {
super.onStart();
-
- final Activity activity = getActivity();
-
- try {
- ((OnDialpadFragmentStartedListener) activity).onDialpadFragmentStarted();
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement OnDialpadFragmentStartedListener");
- }
-
- final View overflowButton = getView().findViewById(R.id.overflow_menu_on_dialpad);
- overflowButton.setOnClickListener(this);
}
private boolean isLayoutReady() {
@@ -658,10 +631,6 @@
dialpadKey.setContentDescription(numberString);
if (lettersView != null) {
lettersView.setText(resources.getString(letterIds[i]));
- if (buttonIds[i] == R.id.zero) {
- lettersView.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimension(
- R.dimen.dialpad_key_plus_size));
- }
}
}
@@ -879,7 +848,7 @@
switch (view.getId()) {
case R.id.digits:
if (keyCode == KeyEvent.KEYCODE_ENTER) {
- dialButtonPressed();
+ handleDialButtonPressed();
return true;
}
break;
@@ -961,27 +930,36 @@
}
}
+ /**
+ * Called by the containing Activity to tell this Fragment to build an overflow options
+ * menu for display by the container when appropriate.
+ *
+ * @param invoker the View that invoked the options menu, to act as an anchor location.
+ */
+ public PopupMenu buildOptionsMenu(View invoker) {
+ final PopupMenu popupMenu = new PopupMenu(getActivity(), invoker);
+ popupMenu.inflate(R.menu.dialpad_options);
+ popupMenu.setOnMenuItemClickListener(this);
+ setupMenuItems(popupMenu.getMenu());
+ return popupMenu;
+ }
+
+ /**
+ * Called by the containing Activity to tell this Fragment that the dial button has been
+ * pressed.
+ */
+ public void dialButtonPressed() {
+ mHaptic.vibrate();
+ handleDialButtonPressed();
+ }
+
@Override
public void onClick(View view) {
switch (view.getId()) {
- case R.id.overflow_menu_on_dialpad: {
- final PopupMenu popupMenu = new PopupMenu(getActivity(), view);
- final Menu menu = popupMenu.getMenu();
- popupMenu.inflate(R.menu.dialpad_options);
- popupMenu.setOnMenuItemClickListener(this);
- setupMenuItems(menu);
- popupMenu.show();
- break;
- }
case R.id.deleteButton: {
keyPressed(KeyEvent.KEYCODE_DEL);
return;
}
- case R.id.dialButton: {
- mHaptic.vibrate(); // Vibrate here too, just like we do for the regular keys
- dialButtonPressed();
- return;
- }
case R.id.digits: {
if (!isDigitsEmpty()) {
mDigits.setCursorVisible(true);
@@ -1056,16 +1034,6 @@
mDigits.setCursorVisible(true);
return false;
}
- case R.id.dialButton: {
- if (isDigitsEmpty()) {
- handleDialButtonClickWithEmptyDigits();
- // This event should be consumed so that onClick() won't do the exactly same
- // thing.
- return true;
- } else {
- return false;
- }
- }
}
return false;
}
@@ -1085,11 +1053,11 @@
public void callVoicemail() {
startActivity(getVoicemailIntent());
- hideAndClearDialpad();
+ hideAndClearDialpad(false);
}
- private void hideAndClearDialpad() {
- ((DialtactsActivity) getActivity()).hideDialpadFragment(false, true);
+ private void hideAndClearDialpad(boolean animate) {
+ ((DialtactsActivity) getActivity()).hideDialpadFragment(animate, true);
}
public static class ErrorDialogFragment extends DialogFragment {
@@ -1158,7 +1126,7 @@
* user needs to press the dial button again, to dial it (general
* case described above).
*/
- public void dialButtonPressed() {
+ private void handleDialButtonPressed() {
if (isDigitsEmpty()) { // No number entered.
handleDialButtonClickWithEmptyDigits();
} else {
@@ -1185,7 +1153,7 @@
(getActivity() instanceof DialtactsActivity ?
((DialtactsActivity) getActivity()).getCallOrigin() : null));
startActivity(intent);
- hideAndClearDialpad();
+ hideAndClearDialpad(false);
}
}
}
@@ -1329,7 +1297,7 @@
mDigits.setVisibility(View.GONE);
}
if (mDialpad != null) mDialpad.setVisibility(View.GONE);
- if (mDialButtonContainer != null) mDialButtonContainer.setVisibility(View.GONE);
+ ((HostInterface) getActivity()).setDialButtonContainerVisible(false);
mDialpadChooser.setVisibility(View.VISIBLE);
@@ -1347,7 +1315,7 @@
mDigits.setVisibility(View.VISIBLE);
}
if (mDialpad != null) mDialpad.setVisibility(View.VISIBLE);
- if (mDialButtonContainer != null) mDialButtonContainer.setVisibility(View.VISIBLE);
+ ((HostInterface) getActivity()).setDialButtonContainerVisible(true);
mDialpadChooser.setVisibility(View.GONE);
}
}
@@ -1560,9 +1528,9 @@
* or Wait character (;).
*/
private void updateDialString(char newDigit) {
- if(newDigit != WAIT && newDigit != PAUSE) {
- Log.wtf(TAG, "Not expected for anything other than PAUSE & WAIT");
- return;
+ if (newDigit != WAIT && newDigit != PAUSE) {
+ throw new IllegalArgumentException(
+ "Not expected for anything other than PAUSE & WAIT");
}
int selectionStart;
@@ -1595,24 +1563,22 @@
* Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
*/
private void updateDialAndDeleteButtonEnabledState() {
- final boolean digitsNotEmpty = !isDigitsEmpty();
-
- if (mDialButton != null) {
- // On CDMA phones, if we're already on a call, we *always*
- // enable the Dial button (since you can press it without
- // entering any digits to send an empty flash.)
- if (phoneIsCdma() && phoneIsOffhook()) {
- mDialButton.setEnabled(true);
- } else {
- // Common case: GSM, or CDMA but not on a call.
- // Enable the Dial button if some digits have
- // been entered, or if there is a last dialed number
- // that could be redialed.
- mDialButton.setEnabled(digitsNotEmpty ||
- !TextUtils.isEmpty(mLastNumberDialed));
- }
+ if (getActivity() == null) {
+ return;
}
+ final boolean digitsNotEmpty = !isDigitsEmpty();
mDelete.setEnabled(digitsNotEmpty);
+ // On CDMA phones, if we're already on a call, we *always* enable the Dial button (since
+ // you can press it without entering any digits to send an empty flash.)
+ if (phoneIsCdma() && phoneIsOffhook()) {
+ ((HostInterface) getActivity()).setDialButtonEnabled(true);
+ } else {
+ // Common case: GSM, or CDMA but not on a call. Enable the Dial button if something
+ // has been entered into the digits field, or if there is a last dialed number that
+ // could be redialed.
+ ((HostInterface) getActivity()).setDialButtonEnabled(
+ digitsNotEmpty || !TextUtils.isEmpty(mLastNumberDialed));
+ }
}
/**
@@ -1642,8 +1608,8 @@
/* package */ static boolean canAddDigit(CharSequence digits, int start, int end,
char newDigit) {
if(newDigit != WAIT && newDigit != PAUSE) {
- Log.wtf(TAG, "Should not be called for anything other than PAUSE & WAIT");
- return false;
+ throw new IllegalArgumentException(
+ "Should not be called for anything other than PAUSE & WAIT");
}
// False if no selection, or selection is reversed (end < start)
diff --git a/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java b/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
index 30d01d2..c7e4c5f 100644
--- a/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
+++ b/src/com/android/dialer/interactions/UndemoteOutgoingCallReceiver.java
@@ -41,16 +41,16 @@
if (TextUtils.isEmpty(number)) {
return;
}
- final long id = getContactIdFromPhoneNumber(context, number);
- if (id != NO_CONTACT_FOUND) {
- final Thread thread = new Thread() {
- @Override
- public void run() {
+ final Thread thread = new Thread() {
+ @Override
+ public void run() {
+ final long id = getContactIdFromPhoneNumber(context, number);
+ if (id != NO_CONTACT_FOUND) {
undemoteContactWithId(context, id);
}
- };
- thread.start();
- }
+ }
+ };
+ thread.start();
}
}
@@ -68,6 +68,9 @@
Uri.encode(number));
final Cursor cursor = context.getContentResolver().query(contactUri, new String[] {
PhoneLookup._ID}, null, null, null);
+ if (cursor == null) {
+ return NO_CONTACT_FOUND;
+ }
try {
if (cursor.moveToFirst()) {
final long id = cursor.getLong(0);
diff --git a/src/com/android/dialer/list/DragDropController.java b/src/com/android/dialer/list/DragDropController.java
new file mode 100644
index 0000000..399cd09
--- /dev/null
+++ b/src/com/android/dialer/list/DragDropController.java
@@ -0,0 +1,71 @@
+package com.android.dialer.list;
+
+import android.view.View;
+
+import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class that handles and combines drag events generated from multiple views, and then fires
+ * off events to any OnDragDropListeners that have registered for callbacks.
+ */
+public class DragDropController {
+ private List<OnDragDropListener> mOnDragDropListeners = new ArrayList<OnDragDropListener>();
+
+ /**
+ * @return True if the drag is started, false if the drag is cancelled for some reason.
+ */
+ boolean handleDragStarted(int x, int y, ContactTileRow tileRow) {
+ final PhoneFavoriteTileView tileView =
+ (PhoneFavoriteTileView) tileRow.getViewAtPosition(x, y);
+
+ final int itemIndex = tileRow.getItemIndex(x, y);
+ if (itemIndex != -1 && !mOnDragDropListeners.isEmpty()) {
+ for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+ mOnDragDropListeners.get(i).onDragStarted(itemIndex, x, y, tileView);
+ }
+ }
+
+ return true;
+ }
+
+ public void handleDragHovered(int x, int y, View view) {
+ int itemIndex;
+ if (!(view instanceof ContactTileRow)) {
+ itemIndex = -1;
+ } else {
+ final ContactTileRow tile = (ContactTileRow) view;
+ itemIndex = tile.getItemIndex(x, y);
+ }
+ for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+ mOnDragDropListeners.get(i).onDragHovered(itemIndex, x, y);
+ }
+ }
+
+ public void handleDragFinished(int x, int y, boolean isRemoveView) {
+ if (isRemoveView) {
+ for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+ mOnDragDropListeners.get(i).onDroppedOnRemove();
+ }
+ }
+
+ for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+ mOnDragDropListeners.get(i).onDragFinished(x, y);
+ }
+ }
+
+ public void addOnDragDropListener(OnDragDropListener listener) {
+ if (!mOnDragDropListeners.contains(listener)) {
+ mOnDragDropListeners.add(listener);
+ }
+ }
+
+ public void removeOnDragDropListener(OnDragDropListener listener) {
+ if (mOnDragDropListeners.contains(listener)) {
+ mOnDragDropListeners.remove(listener);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/com/android/dialer/list/OnDragDropListener.java b/src/com/android/dialer/list/OnDragDropListener.java
new file mode 100644
index 0000000..7f6d179
--- /dev/null
+++ b/src/com/android/dialer/list/OnDragDropListener.java
@@ -0,0 +1,41 @@
+package com.android.dialer.list;
+
+
+/**
+ * Classes that want to receive callbacks in response to drag events should implement this
+ * interface.
+ */
+public interface OnDragDropListener {
+ /**
+ * Called when a drag is started.
+ * @param itemIndex Index of the contact on which the drag was triggered
+ * @param x X-coordinate of the drag event
+ * @param y Y-coordinate of the drag event
+ * @param view The contact tile which the drag was started on
+ */
+ public void onDragStarted(int itemIndex, int x, int y, PhoneFavoriteTileView view);
+
+ /**
+ * Called when a drag is in progress and the user moves the dragged contact to a
+ * location.
+ * @param itemIndex Index of the contact in the ListView which is currently being displaced
+ * by the dragged contact
+ * @param x X-coordinate of the drag event
+ * @param y Y-coordinate of the drag event
+ */
+ public void onDragHovered(int itemIndex, int x, int y);
+
+ /**
+ * Called when a drag is completed (whether by dropping it somewhere or simply by dragging
+ * the contact off the screen)
+ * @param x X-coordinate of the drag event
+ * @param y Y-coordinate of the drag event
+ */
+ public void onDragFinished(int x, int y);
+
+ /**
+ * Called when a contact has been dropped on the remove view, indicating that the user
+ * wants to remove this contact.
+ */
+ public void onDroppedOnRemove();
+}
\ No newline at end of file
diff --git a/src/com/android/dialer/list/PhoneFavoriteFragment.java b/src/com/android/dialer/list/PhoneFavoriteFragment.java
index 34666ee..0d33b4c 100644
--- a/src/com/android/dialer/list/PhoneFavoriteFragment.java
+++ b/src/com/android/dialer/list/PhoneFavoriteFragment.java
@@ -17,9 +17,7 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
-import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager;
@@ -27,7 +25,6 @@
import android.content.CursorLoader;
import android.content.Loader;
import android.content.SharedPreferences;
-import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Rect;
import android.net.Uri;
@@ -36,12 +33,13 @@
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewTreeObserver;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
@@ -55,10 +53,10 @@
import com.android.contacts.common.list.ContactTileView;
import com.android.dialer.DialtactsActivity;
import com.android.dialer.R;
-import com.android.dialer.calllog.CallLogQuery;
-import com.android.dialer.calllog.ContactInfoHelper;
import com.android.dialer.calllog.CallLogAdapter;
+import com.android.dialer.calllog.CallLogQuery;
import com.android.dialer.calllog.CallLogQueryHandler;
+import com.android.dialer.calllog.ContactInfoHelper;
import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow;
import com.android.dialerbind.ObjectFactory;
@@ -109,6 +107,10 @@
public void onCallNumberDirectly(String phoneNumber);
}
+ public interface HostInterface {
+ public void setDragDropController(DragDropController controller);
+ }
+
private class MissedCallLogLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
@Override
@@ -197,8 +199,7 @@
private PhoneFavoriteListView mListView;
- private View mShowAllContactsButton;
- private View mShowAllContactsInEmptyViewButton;
+ private View mPhoneFavoritesMenu;
private View mContactTileFrame;
private TileInteractionTeaserView mTileInteractionTeaserView;
@@ -238,8 +239,8 @@
// that will be available on onCreateView().
mContactTileAdapter = new PhoneFavoritesTileAdapter(activity, mContactTileAdapterListener,
this,
- getResources().getInteger(R.integer.contact_tile_column_count_in_favorites_new),
- 1);
+ getResources().getInteger(R.integer.contact_tile_column_count_in_favorites),
+ PhoneFavoritesTileAdapter.NO_ROW_LIMIT);
mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity));
}
@@ -253,7 +254,7 @@
this, 1);
final String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
mCallLogAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this,
- new ContactInfoHelper(getActivity(), currentCountryIso), true, false);
+ new ContactInfoHelper(getActivity(), currentCountryIso), false, false);
setHasOptionsMenu(true);
}
@@ -282,7 +283,7 @@
mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
mListView.setOnItemSwipeListener(mContactTileAdapter);
- mListView.setOnDragDropListener(mContactTileAdapter);
+ mListView.getDragDropController().addOnDragDropListener(mContactTileAdapter);
final ImageView dragShadowOverlay =
(ImageView) mParentView.findViewById(R.id.contact_tile_drag_shadow_overlay);
@@ -290,14 +291,8 @@
mEmptyView = mParentView.findViewById(R.id.phone_no_favorites_view);
- mShowAllContactsInEmptyViewButton = mParentView.findViewById(
- R.id.show_all_contact_button_in_nofav);
- prepareAllContactsButton(mShowAllContactsInEmptyViewButton);
-
- mShowAllContactsButton = inflater.inflate(R.layout.show_all_contact_button, mListView,
- false);
-
- prepareAllContactsButton(mShowAllContactsButton);
+ mPhoneFavoritesMenu = inflater.inflate(R.layout.phone_favorites_menu, mListView, false);
+ prepareFavoritesMenu(mPhoneFavoritesMenu);
mContactTileFrame = mParentView.findViewById(R.id.contact_tile_frame);
@@ -305,7 +300,7 @@
R.layout.tile_interactions_teaser_view, mListView, false);
mAdapter = new PhoneFavoriteMergedAdapter(getActivity(), this, mContactTileAdapter,
- mCallLogAdapter, mShowAllContactsButton, mTileInteractionTeaserView);
+ mCallLogAdapter, mPhoneFavoritesMenu, mTileInteractionTeaserView);
mTileInteractionTeaserView.setAdapter(mAdapter);
@@ -356,6 +351,15 @@
+ " must implement OnShowAllContactsListener");
}
+ try {
+ OnDragDropListener listener = (OnDragDropListener) activity;
+ mListView.getDragDropController().addOnDragDropListener(listener);
+ ((HostInterface) activity).setDragDropController(mListView.getDragDropController());
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement OnDragDropListener and HostInterface");
+ }
+
// Use initLoader() instead of restartLoader() to refraining unnecessary reload.
// This method call implicitly assures ContactTileLoaderListener's onLoadFinished() will
// be called, on which we'll check if "all" contacts should be reloaded again or not.
@@ -424,7 +428,10 @@
}
/**
- * Saves the current view offsets into memory
+ * Cache the current view offsets into memory. Once a relayout of views in the ListView
+ * has happened due to a dataset change, the cached offsets are used to create animations
+ * that slide views from their previous positions to their new ones, to give the appearance
+ * that the views are sliding into their new positions.
*/
@SuppressWarnings("unchecked")
private void saveOffsets(int removedItemHeight) {
@@ -437,10 +444,12 @@
final int position = firstVisiblePosition + i;
final long itemId = mAdapter.getItemId(position);
final int itemViewType = mAdapter.getItemViewType(position);
- if (itemViewType == PhoneFavoritesTileAdapter.ViewTypes.TOP) {
+ if (itemViewType == PhoneFavoritesTileAdapter.ViewTypes.TOP &&
+ child instanceof ContactTileRow) {
// This is a tiled row, so save horizontal offsets instead
saveHorizontalOffsets((ContactTileRow) child, (ArrayList<ContactEntry>)
- mAdapter.getItem(position));
+ mAdapter.getItem(position),
+ mAdapter.getAdjustedPositionInContactTileAdapter(position));
}
if (DEBUG) {
Log.d(TAG, "Saving itemId: " + itemId + " for listview child " + i + " Top: "
@@ -452,15 +461,25 @@
mItemIdTopMap.put(KEY_REMOVED_ITEM_HEIGHT, removedItemHeight);
}
- private void saveHorizontalOffsets(ContactTileRow row, ArrayList<ContactEntry> list) {
- for (int i = 0; i < list.size(); i++) {
+ /**
+ * Saves the horizontal offsets for contacts that are displayed as tiles in a row. Saving
+ * these offsets allow us to animate tiles sliding left and right within the same row.
+ * See {@link #saveOffsets(int removedItemHeight)}
+ */
+ private void saveHorizontalOffsets(ContactTileRow row, ArrayList<ContactEntry> list,
+ int currentRowIndex) {
+ for (int i = 0; i < list.size() && i < row.getChildCount(); i++) {
final View child = row.getChildAt(i);
+ if (child == null) {
+ continue;
+ }
final ContactEntry entry = list.get(i);
final long itemId = mContactTileAdapter.getAdjustedItemId(entry.id);
if (DEBUG) {
Log.d(TAG, "Saving itemId: " + itemId + " for tileview child " + i + " Left: "
+ child.getTop());
}
+ mItemIdTopMap.put(itemId, currentRowIndex);
mItemIdLeftMap.put(itemId, child.getLeft());
}
}
@@ -469,7 +488,7 @@
* Performs a animations for a row of tiles
*/
private void performHorizontalAnimations(ContactTileRow row, ArrayList<ContactEntry> list,
- long[] idsInPlace) {
+ long[] idsInPlace, int currentRow) {
if (mItemIdLeftMap.isEmpty()) {
return;
}
@@ -487,6 +506,25 @@
} else {
Integer startLeft = mItemIdLeftMap.get(itemId);
int left = child.getLeft();
+
+ Integer startRow = mItemIdTopMap.get(itemId);
+ if (startRow != null) {
+ if (startRow > currentRow) {
+ // Item has shifted upwards to the previous row.
+ // It should now animate in from right to left.
+ startLeft = left + child.getWidth();
+ } else if (startRow < currentRow) {
+ // Item has shifted downwards to the next row.
+ // It should now animate in from left to right.
+ startLeft = left - child.getWidth();
+ }
+
+ // If the item hasn't shifted rows (startRow == currentRow), it either remains
+ // in the same position or has shifted left or right within its current row.
+ // Either way, startLeft has already been correctly saved and retrieved from
+ // mItemIdTopMap.
+ }
+
if (startLeft != null) {
if (startLeft != left) {
int delta = startLeft - left;
@@ -498,10 +536,6 @@
animators.add(ObjectAnimator.ofFloat(
child, "translationX", delta, 0.0f));
}
- } else {
- // In case the last square row is pushed up from the non-square section.
- animators.add(ObjectAnimator.ofFloat(
- child, "translationX", left, 0.0f));
}
}
}
@@ -538,10 +572,12 @@
final View child = mListView.getChildAt(i);
int position = firstVisiblePosition + i;
final int itemViewType = mAdapter.getItemViewType(position);
- if (itemViewType == PhoneFavoritesTileAdapter.ViewTypes.TOP) {
+ if (itemViewType == PhoneFavoritesTileAdapter.ViewTypes.TOP &&
+ child instanceof ContactTileRow) {
// This is a tiled row, so perform horizontal animations instead
performHorizontalAnimations((ContactTileRow) child, (
- ArrayList<ContactEntry>) mAdapter.getItem(position), idsInPlace);
+ ArrayList<ContactEntry>) mAdapter.getItem(position), idsInPlace,
+ mAdapter.getAdjustedPositionInContactTileAdapter(position));
}
final long itemId = mAdapter.getItemId(position);
@@ -571,11 +607,6 @@
}
startTop = top + (i > 0 ? itemHeight : -itemHeight);
delta = startTop - top;
- } else {
- // In case the first non-square row is pushed down
- // from the square section.
- animators.add(ObjectAnimator.ofFloat(
- child, "alpha", 0.0f, 1.0f));
}
if (DEBUG) {
Log.d(TAG, "Found itemId: " + itemId + " for listview child " + i +
@@ -633,31 +664,17 @@
}
/**
- * Returns a view that is laid out and styled to look like a regular contact, with the correct
- * click behavior (to launch the all contacts activity when it is clicked).
+ * Prepares the favorites menu which contains the static label "Speed Dial" and the
+ * "All Contacts" button. Sets the onClickListener for the "All Contacts" button.
*/
- private View prepareAllContactsButton(View v) {
- final ContactListItemView view = (ContactListItemView) v;
- view.setOnClickListener(new OnClickListener() {
+ private void prepareFavoritesMenu(View favoritesMenu) {
+ Button allContactsButton = (Button) favoritesMenu.findViewById(R.id.all_contacts_button);
+ // Set the onClick listener for the button to bring up the all contacts view.
+ allContactsButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
showAllContacts();
}
});
-
- view.setPhotoPosition(ContactListItemView.PhotoPosition.LEFT);
- final Resources resources = getResources();
- view.setBackgroundResource(R.drawable.contact_list_item_background);
-
- view.setPaddingRelative(
- resources.getDimensionPixelSize(R.dimen.favorites_row_start_padding),
- resources.getDimensionPixelSize(R.dimen.favorites_row_end_padding),
- resources.getDimensionPixelSize(R.dimen.favorites_row_top_padding),
- resources.getDimensionPixelSize(R.dimen.favorites_row_bottom_padding));
-
- view.setDisplayName(resources.getString(R.string.show_all_contacts_button_text));
- view.setDrawableResource(R.drawable.list_item_avatar_bg,
- R.drawable.ic_menu_all_contacts_dk);
- return view;
}
}
diff --git a/src/com/android/dialer/list/PhoneFavoriteListView.java b/src/com/android/dialer/list/PhoneFavoriteListView.java
index 99979dd..078cf3e 100644
--- a/src/com/android/dialer/list/PhoneFavoriteListView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteListView.java
@@ -29,7 +29,6 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListView;
@@ -44,7 +43,8 @@
* - Swiping, which is borrowed from packages/apps/UnifiedEmail (com.android.mail.ui.Swipeable)
* - Drag and drop
*/
-public class PhoneFavoriteListView extends ListView implements SwipeHelperCallback {
+public class PhoneFavoriteListView extends ListView implements SwipeHelperCallback,
+ OnDragDropListener {
public static final String LOG_TAG = PhoneFavoriteListView.class.getSimpleName();
@@ -52,7 +52,6 @@
private boolean mEnableSwipe = true;
private OnItemGestureListener mOnItemGestureListener;
- private OnDragDropListener mOnDragDropListener;
private float mDensityScale;
private float mTouchSlop;
@@ -63,7 +62,7 @@
private Handler mScrollHandler;
private final long SCROLL_HANDLER_DELAY_MILLIS = 5;
- private final int DRAG_SCROLL_PX_UNIT = 10;
+ private final int DRAG_SCROLL_PX_UNIT = 25;
private boolean mIsDragScrollerRunning = false;
private int mTouchDownForDragStartX;
@@ -81,6 +80,8 @@
private int mDragShadowLeft;
private int mDragShadowTop;
+ private DragDropController mDragDropController = new DragDropController();
+
private final float DRAG_SHADOW_ALPHA = 0.7f;
/**
@@ -130,6 +131,7 @@
mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this,
mDensityScale, mTouchSlop);
setItemsCanFocus(true);
+ mDragDropController.addOnDragDropListener(this);
}
@Override
@@ -156,10 +158,10 @@
mOnItemGestureListener = listener;
}
- public void setOnDragDropListener(OnDragDropListener listener) {
- mOnDragDropListener = listener;
- }
-
+ /**
+ * TODO: This is all swipe to remove code (nothing to do with drag to remove). This should
+ * be cleaned up and removed once drag to remove becomes the only way to remove contacts.
+ */
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
@@ -232,6 +234,10 @@
requestDisallowInterceptTouchEvent(true);
}
+ /**
+ * End of swipe-to-remove code
+ */
+
@Override
public boolean dispatchDragEvent(DragEvent event) {
final int action = event.getAction();
@@ -239,13 +245,34 @@
final int eY = (int) event.getY();
switch (action) {
case DragEvent.ACTION_DRAG_STARTED:
- if (!handleDragStarted(mTouchDownForDragStartX, mTouchDownForDragStartY)) {
+ final int[] coordinates = new int[2];
+ getLocationOnScreen(coordinates);
+ // Calculate the X and Y coordinates of the drag event relative to the view
+ final int viewX = eX - coordinates[0];
+ final int viewY = eY - coordinates[1];
+ final View child = getViewAtPosition(viewX, viewY);
+
+ if (!(child instanceof ContactTileRow)) {
+ // Bail early.
return false;
- };
+ }
+
+ final ContactTileRow tile = (ContactTileRow) child;
+
+ // Disable drag and drop if there is a contact that has been swiped and is currently
+ // in the pending remove state
+ if (tile.getTileAdapter().hasPotentialRemoveEntryIndex()) {
+ return false;
+ }
+
+ if (!mDragDropController.handleDragStarted(viewX, viewY, tile)) {
+ return false;
+ }
break;
case DragEvent.ACTION_DRAG_LOCATION:
mLastDragY = eY;
- handleDragHovered(eX, eY);
+ final View view = getViewAtPosition(eX, eY);
+ mDragDropController.handleDragHovered(eX, eY, view);
// Kick off {@link #mScrollHandler} if it's not started yet.
if (!mIsDragScrollerRunning &&
// And if the distance traveled while dragging exceeds the touch slop
@@ -268,13 +295,13 @@
mIsDragScrollerRunning = false;
// Either a successful drop or it's ended with out drop.
if (action == DragEvent.ACTION_DROP || action == DragEvent.ACTION_DRAG_ENDED) {
- handleDragFinished(eX, eY);
+ mDragDropController.handleDragFinished(eX, eY, false);
}
break;
default:
break;
}
- // This ListView will consumer the drag events on behalf of its children.
+ // This ListView will consume the drag events on behalf of its children.
return true;
}
@@ -303,69 +330,48 @@
}
}
- /**
- * @return True if the drag is started.
- */
- private boolean handleDragStarted(int x, int y) {
- final View child = getViewAtPosition(x, y);
- if (!(child instanceof ContactTileRow)) {
- // Bail early.
- return false;
- }
-
- final ContactTileRow tile = (ContactTileRow) child;
-
- if (tile.getTileAdapter().hasPotentialRemoveEntryIndex()) {
- return false;
- }
-
- final int itemIndex = tile.getItemIndex(x, y);
- if (itemIndex != -1 && mOnDragDropListener != null) {
- final PhoneFavoriteTileView tileView =
- (PhoneFavoriteTileView) tile.getViewAtPosition(x, y);
- if (mDragShadowOverlay == null) {
- return false;
- }
-
- mDragShadowOverlay.clearAnimation();
- mDragShadowBitmap = createDraggedChildBitmap(tileView);
- if (mDragShadowBitmap == null) {
- return false;
- }
-
- if (tileView instanceof PhoneFavoriteRegularRowView) {
- mDragShadowLeft = tile.getLeft();
- mDragShadowTop = tile.getTop();
- } else {
- // Square tile is relative to the contact tile,
- // and contact tile is relative to this list view.
- mDragShadowLeft = tileView.getLeft() + tileView.getParentRow().getLeft();
- mDragShadowTop = tileView.getTop() + tileView.getParentRow().getTop();
- }
-
- mDragShadowOverlay.setImageBitmap(mDragShadowBitmap);
- mDragShadowOverlay.setVisibility(VISIBLE);
- mDragShadowOverlay.setAlpha(DRAG_SHADOW_ALPHA);
-
- mDragShadowOverlay.setX(mDragShadowLeft);
- mDragShadowOverlay.setY(mDragShadowTop);
-
- // x and y passed in are the coordinates of where the user has touched down, calculate
- // the offset to the top left coordinate of the dragged child. This will be used for
- // drawing the drag shadow.
- mTouchOffsetToChildLeft = x - mDragShadowLeft;
- mTouchOffsetToChildTop = y - mDragShadowTop;
-
- // invalidate to trigger a redraw of the drag shadow.
- invalidate();
-
- mOnDragDropListener.onDragStarted(itemIndex);
- }
-
- return true;
+ public DragDropController getDragDropController() {
+ return mDragDropController;
}
- private void handleDragHovered(int x, int y) {
+ @Override
+ public void onDragStarted(int itemIndex, int x, int y, PhoneFavoriteTileView tileView) {
+ if (mDragShadowOverlay == null) {
+ return;
+ }
+
+ mDragShadowOverlay.clearAnimation();
+ mDragShadowBitmap = createDraggedChildBitmap(tileView);
+ if (mDragShadowBitmap == null) {
+ return;
+ }
+
+ if (tileView instanceof PhoneFavoriteRegularRowView) {
+ mDragShadowLeft = tileView.getParentRow().getLeft();
+ mDragShadowTop = tileView.getParentRow().getTop();
+ } else {
+ // Square tile is relative to the contact tile,
+ // and contact tile is relative to this list view.
+ mDragShadowLeft = tileView.getLeft() + tileView.getParentRow().getLeft();
+ mDragShadowTop = tileView.getTop() + tileView.getParentRow().getTop();
+ }
+
+ mDragShadowOverlay.setImageBitmap(mDragShadowBitmap);
+ mDragShadowOverlay.setVisibility(VISIBLE);
+ mDragShadowOverlay.setAlpha(DRAG_SHADOW_ALPHA);
+
+ mDragShadowOverlay.setX(mDragShadowLeft);
+ mDragShadowOverlay.setY(mDragShadowTop);
+
+ // x and y passed in are the coordinates of where the user has touched down,
+ // calculate the offset to the top left coordinate of the dragged child. This
+ // will be used for drawing the drag shadow.
+ mTouchOffsetToChildLeft = x - mDragShadowLeft;
+ mTouchOffsetToChildTop = y - mDragShadowTop;
+ }
+
+ @Override
+ public void onDragHovered(int itemIndex, int x, int y) {
// Update the drag shadow location.
mDragShadowLeft = x - mTouchOffsetToChildLeft;
mDragShadowTop = y - mTouchOffsetToChildTop;
@@ -374,21 +380,10 @@
mDragShadowOverlay.setX(mDragShadowLeft);
mDragShadowOverlay.setY(mDragShadowTop);
}
-
- final View child = getViewAtPosition(x, y);
- if (!(child instanceof ContactTileRow)) {
- // Bail early.
- return;
- }
-
- final ContactTileRow tile = (ContactTileRow) child;
- final int itemIndex = tile.getItemIndex(x, y);
- if (itemIndex != -1 && mOnDragDropListener != null) {
- mOnDragDropListener.onDragHovered(itemIndex);
- }
}
- private void handleDragFinished(int x, int y) {
+ @Override
+ public void onDragFinished(int x, int y) {
// Update the drag shadow location.
mDragShadowLeft = x - mTouchOffsetToChildLeft;
mDragShadowTop = y - mTouchOffsetToChildTop;
@@ -400,12 +395,11 @@
.setListener(mDragShadowOverAnimatorListener)
.start();
}
-
- if (mOnDragDropListener != null) {
- mOnDragDropListener.onDragFinished();
- }
}
+ @Override
+ public void onDroppedOnRemove() {}
+
private Bitmap createDraggedChildBitmap(View view) {
view.setDrawingCacheEnabled(true);
final Bitmap cache = view.getDrawingCache();
@@ -425,10 +419,4 @@
return bitmap;
}
-
- public interface OnDragDropListener {
- public void onDragStarted(int itemIndex);
- public void onDragHovered(int itemIndex);
- public void onDragFinished();
- }
}
diff --git a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
index daba39e..6307613 100644
--- a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
@@ -49,12 +49,12 @@
private static final String TAG = PhoneFavoriteMergedAdapter.class.getSimpleName();
- private static final int TILE_INTERACTION_TEASER_VIEW_POSITION = 2;
+ private static final int TILE_INTERACTION_TEASER_VIEW_POSITION = 3;
private static final int TILE_INTERACTION_TEASER_VIEW_ID = -2;
- private static final int ALL_CONTACTS_BUTTON_ITEM_ID = -1;
+ private static final int FAVORITES_MENU_ITEM_ID = -3;
private final PhoneFavoritesTileAdapter mContactTileAdapter;
private final CallLogAdapter mCallLogAdapter;
- private final View mShowAllContactsButton;
+ private final View mPhoneFavoritesMenu;
private final PhoneFavoriteFragment mFragment;
private final TileInteractionTeaserView mTileInteractionTeaserView;
@@ -103,7 +103,7 @@
PhoneFavoriteFragment fragment,
PhoneFavoritesTileAdapter contactTileAdapter,
CallLogAdapter callLogAdapter,
- View showAllContactsButton,
+ View phoneFavoritesMenu,
TileInteractionTeaserView tileInteractionTeaserView) {
final Resources resources = context.getResources();
mContext = context;
@@ -114,20 +114,24 @@
mObserver = new CustomDataSetObserver();
mCallLogAdapter.registerDataSetObserver(mObserver);
mContactTileAdapter.registerDataSetObserver(mObserver);
- mShowAllContactsButton = showAllContactsButton;
+ mPhoneFavoritesMenu = phoneFavoritesMenu;
mTileInteractionTeaserView = tileInteractionTeaserView;
mCallLogQueryHandler = new CallLogQueryHandler(mContext.getContentResolver(),
mCallLogQueryHandlerListener);
}
+ /**
+ * Determines the number of items in the adapter.
+ * mCallLogAdapter contains the item for the most recent caller.
+ * mContactTileAdapter contains the starred contacts.
+ * The +1 is to account for the presence of the favorites menu.
+ *
+ * @return Number of items in the adapter.
+ */
@Override
public int getCount() {
- if (mContactTileAdapter.getCount() > 0) {
- return mContactTileAdapter.getCount() + mCallLogAdapter.getCount() + 1 +
- getTeaserViewCount();
- } else {
- return mCallLogAdapter.getCount();
- }
+ return mContactTileAdapter.getCount() + mCallLogAdapter.getCount() + getTeaserViewCount()
+ + 1;
}
@Override
@@ -140,7 +144,7 @@
}
}
// Set position to the position of the actual favorite contact in the favorites adapter
- position = getAdjustedFavoritePosition(position, callLogAdapterCount);
+ position = getAdjustedPositionInContactTileAdapter(position);
return mContactTileAdapter.getItem(position);
}
@@ -151,9 +155,9 @@
*
* These are the ranges of IDs reserved for each item type.
*
- * -(N + 1) to -3: CallLogAdapterItems, where N is equal to the number of call log items
+ * -4 and lower: CallLogAdapterItems representing most recent call.
+ * -3: Favorites menu
* -2: Teaser
- * -1: All contacts button
* 0 to (N -1): Rows of tiled contacts, where N is equal to the max rows of tiled contacts
* N to infinity: Rows of regular contacts. Their item id is calculated by N + contact_id,
* where contact_id is guaranteed to never be negative.
@@ -163,17 +167,19 @@
final int callLogAdapterCount = mCallLogAdapter.getCount();
if (position < callLogAdapterCount) {
// Call log items are not animated, so reusing their position for IDs is fine.
- return ALL_CONTACTS_BUTTON_ITEM_ID - 2 - position;
+ return FAVORITES_MENU_ITEM_ID - 1 - position;
} else if (position == TILE_INTERACTION_TEASER_VIEW_POSITION + callLogAdapterCount &&
- mTileInteractionTeaserView.getShouldDisplayInList()){
+ mTileInteractionTeaserView.getShouldDisplayInList()) {
return TILE_INTERACTION_TEASER_VIEW_ID;
+ } else if (position == callLogAdapterCount) {
+ return FAVORITES_MENU_ITEM_ID;
} else if (position < (callLogAdapterCount + mContactTileAdapter.getCount() +
- getTeaserViewCount())) {
+ getTeaserViewCount() + 1)) {
return mContactTileAdapter.getItemId(
- getAdjustedFavoritePosition(position, callLogAdapterCount));
+ getAdjustedPositionInContactTileAdapter(position));
} else {
- // All contacts button
- return ALL_CONTACTS_BUTTON_ITEM_ID;
+ // Default fallback. We don't normally get here.
+ return FAVORITES_MENU_ITEM_ID;
}
}
@@ -182,12 +188,15 @@
return true;
}
+ /**
+ * Determine the number of view types present.
+ */
@Override
public int getViewTypeCount() {
return (mContactTileAdapter.getViewTypeCount() + /* Favorite and frequent */
mCallLogAdapter.getViewTypeCount() + /* Recent call log */
getTeaserViewCount() + /* Teaser */
- 1); /* Show all contacts button. */
+ 1); /* Favorites menu. */
}
@Override
@@ -200,61 +209,67 @@
return mContactTileAdapter.getViewTypeCount();
} else if (position == TILE_INTERACTION_TEASER_VIEW_POSITION + callLogAdapterCount &&
mTileInteractionTeaserView.getShouldDisplayInList()) {
- // View type of the teaser row is the last view type of the contact tile adapter + 3
+ // View type of the teaser row is the last view type of the contact tile adapter +2
return mContactTileAdapter.getViewTypeCount() + 2;
- } else if (position < getCount() - 1) {
+ } else if (position == callLogAdapterCount) {
+ // View type of the favorites menu is last view type of contact tile adapter +3
+ return mContactTileAdapter.getViewTypeCount() + 3;
+ } else if (position < getCount()) {
return mContactTileAdapter.getItemViewType(
- getAdjustedFavoritePosition(position, callLogAdapterCount));
+ getAdjustedPositionInContactTileAdapter(position));
} else {
- // View type of the show all contact button is the last view type of the contact tile
- // adapter + 2
- return mContactTileAdapter.getViewTypeCount() + 1;
+ // Catch-all - we shouldn't get here but if we do use the same as the favorites menu.
+ return mContactTileAdapter.getViewTypeCount() + 3;
}
}
+ /**
+ * Determines the view for a specified position.
+ *
+ * @param position Position for which to retrieve view.
+ * @return view corresponding to position.
+ */
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final int callLogAdapterCount = mCallLogAdapter.getCount();
- if ((position == getCount() - 1) && (mContactTileAdapter.getCount() > 0)) {
- return mShowAllContactsButton;
- }
+ // Get the view for the "teaser view" which describes how to re-arrange favorites.
+ if (mTileInteractionTeaserView.getShouldDisplayInList()
+ && position == TILE_INTERACTION_TEASER_VIEW_POSITION + callLogAdapterCount) {
+ return mTileInteractionTeaserView;
+ } else if (callLogAdapterCount > 0 && position < callLogAdapterCount) {
+ // Handle case where we are requesting the view for the "most recent caller".
- if (mTileInteractionTeaserView.getShouldDisplayInList()) {
- if (position == TILE_INTERACTION_TEASER_VIEW_POSITION + callLogAdapterCount) {
- return mTileInteractionTeaserView;
+ final SwipeableCallLogRow wrapper;
+ if (convertView == null) {
+ wrapper = new SwipeableCallLogRow(mContext);
+ wrapper.setOnItemSwipeListener(mCallLogOnItemSwipeListener);
+ } else {
+ wrapper = (SwipeableCallLogRow) convertView;
}
+
+ // Special case wrapper view for the most recent call log item. This allows
+ // us to create a card-like effect for the more recent call log item in
+ // the PhoneFavoriteMergedAdapter, but keep the original look of the item in
+ // the CallLogAdapter.
+ final View view = mCallLogAdapter.getView(position, convertView == null ?
+ null : wrapper.getChildAt(0), parent);
+ wrapper.removeAllViews();
+ final View callLogItem = view.findViewById(R.id.call_log_list_item);
+ // Reset the internal call log item view if it is being recycled
+ callLogItem.setTranslationX(0);
+ callLogItem.setAlpha(1);
+ wrapper.addView(view);
+ return wrapper;
+ } else if (position == callLogAdapterCount) {
+ // If position is just after the entries in the mCallLogAdapter (most recent call),
+ // return the favorites menu.
+ return mPhoneFavoritesMenu;
}
- if (callLogAdapterCount > 0) {
- if (position == 0) {
- final SwipeableCallLogRow wrapper;
- if (convertView == null) {
- wrapper = new SwipeableCallLogRow(mContext);
- wrapper.setOnItemSwipeListener(mCallLogOnItemSwipeListener);
- } else {
- wrapper = (SwipeableCallLogRow) convertView;
- }
-
- // Special case wrapper view for the most recent call log item. This allows
- // us to create a card-like effect for the more recent call log item in
- // the PhoneFavoriteMergedAdapter, but keep the original look of the item in
- // the CallLogAdapter.
- final View view = mCallLogAdapter.getView(position, convertView == null ?
- null : wrapper.getChildAt(0), parent);
- wrapper.removeAllViews();
- final View callLogItem = view.findViewById(R.id.call_log_list_item);
- // Reset the internal call log item view if it is being recycled
- callLogItem.setTranslationX(0);
- callLogItem.setAlpha(1);
- wrapper.addView(view);
- return wrapper;
- }
- }
-
- // Set position to the position of the actual favorite contact in the
- // favorites adapter
- position = getAdjustedFavoritePosition(position, callLogAdapterCount);
+ // Set position to the position of the actual favorite contact in the favorites adapter.
+ // Adjusts based on the presence of other views, such as the favorites menu.
+ position = getAdjustedPositionInContactTileAdapter(position);
// Favorites section
final View view = mContactTileAdapter.getView(position, convertView, parent);
@@ -281,19 +296,31 @@
return mCallLogAdapter.isEnabled(position);
} else { // For favorites section
return mContactTileAdapter.isEnabled(
- getAdjustedFavoritePosition(position, callLogAdapterCount));
+ getAdjustedPositionInContactTileAdapter(position));
}
}
- private int getAdjustedFavoritePosition(int position, int callLogAdapterCount) {
+ /**
+ * Given the current position in the merged adapter, return the index in the
+ * mContactTileAdapter this position corresponds to.
+ *
+ * @param position current position in the overall (merged) adapter.
+ * @return position in the mContactTileAdapter.
+ */
+ public int getAdjustedPositionInContactTileAdapter(int position) {
+ final int callLogAdapterCount = mCallLogAdapter.getCount();
if (position - callLogAdapterCount > TILE_INTERACTION_TEASER_VIEW_POSITION &&
mTileInteractionTeaserView.getShouldDisplayInList()) {
- return position - callLogAdapterCount - 1;
+ return position - callLogAdapterCount - 2;
} else {
- return position - callLogAdapterCount;
+ return position - callLogAdapterCount - 1;
}
}
+ /**
+ * Determines the number of teaser views visible.
+ * @return 1 or 0 depending on if the teaser view is showing.
+ */
private int getTeaserViewCount() {
return (mContactTileAdapter.getCount() > TILE_INTERACTION_TEASER_VIEW_POSITION &&
mTileInteractionTeaserView.getShouldDisplayInList() ? 1 : 0);
@@ -318,7 +345,7 @@
@Override
public void addView(View view) {
- view.setBackgroundResource(R.drawable.dialer_recent_card_bg);
+ view.setBackgroundResource(R.drawable.ic_tile_for_recents_and_contact_tile);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
diff --git a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
index 85e7216..181d602 100644
--- a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
@@ -36,10 +36,6 @@
private static final String TAG = PhoneFavoriteSquareTileView.class.getSimpleName();
private ImageButton mSecondaryButton;
- // TODO: Use a more expansive name token separator if needed. For now it should be fine to
- // not split by dashes, underscore etc.
- private static final Pattern NAME_TOKEN_SEPARATOR_PATTERN = Pattern.compile("\\s+");
-
public PhoneFavoriteSquareTileView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -63,35 +59,15 @@
}
@Override
- protected String getNameForView(String name) {
- if (TextUtils.isEmpty(name)) return name;
- final String[] tokens = NAME_TOKEN_SEPARATOR_PATTERN.split(name, 2);
- if (tokens.length < 1) return name;
- return tokens[0];
- }
-
- @Override
public void loadFromContact(ContactEntry entry) {
super.loadFromContact(entry);
if (entry != null) {
- final boolean contactIsFavorite = entry.isFavorite;
- mSecondaryButton.setVisibility(contactIsFavorite ? GONE : VISIBLE);
-
- if (contactIsFavorite) {
- mStarView.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- launchQuickContact();
- }
- });
- } else {
- mSecondaryButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- launchQuickContact();
- }
- });
- }
+ mSecondaryButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ launchQuickContact();
+ }
+ });
}
}
}
diff --git a/src/com/android/dialer/list/PhoneFavoriteTileView.java b/src/com/android/dialer/list/PhoneFavoriteTileView.java
index 371c805..f55d4fc 100644
--- a/src/com/android/dialer/list/PhoneFavoriteTileView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteTileView.java
@@ -24,13 +24,12 @@
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.GestureDetector;
-import android.view.MotionEvent;
import android.view.View;
-import android.widget.ImageView;
+import com.android.contacts.common.ContactPhotoManager;
import com.android.contacts.common.MoreContactUtils;
+import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
import com.android.contacts.common.list.ContactEntry;
import com.android.contacts.common.list.ContactTileView;
import com.android.dialer.R;
@@ -49,7 +48,13 @@
private static final String TAG = PhoneFavoriteTileView.class.getSimpleName();
private static final boolean DEBUG = false;
- /** Length of all animations in miniseconds. */
+ // These parameters instruct the photo manager to display the default image/letter at 70% of
+ // its normal size, and vertically offset upwards 14% towards the top of the letter tile, to
+ // make room for the contact name and number label at the bottom of the image.
+ private static final float DEFAULT_IMAGE_LETTER_OFFSET = -0.14f;
+ private static final float DEFAULT_IMAGE_LETTER_SCALE = 0.70f;
+
+ /** Length of all animations in milliseconds. */
private int mAnimationDuration;
/** The view that holds the front layer of the favorite contact card. */
@@ -60,8 +65,8 @@
private View mUndoRemovalButton;
/** The view that holds the list view row. */
protected ContactTileRow mParentRow;
- /** The view that indicates whether the contact is a favorate. */
- protected ImageView mStarView;
+ /** View that contains the transparent shadow that is overlaid on top of the contact image. */
+ private View mShadowOverlay;
/** Users' most frequent phone number. */
private String mPhoneNumberString;
@@ -85,11 +90,10 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mFavoriteContactCard = findViewById(com.android.dialer.R.id.contact_favorite_card);
- mRemovalDialogue = findViewById(com.android.dialer.R.id.favorite_remove_dialogue);
- mUndoRemovalButton = findViewById(com.android.dialer.R.id
- .favorite_remove_undo_button);
- mStarView = (ImageView) findViewById(com.android.dialer.R.id.contact_favorite_star);
+ mShadowOverlay = findViewById(R.id.shadow_overlay);
+ mFavoriteContactCard = findViewById(R.id.contact_favorite_card);
+ mRemovalDialogue = findViewById(R.id.favorite_remove_dialogue);
+ mUndoRemovalButton = findViewById(R.id.favorite_remove_undo_button);
mUndoRemovalButton.setOnClickListener(new OnClickListener() {
@Override
@@ -127,7 +131,6 @@
// Grab the phone-number to call directly... see {@link onClick()}
mPhoneNumberString = entry.phoneNumber;
- mStarView.setVisibility(entry.isFavorite ? VISIBLE : GONE);
// If this is a blank entry, don't show anything.
// TODO krelease:Just hide the view for now. For this to truly look like an empty row
// the entire ContactTileRow needs to be hidden.
@@ -241,4 +244,18 @@
}
};
}
+
+ @Override
+ protected DefaultImageRequest getDefaultImageRequest(String displayName, String lookupKey) {
+ return new DefaultImageRequest(displayName, lookupKey, ContactPhotoManager.TYPE_DEFAULT,
+ DEFAULT_IMAGE_LETTER_SCALE, DEFAULT_IMAGE_LETTER_OFFSET);
+ }
+
+ @Override
+ protected void configureViewForImage(boolean isDefaultImage) {
+ // Hide the shadow overlay if the image is a default image (i.e. colored letter tile)
+ if (mShadowOverlay != null) {
+ mShadowOverlay.setVisibility(isDefaultImage ? View.GONE : View.VISIBLE);
+ }
+ }
}
diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
index e28fd73..d1ac955 100644
--- a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
@@ -15,7 +15,9 @@
*/
package com.android.dialer.list;
-import android.animation.ObjectAnimator;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ComparisonChain;
+
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
@@ -43,13 +45,9 @@
import com.android.contacts.common.list.ContactTileView;
import com.android.dialer.list.SwipeHelper.OnItemGestureListener;
import com.android.dialer.list.SwipeHelper.SwipeHelperCallback;
-import com.android.internal.annotations.VisibleForTesting;
-
-import com.google.common.collect.ComparisonChain;
import java.util.ArrayList;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
@@ -61,11 +59,13 @@
*
*/
public class PhoneFavoritesTileAdapter extends BaseAdapter implements
- SwipeHelper.OnItemGestureListener, PhoneFavoriteListView.OnDragDropListener {
+ SwipeHelper.OnItemGestureListener, OnDragDropListener {
private static final String TAG = PhoneFavoritesTileAdapter.class.getSimpleName();
private static final boolean DEBUG = false;
- public static final int ROW_LIMIT_DEFAULT = 1;
+ public static final int NO_ROW_LIMIT = -1;
+
+ public static final int ROW_LIMIT_DEFAULT = NO_ROW_LIMIT;
private ContactTileView.Listener mListener;
private OnDataSetChangedForAnimationListener mDataSetChangedListener;
@@ -88,6 +88,7 @@
private long mIdToKeepInPlace = -1;
private boolean mAwaitingRemove = false;
+ private boolean mDelayCursorUpdates = false;
private ContactPhotoManager mPhotoManager;
protected int mNumFrequents;
@@ -155,7 +156,7 @@
mContactEntries = new ArrayList<ContactEntry>();
// Converting padding in dips to padding in pixels
mPaddingInPixels = mContext.getResources()
- .getDimensionPixelSize(R.dimen.contact_tile_divider_padding);
+ .getDimensionPixelSize(R.dimen.contact_tile_divider_width);
bindColumnIndices();
}
@@ -178,6 +179,7 @@
* @param inDragging Boolean variable indicating whether there is a drag in process.
*/
public void setInDragging(boolean inDragging) {
+ mDelayCursorUpdates = inDragging;
mInDragging = inDragging;
}
@@ -224,7 +226,7 @@
* Else use {@link ContactTileLoaderFactory}
*/
public void setContactCursor(Cursor cursor) {
- if (cursor != null && !cursor.isClosed()) {
+ if (!mDelayCursorUpdates && cursor != null && !cursor.isClosed()) {
mNumStarred = getNumStarredContacts(cursor);
if (mAwaitingRemove) {
mDataSetChangedListener.cacheOffsetsForDatasetChange();
@@ -296,7 +298,8 @@
contact.name = (!TextUtils.isEmpty(name)) ? name :
mResources.getString(R.string.missing_name);
contact.photoUri = (photoUri != null ? Uri.parse(photoUri) : null);
- contact.lookupKey = ContentUris.withAppendedId(
+ contact.lookupKey = lookupKey;
+ contact.lookupUri = ContentUris.withAppendedId(
Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
contact.isFavorite = isStarred;
contact.isDefaultNumber = isDefaultNumber;
@@ -385,18 +388,24 @@
protected int getRowCount(int entryCount) {
if (entryCount == 0) return 0;
final int nonLimitedRows = ((entryCount - 1) / mColumnCount) + 1;
+ if (mMaxTiledRows == NO_ROW_LIMIT) {
+ return nonLimitedRows;
+ }
return Math.min(mMaxTiledRows, nonLimitedRows);
}
private int getMaxContactsInTiles() {
+ if (mMaxTiledRows == NO_ROW_LIMIT) {
+ return Integer.MAX_VALUE;
+ }
return mColumnCount * mMaxTiledRows;
}
public int getRowIndex(int entryIndex) {
- if (entryIndex < mMaxTiledRows * mColumnCount) {
+ if (entryIndex < getMaxContactsInTiles()) {
return entryIndex / mColumnCount;
} else {
- return entryIndex - mMaxTiledRows * mColumnCount + mMaxTiledRows;
+ return entryIndex - mMaxTiledRows * (mColumnCount + 1);
}
}
@@ -466,9 +475,13 @@
}
/**
- * Calculates the stable itemId for a particular entry based on its contactID
+ * Calculates the stable itemId for a particular entry based on the entry's contact ID. This
+ * stable itemId is used for animation purposes.
*/
public long getAdjustedItemId(long id) {
+ if (mMaxTiledRows == NO_ROW_LIMIT) {
+ return id;
+ }
return mMaxTiledRows + id;
}
@@ -478,7 +491,6 @@
}
@Override
-
public boolean areAllItemsEnabled() {
// No dividers, so all items are enabled.
return true;
@@ -540,7 +552,7 @@
@Override
public int getItemViewType(int position) {
- if (position < getRowCount(getMaxContactsInTiles())) {
+ if (position < getMaxContactsInTiles()) {
return ViewTypes.TOP;
} else {
return ViewTypes.FREQUENT;
@@ -656,7 +668,7 @@
boolean removed = false;
if (isIndexInBound(mPotentialRemoveEntryIndex)) {
final ContactEntry entry = mContactEntries.get(mPotentialRemoveEntryIndex);
- unstarAndUnpinContact(entry.lookupKey);
+ unstarAndUnpinContact(entry.lookupUri);
removed = true;
mAwaitingRemove = true;
}
@@ -699,6 +711,7 @@
private final int mRowPaddingEnd;
private final int mRowPaddingTop;
private final int mRowPaddingBottom;
+ private final float mHeightToWidthRatio;
private int mPosition;
private SwipeHelper mSwipeHelper;
private OnItemGestureListener mOnItemSwipeListener;
@@ -711,6 +724,9 @@
final Resources resources = mContext.getResources();
+ mHeightToWidthRatio = getResources().getFraction(
+ R.dimen.contact_tile_height_to_width_ratio, 1, 1);
+
if (mItemViewType == ViewTypes.TOP) {
// For tiled views, we still want padding to be set on the ContactTileRow.
// Otherwise the padding would be set around each of the tiles, which we don't want
@@ -722,8 +738,6 @@
R.dimen.favorites_row_start_padding);
mRowPaddingEnd = resources.getDimensionPixelSize(
R.dimen.favorites_row_end_padding);
-
- setBackgroundResource(R.drawable.bottom_border_background);
} else {
// For row views, padding is set on the view itself.
mRowPaddingTop = 0;
@@ -893,21 +907,22 @@
// Preferred width / height for images (excluding the padding).
// The actual width may be 1 pixel larger than this if we have a remainder.
- final int imageSize = (width - totalPaddingsInPixels) / mColumnCount;
- final int remainder = width - (imageSize * mColumnCount) - totalPaddingsInPixels;
+ final int imageWidth = (width - totalPaddingsInPixels) / mColumnCount;
+ final int remainder = width - (imageWidth * mColumnCount) - totalPaddingsInPixels;
+
+ final int height = (int) (mHeightToWidthRatio * imageWidth);
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
- final int childWidth = imageSize + child.getPaddingRight()
+ final int childWidth = imageWidth + child.getPaddingRight()
// Compensate for the remainder
+ (i < remainder ? 1 : 0);
- final int childHeight = imageSize;
child.measure(
MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY)
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
);
}
- setMeasuredDimension(width, imageSize + getPaddingTop() + getPaddingBottom());
+ setMeasuredDimension(width, height + getPaddingTop() + getPaddingBottom());
}
/**
@@ -918,7 +933,7 @@
* @return Index of the selected item in the cached array.
*/
public int getItemIndex(float itemX, float itemY) {
- if (mPosition < mMaxTiledRows) {
+ if (mMaxTiledRows == NO_ROW_LIMIT || mPosition < mMaxTiledRows) {
if (DEBUG) {
Log.v(TAG, String.valueOf(itemX) + " " + String.valueOf(itemY));
}
@@ -1190,24 +1205,38 @@
}
@Override
- public void onDragStarted(int itemIndex) {
+ public void onDragStarted(int itemIndex, int x, int y, PhoneFavoriteTileView view) {
setInDragging(true);
popContactEntry(itemIndex);
}
@Override
- public void onDragHovered(int itemIndex) {
+ public void onDragHovered(int itemIndex, int x, int y) {
if (mInDragging &&
mDragEnteredEntryIndex != itemIndex &&
isIndexInBound(itemIndex) &&
- itemIndex < PIN_LIMIT) {
+ itemIndex < PIN_LIMIT &&
+ itemIndex >= 0) {
markDropArea(itemIndex);
}
}
@Override
- public void onDragFinished() {
+ public void onDragFinished(int x, int y) {
setInDragging(false);
- handleDrop();
+ // A contact has been dragged to the RemoveView in order to be unstarred, so simply wait
+ // for the new contact cursor which will cause the UI to be refreshed without the unstarred
+ // contact.
+ if (!mAwaitingRemove) {
+ handleDrop();
+ }
+ }
+
+ @Override
+ public void onDroppedOnRemove() {
+ if (mDraggedEntry != null) {
+ unstarAndUnpinContact(mDraggedEntry.lookupUri);
+ mAwaitingRemove = true;
+ }
}
}
diff --git a/src/com/android/dialer/list/RemoveView.java b/src/com/android/dialer/list/RemoveView.java
new file mode 100644
index 0000000..16942fe
--- /dev/null
+++ b/src/com/android/dialer/list/RemoveView.java
@@ -0,0 +1,90 @@
+package com.android.dialer.list;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.DragEvent;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.dialer.R;
+
+public class RemoveView extends LinearLayout {
+
+ DragDropController mDragDropController;
+ TextView mRemoveText;
+ ImageView mRemoveIcon;
+ int mUnhighlightedColor;
+ int mHighlightedColor;
+ Drawable mRemoveDrawable;
+ Drawable mRemoveHighlightedDrawable;
+
+ public RemoveView(Context context) {
+ super(context);
+ }
+
+ public RemoveView(Context context, AttributeSet attrs) {
+ this(context, attrs, -1);
+ }
+
+ public RemoveView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mRemoveText = (TextView) findViewById(R.id.remove_view_text);
+ mRemoveIcon = (ImageView) findViewById(R.id.remove_view_icon);
+ final Resources r = getResources();
+ mUnhighlightedColor = r.getColor(R.color.remove_text_color);
+ mHighlightedColor = r.getColor(R.color.remove_highlighted_text_color);
+ mRemoveDrawable = r.getDrawable(R.drawable.ic_remove);
+ mRemoveHighlightedDrawable = r.getDrawable(R.drawable.ic_remove_highlight);
+ }
+
+ public void setDragDropController(DragDropController controller) {
+ mDragDropController = controller;
+ }
+
+ @Override
+ public boolean dispatchDragEvent(DragEvent event) {
+ final int action = event.getAction();
+ switch (action) {
+ case DragEvent.ACTION_DRAG_ENTERED:
+ setAppearanceHighlighted();
+ break;
+ case DragEvent.ACTION_DRAG_EXITED:
+ setAppearanceNormal();
+ break;
+ case DragEvent.ACTION_DRAG_LOCATION:
+ if (mDragDropController != null) {
+ mDragDropController.handleDragHovered((int) event.getX(),
+ // the true y-coordinate of the event with respect to the listview is
+ // offset by the height of the remove view
+ (int) event.getY() - getHeight(), null);
+ }
+ break;
+ case DragEvent.ACTION_DROP:
+ if (mDragDropController != null) {
+ mDragDropController.handleDragFinished((int) event.getX(), (int) event.getY(), true);
+ }
+ setAppearanceNormal();
+ break;
+ }
+ return true;
+ }
+
+ private void setAppearanceNormal() {
+ mRemoveText.setTextColor(mUnhighlightedColor);
+ mRemoveIcon.setImageDrawable(mRemoveDrawable);
+ invalidate();
+ }
+
+ private void setAppearanceHighlighted() {
+ mRemoveText.setTextColor(mHighlightedColor);
+ mRemoveIcon.setImageDrawable(mRemoveHighlightedDrawable);
+ invalidate();
+ }
+}
diff --git a/src/com/android/dialer/service/CachedNumberLookupService.java b/src/com/android/dialer/service/CachedNumberLookupService.java
index 5745c9d..73fd895 100644
--- a/src/com/android/dialer/service/CachedNumberLookupService.java
+++ b/src/com/android/dialer/service/CachedNumberLookupService.java
@@ -33,6 +33,8 @@
public boolean isCacheUri(String uri);
+ public boolean isBusiness(int sourceType);
+
public boolean addPhoto(Context context, String number, byte[] photo);
/**
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackFragment.java b/src/com/android/dialer/voicemail/VoicemailPlaybackFragment.java
index 1dbae65..826dec0 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackFragment.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackFragment.java
@@ -74,7 +74,9 @@
};
private VoicemailPlaybackPresenter mPresenter;
- private ScheduledExecutorService mScheduledExecutorService;
+ private static int mMediaPlayerRefCount = 0;
+ private static MediaPlayerProxy mMediaPlayerInstance;
+ private static ScheduledExecutorService mScheduledExecutorService;
private View mPlaybackLayout;
@Override
@@ -87,7 +89,6 @@
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mScheduledExecutorService = createScheduledExecutorService();
Bundle arguments = getArguments();
Preconditions.checkNotNull(arguments, "fragment must be started with arguments");
Uri voicemailUri = arguments.getParcelable(EXTRA_VOICEMAIL_URI);
@@ -99,8 +100,8 @@
powerManager.newWakeLock(
PowerManager.SCREEN_DIM_WAKE_LOCK, getClass().getSimpleName());
mPresenter = new VoicemailPlaybackPresenter(createPlaybackViewImpl(),
- createMediaPlayer(mScheduledExecutorService), voicemailUri,
- mScheduledExecutorService, startPlayback,
+ getMediaPlayerInstance(), voicemailUri,
+ getScheduledExecutorServiceInstance(), startPlayback,
AsyncTaskExecutors.createAsyncTaskExecutor(), wakeLock);
mPresenter.onCreate(savedInstanceState);
}
@@ -113,8 +114,8 @@
@Override
public void onDestroy() {
+ shutdownMediaPlayer();
mPresenter.onDestroy();
- mScheduledExecutorService.shutdown();
super.onDestroy();
}
@@ -129,12 +130,36 @@
mPlaybackLayout);
}
- private MediaPlayerProxy createMediaPlayer(ExecutorService executorService) {
- return VariableSpeed.createVariableSpeed(executorService);
+ private static synchronized MediaPlayerProxy getMediaPlayerInstance() {
+ ++mMediaPlayerRefCount;
+ if (mMediaPlayerInstance == null) {
+ mMediaPlayerInstance = VariableSpeed.createVariableSpeed(
+ getScheduledExecutorServiceInstance());
+ }
+ return mMediaPlayerInstance;
}
- private ScheduledExecutorService createScheduledExecutorService() {
- return Executors.newScheduledThreadPool(NUMBER_OF_THREADS_IN_POOL);
+ private static synchronized ScheduledExecutorService getScheduledExecutorServiceInstance() {
+ if (mScheduledExecutorService == null) {
+ mScheduledExecutorService = Executors.newScheduledThreadPool(
+ NUMBER_OF_THREADS_IN_POOL);
+ }
+ return mScheduledExecutorService;
+ }
+
+ private static synchronized void shutdownMediaPlayer() {
+ --mMediaPlayerRefCount;
+ if (mMediaPlayerRefCount > 0) {
+ return;
+ }
+ if (mScheduledExecutorService != null) {
+ mScheduledExecutorService.shutdown();
+ mScheduledExecutorService = null;
+ }
+ if (mMediaPlayerInstance != null) {
+ mMediaPlayerInstance.release();
+ mMediaPlayerInstance = null;
+ }
}
/**
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
index ef29f13..085ef66 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
@@ -16,8 +16,6 @@
package com.android.dialer.voicemail;
-import static android.util.MathUtils.constrain;
-
import android.content.Context;
import android.database.ContentObserver;
import android.media.AudioManager;
@@ -37,6 +35,7 @@
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -313,6 +312,7 @@
mPlayer.setDataSource(mView.getDataSourceContext(), mVoicemailUri);
mPlayer.setAudioStreamType(PLAYBACK_STREAM);
mPlayer.prepare();
+ mDuration.set(mPlayer.getDuration());
return null;
} catch (Exception e) {
return e;
@@ -346,7 +346,7 @@
mView.setSpeakerPhoneOn(mView.isSpeakerPhoneOn());
mView.setRateDecreaseButtonListener(createRateDecreaseListener());
mView.setRateIncreaseButtonListener(createRateIncreaseListener());
- mView.setClipPosition(0, mPlayer.getDuration());
+ mView.setClipPosition(0, mDuration.get());
mView.playbackStopped();
// Always disable on stop.
mView.disableProximitySensor();
@@ -365,6 +365,10 @@
}
public void onDestroy() {
+ if (mPrepareTask != null) {
+ mPrepareTask.cancel(false);
+ mPrepareTask = null;
+ }
mPlayer.release();
if (mFetchResultHandler != null) {
mFetchResultHandler.destroy();
@@ -432,49 +436,67 @@
}
}
+ private class AsyncPrepareTask extends AsyncTask<Void, Void, Exception> {
+ private int mClipPositionInMillis;
+
+ AsyncPrepareTask(int clipPositionInMillis) {
+ mClipPositionInMillis = clipPositionInMillis;
+ }
+
+ @Override
+ public Exception doInBackground(Void... params) {
+ try {
+ if (!mPlayer.isReadyToPlay()) {
+ mPlayer.reset();
+ mPlayer.setDataSource(mView.getDataSourceContext(), mVoicemailUri);
+ mPlayer.setAudioStreamType(PLAYBACK_STREAM);
+ mPlayer.prepare();
+ }
+ return null;
+ } catch (Exception e) {
+ return e;
+ }
+ }
+
+ @Override
+ public void onPostExecute(Exception exception) {
+ mPrepareTask = null;
+ if (exception == null) {
+ final int duration = mPlayer.getDuration();
+ mDuration.set(duration);
+ int startPosition =
+ constrain(mClipPositionInMillis, 0, duration);
+ mPlayer.seekTo(startPosition);
+ mView.setClipPosition(startPosition, duration);
+ try {
+ // Can throw RejectedExecutionException
+ mPlayer.start();
+ mView.playbackStarted();
+ if (!mWakeLock.isHeld()) {
+ mWakeLock.acquire();
+ }
+ // Only enable if we are not currently using the speaker phone.
+ if (!mView.isSpeakerPhoneOn()) {
+ mView.enableProximitySensor();
+ }
+ // Can throw RejectedExecutionException
+ mPositionUpdater.startUpdating(startPosition, duration);
+ } catch (RejectedExecutionException e) {
+ handleError(e);
+ }
+ } else {
+ handleError(exception);
+ }
+ }
+ }
+
private void resetPrepareStartPlaying(final int clipPositionInMillis) {
if (mPrepareTask != null) {
mPrepareTask.cancel(false);
+ mPrepareTask = null;
}
mPrepareTask = mAsyncTaskExecutor.submit(Tasks.RESET_PREPARE_START_MEDIA_PLAYER,
- new AsyncTask<Void, Void, Exception>() {
- @Override
- public Exception doInBackground(Void... params) {
- try {
- mPlayer.reset();
- mPlayer.setDataSource(mView.getDataSourceContext(), mVoicemailUri);
- mPlayer.setAudioStreamType(PLAYBACK_STREAM);
- mPlayer.prepare();
- return null;
- } catch (Exception e) {
- return e;
- }
- }
-
- @Override
- public void onPostExecute(Exception exception) {
- mPrepareTask = null;
- if (exception == null) {
- mDuration.set(mPlayer.getDuration());
- int startPosition =
- constrain(clipPositionInMillis, 0, mDuration.get());
- mView.setClipPosition(startPosition, mDuration.get());
- mPlayer.seekTo(startPosition);
- mPlayer.start();
- mView.playbackStarted();
- if (!mWakeLock.isHeld()) {
- mWakeLock.acquire();
- }
- // Only enable if we are not currently using the speaker phone.
- if (!mView.isSpeakerPhoneOn()) {
- mView.enableProximitySensor();
- }
- mPositionUpdater.startUpdating(startPosition, mDuration.get());
- } else {
- handleError(exception);
- }
- }
- });
+ new AsyncPrepareTask(clipPositionInMillis));
}
private void handleError(Exception e) {
@@ -600,6 +622,7 @@
synchronized (mLock) {
if (mScheduledFuture != null) {
mScheduledFuture.cancel(false);
+ mScheduledFuture = null;
}
mScheduledFuture = mExecutorService.scheduleAtFixedRate(this, 0, mPeriodMillis,
TimeUnit.MILLISECONDS);
@@ -622,9 +645,14 @@
}
if (mPrepareTask != null) {
mPrepareTask.cancel(false);
+ mPrepareTask = null;
}
if (mWakeLock.isHeld()) {
mWakeLock.release();
}
}
+
+ private static int constrain(int amount, int low, int high) {
+ return amount < low ? low : (amount > high ? high : amount);
+ }
}
diff --git a/src/com/android/dialerbind/ObjectFactory.java b/src/com/android/dialerbind/ObjectFactory.java
index c43dffc..be91e33 100644
--- a/src/com/android/dialerbind/ObjectFactory.java
+++ b/src/com/android/dialerbind/ObjectFactory.java
@@ -34,10 +34,20 @@
return null;
}
+ /**
+ * Create a new instance of the call log adapter.
+ * @param context The context to use.
+ * @param callFetcher Instance of call fetcher to use.
+ * @param contactInfoHelper Instance of contact info helper class to use.
+ * @param hideSecondaryAction If true, secondary action will be hidden (ie call details
+ * or play voicemail).
+ * @param isCallLog Is this call log adapter being used on the call log?
+ * @return Instance of CallLogAdapter.
+ */
public static CallLogAdapter newCallLogAdapter(Context context, CallFetcher callFetcher,
- ContactInfoHelper contactInfoHelper, boolean useCallAsPrimaryAction,
+ ContactInfoHelper contactInfoHelper, boolean hideSecondaryAction,
boolean isCallLog) {
- return new CallLogAdapter(context, callFetcher, contactInfoHelper, useCallAsPrimaryAction,
+ return new CallLogAdapter(context, callFetcher, contactInfoHelper, hideSecondaryAction,
isCallLog);
}
}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 3a714e3..94aa3aa 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -25,6 +25,7 @@
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_SYNC_STATS" />
diff --git a/tests/src/com/android/dialer/PhoneCallDetailsHelperTest.java b/tests/src/com/android/dialer/PhoneCallDetailsHelperTest.java
index 6a9817f..9b7d9de 100644
--- a/tests/src/com/android/dialer/PhoneCallDetailsHelperTest.java
+++ b/tests/src/com/android/dialer/PhoneCallDetailsHelperTest.java
@@ -51,6 +51,8 @@
private static final String TEST_COUNTRY_ISO = "US";
/** The geocoded location used in the tests. */
private static final String TEST_GEOCODE = "United States";
+ /** Empty geocode label */
+ private static final String EMPTY_GEOCODE = "";
/** The object under test. */
private PhoneCallDetailsHelper mHelper;
@@ -183,18 +185,18 @@
public void testSetPhoneCallDetails_NoGeocode() {
setPhoneCallDetailsWithNumberAndGeocode("+14125555555", "1-412-555-5555", null);
assertNameEquals("1-412-555-5555"); // The phone number is shown as the name.
- assertLabelEquals("-"); // The empty geocode is shown as the label.
+ assertLabelEquals(EMPTY_GEOCODE); // The empty geocode is shown as the label.
}
public void testSetPhoneCallDetails_EmptyGeocode() {
setPhoneCallDetailsWithNumberAndGeocode("+14125555555", "1-412-555-5555", "");
assertNameEquals("1-412-555-5555"); // The phone number is shown as the name.
- assertLabelEquals("-"); // The empty geocode is shown as the label.
+ assertLabelEquals(EMPTY_GEOCODE); // The empty geocode is shown as the label.
}
public void testSetPhoneCallDetails_NoGeocodeForVoicemail() {
setPhoneCallDetailsWithNumberAndGeocode(TEST_VOICEMAIL_NUMBER, "", "United States");
- assertLabelEquals("-"); // The empty geocode is shown as the label.
+ assertLabelEquals(EMPTY_GEOCODE); // The empty geocode is shown as the label.
}
public void testSetPhoneCallDetails_Highlighted() {
@@ -333,6 +335,6 @@
new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
TEST_FORMATTED_NUMBER, TEST_COUNTRY_ISO, TEST_GEOCODE,
new int[]{ Calls.INCOMING_TYPE }, TEST_DATE, TEST_DURATION,
- name, 0, "", null, null));
+ name, 0, "", null, null, 0));
}
}
diff --git a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
index 7ceec8f..49d32e5 100644
--- a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
@@ -70,6 +70,8 @@
/** The formatted version of {@link #TEST_NUMBER}. */
private static final String TEST_FORMATTED_NUMBER = "1 212-555-1000";
+ private static final String TEST_DEFAULT_CUSTOM_LABEL = "myLabel";
+
/** The activity in which we are hosting the fragment. */
private FragmentTestActivity mActivity;
private CallLogFragment mFragment;
@@ -115,7 +117,7 @@
FragmentManager fragmentManager = mActivity.getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(FragmentTestActivity.LAYOUT_ID, mFragment);
- transaction.commit();
+ transaction.commitAllowingStateLoss();
// Wait for the fragment to be loaded.
getInstrumentation().waitForIdleSync();
@@ -218,8 +220,10 @@
@MediumTest
public void testBindView_WithCachedName() {
mCursor.moveToFirst();
+ // provide a default custom label instead of an empty string, which corresponds to
+ // {@value com.android.dialer.calllog.ContactInfo#GEOCODE_AS_LABEL}
insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
- "John Doe", Phone.TYPE_HOME, "");
+ "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL);
View view = mAdapter.newStandAloneView(getActivity(), mParentView);
mAdapter.bindStandAloneView(view, getActivity(), mCursor);
@@ -232,20 +236,22 @@
public void testBindView_UriNumber() {
mCursor.moveToFirst();
insertWithCachedValues("sip:johndoe@gmail.com", NOW, 0, Calls.INCOMING_TYPE,
- "John Doe", Phone.TYPE_HOME, "");
+ "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL);
View view = mAdapter.newStandAloneView(getActivity(), mParentView);
mAdapter.bindStandAloneView(view, getActivity(), mCursor);
CallLogListItemViews views = (CallLogListItemViews) view.getTag();
assertNameIs(views, "John Doe");
- assertLabel(views, "sip:johndoe@gmail.com", null);
+ assertLabel(views, "sip:johndoe@gmail.com", "sip:johndoe@gmail.com");
}
@MediumTest
public void testBindView_HomeLabel() {
mCursor.moveToFirst();
+ // provide a default custom label instead of an empty string, which corresponds to
+ // {@value com.android.dialer.calllog.ContactInfo#GEOCODE_AS_LABEL}
insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
- "John Doe", Phone.TYPE_HOME, "");
+ "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL);
View view = mAdapter.newStandAloneView(getActivity(), mParentView);
mAdapter.bindStandAloneView(view, getActivity(), mCursor);
@@ -257,8 +263,10 @@
@MediumTest
public void testBindView_WorkLabel() {
mCursor.moveToFirst();
+ // provide a default custom label instead of an empty string, which corresponds to
+ // {@link com.android.dialer.calllog.ContactInfo#GEOCODE_AS_LABEL}
insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
- "John Doe", Phone.TYPE_WORK, "");
+ "John Doe", Phone.TYPE_WORK, TEST_DEFAULT_CUSTOM_LABEL);
View view = mAdapter.newStandAloneView(getActivity(), mParentView);
mAdapter.bindStandAloneView(view, getActivity(), mCursor);
@@ -312,7 +320,12 @@
mAdapter.bindStandAloneView(view, getActivity(), mCursor);
CallLogListItemViews views = (CallLogListItemViews) view.getTag();
- IntentProvider intentProvider = (IntentProvider) views.secondaryActionView.getTag();
+
+ // The primaryActionView tag is set in the
+ // {@link com.android.dialer.calllog.CallLogAdapter#bindView} method. If it is possible
+ // to place a call to the phone number, a call intent will have been created for the
+ // primaryActionView.
+ IntentProvider intentProvider = (IntentProvider) views.primaryActionView.getTag();
Intent intent = intentProvider.getIntent(mActivity);
// Starts a call.
assertEquals(Intent.ACTION_CALL_PRIVILEGED, intent.getAction());
@@ -328,7 +341,7 @@
mAdapter.bindStandAloneView(view, getActivity(), mCursor);
CallLogListItemViews views = (CallLogListItemViews) view.getTag();
- IntentProvider intentProvider = (IntentProvider) views.secondaryActionView.getTag();
+ IntentProvider intentProvider = (IntentProvider) views.secondaryActionButtonView.getTag();
Intent intent = intentProvider.getIntent(mActivity);
// Starts the call detail activity.
assertEquals(new ComponentName(mActivity, CallDetailActivity.class),
@@ -354,8 +367,9 @@
// HELPERS to check conditions on the DB/views
//
/**
- * Go over all the views in the list and check that the Call
- * icon's visibility matches the nature of the number.
+ * Go over the views in the list and check to ensure that
+ * callable numbers have an associated call intent, where numbers
+ * which are not callable have a null intent.
*/
private void checkCallStatus() {
for (int i = 0; i < mList.length; i++) {
@@ -366,9 +380,17 @@
int presentation = getPhoneNumberPresentationForListEntry(i);
if (presentation == Calls.PRESENTATION_RESTRICTED ||
presentation == Calls.PRESENTATION_UNKNOWN) {
- assertFalse(View.VISIBLE == mItem.secondaryActionView.getVisibility());
+ //If number is not callable, the primary action view should have a null tag.
+ assertNull(mItem.primaryActionView.getTag());
} else {
- assertEquals(View.VISIBLE, mItem.secondaryActionView.getVisibility());
+ //If the number is callable, the primary action view should have a non-null tag.
+ assertNotNull(mItem.primaryActionView.getTag());
+
+ IntentProvider intentProvider = (IntentProvider)mItem.primaryActionView.getTag();
+ Intent callIntent = intentProvider.getIntent(mActivity);
+
+ //The intent should be to make the call
+ assertEquals(Intent.ACTION_CALL_PRIVILEGED, callIntent.getAction());
}
}
}
@@ -607,7 +629,9 @@
privateOrUnknownOrVm[2] = true;
} else {
int inout = mRnd.nextBoolean() ? Calls.OUTGOING_TYPE : Calls.INCOMING_TYPE;
- String number = new Formatter().format("1800123%04d", i).toString();
+ final Formatter formatter = new Formatter();
+ String number = formatter.format("1800123%04d", i).toString();
+ formatter.close();
insert(number, Calls.PRESENTATION_ALLOWED, NOW, RAND_DURATION, inout);
}
}
diff --git a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
index bb940ed..7e4736e 100644
--- a/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogListItemHelperTest.java
@@ -24,6 +24,7 @@
import com.android.dialer.PhoneCallDetails;
import com.android.dialer.PhoneCallDetailsHelper;
+import com.android.dialer.R;
/**
* Unit tests for {@link CallLogListItemHelper}.
@@ -49,19 +50,24 @@
/** The views used in the tests. */
private CallLogListItemViews mViews;
- private PhoneNumberHelper mPhoneNumberHelper;
+ private PhoneNumberDisplayHelper mPhoneNumberHelper;
+ private PhoneNumberDisplayHelper mPhoneNumberDisplayHelper;
+
+ private Resources mResources;
@Override
protected void setUp() throws Exception {
super.setUp();
Context context = getContext();
- Resources resources = context.getResources();
- CallTypeHelper callTypeHelper = new CallTypeHelper(resources);
+ mResources = context.getResources();
+ CallTypeHelper callTypeHelper = new CallTypeHelper(mResources);
final TestPhoneNumberUtilsWrapper phoneUtils = new TestPhoneNumberUtilsWrapper(
TEST_VOICEMAIL_NUMBER);
PhoneCallDetailsHelper phoneCallDetailsHelper = new PhoneCallDetailsHelper(
- resources, callTypeHelper, phoneUtils);
- mHelper = new CallLogListItemHelper(phoneCallDetailsHelper, mPhoneNumberHelper, resources);
+ mResources, callTypeHelper, phoneUtils);
+ mPhoneNumberDisplayHelper = new PhoneNumberDisplayHelper(mResources);
+ mHelper = new CallLogListItemHelper(phoneCallDetailsHelper, mPhoneNumberDisplayHelper,
+ mResources);
mViews = CallLogListItemViews.createForTest(context);
}
@@ -75,92 +81,286 @@
public void testSetPhoneCallDetails() {
setPhoneCallDetailsWithNumber("12125551234", Calls.PRESENTATION_ALLOWED,
"1-212-555-1234");
- assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+ assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
}
public void testSetPhoneCallDetailsInFavorites() {
setPhoneCallDetailsWithNumberInFavorites("12125551234", Calls.PRESENTATION_ALLOWED,
"1-212-555-1234");
- assertNoCallButton();
+ assertNoCallIntent();
}
public void testSetPhoneCallDetails_Unknown() {
setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_UNKNOWN, "");
- assertNoCallButton();
+ assertNoCallIntent();
}
public void testSetPhoneCallDetailsInFavorites_Unknown() {
setPhoneCallDetailsWithNumberInFavorites("", Calls.PRESENTATION_UNKNOWN, "");
- assertNoCallButton();
+ assertNoCallIntent();
}
public void testSetPhoneCallDetails_Private() {
setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_RESTRICTED, "");
- assertNoCallButton();
+ assertNoCallIntent();
}
public void testSetPhoneCallDetailsInFavorites_Private() {
setPhoneCallDetailsWithNumberInFavorites("", Calls.PRESENTATION_RESTRICTED, "");
- assertNoCallButton();
+ assertNoCallIntent();
}
public void testSetPhoneCallDetails_Payphone() {
setPhoneCallDetailsWithNumber("", Calls.PRESENTATION_PAYPHONE, "");
- assertNoCallButton();
+ assertNoCallIntent();
}
public void testSetPhoneCallDetailsInFavorites_Payphone() {
setPhoneCallDetailsWithNumberInFavorites("", Calls.PRESENTATION_PAYPHONE, "");
- assertNoCallButton();
+ assertNoCallIntent();
}
public void testSetPhoneCallDetails_VoicemailNumber() {
setPhoneCallDetailsWithNumber(TEST_VOICEMAIL_NUMBER,
Calls.PRESENTATION_ALLOWED, TEST_VOICEMAIL_NUMBER);
- assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+ assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
}
public void testSetPhoneCallDetails_ReadVoicemail() {
setPhoneCallDetailsWithTypes(Calls.VOICEMAIL_TYPE);
- assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+ assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
}
public void testSetPhoneCallDetails_UnreadVoicemail() {
setUnreadPhoneCallDetailsWithTypes(Calls.VOICEMAIL_TYPE);
- assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+ assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
}
public void testSetPhoneCallDetails_VoicemailFromUnknown() {
setPhoneCallDetailsWithNumberAndType("", Calls.PRESENTATION_UNKNOWN,
"", Calls.VOICEMAIL_TYPE);
- assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+ assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
}
public void testSetPhoneCallDetailsInFavorites_VoicemailNumber() {
setPhoneCallDetailsWithNumberInFavorites(TEST_VOICEMAIL_NUMBER,
Calls.PRESENTATION_ALLOWED, TEST_VOICEMAIL_NUMBER);
- assertNoCallButton();
+ assertNoCallIntent();
}
public void testSetPhoneCallDetailsInFavorites_ReadVoicemail() {
setPhoneCallDetailsWithTypesInFavorites(Calls.VOICEMAIL_TYPE);
- assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+ assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
}
public void testSetPhoneCallDetailsInFavorites_UnreadVoicemail() {
setUnreadPhoneCallDetailsWithTypesInFavorites(Calls.VOICEMAIL_TYPE);
- assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+ assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
}
public void testSetPhoneCallDetailsInFavorites_VoicemailFromUnknown() {
setPhoneCallDetailsWithNumberAndTypeInFavorites("", Calls.PRESENTATION_UNKNOWN,
"", Calls.VOICEMAIL_TYPE);
- assertEquals(View.VISIBLE, mViews.secondaryActionView.getVisibility());
+ assertEquals(View.VISIBLE, mViews.secondaryActionButtonView.getVisibility());
}
- /** Asserts that the whole call area is gone. */
- private void assertNoCallButton() {
- assertEquals(View.GONE, mViews.secondaryActionView.getVisibility());
+ /**
+ * Test getCallDescriptionID method used to get the accessibility description for calls.
+ * Test case where an answered unknown call is received.
+ */
+ public void testGetCallDescriptionID_UnknownAnswered() {
+ PhoneCallDetails details = new PhoneCallDetails("", Calls.PRESENTATION_UNKNOWN, "",
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.INCOMING_TYPE}, TEST_DATE, TEST_DURATION);
+ assertEquals(R.string.description_unknown_answered_call,
+ mHelper.getCallDescriptionStringID(details));
+ }
+
+ /**
+ * Test getCallDescriptionID method used to get the accessibility description for calls.
+ * Test case where an missed unknown call is received.
+ */
+ public void testGetCallDescriptionID_UnknownMissed() {
+ PhoneCallDetails details = new PhoneCallDetails("", Calls.PRESENTATION_UNKNOWN, "",
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.MISSED_TYPE}, TEST_DATE, TEST_DURATION);
+ assertEquals(R.string.description_unknown_missed_call,
+ mHelper.getCallDescriptionStringID(details));
+ }
+
+ /**
+ * Test getCallDescriptionID method used to get the accessibility description for calls.
+ * Test case where an missed unknown call is received and a voicemail was left.
+ */
+ public void testGetCallDescriptionID_UnknownVoicemail() {
+ PhoneCallDetails details = new PhoneCallDetails("", Calls.PRESENTATION_UNKNOWN, "",
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.VOICEMAIL_TYPE}, TEST_DATE, TEST_DURATION);
+ assertEquals(R.string.description_unknown_missed_call,
+ mHelper.getCallDescriptionStringID(details));
+ }
+
+ /**
+ * Test getCallDescriptionID method used to get the accessibility description for calls.
+ * Test case where an answered call from a known caller is received.
+ */
+ public void testGetCallDescriptionID_KnownAnswered() {
+ PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+ TEST_FORMATTED_NUMBER,
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.INCOMING_TYPE}, TEST_DATE, TEST_DURATION);
+ assertEquals(R.string.description_return_answered_call,
+ mHelper.getCallDescriptionStringID(details));
+ }
+
+ /**
+ * Test getCallDescriptionID method used to get the accessibility description for calls.
+ * Test case where a missed call from a known caller is received.
+ */
+ public void testGetCallDescriptionID_KnownMissed() {
+ PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+ TEST_FORMATTED_NUMBER,
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.MISSED_TYPE}, TEST_DATE, TEST_DURATION);
+ assertEquals(R.string.description_return_missed_call,
+ mHelper.getCallDescriptionStringID(details));
+ }
+
+ /**
+ * Test getCallDescriptionID method used to get the accessibility description for calls.
+ * Test case where a missed call from a known caller is received and a voicemail was left.
+ */
+ public void testGetCallDescriptionID_KnownVoicemail() {
+ PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+ TEST_FORMATTED_NUMBER,
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.VOICEMAIL_TYPE}, TEST_DATE, TEST_DURATION);
+ assertEquals(R.string.description_return_missed_call,
+ mHelper.getCallDescriptionStringID(details));
+ }
+
+ /**
+ * Test getCallDescriptionID method used to get the accessibility description for calls.
+ * Test case where an outgoing call is made to a known number and there is a history of
+ * only a single call for this caller.
+ */
+ public void testGetCallDescriptionID_OutgoingSingle() {
+ PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+ TEST_FORMATTED_NUMBER,
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+ assertEquals(R.string.description_call_last,
+ mHelper.getCallDescriptionStringID(details));
+ }
+
+ /**
+ * Test getCallDescriptionID method used to get the accessibility description for calls.
+ * Test case where an outgoing call is made to a known number and there is a history of
+ * many calls for this caller.
+ */
+ public void testGetCallDescriptionID_OutgoingMultiple() {
+ PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+ TEST_FORMATTED_NUMBER,
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+ assertEquals(R.string.description_call_last_multiple,
+ mHelper.getCallDescriptionStringID(details));
+ }
+
+ /**
+ * Test getCallDescription method used to get the accessibility description for calls.
+ * For outgoing calls, we should NOT have "New Voicemail" in the description.
+ */
+ public void testGetCallDescription_NoVoicemailOutgoing() {
+ PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+ TEST_FORMATTED_NUMBER,
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.OUTGOING_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+ CharSequence description = mHelper.getCallDescription(details);
+ assertFalse(description.toString()
+ .contains(this.mResources.getString(R.string.description_new_voicemail)));
+ }
+
+ /**
+ * Test getCallDescription method used to get the accessibility description for calls.
+ * For regular incoming calls, we should NOT have "New Voicemail" in the description.
+ */
+ public void testGetCallDescription_NoVoicemailIncoming() {
+ PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+ TEST_FORMATTED_NUMBER,
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.INCOMING_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+ CharSequence description = mHelper.getCallDescription(details);
+ assertFalse(description.toString()
+ .contains(this.mResources.getString(R.string.description_new_voicemail)));
+ }
+
+ /**
+ * Test getCallDescription method used to get the accessibility description for calls.
+ * For regular missed calls, we should NOT have "New Voicemail" in the description.
+ */
+ public void testGetCallDescription_NoVoicemailMissed() {
+ PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+ TEST_FORMATTED_NUMBER,
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.MISSED_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+ CharSequence description = mHelper.getCallDescription(details);
+ assertFalse(description.toString()
+ .contains(this.mResources.getString(R.string.description_new_voicemail)));
+ }
+
+ /**
+ * Test getCallDescription method used to get the accessibility description for calls.
+ * For voicemail calls, we should have "New Voicemail" in the description.
+ */
+ public void testGetCallDescription_Voicemail() {
+ PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+ TEST_FORMATTED_NUMBER,
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.VOICEMAIL_TYPE, Calls.OUTGOING_TYPE}, TEST_DATE, TEST_DURATION);
+ CharSequence description = mHelper.getCallDescription(details);
+ assertTrue(description.toString()
+ .contains(this.mResources.getString(R.string.description_new_voicemail)));
+ }
+
+ /**
+ * Test getCallDescription method used to get the accessibility description for calls.
+ * Test that the "X calls" message is not present if there is only a single call.
+ */
+ public void testGetCallDescription_NumCallsSingle() {
+ PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+ TEST_FORMATTED_NUMBER,
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.VOICEMAIL_TYPE}, TEST_DATE, TEST_DURATION);
+ CharSequence description = mHelper.getCallDescription(details);
+
+ // Rather than hard coding the "X calls" string message, we'll generate it with an empty
+ // number of calls, and trim the resulting string. This gets us just the word "calls",
+ // and ensures any trivial changes to that string resource won't unnecessarily break
+ // the unit test.
+ assertFalse(description.toString()
+ .contains(this.mResources.getString(R.string.description_num_calls, "").trim()));
+ }
+
+ /**
+ * Test getCallDescription method used to get the accessibility description for calls.
+ * Test that the "X calls" message is present if there are many calls.
+ */
+ public void testGetCallDescription_NumCallsMultiple() {
+ PhoneCallDetails details = new PhoneCallDetails(TEST_NUMBER, Calls.PRESENTATION_ALLOWED,
+ TEST_FORMATTED_NUMBER,
+ TEST_COUNTRY_ISO, TEST_GEOCODE,
+ new int[]{Calls.VOICEMAIL_TYPE, Calls.INCOMING_TYPE}, TEST_DATE, TEST_DURATION);
+ CharSequence description = mHelper.getCallDescription(details);
+ assertTrue(description.toString()
+ .contains(this.mResources.getString(R.string.description_num_calls, 2)));
+ }
+
+ /** Asserts that the primary action view does not have a call intent. */
+ private void assertNoCallIntent() {
+ Object intentProvider = (IntentProvider)mViews.primaryActionView.getTag();
+ // The intent provider should be null as there is no ability to make a call.
+ assertNull(intentProvider);
}
/** Sets the details of a phone call using the specified phone number. */
diff --git a/tests/src/com/android/dialer/calllog/TestPhoneNumberUtilsWrapper.java b/tests/src/com/android/dialer/calllog/TestPhoneNumberUtilsWrapper.java
index 0dbd914..7266d88 100644
--- a/tests/src/com/android/dialer/calllog/TestPhoneNumberUtilsWrapper.java
+++ b/tests/src/com/android/dialer/calllog/TestPhoneNumberUtilsWrapper.java
@@ -16,10 +16,8 @@
package com.android.dialer.calllog;
-import android.content.res.Resources;
-
/**
- * Modified version of {@link com.android.dialer.calllog.PhoneNumberHelper} to be used in tests
+ * Modified version of {@link com.android.dialer.calllog.PhoneNumberDisplayHelper} to be used in tests
* that allows injecting the voicemail number.
*/
public final class TestPhoneNumberUtilsWrapper extends PhoneNumberUtilsWrapper {
diff --git a/tests/src/com/android/dialer/dialpad/DialpadFragmentTest.java b/tests/src/com/android/dialer/dialpad/DialpadFragmentTest.java
index a123e74..6f18fe6 100644
--- a/tests/src/com/android/dialer/dialpad/DialpadFragmentTest.java
+++ b/tests/src/com/android/dialer/dialpad/DialpadFragmentTest.java
@@ -34,7 +34,11 @@
public void testCanAddDigit_InvalidCharacter() {
// only handles wait/pause
- assertFalse(DialpadFragment.canAddDigit("123", 1, 1, '5'));
+ try {
+ DialpadFragment.canAddDigit("123", 1, 1, '5');
+ fail("Calling canAddDigit with invalid character should throw an exception");
+ } catch (IllegalArgumentException e) {
+ }
}
public void testCanAddDigit_BadOrNoSelection() {
diff --git a/tests/src/com/android/dialer/interactions/PhoneNumberInteractionTest.java b/tests/src/com/android/dialer/interactions/PhoneNumberInteractionTest.java
index f86675e..fbc64cd 100644
--- a/tests/src/com/android/dialer/interactions/PhoneNumberInteractionTest.java
+++ b/tests/src/com/android/dialer/interactions/PhoneNumberInteractionTest.java
@@ -91,7 +91,7 @@
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
expectQuery(contactUri)
.returnRow(1, "123", 0, null, null, Phone.TYPE_HOME, null,
- Phone.CONTENT_ITEM_TYPE);
+ Phone.CONTENT_ITEM_TYPE, 13);
TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
mContext, ContactDisplayUtils.INTERACTION_SMS, null);
@@ -110,7 +110,7 @@
Uri dataUri = ContentUris.withAppendedId(Data.CONTENT_URI, 1);
expectQuery(dataUri, true /* isDataUri */ )
.returnRow(1, "987", 0, null, null, Phone.TYPE_HOME, null,
- Phone.CONTENT_ITEM_TYPE);
+ Phone.CONTENT_ITEM_TYPE, 1);
TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
mContext, ContactDisplayUtils.INTERACTION_SMS, null);
@@ -128,10 +128,10 @@
public void testSendSmsWhenThereIsPrimaryNumber() {
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
expectQuery(contactUri)
- .returnRow(
- 1, "123", 0, null, null, Phone.TYPE_HOME, null, Phone.CONTENT_ITEM_TYPE)
- .returnRow(
- 2, "456", 1, null, null, Phone.TYPE_HOME, null, Phone.CONTENT_ITEM_TYPE);
+ .returnRow(1, "123", 0, null, null, Phone.TYPE_HOME, null,
+ Phone.CONTENT_ITEM_TYPE, 13)
+ .returnRow(2, "456", 1, null, null, Phone.TYPE_HOME, null,
+ Phone.CONTENT_ITEM_TYPE, 13);
TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
mContext, ContactDisplayUtils.INTERACTION_SMS, null);
@@ -170,9 +170,9 @@
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
expectQuery(contactUri)
.returnRow(1, "123", 0, null, null, Phone.TYPE_HOME, null,
- Phone.CONTENT_ITEM_TYPE)
+ Phone.CONTENT_ITEM_TYPE, 13)
.returnRow(2, "123", 0, null, null, Phone.TYPE_WORK, null,
- Phone.CONTENT_ITEM_TYPE);
+ Phone.CONTENT_ITEM_TYPE, 13);
TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
mContext, ContactDisplayUtils.INTERACTION_CALL, null);
@@ -191,7 +191,7 @@
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
expectQuery(contactUri)
.returnRow(1, "example@example.com", 0, null, null, Phone.TYPE_HOME, null,
- SipAddress.CONTENT_ITEM_TYPE);
+ SipAddress.CONTENT_ITEM_TYPE, 13);
TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
mContext, ContactDisplayUtils.INTERACTION_CALL, null);
@@ -209,9 +209,9 @@
Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 13);
expectQuery(contactUri)
.returnRow(1, "123", 0, "account", null, Phone.TYPE_HOME, "label",
- Phone.CONTENT_ITEM_TYPE)
+ Phone.CONTENT_ITEM_TYPE, 13)
.returnRow(2, "456", 0, null, null, Phone.TYPE_WORK, null,
- Phone.CONTENT_ITEM_TYPE);
+ Phone.CONTENT_ITEM_TYPE, 13);
TestPhoneNumberInteraction interaction = new TestPhoneNumberInteraction(
mContext, ContactDisplayUtils.INTERACTION_CALL, null);
@@ -252,7 +252,8 @@
RawContacts.DATA_SET,
Phone.TYPE,
Phone.LABEL,
- Phone.MIMETYPE)
+ Phone.MIMETYPE,
+ Phone.CONTACT_ID)
.withSelection("mimetype IN ('vnd.android.cursor.item/phone_v2',"
+ " 'vnd.android.cursor.item/sip_address') AND data1 NOT NULL");
}
diff --git a/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java b/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java
index 611b3f1..7a2076d 100644
--- a/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java
+++ b/tests/src/com/android/dialer/list/PhoneFavoritesTileAdapterTest.java
@@ -1,15 +1,26 @@
package com.android.dialer.list;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.provider.ContactsContract.PinnedPositions;
import android.test.AndroidTestCase;
+import com.android.contacts.common.ContactTileLoaderFactory;
+import com.android.contacts.common.list.ContactEntry;
+import com.android.dialer.list.PhoneFavoritesTileAdapter.OnDataSetChangedForAnimationListener;
+
+import java.util.ArrayList;
+
public class PhoneFavoritesTileAdapterTest extends AndroidTestCase {
private PhoneFavoritesTileAdapter mAdapter;
+ private static final OnDataSetChangedForAnimationListener
+ sOnDataSetChangedForAnimationListener = new OnDataSetChangedForAnimationListener() {
+ @Override
+ public void onDataSetChangedForAnimation(long... idsInPlace) {}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mAdapter = new PhoneFavoritesTileAdapter(getContext(), null, null, 3, 1);
- }
+ @Override
+ public void cacheOffsetsForDatasetChange() {}
+ };
/**
* TODO: Add tests
@@ -42,5 +53,124 @@
}
+ public void testGetRowIndex_NoRowLimit() {
+ mAdapter = getAdapterForTest(2, PhoneFavoritesTileAdapter.NO_ROW_LIMIT);
+ assertEquals(0, mAdapter.getRowCount(0));
+ assertEquals(1, mAdapter.getRowCount(1));
+ assertEquals(1, mAdapter.getRowCount(2));
+ assertEquals(2, mAdapter.getRowCount(4));
+ assertEquals(4, mAdapter.getRowCount(7));
+ assertEquals(100, mAdapter.getRowCount(199));
+ mAdapter = getAdapterForTest(5, PhoneFavoritesTileAdapter.NO_ROW_LIMIT);
+ assertEquals(0, mAdapter.getRowCount(0));
+ assertEquals(1, mAdapter.getRowCount(1));
+ assertEquals(1, mAdapter.getRowCount(3));
+ assertEquals(1, mAdapter.getRowCount(5));
+ assertEquals(2, mAdapter.getRowCount(7));
+ assertEquals(2, mAdapter.getRowCount(10));
+ assertEquals(40, mAdapter.getRowCount(199));
+ }
+
+ public void testGetItemId_NoRowLimit() {
+ mAdapter = getAdapterForTest(2, PhoneFavoritesTileAdapter.NO_ROW_LIMIT);
+ assertEquals(0, mAdapter.getItemId(0));
+ assertEquals(1, mAdapter.getItemId(1));
+ assertEquals(5, mAdapter.getItemId(5));
+ assertEquals(10, mAdapter.getItemId(10));
+ }
+
+ public void testGetAdjustedItemId_NoRowLimit() {
+ mAdapter = getAdapterForTest(2, PhoneFavoritesTileAdapter.NO_ROW_LIMIT);
+ assertEquals(0, mAdapter.getAdjustedItemId(0));
+ assertEquals(1, mAdapter.getAdjustedItemId(1));
+ assertEquals(5, mAdapter.getAdjustedItemId(5));
+ assertEquals(10, mAdapter.getAdjustedItemId(10));
+ }
+
+ public void testGetItem_NoRowLimit() {
+ mAdapter = getAdapterForTest(2, PhoneFavoritesTileAdapter.NO_ROW_LIMIT);
+ mAdapter.setContactCursor(getCursorForTest(5, 5));
+
+ final ArrayList<ContactEntry> row1 = new ArrayList<ContactEntry> ();
+ row1.add(getTestContactEntry(0, true));
+ row1.add(getTestContactEntry(1, true));
+ assertContactEntryRowsEqual(row1, mAdapter.getItem(0));
+
+ final ArrayList<ContactEntry> row3 = new ArrayList<ContactEntry> ();
+ row3.add(getTestContactEntry(4, true));
+ row3.add(getTestContactEntry(5, false));
+ assertContactEntryRowsEqual(row3, mAdapter.getItem(2));
+
+ final ArrayList<ContactEntry> row5 = new ArrayList<ContactEntry> ();
+ row5.add(getTestContactEntry(8, false));
+ row5.add(getTestContactEntry(9, false));
+ assertContactEntryRowsEqual(row5, mAdapter.getItem(4));
+ }
+
+ /**
+ * Ensures that PhoneFavoritesTileAdapter returns true for hasStableIds. This is needed for
+ * animation purposes.
+ */
+ public void testHasStableIds() {
+ mAdapter = new PhoneFavoritesTileAdapter(getContext(), null, null, 2, 2);
+ assertTrue(mAdapter.hasStableIds());
+ }
+
+ private PhoneFavoritesTileAdapter getAdapterForTest(int numCols, int numRows) {
+ return new PhoneFavoritesTileAdapter(getContext(), null,
+ sOnDataSetChangedForAnimationListener, numCols, numRows);
+ }
+
+ /**
+ * Returns a cursor containing starred and frequent contacts for test purposes.
+ *
+ * @param numStarred Number of starred contacts in the cursor. Cannot be a negative number.
+ * @param numFrequents Number of frequent contacts in the cursor. Cannot be a negative number.
+ * @return Cursor containing the required number of rows, each representing one ContactEntry
+ */
+ private Cursor getCursorForTest(int numStarred, int numFrequents) {
+ assertTrue(numStarred >= 0);
+ assertTrue(numFrequents >= 0);
+ final MatrixCursor c = new MatrixCursor(ContactTileLoaderFactory.COLUMNS_PHONE_ONLY);
+ int countId = 0;
+
+ // Add starred contact entries. These entries have the starred field set to 1 (true).
+ // The only field that really matters for testing is the contact id.
+ for (int i = 0; i < numStarred; i++) {
+ c.addRow(new Object[] {countId, null, 1, null, null, 0, 0, null, 0,
+ PinnedPositions.UNPINNED, countId});
+ countId++;
+ }
+
+ // Add frequent contact entries. These entries have the starred field set to 0 (false).
+ for (int i = 0; i < numFrequents; i++) {
+ c.addRow(new Object[] {countId, null, 0, null, null, 0, 0, null, 0,
+ PinnedPositions.UNPINNED, countId});
+ countId++;
+ }
+ return c;
+ }
+
+ /**
+ * Returns a ContactEntry with test data corresponding to the provided contact Id
+ *
+ * @param id Non-negative id
+ * @return ContactEntry item used for testing
+ */
+ private ContactEntry getTestContactEntry(int id, boolean isFavorite) {
+ ContactEntry contactEntry = new ContactEntry();
+ contactEntry.id = id;
+ contactEntry.isFavorite = isFavorite;
+ return contactEntry;
+ }
+
+ private void assertContactEntryRowsEqual(ArrayList<ContactEntry> expected,
+ ArrayList<ContactEntry> actual) {
+ assertEquals(expected.size(), actual.size());
+ for (int i = 0; i < actual.size(); i++) {
+ assertEquals(expected.get(i).id, actual.get(i).id);
+ assertEquals(expected.get(i).isFavorite, actual.get(i).isFavorite);
+ }
+ }
}