Merge "Fix extra row issue in ContactTileAdapter."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6856452..b669152 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -390,7 +390,7 @@
<!-- Used to show QuickContact window over a translucent activity, which is a
temporary hack until we add better framework support. -->
<activity
- android:name=".quickcontact.QuickContactWindow"
+ android:name=".quickcontact.QuickContactActivity"
android:theme="@style/Theme.QuickContact"
android:launchMode="singleTop"
android:excludeFromRecents="true"
@@ -532,16 +532,6 @@
android:name=".ContactSaveService"
android:exported="false" />
- <!-- Views the details of a single contact -->
- <activity android:name="ContactOptionsActivity"
- android:label="@string/contactOptionsTitle"
- >
- <intent-filter>
- <action android:name="android.intent.action.EDIT" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
-
<!-- Attaches a photo to a contact. Started from external applications -->
<activity android:name=".activities.AttachPhotoActivity"
android:label="@string/attach_photo_dialog_title"
diff --git a/res/anim/quickcontact_above_enter.xml b/res/anim/quickcontact_above_enter.xml
deleted file mode 100644
index d86c98c..0000000
--- a/res/anim/quickcontact_above_enter.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:interpolator="@android:interpolator/decelerate_quint"
- android:fromXScale="1" android:toXScale="1.0"
- android:fromYScale="0.8" android:toYScale="1.0"
- android:pivotX="50%" android:pivotY="100%"
- android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:interpolator="@android:interpolator/decelerate_cubic"
- android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_shortAnimTime" />
-</set>
diff --git a/res/anim/quickcontact_above_exit.xml b/res/anim/quickcontact_above_exit.xml
deleted file mode 100644
index e8a16b4..0000000
--- a/res/anim/quickcontact_above_exit.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:interpolator="@android:interpolator/accelerate_quint"
- android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="1.0" android:toYScale=".8"
- android:pivotX="50%" android:pivotY="100%"
- android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:interpolator="@android:interpolator/accelerate_cubic"
- android:fromAlpha="1.0" android:toAlpha="0"
- android:duration="@android:integer/config_shortAnimTime" />
-</set>
diff --git a/res/anim/quickcontact_below_enter.xml b/res/anim/quickcontact_below_enter.xml
deleted file mode 100644
index 39aa177..0000000
--- a/res/anim/quickcontact_below_enter.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:interpolator="@android:interpolator/decelerate_quint"
- android:fromXScale="1" android:toXScale="1.0"
- android:fromYScale="0.8" android:toYScale="1.0"
- android:pivotX="50%" android:pivotY="0%"
- android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:interpolator="@android:interpolator/decelerate_cubic"
- android:fromAlpha="0.0" android:toAlpha="1.0"
- android:duration="@android:integer/config_shortAnimTime" />
-</set>
diff --git a/res/anim/quickcontact_below_exit.xml b/res/anim/quickcontact_below_exit.xml
deleted file mode 100644
index 8f70751..0000000
--- a/res/anim/quickcontact_below_exit.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2009, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
- <scale android:interpolator="@android:interpolator/accelerate_quint"
- android:fromXScale="1.0" android:toXScale="1.0"
- android:fromYScale="1.0" android:toYScale="0.8"
- android:pivotX="50%" android:pivotY="0%"
- android:duration="@android:integer/config_shortAnimTime" />
- <alpha android:interpolator="@android:interpolator/accelerate_cubic"
- android:fromAlpha="1.0" android:toAlpha="0"
- android:duration="@android:integer/config_shortAnimTime" />
-</set>
diff --git a/res/drawable-hdpi/ic_fav_quickcontact_holo_dark.png b/res/drawable-hdpi/ic_fav_quickcontact_holo_dark.png
new file mode 100644
index 0000000..06dc777
--- /dev/null
+++ b/res/drawable-hdpi/ic_fav_quickcontact_holo_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_text_holo_dark.png b/res/drawable-hdpi/ic_text_holo_dark.png
new file mode 100644
index 0000000..1816201
--- /dev/null
+++ b/res/drawable-hdpi/ic_text_holo_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_arrowdown_left_holo_light.9.png b/res/drawable-hdpi/quickactions_arrowdown_left_holo_light.9.png
deleted file mode 100644
index b09016b..0000000
--- a/res/drawable-hdpi/quickactions_arrowdown_left_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_arrowdown_middle_holo_light.9.png b/res/drawable-hdpi/quickactions_arrowdown_middle_holo_light.9.png
deleted file mode 100644
index 670d89f..0000000
--- a/res/drawable-hdpi/quickactions_arrowdown_middle_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_arrowdown_right_holo_light.9.png b/res/drawable-hdpi/quickactions_arrowdown_right_holo_light.9.png
deleted file mode 100644
index 81f4859..0000000
--- a/res/drawable-hdpi/quickactions_arrowdown_right_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_arrowup_left_holo_light.9.png b/res/drawable-hdpi/quickactions_arrowup_left_holo_light.9.png
deleted file mode 100644
index 99ad9e2..0000000
--- a/res/drawable-hdpi/quickactions_arrowup_left_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_arrowup_middle_holo_light.9.png b/res/drawable-hdpi/quickactions_arrowup_middle_holo_light.9.png
deleted file mode 100644
index 500d820..0000000
--- a/res/drawable-hdpi/quickactions_arrowup_middle_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/quickactions_arrowup_right_holo_light.9.png b/res/drawable-hdpi/quickactions_arrowup_right_holo_light.9.png
deleted file mode 100644
index d99058b..0000000
--- a/res/drawable-hdpi/quickactions_arrowup_right_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/quickcon_background_texture.png b/res/drawable-hdpi/quickcon_background_texture.png
new file mode 100644
index 0000000..60bb6cc
--- /dev/null
+++ b/res/drawable-hdpi/quickcon_background_texture.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_fav_quickcontact_holo_dark.png b/res/drawable-mdpi/ic_fav_quickcontact_holo_dark.png
new file mode 100644
index 0000000..a089a67
--- /dev/null
+++ b/res/drawable-mdpi/ic_fav_quickcontact_holo_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_text_holo_dark.png b/res/drawable-mdpi/ic_text_holo_dark.png
new file mode 100644
index 0000000..bbaa84f
--- /dev/null
+++ b/res/drawable-mdpi/ic_text_holo_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_arrowdown_left_holo_light.9.png b/res/drawable-mdpi/quickactions_arrowdown_left_holo_light.9.png
deleted file mode 100644
index bd43850..0000000
--- a/res/drawable-mdpi/quickactions_arrowdown_left_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_arrowdown_middle_holo_light.9.png b/res/drawable-mdpi/quickactions_arrowdown_middle_holo_light.9.png
deleted file mode 100644
index c284dbb..0000000
--- a/res/drawable-mdpi/quickactions_arrowdown_middle_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_arrowdown_right_holo_light.9.png b/res/drawable-mdpi/quickactions_arrowdown_right_holo_light.9.png
deleted file mode 100644
index c057f71..0000000
--- a/res/drawable-mdpi/quickactions_arrowdown_right_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_arrowup_left_holo_light.9.png b/res/drawable-mdpi/quickactions_arrowup_left_holo_light.9.png
deleted file mode 100644
index 85d092f..0000000
--- a/res/drawable-mdpi/quickactions_arrowup_left_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_arrowup_middle_holo_light.9.png b/res/drawable-mdpi/quickactions_arrowup_middle_holo_light.9.png
deleted file mode 100644
index 828b718..0000000
--- a/res/drawable-mdpi/quickactions_arrowup_middle_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/quickactions_arrowup_right_holo_light.9.png b/res/drawable-mdpi/quickactions_arrowup_right_holo_light.9.png
deleted file mode 100644
index e4994b4..0000000
--- a/res/drawable-mdpi/quickactions_arrowup_right_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/quickcon_background_texture.png b/res/drawable-mdpi/quickcon_background_texture.png
new file mode 100644
index 0000000..86e1635
--- /dev/null
+++ b/res/drawable-mdpi/quickcon_background_texture.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_fav_quickcontact_holo_dark.png b/res/drawable-xhdpi/ic_fav_quickcontact_holo_dark.png
new file mode 100644
index 0000000..0384abc
--- /dev/null
+++ b/res/drawable-xhdpi/ic_fav_quickcontact_holo_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_text_holo_dark.png b/res/drawable-xhdpi/ic_text_holo_dark.png
new file mode 100644
index 0000000..8c697d9
--- /dev/null
+++ b/res/drawable-xhdpi/ic_text_holo_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/quickactions_arrowdown_left_holo_light.9.png b/res/drawable-xhdpi/quickactions_arrowdown_left_holo_light.9.png
deleted file mode 100644
index 12e2988..0000000
--- a/res/drawable-xhdpi/quickactions_arrowdown_left_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/quickactions_arrowdown_middle_holo_light.9.png b/res/drawable-xhdpi/quickactions_arrowdown_middle_holo_light.9.png
deleted file mode 100644
index bff78a4..0000000
--- a/res/drawable-xhdpi/quickactions_arrowdown_middle_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/quickactions_arrowdown_right_holo_light.9.png b/res/drawable-xhdpi/quickactions_arrowdown_right_holo_light.9.png
deleted file mode 100644
index 1ef7191..0000000
--- a/res/drawable-xhdpi/quickactions_arrowdown_right_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/quickactions_arrowup_left_holo_light.9.png b/res/drawable-xhdpi/quickactions_arrowup_left_holo_light.9.png
deleted file mode 100644
index cf280d9..0000000
--- a/res/drawable-xhdpi/quickactions_arrowup_left_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/quickactions_arrowup_middle_holo_light.9.png b/res/drawable-xhdpi/quickactions_arrowup_middle_holo_light.9.png
deleted file mode 100644
index b5d9a0d..0000000
--- a/res/drawable-xhdpi/quickactions_arrowup_middle_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/quickactions_arrowup_right_holo_light.9.png b/res/drawable-xhdpi/quickactions_arrowup_right_holo_light.9.png
deleted file mode 100644
index 6c6a99c..0000000
--- a/res/drawable-xhdpi/quickactions_arrowup_right_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/quickcon_background_texture.png b/res/drawable-xhdpi/quickcon_background_texture.png
new file mode 100644
index 0000000..9134f93
--- /dev/null
+++ b/res/drawable-xhdpi/quickcon_background_texture.png
Binary files differ
diff --git a/res/drawable/quickactions_arrow_left_holo_light.xml b/res/drawable/quickactions_arrow_left_holo_light.xml
deleted file mode 100644
index c1e18bd..0000000
--- a/res/drawable/quickactions_arrow_left_holo_light.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_first="true" android:drawable="@drawable/quickactions_arrowdown_left_holo_light" />
- <item android:state_last="true" android:drawable="@drawable/quickactions_arrowup_left_holo_light" />
- <!-- TODO: provide callout-less state -->
- <item android:drawable="@drawable/quickactions_arrowup_left_holo_light" />
-</selector>
diff --git a/res/drawable/quickactions_arrow_middle_holo_light.xml b/res/drawable/quickactions_arrow_middle_holo_light.xml
deleted file mode 100644
index f88b513..0000000
--- a/res/drawable/quickactions_arrow_middle_holo_light.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_first="true" android:drawable="@drawable/quickactions_arrowdown_middle_holo_light" />
- <item android:state_last="true" android:drawable="@drawable/quickactions_arrowup_middle_holo_light" />
- <!-- TODO: provide callout-less state -->
- <item android:drawable="@drawable/quickactions_arrowup_middle_holo_light" />
-</selector>
diff --git a/res/drawable/quickactions_arrow_right_holo_light.xml b/res/drawable/quickactions_arrow_right_holo_light.xml
deleted file mode 100644
index 3e309fe..0000000
--- a/res/drawable/quickactions_arrow_right_holo_light.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_first="true" android:drawable="@drawable/quickactions_arrowdown_right_holo_light" />
- <item android:state_last="true" android:drawable="@drawable/quickactions_arrowup_right_holo_light" />
- <!-- TODO: provide callout-less state -->
- <item android:drawable="@drawable/quickactions_arrowup_right_holo_light" />
-</selector>
diff --git a/res/drawable/quickcontact_list_item_background.xml b/res/drawable/quickcontact_list_item_background.xml
new file mode 100644
index 0000000..e6d773e
--- /dev/null
+++ b/res/drawable/quickcontact_list_item_background.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- TODO These all have to be refined -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_window_focused="false"
+ android:drawable="@drawable/list_background_holo" />
+ <item
+ android:state_focused="true"
+ android:state_pressed="true"
+ android:drawable="@drawable/list_pressed_holo" />
+ <item
+ android:state_focused="false"
+ android:state_pressed="true"
+ android:drawable="@drawable/list_pressed_holo" />
+ <item
+ android:state_focused="true"
+ android:drawable="@drawable/list_focused_holo" />
+ <item
+ android:drawable="@color/people_app_theme_color" />
+</selector>
diff --git a/res/drawable/quickcontact_slider_btn.xml b/res/drawable/quickcontact_slider_btn.xml
deleted file mode 100644
index b3d0ec7..0000000
--- a/res/drawable/quickcontact_slider_btn.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android"
- android:dither="true">
- <!-- TODO Still a hack. This needs a default from the framework instead for the checked state -->
- <item android:state_checked="true"
- android:drawable="@drawable/quickactions_icon_activated" />
- <item android:state_window_focused="false"
- android:drawable="@android:color/transparent" />
- <item android:state_pressed="true"
- android:drawable="@drawable/quickactions_icon_activated" />
- <item android:state_focused="true"
- android:drawable="@drawable/quickactions_icon_activated" />
- <item
- android:drawable="@android:color/transparent" />
-</selector>
diff --git a/res/layout/quickcontact_header_small.xml b/res/drawable/quickcontact_track_background.xml
similarity index 67%
rename from res/layout/quickcontact_header_small.xml
rename to res/drawable/quickcontact_track_background.xml
index f3a46d5..fcf61fd 100644
--- a/res/layout/quickcontact_header_small.xml
+++ b/res/drawable/quickcontact_track_background.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- Copyright (C) 2011 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,9 +14,6 @@
limitations under the License.
-->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/header_small"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" />
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/quickcon_background_texture"
+ android:tileMode="repeat" />
diff --git a/res/layout-finger/quickcontact_item_nodata.xml b/res/layout-finger/quickcontact_item_nodata.xml
deleted file mode 100644
index 5f951e2..0000000
--- a/res/layout-finger/quickcontact_item_nodata.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingLeft="12dip"
- android:paddingRight="12dip"
- android:scaleType="centerInside"
- android:focusable="false"
- android:clickable="false"
- android:gravity="center_vertical"
- android:textColor="@android:color/black"
- android:text="@string/quickcontact_no_data" />
diff --git a/res/layout-sw580dp-w1000dp/contact_detail_fragment.xml b/res/layout-sw580dp-w1000dp/contact_detail_fragment.xml
index 9dd3690..f4c95f8 100644
--- a/res/layout-sw580dp-w1000dp/contact_detail_fragment.xml
+++ b/res/layout-sw580dp-w1000dp/contact_detail_fragment.xml
@@ -32,8 +32,9 @@
<!-- Real list -->
<LinearLayout
android:orientation="horizontal"
+ android:layout_weight="1"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="0dip">
<ImageView android:id="@+id/photo"
android:scaleType="centerCrop"
diff --git a/res/layout-sw580dp-w1000dp/contact_detail_list_item.xml b/res/layout-sw580dp-w1000dp/contact_detail_list_item.xml
index 2de488d..22f0412 100644
--- a/res/layout-sw580dp-w1000dp/contact_detail_list_item.xml
+++ b/res/layout-sw580dp-w1000dp/contact_detail_list_item.xml
@@ -47,9 +47,24 @@
android:layout_height="wrap_content"
android:visibility="gone" />
- <TextView
- android:id="@+id/type"
- style="@style/ContactDetailItemType" />
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/type"
+ style="@style/ContactDetailItemType" />
+
+ <View
+ android:id="@+id/primary_indicator"
+ android:layout_width="16dip"
+ android:layout_height="16dip"
+ android:visibility="gone"
+ android:layout_gravity="center_vertical"
+ android:background="@drawable/ic_menu_mark" />
+
+ </LinearLayout>
</FrameLayout>
diff --git a/res/layout-sw580dp/external_group_editor_view.xml b/res/layout-sw580dp/external_group_editor_view.xml
new file mode 100644
index 0000000..39e0c56
--- /dev/null
+++ b/res/layout-sw580dp/external_group_editor_view.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="50dip"
+ android:paddingLeft="50dip"
+ android:paddingRight="100dip"
+ android:orientation="horizontal"
+ android:background="@color/background_primary">
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="3"
+ android:layout_marginRight="30dip"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/read_only_warning"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="10dip"
+ android:paddingLeft="10dip"
+ android:paddingRight="10dip"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorTertiary"
+ android:text="@string/group_read_only" />
+
+ <include
+ android:id="@+id/account_header"
+ layout="@layout/editor_account_header"/>
+
+ <TextView
+ android:id="@+id/group_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textStyle="bold"
+ android:padding="10dip"/>
+
+ </LinearLayout>
+
+ <include
+ layout="@layout/group_editor_existing_member_list"
+ android:id="@android:id/list"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="2"/>
+
+</LinearLayout>
diff --git a/res/layout-sw580dp/group_editor_fragment.xml b/res/layout-sw580dp/group_editor_view.xml
similarity index 64%
rename from res/layout-sw580dp/group_editor_fragment.xml
rename to res/layout-sw580dp/group_editor_view.xml
index bc41710..7874c8c 100644
--- a/res/layout-sw580dp/group_editor_fragment.xml
+++ b/res/layout-sw580dp/group_editor_view.xml
@@ -25,10 +25,33 @@
android:background="@color/background_primary">
<LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="3"
+ android:layout_marginRight="30dip"
+ android:orientation="vertical">
+
+ <include
+ android:id="@+id/account_header"
+ layout="@layout/editor_account_header"/>
+
+ <EditText
+ android:id="@+id/group_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:inputType="textCapWords"
+ android:hint="@string/group_name_hint"
+ android:layout_marginBottom="5dip"/>
+
+ </LinearLayout>
+
+ <LinearLayout
android:id="@+id/group_members"
android:layout_width="0dip"
android:layout_height="match_parent"
- android:layout_weight="1"
+ android:layout_weight="2"
android:orientation="vertical">
<include
@@ -41,8 +64,4 @@
</LinearLayout>
- <include
- layout="@layout/group_editor_header"
- android:id="@+id/header"/>
-
</LinearLayout>
diff --git a/res/layout-sw580dp/quickcontact_activity.xml b/res/layout-sw580dp/quickcontact_activity.xml
new file mode 100644
index 0000000..6b381a9
--- /dev/null
+++ b/res/layout-sw580dp/quickcontact_activity.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<view
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.contacts.quickcontact.FloatingChildLayout"
+ android:id="@+id/floating_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:descendantFocusability="afterDescendants">
+ <LinearLayout
+ android:id="@android:id/content"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <include layout="@layout/quickcontact_photo_container" />
+ <View
+ android:id="@+id/line_before_track"
+ android:layout_width="match_parent"
+ android:layout_height="2dip"
+ android:background="@color/quickcontact_list_background" />
+ <include layout="@layout/quickcontact_track" />
+ <android.support.v4.view.ViewPager
+ android:id="@+id/item_list_pager"
+ android:layout_width="match_parent"
+ android:layout_height="180dip" />
+ </LinearLayout>
+</view>
diff --git a/res/layout-w470dp/contact_detail_fragment.xml b/res/layout-w470dp/contact_detail_fragment.xml
index 42266d5..17cbc2d 100644
--- a/res/layout-w470dp/contact_detail_fragment.xml
+++ b/res/layout-w470dp/contact_detail_fragment.xml
@@ -14,15 +14,17 @@
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contact_detail"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="0px"
+ android:layout_weight="1" >
<ImageView android:id="@+id/photo"
android:scaleType="centerCrop"
@@ -87,5 +89,5 @@
android:layout_alignParentTop="true"
android:background="@android:color/transparent"
android:visibility="gone"/>
-</RelativeLayout>
+</LinearLayout>
diff --git a/res/layout-w470dp/quickcontact_activity.xml b/res/layout-w470dp/quickcontact_activity.xml
new file mode 100644
index 0000000..df68761
--- /dev/null
+++ b/res/layout-w470dp/quickcontact_activity.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<view
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.contacts.quickcontact.FloatingChildLayout"
+ android:id="@+id/floating_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:descendantFocusability="afterDescendants">
+ <LinearLayout
+ android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/quick_contact_picture_height"
+ android:padding="32dip"
+ android:orientation="horizontal">
+ <include layout="@layout/quickcontact_photo_container" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/quick_contact_picture_height"
+ android:orientation="vertical">
+ <include layout="@layout/quickcontact_track" />
+ <android.support.v4.view.ViewPager
+ android:id="@+id/item_list_pager"
+ android:background="@color/quickcontact_list_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </LinearLayout>
+ </LinearLayout>
+</view>
diff --git a/res/layout/call_log_fragment.xml b/res/layout/call_log_fragment.xml
index 2d0b9b5..ff7dd4b 100644
--- a/res/layout/call_log_fragment.xml
+++ b/res/layout/call_log_fragment.xml
@@ -18,7 +18,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
->
+ android:paddingBottom="?android:attr/actionBarSize">
+
<FrameLayout
android:id="@+id/voicemail_status"
android:layout_width="match_parent"
diff --git a/res/layout/carousel_about_tab.xml b/res/layout/carousel_about_tab.xml
index dfcf0da..e902c8e 100644
--- a/res/layout/carousel_about_tab.xml
+++ b/res/layout/carousel_about_tab.xml
@@ -14,8 +14,9 @@
limitations under the License.
-->
-<RelativeLayout
+<view
xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.contacts.detail.CarouselTab"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
@@ -51,4 +52,22 @@
android:textColor="@color/detail_tab_carousel_tab_label_color"
style="@android:style/Widget.Holo.ActionBar.TabView" />
-</RelativeLayout>
+ <View
+ android:id="@+id/alpha_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:background="@android:color/black"
+ android:alpha="0"
+ android:visibility="gone"/>
+
+ <View
+ android:id="@+id/touch_intercept_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone"/>
+</view>
diff --git a/res/layout/carousel_updates_tab.xml b/res/layout/carousel_updates_tab.xml
index b036523..689e17b 100644
--- a/res/layout/carousel_updates_tab.xml
+++ b/res/layout/carousel_updates_tab.xml
@@ -14,8 +14,13 @@
limitations under the License.
-->
-<RelativeLayout
+<!--
+ TODO: Collapse carousel_about_tab with carousel_updates_tab into 1 XML that
+ handles all cases when updates fragment is more finalized.
+-->
+<view
xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.contacts.detail.CarouselTab"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
@@ -68,4 +73,23 @@
android:textStyle="bold"
android:maxLines="3"/>
-</RelativeLayout>
+ <View
+ android:id="@+id/alpha_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:background="@android:color/black"
+ android:alpha="0"
+ android:visibility="gone"/>
+
+ <View
+ android:id="@+id/touch_intercept_overlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone"/>
+
+</view>
diff --git a/res/layout/contact_detail_list_item.xml b/res/layout/contact_detail_list_item.xml
index 4fcd881..e96b7f0 100644
--- a/res/layout/contact_detail_list_item.xml
+++ b/res/layout/contact_detail_list_item.xml
@@ -45,9 +45,24 @@
android:layout_gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceMedium" />
- <TextView
- android:id="@+id/type"
- style="@style/ContactDetailItemType" />
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/type"
+ style="@style/ContactDetailItemType" />
+
+ <View
+ android:id="@+id/primary_indicator"
+ android:layout_width="16dip"
+ android:layout_height="16dip"
+ android:visibility="gone"
+ android:layout_gravity="center_vertical"
+ android:background="@drawable/ic_menu_mark" />
+
+ </LinearLayout>
<TextView
android:id="@+id/footer"
diff --git a/res/layout/contact_options.xml b/res/layout/contact_options.xml
deleted file mode 100644
index 5bd8836..0000000
--- a/res/layout/contact_options.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
->
-
- <include layout="@layout/preference_with_more_button" android:id="@+id/ringtone" />
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider"
- />
- <include layout="@layout/edit_contact_entry_voicemail" android:id="@+id/voicemail"/>
-
-</LinearLayout>
diff --git a/res/layout/contact_tile_frequent_phone.xml b/res/layout/contact_tile_frequent_phone.xml
new file mode 100644
index 0000000..e0ea0fb
--- /dev/null
+++ b/res/layout/contact_tile_frequent_phone.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<view
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ class="com.android.contacts.list.ContactTileView"
+ android:focusable="true"
+ android:background="@drawable/list_selector"
+ android:paddingRight="16dip"
+ android:paddingLeft="16dip" >
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <QuickContactBadge
+ android:id="@+id/contact_tile_quick"
+ android:layout_width="64dip"
+ android:layout_height="64dip"
+ android:scaleType="centerCrop"
+ android:layout_alignParentLeft="true" />
+
+ <TextView
+ android:id="@+id/contact_tile_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_marginLeft="8dip"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginTop="8dip"
+ android:layout_toRightOf="@id/contact_tile_quick" />
+
+ <TextView
+ android:id="@+id/contact_tile_phone_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="12sp"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="#cccccc"
+ android:layout_marginLeft="12dip"
+ android:layout_toRightOf="@id/contact_tile_quick"
+ android:layout_below="@id/contact_tile_name" />
+
+ <TextView
+ android:id="@+id/contact_tile_phone_type"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="12sp"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="#cccccc"
+ android:layout_marginLeft="12dip"
+ android:layout_alignParentRight="true"
+ android:layout_alignTop="@id/contact_tile_phone_number" />
+
+ </RelativeLayout>
+
+</view>
diff --git a/res/layout/contact_tile_list.xml b/res/layout/contact_tile_list.xml
index 69ae1cc..2047b13 100644
--- a/res/layout/contact_tile_list.xml
+++ b/res/layout/contact_tile_list.xml
@@ -17,7 +17,8 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:paddingBottom="?attr/favorites_padding_bottom">
<ListView
android:id="@+id/contact_tile_list"
@@ -30,6 +31,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
- android:textAppearance="?android:attr/textAppearanceLarge"/>
+ android:layout_marginTop="@dimen/empty_message_top_margin"
+ android:textAppearance="?android:attr/textAppearanceMedium"/>
</FrameLayout>
diff --git a/res/layout/contact_tile_starred_secondary_target.xml b/res/layout/contact_tile_starred_secondary_target.xml
index 5d4e55e..f7b8673 100644
--- a/res/layout/contact_tile_starred_secondary_target.xml
+++ b/res/layout/contact_tile_starred_secondary_target.xml
@@ -35,29 +35,24 @@
android:layout_alignParentBottom="true"
style="@style/ContactTileStarredShadowBox" />
- <LinearLayout
+ <TextView
+ android:id="@+id/contact_tile_name"
android:layout_width="match_parent"
- android:layout_height="48dip"
+ android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
- android:orientation="horizontal" >
+ android:layout_alignParentLeft="true"
+ android:layout_marginLeft="8dip"
+ android:layout_marginBottom="20dip"
+ android:textColor="@android:color/white"
+ stlye="@style/ContactTileStarredName" />
- <TextView
- android:id="@+id/contact_tile_name"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_vertical"
- android:layout_marginLeft="8dip"
- android:textColor="@android:color/white"
- stlye="@style/ContactTileStarredName" />
-
- <ImageButton
- android:id="@+id/contact_tile_secondary_button"
- android:src="@drawable/ic_tab_unselected_contacts"
- android:layout_height="match_parent"
- android:layout_width="wrap_content" />
-
- </LinearLayout>
+ <ImageButton
+ android:id="@+id/contact_tile_secondary_button"
+ android:src="@drawable/ic_tab_unselected_contacts"
+ android:layout_height="@dimen/contact_tile_shadowbox_height"
+ android:layout_width="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true" />
</RelativeLayout>
diff --git a/res/layout/contacts_unavailable_fragment.xml b/res/layout/contacts_unavailable_fragment.xml
index 44c51b5..5566589 100644
--- a/res/layout/contacts_unavailable_fragment.xml
+++ b/res/layout/contacts_unavailable_fragment.xml
@@ -35,7 +35,7 @@
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_marginBottom="20dip" />
<Button
diff --git a/res/layout/dialtacts_activity.xml b/res/layout/dialtacts_activity.xml
index 14fb137..6484d87 100644
--- a/res/layout/dialtacts_activity.xml
+++ b/res/layout/dialtacts_activity.xml
@@ -16,7 +16,8 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:layout_marginTop="?android:attr/actionBarSize">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
diff --git a/res/layout/contact_editor_custom_action_bar.xml b/res/layout/editor_custom_action_bar.xml
similarity index 97%
rename from res/layout/contact_editor_custom_action_bar.xml
rename to res/layout/editor_custom_action_bar.xml
index ba09929..0f42213 100644
--- a/res/layout/contact_editor_custom_action_bar.xml
+++ b/res/layout/editor_custom_action_bar.xml
@@ -49,6 +49,7 @@
android:layout_gravity="center_vertical"
android:layout_marginRight="20dip"
android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="@android:color/white"
android:text="@string/menu_done"
style="@android:style/Widget.Holo.ActionBar.TabText" />
diff --git a/res/layout/group_browse_list_fragment.xml b/res/layout/group_browse_list_fragment.xml
index 66059fe..9e6bd27 100644
--- a/res/layout/group_browse_list_fragment.xml
+++ b/res/layout/group_browse_list_fragment.xml
@@ -31,12 +31,40 @@
android:cacheColorHint="@android:color/transparent"
android:divider="@null" />
- <TextView
- android:id="@+id/empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:text="@string/noGroups"
- android:visibility="gone"/>
+ <TextView
+ android:id="@+id/empty"
+ android:layout_marginTop="@dimen/empty_message_top_margin"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/noGroups" />
+ <LinearLayout
+ android:id="@+id/add_accounts"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:layout_marginTop="@dimen/no_accounts_message_margin"
+ android:layout_marginBottom="@dimen/no_accounts_message_margin"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="@string/noAccounts" />
+
+ <Button
+ android:id="@+id/add_account_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/add_account_button_left_margin"
+ android:layout_marginRight="@dimen/add_account_button_right_margin"
+ android:gravity="center"
+ android:layout_gravity="center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/contacts_unavailable_add_account" />
+
+ </LinearLayout>
</LinearLayout>
diff --git a/res/layout/group_editor_header.xml b/res/layout/group_editor_header.xml
deleted file mode 100644
index 9b8bf88..0000000
--- a/res/layout/group_editor_header.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!--
- Header for group editor that contains the account name, type, icon, as well
- as the group name field.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginLeft="80dip"
- android:orientation="vertical">
-
- <include
- layout="@layout/group_editor_account_header"
- android:id="@+id/account_header"/>
-
- <include
- layout="@layout/edit_divider"
- android:id="@+id/divider"/>
-
- <EditText
- android:id="@+id/group_name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="@string/group_name_hint"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:inputType="textCapWords"/>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/quickcontact.xml b/res/layout/quickcontact.xml
deleted file mode 100644
index e2b291c..0000000
--- a/res/layout/quickcontact.xml
+++ /dev/null
@@ -1,136 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/content"
- android:layout_width="@dimen/quick_contact_width"
- android:layout_height="wrap_content"
- android:visibility="invisible"
- android:orientation="vertical">
-
- <FrameLayout
- android:id="@+id/header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dip">
-
- <ViewStub
- android:id="@+id/header_small"
- android:inflatedId="@+id/header_small"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout="@layout/quickcontact_header_small" />
-
- <ViewStub
- android:id="@+id/header_medium"
- android:inflatedId="@+id/header_medium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout="@layout/quickcontact_header_med" />
-
- <ViewStub
- android:id="@+id/header_large"
- android:inflatedId="@+id/header_large"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout="@layout/quickcontact_header_large" />
-
- </FrameLayout>
-
- <HorizontalScrollView
- android:id="@+id/scroll"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="5dip"
- android:layout_marginLeft="15dip"
- android:layout_marginRight="15dip"
- android:layout_marginBottom="10dip"
- android:fadingEdgeLength="0dip"
- android:scrollbars="none">
-
- <LinearLayout
- android:id="@+id/quickcontact"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal" />
- </HorizontalScrollView>
-
- <FrameLayout
- android:id="@+id/footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:visibility="gone">
- <LinearLayout
- android:id="@+id/footer_disambig"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone">
-
- <ListView
- android:id="@android:id/list"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:layout_marginLeft="5dip"
- android:layout_marginRight="5dip"
- android:cacheColorHint="@null" />
-
- <CheckBox
- android:id="@android:id/checkbox"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="19dip"
- android:layout_marginRight="19dip"
- android:minHeight="60dip"
- android:textColor="#f000"
- android:textStyle="bold"
- android:text="@string/quickcontact_remember_choice"
- android:textAppearance="?android:attr/textAppearanceSmallInverse" />
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/footer_clear_defaults"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="30dip"
- android:layout_marginRight="5dip"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:text="@string/quickcontact_clear_defaults_caption" />
- <ListView
- android:id="@+id/defaults_list"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:layout_marginLeft="5dip"
- android:layout_marginRight="5dip"
- android:cacheColorHint="@null" />
- <Button
- android:id="@+id/clear_defaults_button"
- android:layout_marginLeft="20dip"
- android:layout_marginBottom="20dip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/quickcontact_clear_defaults_button" />
- </LinearLayout>
- </FrameLayout>
-</LinearLayout>
diff --git a/res/layout/quickcontact_activity.xml b/res/layout/quickcontact_activity.xml
index aced4a8..7aa2aa4 100644
--- a/res/layout/quickcontact_activity.xml
+++ b/res/layout/quickcontact_activity.xml
@@ -22,7 +22,23 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:descendantFocusability="afterDescendants">
-
- <include layout="@layout/quickcontact" />
-
+ <LinearLayout
+ android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="15dip"
+ android:paddingRight="15dip"
+ android:orientation="vertical">
+ <include layout="@layout/quickcontact_photo_container" />
+ <View
+ android:id="@+id/line_before_track"
+ android:layout_width="match_parent"
+ android:layout_height="2dip"
+ android:background="@color/quickcontact_list_background" />
+ <include layout="@layout/quickcontact_track" />
+ <android.support.v4.view.ViewPager
+ android:id="@+id/item_list_pager"
+ android:layout_width="match_parent"
+ android:layout_height="180dip" />
+ </LinearLayout>
</view>
diff --git a/res/layout/quickcontact_default_item.xml b/res/layout/quickcontact_default_item.xml
deleted file mode 100755
index 3a918f0..0000000
--- a/res/layout/quickcontact_default_item.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="25dip"
- android:paddingRight="25dip"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical">
-
- <TextView
- android:id="@android:id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textStyle="bold"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <TextView
- android:id="@android:id/text2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="-4dip"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
-</LinearLayout>
diff --git a/res/layout/quickcontact_header_large.xml b/res/layout/quickcontact_header_large.xml
deleted file mode 100644
index b8a19cf..0000000
--- a/res/layout/quickcontact_header_large.xml
+++ /dev/null
@@ -1,79 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/header_large"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="87dip"
- android:gravity="center_vertical"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/photo"
- android:layout_width="64dip"
- android:layout_height="64dip"
- android:layout_marginLeft="15dip" />
-
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginLeft="15dip"
- android:paddingRight="8dip"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/name"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@*android:color/primary_text_light"
- android:textStyle="bold"
- android:textSize="18dip" />
-
- <TextView
- android:id="@+id/status"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@*android:color/secondary_text_light"
- android:textSize="15dip"
- android:layout_marginTop="-3dip" />
-
- <TextView
- android:id="@+id/timestamp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@*android:color/secondary_text_light"
- android:textSize="12dip"
- android:layout_marginTop="-2dip" />
-
- </LinearLayout>
-
- <ImageView
- android:id="@+id/presence"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="15dip"
- android:scaleType="centerInside" />
-
-</LinearLayout>
diff --git a/res/layout/quickcontact_header_med.xml b/res/layout/quickcontact_header_med.xml
deleted file mode 100644
index 77cb1a5..0000000
--- a/res/layout/quickcontact_header_med.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/header_medium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="51dip"
- android:gravity="center_vertical"
- android:orientation="horizontal">
-
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginLeft="15dip"
- android:layout_marginRight="15dip"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/status"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@*android:color/primary_text_light"
- android:textSize="15sp" />
-
- <TextView
- android:id="@+id/timestamp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textColor="@*android:color/secondary_text_light"
- android:textSize="12sp"
- android:layout_marginTop="-2dip" />
-
- </LinearLayout>
-
-</LinearLayout>
diff --git a/res/layout/quickcontact_list_fragment.xml b/res/layout/quickcontact_list_fragment.xml
new file mode 100755
index 0000000..8c62c4c
--- /dev/null
+++ b/res/layout/quickcontact_list_fragment.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <!-- Line that looks like a list divider -->
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="@color/quickcontact_list_divider" />
+ <ListView
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:divider="@color/quickcontact_list_divider"
+ android:dividerHeight="1dip"
+ android:background="@color/quickcontact_list_background"
+ android:cacheColorHint="@null" />
+</LinearLayout>
diff --git a/res/layout/quickcontact_list_item.xml b/res/layout/quickcontact_list_item.xml
new file mode 100755
index 0000000..f77ed3f
--- /dev/null
+++ b/res/layout/quickcontact_list_item.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="@drawable/quickcontact_list_item_background"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical">
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingLeft="16dip"
+ android:paddingRight="16dip"
+ android:gravity="center_vertical">
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/white"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ <TextView
+ android:id="@android:id/text2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/white"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+ <View
+ android:id="@+id/vertical_divider"
+ android:layout_width="1dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:background="@drawable/ic_divider_dashed_holo_dark" />
+ <ImageView
+ android:id="@+id/secondary_action_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingLeft="@dimen/detail_item_icon_margin"
+ android:paddingRight="@dimen/detail_item_icon_margin"
+ android:background="@drawable/quickcontact_list_item_background"
+ android:duplicateParentState="false" />
+</LinearLayout>
diff --git a/res/layout/quickcontact_photo_container.xml b/res/layout/quickcontact_photo_container.xml
new file mode 100644
index 0000000..3e0c935
--- /dev/null
+++ b/res/layout/quickcontact_photo_container.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <RelativeLayout
+ android:id="@+id/photo_container"
+ android:layout_width="@dimen/quick_contact_picture_width"
+ android:layout_height="@dimen/quick_contact_picture_height"
+ android:gravity="center_vertical">
+ <ImageView
+ android:id="@+id/photo"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:scaleType="centerCrop" />
+ <View
+ android:id="@+id/photo_text_bar"
+ android:layout_width="0dip"
+ android:layout_height="42dip"
+ android:layout_alignBottom="@id/photo"
+ android:layout_alignLeft="@id/photo"
+ android:layout_alignRight="@id/photo"
+ android:alpha="0.5"
+ android:background="@android:color/black" />
+ <ImageButton
+ android:id="@+id/open_details_button"
+ android:src="@drawable/ic_fav_quickcontact_holo_dark"
+ android:background="?android:attr/selectableItemBackground"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginRight="16dip"
+ android:layout_marginBottom="5dip"
+ android:layout_alignBottom="@id/photo_text_bar"
+ android:layout_alignRight="@id/photo_text_bar" />
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="42dip"
+ android:layout_alignBottom="@id/photo"
+ android:layout_alignLeft="@id/photo"
+ android:layout_toLeftOf="@id/open_details_button"
+ android:gravity="center_vertical"
+ android:paddingLeft="8dip"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textColor="@android:color/white"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ <TextView
+ android:id="@+id/status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textColor="@*android:color/secondary_text_light"
+ android:textSize="15dip"
+ android:layout_marginTop="-3dip" />
+ <TextView
+ android:id="@+id/timestamp"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textColor="@*android:color/secondary_text_light"
+ android:textSize="12dip"
+ android:layout_marginTop="-2dip" />
+ <ImageView
+ android:id="@+id/presence"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="15dip"
+ android:scaleType="centerInside" />
+ <ImageButton
+ android:id="@+id/open_details_push_layer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?android:attr/selectableItemBackground" />
+ </RelativeLayout>
+</merge>
diff --git a/res/layout/quickcontact_resolve_item.xml b/res/layout/quickcontact_resolve_item.xml
deleted file mode 100755
index 2805722..0000000
--- a/res/layout/quickcontact_resolve_item.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="25dip"
- android:paddingRight="25dip"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical">
-
- <TextView
- android:id="@android:id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textStyle="bold"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <TextView
- android:id="@android:id/text2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="-4dip"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
-</LinearLayout>
diff --git a/res/layout/quickcontact_track.xml b/res/layout/quickcontact_track.xml
new file mode 100644
index 0000000..8177174
--- /dev/null
+++ b/res/layout/quickcontact_track.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <HorizontalScrollView
+ android:id="@+id/track_scroller"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fadingEdgeLength="0dip"
+ android:background="@drawable/quickcontact_track_background"
+ android:scrollbars="none">
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:id="@+id/track"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" />
+ <View
+ android:id="@+id/selected_tab_rectangle"
+ android:layout_width="60dip"
+ android:layout_height="8dip"
+ android:layout_alignBottom="@id/track"
+ android:layout_alignParentLeft="true"
+ android:background="@color/quickcontact_list_background" />
+ </RelativeLayout>
+ </HorizontalScrollView>
+</merge>
diff --git a/res/layout/quickcontact_item.xml b/res/layout/quickcontact_track_button.xml
similarity index 87%
rename from res/layout/quickcontact_item.xml
rename to res/layout/quickcontact_track_button.xml
index ca57d66..f1353f2 100644
--- a/res/layout/quickcontact_item.xml
+++ b/res/layout/quickcontact_track_button.xml
@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -16,8 +16,8 @@
<com.android.contacts.quickcontact.CheckableImageView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="59dip"
- android:layout_height="51dip"
+ android:layout_width="60dip"
+ android:layout_height="60dip"
android:paddingLeft="12dip"
android:paddingRight="12dip"
android:paddingTop="8dip"
@@ -25,4 +25,4 @@
android:scaleType="centerInside"
android:focusable="true"
android:clickable="true"
- android:background="@drawable/quickcontact_slider_btn" />
+ android:background="?android:attr/selectableItemBackground" />
diff --git a/res/menu-sw580dp-w720dp/actions.xml b/res/menu-sw580dp-w720dp/actions.xml
index 604813e..534332c 100644
--- a/res/menu-sw580dp-w720dp/actions.xml
+++ b/res/menu-sw580dp-w720dp/actions.xml
@@ -35,21 +35,25 @@
<item
android:id="@+id/menu_contacts_filter"
android:icon="@drawable/ic_menu_settings_holo_light"
+ android:orderInCategory="1"
android:title="@string/menu_contacts_filter" />
<item
android:id="@+id/menu_settings"
android:icon="@drawable/ic_menu_settings_holo_light"
+ android:orderInCategory="2"
android:title="@string/menu_settings" />
<item
- android:id="@+id/menu_accounts"
- android:icon="@drawable/ic_menu_accounts_holo_light"
- android:title="@string/menu_accounts" />
-
- <item
android:id="@+id/menu_import_export"
android:icon="@drawable/ic_menu_import_export_holo_light"
+ android:orderInCategory="3"
android:title="@string/menu_import_export" />
+ <item
+ android:id="@+id/menu_accounts"
+ android:icon="@drawable/ic_menu_accounts_holo_light"
+ android:orderInCategory="4"
+ android:title="@string/menu_accounts" />
+
</menu>
diff --git a/res/menu-sw580dp-w720dp/view_contact.xml b/res/menu-sw580dp-w720dp/view_contact.xml
index 733ab25..0fc918b 100644
--- a/res/menu-sw580dp-w720dp/view_contact.xml
+++ b/res/menu-sw580dp-w720dp/view_contact.xml
@@ -23,18 +23,14 @@
android:showAsAction="always" />
<item
+ android:id="@+id/menu_delete"
+ android:icon="@drawable/ic_menu_trash_holo_light"
+ android:title="@string/menu_deleteContact" />
+
+ <item
android:id="@+id/menu_share"
android:icon="@drawable/ic_menu_share_holo_light"
android:title="@string/menu_share"
android:alphabeticShortcut="s" />
- <item
- android:id="@+id/menu_options"
- android:icon="@drawable/ic_menu_mark"
- android:title="@string/menu_contactOptions" />
-
- <item
- android:id="@+id/menu_delete"
- android:icon="@drawable/ic_menu_trash_holo_light"
- android:title="@string/menu_deleteContact" />
</menu>
diff --git a/res/menu-sw580dp/actions.xml b/res/menu-sw580dp/actions.xml
index ebe4193..593d693 100644
--- a/res/menu-sw580dp/actions.xml
+++ b/res/menu-sw580dp/actions.xml
@@ -32,24 +32,33 @@
android:title="@string/menu_new_group_action_bar"
android:showAsAction="withText" />
+ <!-- Added orderInCategory to keep the following buttons at the end of the menu
+ Buttons will be added in the order added/inflated. Ordered buttons will be added
+ at the end according to the orderInCategory. This setup insures that the buttons below
+ will be the last buttons in the menu regardless of how many buttons are added
+ -->
<item
android:id="@+id/menu_contacts_filter"
android:icon="@drawable/ic_menu_settings_holo_light"
+ android:orderInCategory="1"
android:title="@string/menu_contacts_filter" />
<item
android:id="@+id/menu_settings"
android:icon="@drawable/ic_menu_settings_holo_light"
+ android:orderInCategory="2"
android:title="@string/menu_settings" />
<item
- android:id="@+id/menu_accounts"
- android:icon="@drawable/ic_menu_accounts_holo_light"
- android:title="@string/menu_accounts" />
-
- <item
android:id="@+id/menu_import_export"
android:icon="@drawable/ic_menu_import_export_holo_light"
+ android:orderInCategory="3"
android:title="@string/menu_import_export" />
+ <item
+ android:id="@+id/menu_accounts"
+ android:icon="@drawable/ic_menu_accounts_holo_light"
+ android:orderInCategory="4"
+ android:title="@string/menu_accounts" />
+
</menu>
diff --git a/res/menu-sw580dp/view_contact.xml b/res/menu-sw580dp/view_contact.xml
index 542cfe3..e4b4b37 100644
--- a/res/menu-sw580dp/view_contact.xml
+++ b/res/menu-sw580dp/view_contact.xml
@@ -22,18 +22,14 @@
android:alphabeticShortcut="e" />
<item
+ android:id="@+id/menu_delete"
+ android:icon="@drawable/ic_menu_trash_holo_light"
+ android:title="@string/menu_deleteContact" />
+
+ <item
android:id="@+id/menu_share"
android:icon="@drawable/ic_menu_share_holo_light"
android:title="@string/menu_share"
android:alphabeticShortcut="s" />
- <item
- android:id="@+id/menu_options"
- android:icon="@drawable/ic_menu_mark"
- android:title="@string/menu_contactOptions" />
-
- <item
- android:id="@+id/menu_delete"
- android:icon="@drawable/ic_menu_trash_holo_light"
- android:title="@string/menu_deleteContact" />
</menu>
diff --git a/res/menu/actions.xml b/res/menu/actions.xml
index fd2b967..346875b 100644
--- a/res/menu/actions.xml
+++ b/res/menu/actions.xml
@@ -43,13 +43,13 @@
android:title="@string/menu_settings" />
<item
- android:id="@+id/menu_accounts"
- android:icon="@drawable/ic_menu_accounts_holo_light"
- android:title="@string/menu_accounts" />
-
- <item
android:id="@+id/menu_import_export"
android:icon="@drawable/ic_menu_import_export_holo_light"
android:title="@string/menu_import_export" />
+ <item
+ android:id="@+id/menu_accounts"
+ android:icon="@drawable/ic_menu_accounts_holo_light"
+ android:title="@string/menu_accounts" />
+
</menu>
diff --git a/res/menu/edit_group.xml b/res/menu/edit_group.xml
index d207317..a3387ca 100644
--- a/res/menu/edit_group.xml
+++ b/res/menu/edit_group.xml
@@ -16,15 +16,8 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:id="@+id/menu_done"
- android:alphabeticShortcut="\n"
- android:icon="@drawable/ic_menu_done_holo_light"
- android:title="@string/menu_done"
- android:showAsAction="always|withText" />
-
- <item
android:id="@+id/menu_discard"
android:alphabeticShortcut="q"
- android:title="@string/menu_doNotSave"
- android:showAsAction="always|withText" />
+ android:title="@string/menu_discard"
+ android:showAsAction="withText" />
</menu>
diff --git a/res/menu/view_contact.xml b/res/menu/view_contact.xml
index 7cf17d6..17ed7c6 100644
--- a/res/menu/view_contact.xml
+++ b/res/menu/view_contact.xml
@@ -28,12 +28,17 @@
android:alphabeticShortcut="s" />
<item
- android:id="@+id/menu_options"
- android:icon="@drawable/ic_menu_mark"
- android:title="@string/menu_contactOptions" />
-
- <item
android:id="@+id/menu_delete"
android:icon="@drawable/ic_menu_trash_holo_light"
android:title="@string/menu_deleteContact" />
+
+ <item
+ android:id="@+id/menu_set_ringtone"
+ android:icon="@drawable/ic_menu_mark"
+ android:title="@string/menu_set_ring_tone" />
+
+ <item
+ android:id="@+id/menu_send_to_voicemail"
+ android:checkable="true"
+ android:title="@string/menu_redirect_calls_to_vm" />
</menu>
diff --git a/res/values-sw580dp-w1000dp/dimens.xml b/res/values-sw580dp-w1000dp/dimens.xml
index ac30b76..7ff9026 100644
--- a/res/values-sw580dp-w1000dp/dimens.xml
+++ b/res/values-sw580dp-w1000dp/dimens.xml
@@ -14,7 +14,6 @@
limitations under the License.
-->
<resources>
- <dimen name="quick_contact_width">452dip</dimen>
<dimen name="action_bar_filter_min_width">220dip</dimen>
<dimen name="action_bar_filter_max_width">300dip</dimen>
<dimen name="action_bar_search_max_width">336dip</dimen>
diff --git a/res/values-sw580dp-w720dp/styles.xml b/res/values-sw580dp-w720dp/styles.xml
index 4efaa09..d4487b2 100644
--- a/res/values-sw580dp-w720dp/styles.xml
+++ b/res/values-sw580dp-w720dp/styles.xml
@@ -14,11 +14,11 @@
limitations under the License.
-->
<resources>
- <style name="PeopleTheme" parent="@android:Theme.Holo.Light">
+ <style name="PeopleTheme" parent="@android:style/Theme.Holo.Light.SolidActionBar.Inverse.SplitActionBarWhenNarrow">
+ <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
<item name="list_item_height">66dip</item>
<item name="activated_background">@drawable/list_item_activated_background</item>
<item name="android:windowContentOverlay">@null</item>
- <item name="android:actionBarStyle">@style/TransparentActionBarStyle</item>
<item name="section_header_background">@drawable/list_title_holo</item>
<item name="list_item_divider">?android:attr/listDivider</item>
<item name="list_item_padding_top">0dip</item>
@@ -39,6 +39,8 @@
<item name="list_item_header_text_size">14sp</item>
<item name="list_item_header_underline_color">@color/people_app_theme_color</item>
<item name="contact_filter_popup_width">320dip</item>
+ <!-- Favorites -->
+ <item name="favorites_padding_bottom">0dip</item>
</style>
<style name="ContactDetailHeaderTextView">
@@ -47,4 +49,4 @@
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
</style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/res/values-sw580dp/dimens.xml b/res/values-sw580dp/dimens.xml
index 9fbdc8e..60da57a 100644
--- a/res/values-sw580dp/dimens.xml
+++ b/res/values-sw580dp/dimens.xml
@@ -23,7 +23,6 @@
<dimen name="editor_field_top_padding">12dip</dimen>
<dimen name="editor_field_bottom_padding">12dip</dimen>
<dimen name="detail_item_side_margin">19dip</dimen>
- <dimen name="quick_contact_width">356dip</dimen>
<dimen name="contact_name_text_size">26sp</dimen>
<dimen name="action_bar_filter_min_width">120dip</dimen>
<dimen name="action_bar_filter_max_width">120dip</dimen>
@@ -37,4 +36,7 @@
<dimen name="search_view_width">400dip</dimen>
<dimen name="contact_browser_list_left_margin">0dip</dimen>
<dimen name="contacts_count_right_margin">24dip</dimen>
+ <dimen name="quick_contact_top_position">-1px</dimen>
+ <dimen name="quick_contact_picture_width">400dip</dimen>
+ <dimen name="quick_contact_picture_height">200dip</dimen>
</resources>
diff --git a/res/values-sw580dp/styles.xml b/res/values-sw580dp/styles.xml
index 2838c81..cf4c4ee 100644
--- a/res/values-sw580dp/styles.xml
+++ b/res/values-sw580dp/styles.xml
@@ -14,11 +14,11 @@
limitations under the License.
-->
<resources>
- <style name="PeopleTheme" parent="@android:Theme.Holo.Light">
+ <style name="PeopleTheme" parent="@android:style/Theme.Holo.Light.SolidActionBar.Inverse.SplitActionBarWhenNarrow">
+ <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
<item name="list_item_height">66dip</item>
<item name="activated_background">@drawable/list_item_activated_background</item>
<item name="android:windowContentOverlay">@null</item>
- <item name="android:actionBarStyle">@style/TransparentActionBarStyle</item>
<item name="section_header_background">@drawable/list_title_holo</item>
<item name="list_item_divider">?android:attr/listDivider</item>
<item name="list_item_padding_top">0dip</item>
@@ -39,6 +39,8 @@
<item name="list_item_header_height">26dip</item>
<item name="list_item_header_underline_color">@color/people_app_theme_color</item>
<item name="contact_filter_popup_width">320dip</item>
+ <!-- Favorites -->
+ <item name="favorites_padding_bottom">0dip</item>
</style>
<style name="ContactPickerTheme" parent="@android:Theme.Holo.Light.Dialog">
@@ -83,15 +85,6 @@
<item name="android:windowContentOverlay">@null</item>
</style>
- <style name="ContactEditorActivityTheme" parent="@android:Theme.Holo.Light">
- <item name="android:actionBarStyle">@style/TransparentActionBarStyle</item>
- <item name="android:windowContentOverlay">@null</item>
- </style>
-
- <style name="TransparentActionBarStyle" parent="android:Widget.Holo.Light.ActionBar">
- <item name="android:background">@null</item>
- </style>
-
<style name="DirectoryHeader" parent="PeopleTheme">
<item name="android:background">@drawable/directory_bg_holo</item>
</style>
diff --git a/res/values-w470dp/dimens.xml b/res/values-w470dp/dimens.xml
index ba7f3f8..c77d8c2 100644
--- a/res/values-w470dp/dimens.xml
+++ b/res/values-w470dp/dimens.xml
@@ -15,4 +15,7 @@
-->
<resources>
<dimen name="detail_tab_carousel_height">0dip</dimen>
+ <dimen name="quick_contact_top_position">-1px</dimen>
+ <dimen name="quick_contact_picture_width">174dip</dimen>
+ <dimen name="quick_contact_picture_height">-1px</dimen>
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 579bf84..325d2e7 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -17,8 +17,8 @@
<color name="textColorIconOverlay">#fff</color>
<color name="textColorIconOverlayShadow">#000</color>
- <color name="quickcontact_disambig">#f2f2f2</color>
- <color name="quickcontact_disambig_divider">#afafaf</color>
+ <color name="quickcontact_list_divider">#ff48bde8</color>
+ <color name="quickcontact_list_background">#ff33b5e6</color>
<color name="edit_divider">#ff666666</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a878fc3..45aa75e 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -30,10 +30,17 @@
<dimen name="photo_action_popup_width">400dip</dimen>
- <!-- Width of the quick contact popup. This size is chosen so that the last icon is clipped
- to indicate horizontal scrollability. Also, this size is the same as the widget to make them
- aligned -->
- <dimen name="quick_contact_width">352dip</dimen>
+ <!-- Top position of quick contact. If this is -1, the vertical position is determined
+ based on the source of the request -->
+ <dimen name="quick_contact_top_position">48dip</dimen>
+
+ <!-- Width of the picture in the QuickContact popup. This can be -1 for the full width
+ of the parent -->
+ <dimen name="quick_contact_picture_width">-1px</dimen>
+
+ <!-- Height of the picture in the QuickContact popup. This can be -1 for the full height
+ of the parent -->
+ <dimen name="quick_contact_picture_height">174dip</dimen>
<!-- Padding of the rounded plus/minus/expand/collapse buttons in the editor -->
<dimen name="editor_round_button_padding_left">2dip</dimen>
@@ -225,4 +232,10 @@
<dimen name="call_detail_contact_background_overlay_height">42dip</dimen>
<dimen name="call_detail_contact_name_margin">24dip</dimen>
<dimen name="call_detail_action_bar_height">60dip</dimen>
+
+ <!-- Empty message margins -->
+ <dimen name="empty_message_top_margin">43dip</dimen>
+ <dimen name="no_accounts_message_margin">15dip</dimen>
+ <dimen name="add_account_button_left_margin">50dip</dimen>
+ <dimen name="add_account_button_right_margin">50dip</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d2c2536..999531e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -176,15 +176,18 @@
<!-- Toast shown after two contacts have been joined by a user action -->
<string name="contactsJoinedMessage">Contacts joined</string>
- <!-- Menu item that opens the Options activity for a given contact -->
- <string name="menu_contactOptions">Options</string>
-
<!-- Title for the Options activity for a given contact -->
<string name="contactOptionsTitle">Options</string>
<!-- Confirmation dialog title after users selects to delete a contact. -->
<string name="deleteConfirmation_title">Delete</string>
+ <!-- Menu item that opens the Options activity for a given contact [CHAR LIMIT=15] -->
+ <string name="menu_set_ring_tone">Set ringtone</string>
+
+ <!-- Menu item that opens the Options activity for a given contact [CHAR LIMIT=30] -->
+ <string name="menu_redirect_calls_to_vm">Redirect calls to voicemail</string>
+
<!-- Warning dialog contents after users selects to delete a ReadOnly contact. -->
<string name="readOnlyContactWarning">You cannot delete contacts from read-only accounts, but you can hide them in your contacts lists.</string>
@@ -200,7 +203,7 @@
<!-- Menu item to indicate you are done editing a contact and want to save the changes you've made -->
<string name="menu_done">Done</string>
- <!-- Menu item to indicate you want to stop editing a group and NOT save the changes you've made [CHAR LIMIT=12] -->
+ <!-- Menu item to indicate you want to cancel the current editing process and NOT save the changes you've made [CHAR LIMIT=12] -->
<string name="menu_doNotSave">Cancel</string>
<!-- Menu item to indicate you want to stop editing a contact and NOT save the changes you've made [CHAR LIMIT=12] -->
@@ -287,11 +290,14 @@
<!-- The menu item that allows you to remove a photo from a contact [CHAR LIMIT=50] -->
<string name="removePhoto">Remove photo</string>
- <!-- The text displayed when the contacts list is empty while displaying all contacts -->
- <string name="noContacts">No contacts.</string>
+ <!-- The text displayed when the contacts list is empty while displaying all contacts [CHAR LIMIT=NONE] -->
+ <string name="noContacts">No contacts</string>
<!-- The text displayed when the groups list is empty while displaying all groups [CHAR LIMIT=NONE] -->
- <string name="noGroups">No groups.</string>
+ <string name="noGroups">No groups</string>
+
+ <!-- The text displayed when the groups list is empty and no accounts are set on the device while displaying all groups [CHAR LIMIT=NONE] -->
+ <string name="noAccounts">To create groups you need an account</string>
<!-- The text displayed when the contacts list is empty while displaying results after searching contacts -->
<string name="noMatchingContacts">No matching contacts found.</string>
@@ -358,7 +364,7 @@
<string name="listTotalAllContactsZeroCustom">No visible contacts</string>
<!-- Displayed at the top of the contacts showing the zero total number of contacts visible when starred contact list is selected [CHAR LIMIT=64]-->
- <string name="listTotalAllContactsZeroStarred">No starred contacts</string>
+ <string name="listTotalAllContactsZeroStarred">No Favorites</string>
<!-- Displayed at the top of the contacts showing the zero total number of contacts visible when a group or account is selected [CHAR LIMIT=64]-->
<string name="listTotalAllContactsZeroGroup">No contacts in <xliff:g id="name" example="Friends">%s</xliff:g></string>
@@ -1009,32 +1015,13 @@
<!-- Message next to disamgiguation dialog check box -->
<string name="make_primary">Remember this choice</string>
- <!-- Shown as a toast when the user taps on a Fast-Track icon, and no application
+ <!-- Shown as a toast when the user taps on a QuickContact icon, and no application
was found that could perform the selected action -->
<string name="quickcontact_missing_app">No application found to handle this action</string>
- <!-- Shown as the checkbox label that, when checked, will store remember the
- selected choice and shortcut to it in the future. For example, this would
- make a selected phone number the default. -->
- <string name="quickcontact_remember_choice">Remember this choice</string>
-
<!-- Shown as the header name for a person when the name is missing or unknown. -->
<string name="quickcontact_missing_name">Unknown</string>
- <!-- Text that is shown in the Badge, when there is no data to display -->
- <string name="quickcontact_no_data">No data</string>
-
- <!-- Accessibility description for the button in QuickContacts that allows the user to clear
- defaults of a contact (like primary email). [CHAR LIMIT=100] -->
- <string name="quickcontact_clear_defaults_description">Clear defaults</string>
-
- <!-- Caption that shows above the list of defaults (like primary email). [CHAR LIMIT=40] -->
- <string name="quickcontact_clear_defaults_caption">Defaults set for this contact:</string>
-
- <!-- Button caption that allows the user to clear the defaults (like primary email) of one
- contact. [CHAR LIMIT=15] -->
- <string name="quickcontact_clear_defaults_button">Clear</string>
-
<!-- The menu item to open the list of accounts -->
<string name="menu_accounts">Accounts</string>
@@ -1575,6 +1562,15 @@
<!-- Joined contact indicator displayed in the contact detail [CHAR LIMIT=64] -->
<string name="indicator_joined_contact">Joined contact</string>
+ <!-- Option displayed in context menu to copy long pressed item to clipboard [CHAR LIMIT=64] -->
+ <string name="copy_text">Copy to clipboard</string>
+
+ <!-- Option displayed in context menu to set long pressed item as default contact method [CHAR LIMIT=64] -->
+ <string name="set_default">Set default</string>
+
+ <!-- Option displayed in context menu to clear long pressed item as default contact method [CHAR LIMIT=64] -->
+ <string name="clear_default">Clear default</string>
+
<!-- Toast shown when text is copied to the clipboard [CHAR LIMIT=64] -->
<string name="toast_text_copied">Text copied</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index d26affc..785c887 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -14,7 +14,9 @@
limitations under the License.
-->
<resources>
- <style name="DialtactsTheme" parent="android:Theme.Holo.SplitActionBarWhenNarrow">
+ <style name="DialtactsTheme"
+ parent="android:Theme.Holo.SolidActionBar.SplitActionBarWhenNarrow">
+ <item name="android:windowActionBarOverlay">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowBackground">@android:color/black</item>
<item name="activated_background">@drawable/list_item_activated_background</item>
@@ -51,6 +53,8 @@
<item name="call_log_voicemail_status_height">40dip</item>
<item name="call_log_voicemail_status_background_color">#FFFFE0</item>
<item name="call_log_voicemail_status_text_color">#000000</item>
+ <!-- Favorites -->
+ <item name="favorites_padding_bottom">?android:attr/actionBarSize</item>
</style>
<style name="CallDetailActivityTheme" parent="android:Theme.Holo.SplitActionBarWhenNarrow">
@@ -69,10 +73,12 @@
<item name="call_log_voicemail_status_text_color">#000000</item>
</style>
- <style name="ContactDetailActivityTheme" parent="android:Theme.Holo.Light">
+ <style name="ContactDetailActivityTheme" parent="@android:style/Theme.Holo.Light.SolidActionBar.Inverse">
+ <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
<item name="android:windowContentOverlay">@null</item>
</style>
- <style name="ContactEditorActivityTheme" parent="android:Theme.Holo.Light">
+ <style name="ContactEditorActivityTheme" parent="@android:style/Theme.Holo.Light.SolidActionBar.Inverse.SplitActionBarWhenNarrow">
+ <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
<item name="android:windowContentOverlay">@null</item>
</style>
@@ -95,7 +101,7 @@
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowAnimationStyle">@null</item>
<item name="android:windowIsFloating">false</item>
- <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:backgroundDimEnabled">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
</style>
@@ -159,7 +165,12 @@
<attr name="call_log_voicemail_status_text_color" format="color" />
</declare-styleable>
- <style name="PeopleTheme" parent="android:Theme.Holo.Light.SplitActionBarWhenNarrow">
+ <declare-styleable name="Favorites">
+ <attr name="favorites_padding_bottom" format="dimension" />
+ </declare-styleable>
+
+ <style name="PeopleTheme" parent="@android:style/Theme.Holo.Light.SolidActionBar.Inverse.SplitActionBarWhenNarrow">
+ <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
<item name="list_item_height">?android:attr/listPreferredItemHeight</item>
<item name="activated_background">@drawable/list_item_activated_background</item>
<item name="section_header_background">@drawable/list_title_holo</item>
@@ -185,10 +196,18 @@
<item name="list_item_header_underline_height">1px</item>
<item name="list_item_header_underline_color">@color/people_app_theme_color</item>
<item name="contact_filter_popup_width">320dip</item>
+ <!-- Favorites -->
+ <item name="favorites_padding_bottom">0dip</item>
+ </style>
+
+ <style name="ContactsActionBarStyle" parent="@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse">
+ <item name="android:background">@color/people_app_theme_color</item>
+ <item name="android:backgroundStacked">@color/people_app_theme_color</item>
</style>
<!-- TODO: Clean up this file so themes aren't copied. -->
- <style name="GroupDetailTheme" parent="android:Theme.Holo.Light">
+ <style name="GroupDetailTheme" parent="@android:style/Theme.Holo.Light.SolidActionBar.Inverse.SplitActionBarWhenNarrow">
+ <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
<item name="list_item_height">?android:attr/listPreferredItemHeight</item>
<item name="activated_background">@drawable/list_item_activated_background</item>
<item name="section_header_background">@drawable/list_title_holo</item>
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
index 3463d3c..9daa1e0 100644
--- a/src/com/android/contacts/ContactLoader.java
+++ b/src/com/android/contacts/ContactLoader.java
@@ -114,7 +114,7 @@
private final ArrayList<Entity> mEntities;
private ArrayList<StreamItemEntry> mStreamItems;
private final HashMap<Long, DataStatus> mStatuses;
- private final ArrayList<String> mInvitableAccountTypes;
+ private final ArrayList<AccountType> mInvitableAccountTypes;
private String mDirectoryDisplayName;
private String mDirectoryType;
@@ -126,6 +126,8 @@
private boolean mLoadingPhoto;
private byte[] mPhotoBinaryData;
+ private boolean mSendToVoicemail;
+ private String mCustomRingtone;
/**
* Constructor for case "no contact found". This must only be used for the
@@ -150,6 +152,8 @@
mStarred = false;
mPresence = null;
mInvitableAccountTypes = null;
+ mSendToVoicemail = false;
+ mCustomRingtone = null;
}
/**
@@ -158,7 +162,7 @@
private Result(Uri uri, Uri lookupUri, long directoryId, String lookupKey, long id,
long nameRawContactId, int displayNameSource, long photoId, String photoUri,
String displayName, String altDisplayName, String phoneticName, boolean starred,
- Integer presence) {
+ Integer presence, boolean sendToVoicemail, String customRingtone) {
mLookupUri = lookupUri;
mUri = uri;
mDirectoryId = directoryId;
@@ -177,6 +181,8 @@
mStarred = starred;
mPresence = presence;
mInvitableAccountTypes = Lists.newArrayList();
+ mSendToVoicemail = sendToVoicemail;
+ mCustomRingtone = customRingtone;
}
private Result(Result from) {
@@ -209,6 +215,8 @@
mLoadingPhoto = from.mLoadingPhoto;
mPhotoBinaryData = from.mPhotoBinaryData;
+ mSendToVoicemail = from.mSendToVoicemail;
+ mCustomRingtone = from.mCustomRingtone;
}
/**
@@ -284,7 +292,7 @@
return mPresence;
}
- public ArrayList<String> getInvitableAccontTypes() {
+ public ArrayList<AccountType> getInvitableAccountTypes() {
return mInvitableAccountTypes;
}
@@ -378,6 +386,14 @@
public List<GroupMetaData> getGroupMetaData() {
return mGroups;
}
+
+ public boolean isSendToVoicemail() {
+ return mSendToVoicemail;
+ }
+
+ public String getCustomRingtone() {
+ return mCustomRingtone;
+ }
}
/**
@@ -404,6 +420,8 @@
RawContacts.ACCOUNT_NAME,
RawContacts.ACCOUNT_TYPE,
+ RawContacts.DATA_SET,
+ RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
RawContacts.DIRTY,
RawContacts.VERSION,
RawContacts.SOURCE_ID,
@@ -451,6 +469,8 @@
Data.STATUS_TIMESTAMP,
Contacts.PHOTO_URI,
+ Contacts.SEND_TO_VOICEMAIL,
+ Contacts.CUSTOM_RINGTONE,
};
public final static int NAME_RAW_CONTACT_ID = 0;
@@ -471,53 +491,57 @@
public final static int ACCOUNT_NAME = 15;
public final static int ACCOUNT_TYPE = 16;
- public final static int DIRTY = 17;
- public final static int VERSION = 18;
- public final static int SOURCE_ID = 19;
- public final static int SYNC1 = 20;
- public final static int SYNC2 = 21;
- public final static int SYNC3 = 22;
- public final static int SYNC4 = 23;
- public final static int DELETED = 24;
- public final static int NAME_VERIFIED = 25;
+ public final static int DATA_SET = 17;
+ public final static int ACCOUNT_TYPE_AND_DATA_SET = 18;
+ public final static int DIRTY = 19;
+ public final static int VERSION = 20;
+ public final static int SOURCE_ID = 21;
+ public final static int SYNC1 = 22;
+ public final static int SYNC2 = 23;
+ public final static int SYNC3 = 24;
+ public final static int SYNC4 = 25;
+ public final static int DELETED = 26;
+ public final static int NAME_VERIFIED = 27;
- public final static int DATA_ID = 26;
- public final static int DATA1 = 27;
- public final static int DATA2 = 28;
- public final static int DATA3 = 29;
- public final static int DATA4 = 30;
- public final static int DATA5 = 31;
- public final static int DATA6 = 32;
- public final static int DATA7 = 33;
- public final static int DATA8 = 34;
- public final static int DATA9 = 35;
- public final static int DATA10 = 36;
- public final static int DATA11 = 37;
- public final static int DATA12 = 38;
- public final static int DATA13 = 39;
- public final static int DATA14 = 40;
- public final static int DATA15 = 41;
- public final static int DATA_SYNC1 = 42;
- public final static int DATA_SYNC2 = 43;
- public final static int DATA_SYNC3 = 44;
- public final static int DATA_SYNC4 = 45;
- public final static int DATA_VERSION = 46;
- public final static int IS_PRIMARY = 47;
- public final static int IS_SUPERPRIMARY = 48;
- public final static int MIMETYPE = 49;
- public final static int RES_PACKAGE = 50;
+ public final static int DATA_ID = 28;
+ public final static int DATA1 = 29;
+ public final static int DATA2 = 30;
+ public final static int DATA3 = 31;
+ public final static int DATA4 = 32;
+ public final static int DATA5 = 33;
+ public final static int DATA6 = 34;
+ public final static int DATA7 = 35;
+ public final static int DATA8 = 36;
+ public final static int DATA9 = 37;
+ public final static int DATA10 = 38;
+ public final static int DATA11 = 39;
+ public final static int DATA12 = 40;
+ public final static int DATA13 = 41;
+ public final static int DATA14 = 42;
+ public final static int DATA15 = 43;
+ public final static int DATA_SYNC1 = 44;
+ public final static int DATA_SYNC2 = 45;
+ public final static int DATA_SYNC3 = 46;
+ public final static int DATA_SYNC4 = 47;
+ public final static int DATA_VERSION = 48;
+ public final static int IS_PRIMARY = 49;
+ public final static int IS_SUPERPRIMARY = 50;
+ public final static int MIMETYPE = 51;
+ public final static int RES_PACKAGE = 52;
- public final static int GROUP_SOURCE_ID = 51;
+ public final static int GROUP_SOURCE_ID = 53;
- public final static int PRESENCE = 52;
- public final static int CHAT_CAPABILITY = 53;
- public final static int STATUS = 54;
- public final static int STATUS_RES_PACKAGE = 55;
- public final static int STATUS_ICON = 56;
- public final static int STATUS_LABEL = 57;
- public final static int STATUS_TIMESTAMP = 58;
+ public final static int PRESENCE = 54;
+ public final static int CHAT_CAPABILITY = 55;
+ public final static int STATUS = 56;
+ public final static int STATUS_RES_PACKAGE = 57;
+ public final static int STATUS_ICON = 58;
+ public final static int STATUS_LABEL = 59;
+ public final static int STATUS_TIMESTAMP = 60;
- public final static int PHOTO_URI = 59;
+ public final static int PHOTO_URI = 61;
+ public final static int SEND_TO_VOICEMAIL = 62;
+ public final static int CUSTOM_RINGTONE = 63;
}
/**
@@ -545,6 +569,8 @@
final static String[] COLUMNS = new String[] {
Groups.ACCOUNT_NAME,
Groups.ACCOUNT_TYPE,
+ Groups.DATA_SET,
+ Groups.ACCOUNT_TYPE_AND_DATA_SET,
Groups._ID,
Groups.TITLE,
Groups.AUTO_ADD,
@@ -553,10 +579,12 @@
public final static int ACCOUNT_NAME = 0;
public final static int ACCOUNT_TYPE = 1;
- public final static int ID = 2;
- public final static int TITLE = 3;
- public final static int AUTO_ADD = 4;
- public final static int FAVORITES = 5;
+ public final static int DATA_SET = 2;
+ public final static int ACCOUNT_TYPE_AND_DATA_SET = 3;
+ public final static int ID = 4;
+ public final static int TITLE = 5;
+ public final static int AUTO_ADD = 6;
+ public final static int FAVORITES = 7;
}
private final class LoadContactTask extends AsyncTask<Void, Void, Result> {
@@ -753,7 +781,7 @@
}
// Set to mInvitableAccountTypes
- contactData.mInvitableAccountTypes.addAll(result.keySet());
+ contactData.mInvitableAccountTypes.addAll(result.values());
}
/**
@@ -778,6 +806,8 @@
final Integer presence = cursor.isNull(ContactQuery.CONTACT_PRESENCE)
? null
: cursor.getInt(ContactQuery.CONTACT_PRESENCE);
+ final boolean sendToVoicemail = cursor.getInt(ContactQuery.SEND_TO_VOICEMAIL) == 1;
+ final String customRingtone = cursor.getString(ContactQuery.CUSTOM_RINGTONE);
Uri lookupUri;
if (directoryId == Directory.DEFAULT || directoryId == Directory.LOCAL_INVISIBLE) {
@@ -789,7 +819,8 @@
return new Result(contactUri, lookupUri, directoryId, lookupKey, contactId,
nameRawContactId, displayNameSource, photoId, photoUri, displayName,
- altDisplayName, phoneticName, starred, presence);
+ altDisplayName, phoneticName, starred, presence, sendToVoicemail,
+ customRingtone);
}
/**
@@ -802,6 +833,8 @@
cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_NAME);
cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.DATA_SET);
+ cursorColumnToContentValues(cursor, cv, ContactQuery.ACCOUNT_TYPE_AND_DATA_SET);
cursorColumnToContentValues(cursor, cv, ContactQuery.DIRTY);
cursorColumnToContentValues(cursor, cv, ContactQuery.VERSION);
cursorColumnToContentValues(cursor, cv, ContactQuery.SOURCE_ID);
@@ -923,14 +956,23 @@
ContentValues values = entity.getEntityValues();
String accountName = values.getAsString(RawContacts.ACCOUNT_NAME);
String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
+ String dataSet = values.getAsString(RawContacts.DATA_SET);
if (accountName != null && accountType != null) {
if (selection.length() != 0) {
selection.append(" OR ");
}
selection.append(
- "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?)");
+ "(" + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?");
selectionArgs.add(accountName);
selectionArgs.add(accountType);
+
+ if (dataSet != null) {
+ selection.append(" AND " + Groups.DATA_SET + "=?");
+ selectionArgs.add(dataSet);
+ } else {
+ selection.append(" AND " + Groups.DATA_SET + " IS NULL");
+ }
+ selection.append(")");
}
}
Cursor cursor = getContext().getContentResolver().query(Groups.CONTENT_URI,
@@ -940,6 +982,7 @@
while (cursor.moveToNext()) {
final String accountName = cursor.getString(GroupQuery.ACCOUNT_NAME);
final String accountType = cursor.getString(GroupQuery.ACCOUNT_TYPE);
+ final String dataSet = cursor.getString(GroupQuery.DATA_SET);
final long groupId = cursor.getLong(GroupQuery.ID);
final String title = cursor.getString(GroupQuery.TITLE);
final boolean defaultGroup = cursor.isNull(GroupQuery.AUTO_ADD)
@@ -950,7 +993,8 @@
: cursor.getInt(GroupQuery.FAVORITES) != 0;
result.addGroupMetaData(new GroupMetaData(
- accountName, accountType, groupId, title, defaultGroup, favorites));
+ accountName, accountType, dataSet, groupId, title, defaultGroup,
+ favorites));
}
} finally {
cursor.close();
diff --git a/src/com/android/contacts/ContactOptionsActivity.java b/src/com/android/contacts/ContactOptionsActivity.java
deleted file mode 100644
index dd7387f..0000000
--- a/src/com/android/contacts/ContactOptionsActivity.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts;
-
-import com.android.contacts.activities.PeopleActivity;
-
-import android.app.ActionBar;
-import android.app.Activity;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.database.Cursor;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract.Contacts;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.TextView;
-
-/**
- * An activity for selecting options for a given contact: custom ringtone and send-to-voicemail.
- */
-public class ContactOptionsActivity extends Activity implements View.OnClickListener {
-
- private static final String TAG = "ContactOptionsActivity";
-
- private static final String[] AGGREGATES_PROJECTION = new String[] {
- Contacts.CUSTOM_RINGTONE, Contacts.SEND_TO_VOICEMAIL
- };
-
- private static final int COL_CUSTOM_RINGTONE = 0;
- private static final int COL_SEND_TO_VOICEMAIL = 1;
-
- /** The launch code when picking a ringtone */
- private static final int RINGTONE_PICKED = 3023;
-
- private String mCustomRingtone;
- private boolean mSendToVoicemail;
- private TextView mRingtoneTitle;
- private CheckBox mSendToVoicemailCheckbox;
-
- private Uri mLookupUri;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mLookupUri = getIntent().getData();
-
- setContentView(R.layout.contact_options);
-
- View ringtoneLayout = findViewById(R.id.ringtone);
- ringtoneLayout.setFocusable(true);
- ringtoneLayout.setOnClickListener(this);
- TextView label = (TextView)findViewById(R.id.label);
- label.setText(getString(R.string.label_ringtone));
-
- mRingtoneTitle = (TextView)ringtoneLayout.findViewById(R.id.data);
-
- View sendToVoicemailLayout = findViewById(R.id.voicemail);
- sendToVoicemailLayout.setOnClickListener(this);
- label = (TextView)sendToVoicemailLayout.findViewById(R.id.label);
- label.setText(getString(R.string.actionIncomingCall));
-
- ActionBar actionBar = getActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- }
-
- mSendToVoicemailCheckbox = (CheckBox)sendToVoicemailLayout.findViewById(R.id.checkbox);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- if (!loadData()) {
- finish();
- }
-
- updateView();
- }
-
- private void updateView() {
- if (mCustomRingtone == null) {
- mRingtoneTitle.setText(getString(R.string.default_ringtone));
- } else {
- Uri ringtoneUri = Uri.parse(mCustomRingtone);
- Ringtone ringtone = RingtoneManager.getRingtone(this, ringtoneUri);
- if (ringtone == null) {
- Log.w(TAG, "ringtone's URI doesn't resolve to a Ringtone");
- return;
- }
- mRingtoneTitle.setText(ringtone.getTitle(this));
- }
-
- mSendToVoicemailCheckbox.setChecked(mSendToVoicemail);
- }
-
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.ringtone: {
- doPickRingtone();
- break;
- }
- case R.id.voicemail: {
- doToggleSendToVoicemail();
- break;
- }
- }
- }
-
- private void doPickRingtone() {
-
- Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
- // Allow user to pick 'Default'
- intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
- // Show only ringtones
- intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE);
- // Don't show 'Silent'
- intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
-
- Uri ringtoneUri;
- if (mCustomRingtone != null) {
- ringtoneUri = Uri.parse(mCustomRingtone);
- } else {
- // Otherwise pick default ringtone Uri so that something is selected.
- ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
- }
-
- // Put checkmark next to the current ringtone for this contact
- intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, ringtoneUri);
-
- // Launch!
- startActivityForResult(intent, RINGTONE_PICKED);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode != RESULT_OK) {
- return;
- }
-
- switch (requestCode) {
- case RINGTONE_PICKED: {
- Uri pickedUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
- handleRingtonePicked(pickedUri);
- break;
- }
- }
- }
-
- private void handleRingtonePicked(Uri pickedUri) {
- if (pickedUri == null || RingtoneManager.isDefault(pickedUri)) {
- mCustomRingtone = null;
- } else {
- mCustomRingtone = pickedUri.toString();
- }
- saveData();
- updateView();
- }
-
- private void doToggleSendToVoicemail() {
- mSendToVoicemailCheckbox.toggle();
- mSendToVoicemail = mSendToVoicemailCheckbox.isChecked();
- saveData();
- updateView();
- }
-
- private boolean loadData() {
- Cursor c =
- getContentResolver().query(mLookupUri, AGGREGATES_PROJECTION, null, null, null);
- try {
- if (!c.moveToFirst()) {
- return false;
- }
-
- mCustomRingtone = c.getString(COL_CUSTOM_RINGTONE);
- mSendToVoicemail = c.getInt(COL_SEND_TO_VOICEMAIL) != 0;
-
- } finally {
- c.close();
- }
- return true;
- }
-
- private void saveData() {
- ContentValues values = new ContentValues(2);
- values.put(Contacts.CUSTOM_RINGTONE, mCustomRingtone);
- values.put(Contacts.SEND_TO_VOICEMAIL, mSendToVoicemail);
- getContentResolver().update(mLookupUri, values, null, null);
- }
-
- @Override
- public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
- boolean globalSearch) {
- if (globalSearch) {
- super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
- } else {
- ContactsSearchManager.startSearch(this, initialQuery);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
-
- switch (item.getItemId()) {
- case android.R.id.home:
- Intent intent = new Intent(this, PeopleActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
- finish();
- return true;
- default:
- break;
- }
- return super.onOptionsItemSelected(item);
- }
-}
-
-
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index 775009e..221796a 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -17,12 +17,12 @@
package com.android.contacts;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.model.EntityDeltaList;
import com.android.contacts.model.EntityModifier;
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
-import android.accounts.Account;
import android.app.Activity;
import android.app.IntentService;
import android.content.ContentProviderOperation;
@@ -68,6 +68,7 @@
public static final String EXTRA_ACCOUNT_NAME = "accountName";
public static final String EXTRA_ACCOUNT_TYPE = "accountType";
+ public static final String EXTRA_DATA_SET = "dataSet";
public static final String EXTRA_CONTENT_VALUES = "contentValues";
public static final String EXTRA_CALLBACK_INTENT = "callbackIntent";
@@ -98,6 +99,12 @@
public static final String EXTRA_CONTACT_ID2 = "contactId2";
public static final String EXTRA_CONTACT_WRITABLE = "contactWritable";
+ public static final String ACTION_SET_SEND_TO_VOICEMAIL = "sendToVoicemail";
+ public static final String EXTRA_SEND_TO_VOICEMAIL_FLAG = "sendToVoicemailFlag";
+
+ public static final String ACTION_SET_RINGTONE = "setRingtone";
+ public static final String EXTRA_CUSTOM_RINGTONE = "customRingtone";
+
private static final HashSet<String> ALLOWED_DATA_COLUMNS = Sets.newHashSet(
Data.MIMETYPE,
Data.IS_PRIMARY,
@@ -185,6 +192,10 @@
deleteContact(intent);
} else if (ACTION_JOIN_CONTACTS.equals(action)) {
joinContacts(intent);
+ } else if (ACTION_SET_SEND_TO_VOICEMAIL.equals(action)) {
+ setSendToVoicemail(intent);
+ } else if (ACTION_SET_RINGTONE.equals(action)) {
+ setRingtone(intent);
}
}
@@ -193,14 +204,15 @@
* using data presented as a set of ContentValues.
*/
public static Intent createNewRawContactIntent(Context context,
- ArrayList<ContentValues> values, Account account, Class<?> callbackActivity,
- String callbackAction) {
+ ArrayList<ContentValues> values, AccountWithDataSet account,
+ Class<?> callbackActivity, String callbackAction) {
Intent serviceIntent = new Intent(
context, ContactSaveService.class);
serviceIntent.setAction(ContactSaveService.ACTION_NEW_RAW_CONTACT);
if (account != null) {
serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_NAME, account.name);
serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_TYPE, account.type);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_DATA_SET, account.dataSet);
}
serviceIntent.putParcelableArrayListExtra(
ContactSaveService.EXTRA_CONTENT_VALUES, values);
@@ -217,6 +229,7 @@
private void createRawContact(Intent intent) {
String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
String accountType = intent.getStringExtra(EXTRA_ACCOUNT_TYPE);
+ String dataSet = intent.getStringExtra(EXTRA_DATA_SET);
List<ContentValues> valueList = intent.getParcelableArrayListExtra(EXTRA_CONTENT_VALUES);
Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
@@ -224,6 +237,7 @@
operations.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
.withValue(RawContacts.ACCOUNT_NAME, accountName)
.withValue(RawContacts.ACCOUNT_TYPE, accountType)
+ .withValue(RawContacts.DATA_SET, dataSet)
.build());
int size = valueList.size();
@@ -382,13 +396,14 @@
* @param callbackActivity is the activity to send the callback intent to
* @param callbackAction is the intent action for the callback intent
*/
- public static Intent createNewGroupIntent(Context context, Account account,
+ public static Intent createNewGroupIntent(Context context, AccountWithDataSet account,
String label, long[] rawContactsToAdd, Class<?> callbackActivity,
String callbackAction) {
Intent serviceIntent = new Intent(context, ContactSaveService.class);
serviceIntent.setAction(ContactSaveService.ACTION_CREATE_GROUP);
serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_TYPE, account.type);
serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_NAME, account.name);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_DATA_SET, account.dataSet);
serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_LABEL, label);
serviceIntent.putExtra(ContactSaveService.EXTRA_RAW_CONTACTS_TO_ADD, rawContactsToAdd);
@@ -402,14 +417,16 @@
}
private void createGroup(Intent intent) {
- final String accountType = intent.getStringExtra(EXTRA_ACCOUNT_TYPE);
- final String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
- final String label = intent.getStringExtra(EXTRA_GROUP_LABEL);
+ String accountType = intent.getStringExtra(EXTRA_ACCOUNT_TYPE);
+ String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
+ String dataSet = intent.getStringExtra(EXTRA_DATA_SET);
+ String label = intent.getStringExtra(EXTRA_GROUP_LABEL);
final long[] rawContactsToAdd = intent.getLongArrayExtra(EXTRA_RAW_CONTACTS_TO_ADD);
ContentValues values = new ContentValues();
values.put(Groups.ACCOUNT_TYPE, accountType);
values.put(Groups.ACCOUNT_NAME, accountName);
+ values.put(Groups.DATA_SET, dataSet);
values.put(Groups.TITLE, label);
final ContentResolver resolver = getContentResolver();
@@ -657,6 +674,57 @@
}
/**
+ * Creates an intent that can be sent to this service to set the redirect to voicemail.
+ */
+ public static Intent createSetSendToVoicemail(Context context, Uri contactUri,
+ boolean value) {
+ Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_SET_SEND_TO_VOICEMAIL);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_URI, contactUri);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_SEND_TO_VOICEMAIL_FLAG, value);
+
+ return serviceIntent;
+ }
+
+ private void setSendToVoicemail(Intent intent) {
+ Uri contactUri = intent.getParcelableExtra(EXTRA_CONTACT_URI);
+ boolean value = intent.getBooleanExtra(EXTRA_SEND_TO_VOICEMAIL_FLAG, false);
+ if (contactUri == null) {
+ Log.e(TAG, "Invalid arguments for setRedirectToVoicemail");
+ return;
+ }
+
+ final ContentValues values = new ContentValues(1);
+ values.put(Contacts.SEND_TO_VOICEMAIL, value);
+ getContentResolver().update(contactUri, values, null, null);
+ }
+
+ /**
+ * Creates an intent that can be sent to this service to save the contact's ringtone.
+ */
+ public static Intent createSetRingtone(Context context, Uri contactUri,
+ String value) {
+ Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_SET_RINGTONE);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_URI, contactUri);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_CUSTOM_RINGTONE, value);
+
+ return serviceIntent;
+ }
+
+ private void setRingtone(Intent intent) {
+ Uri contactUri = intent.getParcelableExtra(EXTRA_CONTACT_URI);
+ String value = intent.getStringExtra(EXTRA_CUSTOM_RINGTONE);
+ if (contactUri == null) {
+ Log.e(TAG, "Invalid arguments for setRingtone");
+ return;
+ }
+ ContentValues values = new ContentValues(1);
+ values.put(Contacts.CUSTOM_RINGTONE, value);
+ getContentResolver().update(contactUri, values, null, null);
+ }
+
+ /**
* Creates an intent that sets the selected data item as super primary (default)
*/
public static Intent createSetSuperPrimaryIntent(Context context, long dataId) {
diff --git a/src/com/android/contacts/ContactTileLoaderFactory.java b/src/com/android/contacts/ContactTileLoaderFactory.java
index 41532c2..30bd7e4 100644
--- a/src/com/android/contacts/ContactTileLoaderFactory.java
+++ b/src/com/android/contacts/ContactTileLoaderFactory.java
@@ -21,7 +21,9 @@
import android.content.CursorLoader;
import android.net.Uri;
import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Contacts.Data;
/**
* Used to create {@link CursorLoader}s to load different groups of {@link ContactTileView}s
@@ -36,14 +38,36 @@
public final static int CONTACT_PRESENCE = 5;
public final static int CONTACT_STATUS = 6;
+ // Only used for StrequentPhoneOnlyLoader
+ public final static int PHONE_NUMBER = 5;
+ public final static int PHONE_NUMBER_TYPE = 6;
+ public final static int PHONE_NUMBER_LABEL = 7;
+
private static final String[] COLUMNS = new String[] {
- Contacts._ID,
- Contacts.DISPLAY_NAME,
- Contacts.STARRED,
- Contacts.PHOTO_URI,
- Contacts.LOOKUP_KEY,
- Contacts.CONTACT_PRESENCE,
- Contacts.CONTACT_STATUS,
+ Contacts._ID, // ..........................................0
+ Contacts.DISPLAY_NAME, // .................................1
+ Contacts.STARRED, // ......................................2
+ Contacts.PHOTO_URI, // ....................................3
+ Contacts.LOOKUP_KEY, // ...................................4
+ Contacts.CONTACT_PRESENCE, // .............................5
+ Contacts.CONTACT_STATUS, // ...............................6
+ };
+
+ /**
+ * Projection used for the {@link Contacts#CONTENT_STREQUENT_URI}
+ * query when {@link ContactsContract#STREQUENT_PHONE_ONLY} flag
+ * is set to true. The main difference is the lack of presence
+ * and status data and the addition of phone number and label.
+ */
+ private static final String[] COLUMNS_PHONE_ONLY = new String[] {
+ Contacts._ID, // ..........................................0
+ Contacts.DISPLAY_NAME, // .................................1
+ Contacts.STARRED, // ......................................2
+ Contacts.PHOTO_URI, // ....................................3
+ Contacts.LOOKUP_KEY, // ...................................4
+ Phone.NUMBER, // ..........................................5
+ Phone.TYPE, // ............................................6
+ Phone.LABEL // ............................................7
};
public static CursorLoader createStrequentLoader(Context context) {
@@ -54,7 +78,7 @@
Uri uri = Contacts.CONTENT_STREQUENT_URI.buildUpon()
.appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build();
- return new CursorLoader(context, uri, COLUMNS, null, null, null);
+ return new CursorLoader(context, uri, COLUMNS_PHONE_ONLY, null, null, null);
}
public static CursorLoader createStarredLoader(Context context) {
diff --git a/src/com/android/contacts/GroupListLoader.java b/src/com/android/contacts/GroupListLoader.java
index f5716e3..39c428f 100644
--- a/src/com/android/contacts/GroupListLoader.java
+++ b/src/com/android/contacts/GroupListLoader.java
@@ -31,6 +31,7 @@
private final static String[] COLUMNS = new String[] {
Groups.ACCOUNT_NAME,
Groups.ACCOUNT_TYPE,
+ Groups.DATA_SET,
Groups._ID,
Groups.TITLE,
Groups.ACTION,
@@ -41,12 +42,13 @@
public final static int ACCOUNT_NAME = 0;
public final static int ACCOUNT_TYPE = 1;
- public final static int GROUP_ID = 2;
- public final static int TITLE = 3;
- public final static int ACTION = 4;
- public final static int ACTION_URI = 5;
- public final static int MEMBER_COUNT = 6;
- public final static int GROUP_COUNT_PER_ACCOUNT = 7;
+ public final static int DATA_SET = 2;
+ public final static int GROUP_ID = 3;
+ public final static int TITLE = 4;
+ public final static int ACTION = 5;
+ public final static int ACTION_URI = 6;
+ public final static int MEMBER_COUNT = 7;
+ public final static int GROUP_COUNT_PER_ACCOUNT = 8;
private static final Uri GROUP_LIST_URI = Groups.CONTENT_SUMMARY_URI.buildUpon()
.appendQueryParameter(Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT, "true").build();
@@ -55,7 +57,7 @@
super(context, GROUP_LIST_URI, COLUMNS, Groups.ACCOUNT_TYPE + " NOT NULL AND "
+ Groups.ACCOUNT_NAME + " NOT NULL AND " + Groups.AUTO_ADD + "=0 AND " +
Groups.FAVORITES + "=0 AND " + Groups.DELETED + "=0", null,
- Groups.ACCOUNT_TYPE + ", " + Groups.ACCOUNT_NAME + ", " +
+ Groups.ACCOUNT_TYPE + ", " + Groups.ACCOUNT_NAME + ", " + Groups.DATA_SET + ", " +
Groups.TITLE + " COLLATE LOCALIZED ASC");
}
}
diff --git a/src/com/android/contacts/GroupMetaData.java b/src/com/android/contacts/GroupMetaData.java
index 39e955d..462ac4a 100644
--- a/src/com/android/contacts/GroupMetaData.java
+++ b/src/com/android/contacts/GroupMetaData.java
@@ -22,15 +22,17 @@
public final class GroupMetaData {
private String mAccountName;
private String mAccountType;
+ private String mDataSet;
private long mGroupId;
private String mTitle;
private boolean mDefaultGroup;
private boolean mFavorites;
- public GroupMetaData(String accountName, String accountType, long groupId, String title,
- boolean defaultGroup, boolean favorites) {
+ public GroupMetaData(String accountName, String accountType, String dataSet, long groupId,
+ String title, boolean defaultGroup, boolean favorites) {
this.mAccountName = accountName;
this.mAccountType = accountType;
+ this.mDataSet = dataSet;
this.mGroupId = groupId;
this.mTitle = title;
this.mDefaultGroup = defaultGroup;
@@ -45,6 +47,10 @@
return mAccountType;
}
+ public String getDataSet() {
+ return mDataSet;
+ }
+
public long getGroupId() {
return mGroupId;
}
diff --git a/src/com/android/contacts/GroupMetaDataLoader.java b/src/com/android/contacts/GroupMetaDataLoader.java
index f11217c..2f3468b 100644
--- a/src/com/android/contacts/GroupMetaDataLoader.java
+++ b/src/com/android/contacts/GroupMetaDataLoader.java
@@ -29,6 +29,7 @@
private final static String[] COLUMNS = new String[] {
Groups.ACCOUNT_NAME,
Groups.ACCOUNT_TYPE,
+ Groups.DATA_SET,
Groups._ID,
Groups.TITLE,
Groups.AUTO_ADD,
@@ -41,14 +42,15 @@
public final static int ACCOUNT_NAME = 0;
public final static int ACCOUNT_TYPE = 1;
- public final static int GROUP_ID = 2;
- public final static int TITLE = 3;
- public final static int AUTO_ADD = 4;
- public final static int FAVORITES = 5;
- public final static int IS_READ_ONLY = 6;
- public final static int DELETED = 7;
- public final static int ACTION = 8;
- public final static int ACTION_URI = 9;
+ public final static int DATA_SET = 2;
+ public final static int GROUP_ID = 3;
+ public final static int TITLE = 4;
+ public final static int AUTO_ADD = 5;
+ public final static int FAVORITES = 6;
+ public final static int IS_READ_ONLY = 7;
+ public final static int DELETED = 8;
+ public final static int ACTION = 9;
+ public final static int ACTION_URI = 10;
public GroupMetaDataLoader(Context context, Uri groupUri) {
super(context, ensureIsGroupUri(groupUri), COLUMNS, Groups.ACCOUNT_TYPE + " NOT NULL AND "
diff --git a/src/com/android/contacts/SplitAggregateView.java b/src/com/android/contacts/SplitAggregateView.java
index d99ba85..091cd56 100644
--- a/src/com/android/contacts/SplitAggregateView.java
+++ b/src/com/android/contacts/SplitAggregateView.java
@@ -57,18 +57,20 @@
private interface SplitQuery {
String[] COLUMNS = new String[] {
- Data.MIMETYPE, RawContacts.ACCOUNT_TYPE, Data.RAW_CONTACT_ID, Data.IS_PRIMARY,
- StructuredName.DISPLAY_NAME, Nickname.NAME, Email.DATA, Phone.NUMBER
+ Data.MIMETYPE, RawContacts.ACCOUNT_TYPE, RawContacts.DATA_SET, Data.RAW_CONTACT_ID,
+ Data.IS_PRIMARY, StructuredName.DISPLAY_NAME, Nickname.NAME, Email.DATA,
+ Phone.NUMBER
};
int MIMETYPE = 0;
int ACCOUNT_TYPE = 1;
- int RAW_CONTACT_ID = 2;
- int IS_PRIMARY = 3;
- int DISPLAY_NAME = 4;
- int NICKNAME = 5;
- int EMAIL = 6;
- int PHONE = 7;
+ int DATA_SET = 2;
+ int RAW_CONTACT_ID = 3;
+ int IS_PRIMARY = 4;
+ int DISPLAY_NAME = 5;
+ int NICKNAME = 6;
+ int EMAIL = 7;
+ int PHONE = 8;
}
private final Uri mAggregateUri;
@@ -116,6 +118,7 @@
private static class RawContactInfo implements Comparable<RawContactInfo> {
final long rawContactId;
String accountType;
+ String dataSet;
String name;
String phone;
String email;
@@ -165,6 +168,7 @@
info = new RawContactInfo(rawContactId);
rawContactInfos.put(rawContactId, info);
info.accountType = cursor.getString(SplitQuery.ACCOUNT_TYPE);
+ info.dataSet = cursor.getString(SplitQuery.DATA_SET);
}
String mimetype = cursor.getString(SplitQuery.MIMETYPE);
@@ -247,7 +251,7 @@
cache.additionalData.setText(info.getAdditionalData());
Drawable icon = null;
- AccountType accountType = mAccountTypes.getAccountType(info.accountType);
+ AccountType accountType = mAccountTypes.getAccountType(info.accountType, info.dataSet);
if (accountType != null) {
icon = accountType.getDisplayIcon(getContext());
}
diff --git a/src/com/android/contacts/TypePrecedence.java b/src/com/android/contacts/TypePrecedence.java
index e89c5aa..92f3a6e 100644
--- a/src/com/android/contacts/TypePrecedence.java
+++ b/src/com/android/contacts/TypePrecedence.java
@@ -100,8 +100,6 @@
private static int[] getTypePrecedenceList(String mimetype) {
if (mimetype.equals(Phone.CONTENT_ITEM_TYPE)) {
return TYPE_PRECEDENCE_PHONES;
- } else if (mimetype.equals(Constants.MIME_TYPE_SMS_ADDRESS)) {
- return TYPE_PRECEDENCE_PHONES;
} else if (mimetype.equals(Email.CONTENT_ITEM_TYPE)) {
return TYPE_PRECEDENCE_EMAIL;
} else if (mimetype.equals(StructuredPostal.CONTENT_ITEM_TYPE)) {
diff --git a/src/com/android/contacts/activities/AttachPhotoActivity.java b/src/com/android/contacts/activities/AttachPhotoActivity.java
index 1eabaf7..a697c29 100644
--- a/src/com/android/contacts/activities/AttachPhotoActivity.java
+++ b/src/com/android/contacts/activities/AttachPhotoActivity.java
@@ -217,7 +217,8 @@
if (assertAccount) {
// Make sure no pictures exist for Google, Exchange and unsynced phone accounts.
operations.add(ContentProviderOperation.newAssertQuery(rawContactDataUri)
- .withSelection(Photo.MIMETYPE + "=? AND ("
+ .withSelection(Photo.MIMETYPE + "=? AND "
+ + RawContacts.DATA_SET + " IS NULL AND ("
+ RawContacts.ACCOUNT_TYPE + " IN (?,?) OR "
+ RawContacts.ACCOUNT_TYPE + " IS NULL)",
new String[] {Photo.CONTENT_ITEM_TYPE, GoogleAccountType.ACCOUNT_TYPE,
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index c4330f9..6046f5e 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -19,7 +19,6 @@
import com.android.contacts.ContactLoader;
import com.android.contacts.ContactSaveService;
import com.android.contacts.ContactsActivity;
-import com.android.contacts.ContactsSearchManager;
import com.android.contacts.R;
import com.android.contacts.detail.ContactDetailDisplayUtils;
import com.android.contacts.detail.ContactDetailFragment;
@@ -29,9 +28,9 @@
import com.android.contacts.detail.ContactLoaderFragment;
import com.android.contacts.detail.ContactLoaderFragment.ContactLoaderFragmentListener;
import com.android.contacts.interactions.ContactDeletionInteraction;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.util.PhoneCapabilityTester;
-import android.accounts.Account;
import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentManager;
@@ -384,7 +383,7 @@
@Override
public void onCreateRawContactRequested(
- ArrayList<ContentValues> values, Account account) {
+ ArrayList<ContentValues> values, AccountWithDataSet account) {
Toast.makeText(ContactDetailActivity.this, R.string.toast_making_personal_copy,
Toast.LENGTH_LONG).show();
Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index 10be943..54ea05f 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -17,18 +17,15 @@
package com.android.contacts.activities;
import com.android.contacts.ContactsActivity;
-import com.android.contacts.ContactsSearchManager;
import com.android.contacts.R;
import com.android.contacts.editor.ContactEditorFragment;
import com.android.contacts.editor.ContactEditorFragment.SaveMode;
-import com.android.contacts.interactions.ContactDeletionInteraction;
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.util.DialogManager;
-import android.accounts.Account;
import android.app.ActionBar;
-import android.app.Activity;
import android.app.Dialog;
import android.content.ContentValues;
import android.content.Context;
@@ -40,10 +37,8 @@
import android.provider.ContactsContract.RawContacts;
import android.util.Log;
import android.view.LayoutInflater;
-import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.Button;
import java.util.ArrayList;
@@ -84,8 +79,7 @@
// to the contact
LayoutInflater inflater = (LayoutInflater) getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
- View customActionBarView = inflater.inflate(R.layout.contact_editor_custom_action_bar,
- null);
+ View customActionBarView = inflater.inflate(R.layout.editor_custom_action_bar, null);
View saveMenuItem = customActionBarView.findViewById(R.id.save_menu_item);
saveMenuItem.setOnClickListener(new OnClickListener() {
@Override
@@ -199,10 +193,12 @@
}
@Override
- public void onCustomCreateContactActivityRequested(Account account, Bundle intentExtras) {
+ public void onCustomCreateContactActivityRequested(AccountWithDataSet account,
+ Bundle intentExtras) {
final AccountTypeManager accountTypes =
AccountTypeManager.getInstance(ContactEditorActivity.this);
- final AccountType accountType = accountTypes.getAccountType(account.type);
+ final AccountType accountType = accountTypes.getAccountType(
+ account.type, account.dataSet);
Intent intent = new Intent();
intent.setClassName(accountType.resPackageName,
@@ -214,6 +210,7 @@
}
intent.putExtra(RawContacts.ACCOUNT_NAME, account.name);
intent.putExtra(RawContacts.ACCOUNT_TYPE, account.type);
+ intent.putExtra(RawContacts.DATA_SET, account.dataSet);
intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
@@ -221,11 +218,12 @@
}
@Override
- public void onCustomEditContactActivityRequested(Account account, Uri rawContactUri,
- Bundle intentExtras, boolean redirect) {
+ public void onCustomEditContactActivityRequested(AccountWithDataSet account,
+ Uri rawContactUri, Bundle intentExtras, boolean redirect) {
final AccountTypeManager accountTypes =
AccountTypeManager.getInstance(ContactEditorActivity.this);
- final AccountType accountType = accountTypes.getAccountType(account.type);
+ final AccountType accountType = accountTypes.getAccountType(
+ account.type, account.dataSet);
Intent intent = new Intent();
intent.setClassName(accountType.resPackageName,
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 7e41a5c..01212f5 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -122,7 +122,12 @@
}
private class PageChangeListener implements OnPageChangeListener {
- private int mPreviousPosition = -1; // Invalid at first
+ private int mCurrentPosition = -1;
+ /**
+ * Used during page migration, to remember the next position {@link #onPageSelected(int)}
+ * specified.
+ */
+ private int mNextPosition = -1;
@Override
public void onPageScrolled(
@@ -132,34 +137,38 @@
@Override
public void onPageSelected(int position) {
final ActionBar actionBar = getActionBar();
- if (mPreviousPosition == position) {
+ if (mCurrentPosition == position) {
Log.w(TAG, "Previous position and next position became same (" + position + ")");
}
- if (mPreviousPosition >= 0) {
- sendFragmentVisibilityChange(mPreviousPosition, false);
- }
- sendFragmentVisibilityChange(position, true);
-
actionBar.selectTab(actionBar.getTabAt(position));
-
- // Activity#onPrepareOptionsMenu() may not be called when Fragment has it's own
- // options menu. We force this Activity to call it to hide/show bottom bar. Also
- // we don't want to do so when it is unnecessary (buttons may flicker).
- if (mPreviousPosition == TAB_INDEX_DIALER || position == TAB_INDEX_DIALER) {
- // Force this Activity to prepare Menu again.
- invalidateOptionsMenu();
- }
-
- mPreviousPosition = position;
+ mNextPosition = position;
}
public void setCurrentPosition(int position) {
- mPreviousPosition = position;
+ mCurrentPosition = position;
}
@Override
public void onPageScrollStateChanged(int state) {
+ switch (state) {
+ case ViewPager.SCROLL_STATE_IDLE: {
+ if (mCurrentPosition >= 0) {
+ sendFragmentVisibilityChange(mCurrentPosition, false);
+ }
+ if (mNextPosition >= 0) {
+ sendFragmentVisibilityChange(mNextPosition, true);
+ }
+ invalidateOptionsMenu();
+
+ mCurrentPosition = mNextPosition;
+ break;
+ }
+ case ViewPager.SCROLL_STATE_DRAGGING:
+ case ViewPager.SCROLL_STATE_SETTLING:
+ default:
+ break;
+ }
}
}
@@ -246,7 +255,11 @@
new OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
- // Ignore.
+ View view = getCurrentFocus();
+ if (view != null) {
+ hideInputMethod(view);
+ view.clearFocus();
+ }
return true;
}
@@ -265,16 +278,12 @@
* the search UI and let users go back to usual Phone UI.
*
* This does _not_ handle back button.
- *
- * TODO: need "up" button instead of close button
*/
private final OnCloseListener mPhoneSearchCloseListener =
new OnCloseListener() {
@Override
public boolean onClose() {
- if (TextUtils.isEmpty(mSearchView.getQuery())) {
- exitSearchUi();
- } else {
+ if (!TextUtils.isEmpty(mSearchView.getQuery())) {
mSearchView.setQuery(null, true);
}
return true;
@@ -636,13 +645,20 @@
// Instantiate or reset SearchView in ActionBar.
if (mSearchView == null) {
- // TODO: layout is not what we want. Need "up" button instead of "close" button, etc.
final View searchViewLayout =
getLayoutInflater().inflate(R.layout.custom_action_bar, null);
mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
- mSearchView.setQueryHint(getString(R.string.hint_findContacts));
mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);
mSearchView.setOnCloseListener(mPhoneSearchCloseListener);
+ // Since we're using a custom layout for showing SearchView instead of letting the
+ // search menu icon do that job, we need to manually configure the View so it looks
+ // "shown via search menu".
+ // - it should be iconified by default
+ // - it should not be iconified at this time
+ // See also comments for onActionViewExpanded()/onActionViewCollapsed()
+ mSearchView.setIconifiedByDefault(true);
+ mSearchView.setQueryHint(getString(R.string.hint_findContacts));
+ mSearchView.setIconified(false);
mSearchView.requestFocus();
// Show soft keyboard when SearchView has a focus. Need to delay the request in order
// to let InputMethodManager handle it correctly.
@@ -681,6 +697,9 @@
transaction.commit();
mViewPager.setVisibility(View.GONE);
+ // We need to call this and onActionViewCollapsed() manually, since we are using a custom
+ // layout instead of asking the search menu item to take care of SearchView.
+ mSearchView.onActionViewExpanded();
mInSearchUi = true;
}
@@ -723,6 +742,8 @@
// Request to update option menu.
invalidateOptionsMenu();
+ // See comments in onActionViewExpanded()
+ mSearchView.onActionViewCollapsed();
mInSearchUi = false;
}
diff --git a/src/com/android/contacts/activities/GroupDetailActivity.java b/src/com/android/contacts/activities/GroupDetailActivity.java
index c7cf47c..b66d8b8 100644
--- a/src/com/android/contacts/activities/GroupDetailActivity.java
+++ b/src/com/android/contacts/activities/GroupDetailActivity.java
@@ -39,6 +39,7 @@
private boolean mShowGroupSourceInActionBar;
private String mAccountTypeString;
+ private String mDataSet;
private String mGroupSourceAction;
private String mGroupSourceUri;
@@ -61,9 +62,12 @@
fragment.loadGroup(getIntent().getData());
fragment.closeActivityAfterDelete(true);
- ActionBar actionBar = getActionBar();
+ // We want the UP affordance but no app icon.
+ ActionBar actionBar = getActionBar();
if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE,
+ ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE
+ | ActionBar.DISPLAY_SHOW_HOME);
}
}
@@ -81,9 +85,10 @@
}
@Override
- public void onGroupSourceUpdated(
- String accountTypeString, String groupSourceAction, String groupSourceActionUri) {
+ public void onGroupSourceUpdated(String accountTypeString, String dataSet,
+ String groupSourceAction, String groupSourceActionUri) {
mAccountTypeString = accountTypeString;
+ mDataSet = dataSet;
mGroupSourceAction = groupSourceAction;
mGroupSourceUri = groupSourceActionUri;
invalidateOptionsMenu();
@@ -130,7 +135,7 @@
}
View groupSourceView = GroupDetailDisplayUtils.getNewGroupSourceView(this);
GroupDetailDisplayUtils.bindGroupSourceView(this, groupSourceView,
- mAccountTypeString);
+ mAccountTypeString, mDataSet);
groupSourceView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/src/com/android/contacts/activities/GroupEditorActivity.java b/src/com/android/contacts/activities/GroupEditorActivity.java
index 814595c..d61b6c3 100644
--- a/src/com/android/contacts/activities/GroupEditorActivity.java
+++ b/src/com/android/contacts/activities/GroupEditorActivity.java
@@ -17,7 +17,6 @@
package com.android.contacts.activities;
import com.android.contacts.ContactsActivity;
-import com.android.contacts.ContactsSearchManager;
import com.android.contacts.R;
import com.android.contacts.editor.ContactEditorFragment.SaveMode;
import com.android.contacts.group.GroupEditorFragment;
@@ -26,11 +25,14 @@
import android.app.ActionBar;
import android.app.Dialog;
+import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
-import android.view.MenuItem;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
public class GroupEditorActivity extends ContactsActivity
implements DialogManager.DialogShowingViewActivity {
@@ -57,11 +59,26 @@
setContentView(R.layout.group_editor_activity);
- // This Activity will always fall back to the "top" Contacts screen when touched on the
- // app up icon, regardless of launch context.
ActionBar actionBar = getActionBar();
if (actionBar != null) {
- actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
+ // Inflate a custom action bar that contains the "done" button for saving changes
+ // to the group
+ LayoutInflater inflater = (LayoutInflater) getSystemService
+ (Context.LAYOUT_INFLATER_SERVICE);
+ View customActionBarView = inflater.inflate(R.layout.editor_custom_action_bar,
+ null);
+ View saveMenuItem = customActionBarView.findViewById(R.id.save_menu_item);
+ saveMenuItem.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mFragment.doSaveAction();
+ }
+ });
+ // Show the custom action bar but hide the home icon and title
+ actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
+ ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME |
+ ActionBar.DISPLAY_SHOW_TITLE);
+ actionBar.setCustomView(customActionBarView);
}
mFragment = (GroupEditorFragment) getFragmentManager().findFragmentById(
@@ -109,17 +126,6 @@
}
}
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home: {
- mFragment.save(SaveMode.HOME);
- return true;
- }
- }
- return false;
- }
-
private final GroupEditorFragment.Listener mFragmentListener =
new GroupEditorFragment.Listener() {
@Override
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 481b28e..6dc727e 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -51,6 +51,7 @@
import com.android.contacts.list.ProviderStatusLoader.ProviderStatusListener;
import com.android.contacts.list.ContactTileListFragment;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.preference.ContactsPreferenceActivity;
import com.android.contacts.preference.DisplayOptionsPreferenceFragment;
import com.android.contacts.util.AccountSelectionUtil;
@@ -58,7 +59,6 @@
import com.android.contacts.util.DialogManager;
import com.android.contacts.util.PhoneCapabilityTester;
-import android.accounts.Account;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
@@ -92,6 +92,7 @@
import android.widget.Toast;
import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -202,8 +203,8 @@
}
private boolean areAccountsAvailable() {
- final ArrayList<Account> accounts =
- AccountTypeManager.getInstance(this).getAccounts(true /* writeable */);
+ final List<AccountWithDataSet> accounts =
+ AccountTypeManager.getInstance(this).getAccounts(true /* writeable */);
return !accounts.isEmpty();
}
@@ -602,6 +603,10 @@
}
}
invalidateOptionsMenu();
+ showEmptyStateForTab(tab);
+ if (tab == TabState.GROUPS) {
+ mGroupsFragment.setAddAccountsVisibility(!areAccountsAvailable());
+ }
return;
}
@@ -618,6 +623,11 @@
mDetailsView.setVisibility(View.GONE);
break;
case GROUPS:
+ mFavoritesView.setVisibility(View.GONE);
+ mBrowserView.setVisibility(View.VISIBLE);
+ mDetailsView.setVisibility(View.VISIBLE);
+ mGroupsFragment.setAddAccountsVisibility(!areAccountsAvailable());
+ break;
case ALL:
mFavoritesView.setVisibility(View.GONE);
mBrowserView.setVisibility(View.VISIBLE);
@@ -665,6 +675,24 @@
// fragment transaction does it implicitly. We don't have to call invalidateOptionsMenu
// manually.
}
+ showEmptyStateForTab(tab);
+ }
+
+ private void showEmptyStateForTab(TabState tab) {
+ if (mContactsUnavailableFragment != null) {
+ switch (tab) {
+ case FAVORITES:
+ mContactsUnavailableFragment.setMessageText(
+ R.string.listTotalAllContactsZeroStarred);
+ break;
+ case GROUPS:
+ mContactsUnavailableFragment.setMessageText(R.string.noGroups);
+ break;
+ case ALL:
+ mContactsUnavailableFragment.setMessageText(R.string.noContacts);
+ break;
+ }
+ }
}
private class TabPagerListener implements ViewPager.OnPageChangeListener {
@@ -680,7 +708,12 @@
public void onPageSelected(int position) {
// Make sure not in the search mode, in which case position != TabState.ordinal().
if (!mTabPagerAdapter.isSearchMode()) {
- mActionBarAdapter.setCurrentTab(TabState.fromInt(position), false);
+ TabState selectedTab = TabState.fromInt(position);
+ mActionBarAdapter.setCurrentTab(selectedTab, false);
+ showEmptyStateForTab(selectedTab);
+ if (selectedTab == TabState.GROUPS) {
+ mGroupsFragment.setAddAccountsVisibility(!areAccountsAvailable());
+ }
invalidateOptionsMenu();
}
}
@@ -904,6 +937,11 @@
if (mainView != null) {
mainView.setVisibility(View.INVISIBLE);
}
+
+ TabState tab = mActionBarAdapter.getCurrentTab();
+ if (tab == TabState.GROUPS) {
+ mGroupsFragment.setAddAccountsVisibility(!areAccountsAvailable());
+ }
}
invalidateOptionsMenuIfNeeded();
@@ -1054,7 +1092,8 @@
}
@Override
- public void onCreateRawContactRequested(ArrayList<ContentValues> values, Account account) {
+ public void onCreateRawContactRequested(ArrayList<ContentValues> values,
+ AccountWithDataSet account) {
Toast.makeText(PeopleActivity.this, R.string.toast_making_personal_copy,
Toast.LENGTH_LONG).show();
Intent serviceIntent = ContactSaveService.createNewRawContactIntent(
@@ -1132,8 +1171,8 @@
}
@Override
- public void onGroupSourceUpdated(
- String accountTypeString, String groupSourceAction, String groupSourceUri) {
+ public void onGroupSourceUpdated(String accountTypeString, String dataSet,
+ String groupSourceAction, String groupSourceUri) {
// Nothing needs to be done here because the group source will be displayed in the
// detail fragment
}
@@ -1246,14 +1285,18 @@
if (mActionBarAdapter.isSearchMode()) {
addContactMenu.setVisible(false);
addGroupMenu.setVisible(false);
+ contactsFilterMenu.setVisible(false);
} else {
switch (mActionBarAdapter.getCurrentTab()) {
case FAVORITES:
- // TODO: Fall through until we determine what the menu items should be for
- // this tab
+ addContactMenu.setVisible(false);
+ addGroupMenu.setVisible(false);
+ contactsFilterMenu.setVisible(false);
+ break;
case ALL:
addContactMenu.setVisible(true);
addGroupMenu.setVisible(false);
+ contactsFilterMenu.setVisible(true);
break;
case GROUPS:
// Do not display the "new group" button if no accounts are available
@@ -1263,6 +1306,7 @@
addGroupMenu.setVisible(false);
}
addContactMenu.setVisible(false);
+ contactsFilterMenu.setVisible(false);
break;
}
}
@@ -1272,9 +1316,6 @@
searchMenu.setVisible(!mActionBarAdapter.isSearchMode());
}
- if (contactsFilterMenu != null) {
- contactsFilterMenu.setVisible(!mActionBarAdapter.isSearchMode());
- }
MenuItem settings = menu.findItem(R.id.menu_settings);
if (settings != null) {
@@ -1354,7 +1395,7 @@
}
private void createNewGroupWithAccountDisambiguation() {
- final ArrayList<Account> accounts =
+ final List<AccountWithDataSet> accounts =
AccountTypeManager.getInstance(this).getAccounts(true);
if (accounts.size() <= 1 || mAddGroupImageView == null) {
// No account to choose or no control to anchor the popup-menu to
@@ -1376,9 +1417,11 @@
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
popup.dismiss();
+ AccountWithDataSet account = adapter.getItem(position);
final Intent intent = new Intent(PeopleActivity.this, GroupEditorActivity.class);
intent.setAction(Intent.ACTION_INSERT);
- intent.putExtra(Intents.Insert.ACCOUNT, adapter.getItem(position));
+ intent.putExtra(Intents.Insert.ACCOUNT, account);
+ intent.putExtra(Intents.Insert.DATA_SET, account.dataSet);
startActivityForResult(intent, SUBACTIVITY_NEW_GROUP);
}
});
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index 8f7cfa8..abc9adf 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -989,19 +989,16 @@
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
- inflater.inflate(R.menu.call_log_options, menu);
+ if (mShowOptionsMenu) {
+ inflater.inflate(R.menu.call_log_options, menu);
+ }
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
- menu.findItem(R.id.delete_all).setVisible(mShowOptionsMenu);
- menu.findItem(R.id.show_voicemails_only).setVisible(mShowOptionsMenu);
- final MenuItem callSettingsMenuItem = menu.findItem(R.id.menu_call_settings_call_log);
if (mShowOptionsMenu) {
- callSettingsMenuItem.setVisible(true);
- callSettingsMenuItem.setIntent(DialtactsActivity.getCallSettingsIntent());
- } else {
- callSettingsMenuItem.setVisible(false);
+ menu.findItem(R.id.menu_call_settings_call_log)
+ .setIntent(DialtactsActivity.getCallSettingsIntent());
}
}
diff --git a/src/com/android/contacts/detail/CarouselTab.java b/src/com/android/contacts/detail/CarouselTab.java
new file mode 100644
index 0000000..9b8efd5
--- /dev/null
+++ b/src/com/android/contacts/detail/CarouselTab.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.detail;
+
+import com.android.contacts.R;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+/**
+ * This is a tab in the {@link ContactDetailTabCarousel}.
+ */
+public class CarouselTab extends RelativeLayout implements ViewOverlay {
+
+ private static final String TAG = CarouselTab.class.getSimpleName();
+
+ private TextView mLabelView;
+
+ /**
+ * This view adds an alpha layer over the entire tab.
+ */
+ private View mAlphaLayer;
+
+ /**
+ * This view adds a layer over the entire tab so that when visible, it intercepts all touch
+ * events on the tab.
+ */
+ private View mTouchInterceptLayer;
+
+ public CarouselTab(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mLabelView = (TextView) findViewById(R.id.label);
+ mLabelView.setClickable(true);
+
+ mAlphaLayer = findViewById(R.id.alpha_overlay);
+ mTouchInterceptLayer = findViewById(R.id.touch_intercept_overlay);
+ }
+
+ public void setLabel(String label) {
+ mLabelView.setText(label);
+ }
+
+ public void showSelectedState() {
+ mLabelView.setSelected(true);
+ }
+
+ public void showDeselectedState() {
+ mLabelView.setSelected(false);
+ }
+
+ @Override
+ public void disableTouchInterceptor() {
+ // This shouldn't be called because there is no need to disable the touch interceptor if
+ // there is no content within the tab that needs to be clicked.
+ }
+
+ @Override
+ public void enableAlphaLayer() {
+ if (mAlphaLayer != null) {
+ mAlphaLayer.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void enableTouchInterceptor(OnClickListener clickListener) {
+ if (mTouchInterceptLayer != null) {
+ mTouchInterceptLayer.setVisibility(View.VISIBLE);
+ mTouchInterceptLayer.setOnClickListener(clickListener);
+ }
+ }
+
+ @Override
+ public void setAlphaLayerValue(float alpha) {
+ if (mAlphaLayer != null) {
+ mAlphaLayer.setAlpha(alpha);
+ }
+ }
+}
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index a6ba6b0..946fbe8 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -31,6 +31,7 @@
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountType.EditType;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.model.DataKind;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
@@ -43,10 +44,10 @@
import com.android.contacts.widget.TransitionAnimationView;
import com.android.internal.telephony.ITelephony;
-import android.accounts.Account;
import android.app.Activity;
import android.app.Fragment;
import android.app.SearchManager;
+import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ContentUris;
import android.content.ContentValues;
@@ -87,15 +88,18 @@
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
@@ -112,13 +116,19 @@
import java.util.List;
import java.util.Map;
-public class ContactDetailFragment extends Fragment implements FragmentKeyListener, FragmentOverlay,
- OnItemClickListener, OnItemLongClickListener, SelectAccountDialogFragment.Listener {
+public class ContactDetailFragment extends Fragment implements FragmentKeyListener, ViewOverlay,
+ OnItemClickListener, SelectAccountDialogFragment.Listener {
private static final String TAG = "ContactDetailFragment";
private static final int LOADER_DETAILS = 1;
+ private interface ContextMenuIds {
+ static final int COPY_TEXT = 0;
+ static final int CLEAR_DEFAULT = 1;
+ static final int SET_DEFAULT = 2;
+ }
+
private static final String KEY_CONTACT_URI = "contactUri";
private static final String LOADER_ARG_CONTACT_URI = "contactUri";
@@ -255,7 +265,7 @@
mListView = (ListView) mView.findViewById(android.R.id.list);
mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
mListView.setOnItemClickListener(this);
- mListView.setOnItemLongClickListener(this);
+ registerForContextMenu(mListView);
mListView.setOnScrollListener(mVerticalScrollListener);
// Don't set it to mListView yet. We do so later when we bind the adapter.
@@ -481,12 +491,13 @@
for (Entity entity: mContactData.getEntities()) {
final ContentValues entValues = entity.getEntityValues();
final String accountType = entValues.getAsString(RawContacts.ACCOUNT_TYPE);
+ final String dataSet = entValues.getAsString(RawContacts.DATA_SET);
final long rawContactId = entValues.getAsLong(RawContacts._ID);
if (!mRawContactIds.contains(rawContactId)) {
mRawContactIds.add(rawContactId);
}
- AccountType type = accountTypes.getAccountType(accountType);
+ AccountType type = accountTypes.getAccountType(accountType, dataSet);
if (type == null || !type.readOnly) {
mWritableRawContactIds.add(rawContactId);
}
@@ -508,7 +519,7 @@
}
final DataKind kind = accountTypes.getKindOrFallback(
- accountType, mimeType);
+ accountType, dataSet, mimeType);
if (kind == null) continue;
final DetailViewEntry entry = DetailViewEntry.fromValues(mContext, mimeType, kind,
@@ -564,7 +575,7 @@
final DataStatus status = mContactData.getStatuses().get(entry.id);
if (status != null) {
final String imMime = Im.CONTENT_ITEM_TYPE;
- final DataKind imKind = accountTypes.getKindOrFallback(accountType,
+ final DataKind imKind = accountTypes.getKindOrFallback(accountType, dataSet,
imMime);
final DetailViewEntry imEntry = DetailViewEntry.fromValues(mContext, imMime,
imKind, dataId, entryValues, mContactData.isDirectoryEntry(),
@@ -754,7 +765,7 @@
String attribution = ContactDetailDisplayUtils.getAttribution(mContext, mContactData);
boolean hasAttribution = !TextUtils.isEmpty(attribution);
int networksCount = mOtherEntriesMap.keySet().size();
- int invitableCount = mContactData.getInvitableAccontTypes().size();
+ int invitableCount = mContactData.getInvitableAccountTypes().size();
if (!hasAttribution && networksCount == 0 && invitableCount == 0) {
return;
}
@@ -1328,12 +1339,14 @@
public ImageView secondaryActionButton;
public View secondaryActionButtonContainer;
public View secondaryActionDivider;
+ public View primaryIndicator;
public DetailViewCache(View view, OnClickListener secondaryActionClickListener) {
kind = (TextView) view.findViewById(R.id.kind);
type = (TextView) view.findViewById(R.id.type);
data = (TextView) view.findViewById(R.id.data);
footer = (TextView) view.findViewById(R.id.footer);
+ primaryIndicator = view.findViewById(R.id.primary_indicator);
presenceIcon = (ImageView) view.findViewById(R.id.presence_icon);
secondaryActionButton = (ImageView) view.findViewById(
R.id.secondary_action_button);
@@ -1511,6 +1524,9 @@
views.footer.setVisibility(View.GONE);
}
+ // Set the default contact method
+ views.primaryIndicator.setVisibility(entry.isPrimary ? View.VISIBLE : View.GONE);
+
// Set the presence icon
final Drawable presenceIcon = ContactPresenceIconUtil.getPresenceIcon(
mContext, entry.presence);
@@ -1623,11 +1639,11 @@
}
@Override
- public void onAccountChosen(int requestCode, Account account) {
+ public void onAccountChosen(int requestCode, AccountWithDataSet account) {
createCopy(account);
}
- private void createCopy(Account account) {
+ private void createCopy(AccountWithDataSet account) {
if (mListener != null) {
mListener.onCreateRawContactRequested(mContactData.getContentValues(), account);
}
@@ -1642,18 +1658,105 @@
}
@Override
- public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
- if (mListener == null) return false;
- final DetailViewCache cache = (DetailViewCache) view.getTag();
- if (cache == null) return false;
- CharSequence text = cache.data.getText();
- if (TextUtils.isEmpty(text)) return false;
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, view, menuInfo);
- ClipboardManager cm = (ClipboardManager) getActivity().getSystemService(
+ AdapterView.AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
+ DetailViewEntry selectedEntry = (DetailViewEntry) mAllEntries.get(info.position);
+
+ menu.setHeaderTitle(selectedEntry.data);
+ menu.add(ContextMenu.NONE, ContextMenuIds.COPY_TEXT,
+ ContextMenu.NONE, getString(R.string.copy_text));
+
+ String selectedMimeType = selectedEntry.mimetype;
+
+ // Only allow primary support for Phone and Email content types
+ if (Phone.CONTENT_ITEM_TYPE.equals(selectedMimeType) ||
+ Email.CONTENT_ITEM_TYPE.equals(selectedMimeType)) {
+
+ // Used to determine if entry is the only mime type of its kind
+ boolean isUniqueMimeType = true;
+
+ // Checking for unique mime type
+ for (int positionCounter = 0; positionCounter < mAllEntries.size(); positionCounter++) {
+ final ViewEntry entry = mAllEntries.get(positionCounter);
+
+ // Ignoring cases where entry is not a detail entry
+ if (entry.getViewType() != ViewAdapter.VIEW_TYPE_DETAIL_ENTRY) continue;
+
+ final DetailViewEntry checkEntry = (DetailViewEntry) entry;
+ if (positionCounter != info.position &&
+ checkEntry.mimetype.equalsIgnoreCase(selectedMimeType)) {
+ isUniqueMimeType = false;
+ break;
+ }
+ }
+
+ // Checking for previously set default
+ if (selectedEntry.isPrimary) {
+ menu.add(ContextMenu.NONE, ContextMenuIds.CLEAR_DEFAULT,
+ ContextMenu.NONE, getString(R.string.clear_default));
+ } else if (!isUniqueMimeType) {
+ menu.add(ContextMenu.NONE, ContextMenuIds.SET_DEFAULT,
+ ContextMenu.NONE, getString(R.string.set_default));
+ }
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterView.AdapterContextMenuInfo menuInfo;
+ try {
+ menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+ } catch (ClassCastException e) {
+ Log.e(TAG, "bad menuInfo", e);
+ return false;
+ }
+
+ switch (item.getItemId()) {
+ case ContextMenuIds.COPY_TEXT:
+ copyToClipboard(menuInfo.position);
+ return true;
+ case ContextMenuIds.SET_DEFAULT:
+ setDefaultContactMethod(mListView.getItemIdAtPosition(menuInfo.position));
+ return true;
+ case ContextMenuIds.CLEAR_DEFAULT:
+ clearDefaultContactMethod(mListView.getItemIdAtPosition(menuInfo.position));
+ return true;
+ default:
+ throw new IllegalArgumentException("Unknown menu option " + item.getItemId());
+ }
+ }
+
+ private void setDefaultContactMethod(long id) {
+ Intent setIntent = ContactSaveService.createSetSuperPrimaryIntent(mContext, id);
+ mContext.startService(setIntent);
+ }
+
+ private void clearDefaultContactMethod(long id) {
+ Intent clearIntent = ContactSaveService.createClearPrimaryIntent(mContext, id);
+ mContext.startService(clearIntent);
+ }
+
+ private void copyToClipboard(int viewEntryPosition) {
+ // Getting the text to copied
+ DetailViewEntry detailViewEntry = (DetailViewEntry) mAllEntries.get(viewEntryPosition);
+ CharSequence textToCopy = detailViewEntry.data;
+
+ // Checking for empty string
+ if (TextUtils.isEmpty(textToCopy)) return;
+
+ // Adding item to clipboard
+ ClipboardManager clipboardManager = (ClipboardManager) getActivity().getSystemService(
Context.CLIPBOARD_SERVICE);
- cm.setText(text);
- Toast.makeText(getActivity(), R.string.toast_text_copied, Toast.LENGTH_SHORT).show();
- return true;
+ String[] mimeTypes = new String[]{detailViewEntry.mimetype};
+ ClipData.Item clipDataItem = new ClipData.Item(textToCopy);
+ ClipData cd = new ClipData(detailViewEntry.typeString, mimeTypes, clipDataItem);
+ clipboardManager.setPrimaryClip(cd);
+
+ // Display Confirmation Toast
+ String toastText = getString(R.string.toast_text_copied);
+ Toast.makeText(getActivity(), toastText, Toast.LENGTH_SHORT).show();
}
@Override
@@ -1723,11 +1826,12 @@
if (defaultGroupId == -1) return false;
final Entity rawContactEntity = mContactData.getEntities().get(0);
- final String accountType =
- rawContactEntity.getEntityValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ ContentValues rawValues = rawContactEntity.getEntityValues();
+ final String accountType = rawValues.getAsString(RawContacts.ACCOUNT_TYPE);
+ final String dataSet = rawValues.getAsString(RawContacts.DATA_SET);
final AccountTypeManager accountTypes =
AccountTypeManager.getInstance(mContext);
- final AccountType type = accountTypes.getAccountType(accountType);
+ final AccountType type = accountTypes.getAccountType(accountType, dataSet);
// Offline or non-writeable account? Nothing to fix
if (type == null || type.readOnly) return false;
@@ -1769,7 +1873,8 @@
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
final ValuesDelta values = rawContactEntityDelta.getValues();
final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
- final AccountType type = accountTypes.getAccountType(accountType);
+ final String dataSet = values.getAsString(RawContacts.DATA_SET);
+ final AccountType type = accountTypes.getAccountType(accountType, dataSet);
final DataKind groupMembershipKind = type.getKindForMimetype(
GroupMembership.CONTENT_ITEM_TYPE);
final ValuesDelta entry = EntityModifier.insertChild(rawContactEntityDelta,
@@ -1813,12 +1918,12 @@
int exportSupport = mContactData.getDirectoryExportSupport();
switch (exportSupport) {
case Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY: {
- createCopy(new Account(mContactData.getDirectoryAccountName(),
- mContactData.getDirectoryAccountType()));
+ createCopy(new AccountWithDataSet(mContactData.getDirectoryAccountName(),
+ mContactData.getDirectoryAccountType(), null));
break;
}
case Directory.EXPORT_SUPPORT_ANY_ACCOUNT: {
- final ArrayList<Account> accounts =
+ final List<AccountWithDataSet> accounts =
AccountTypeManager.getInstance(mContext).getAccounts(true);
if (accounts.isEmpty()) {
createCopy(null);
@@ -1896,9 +2001,10 @@
* User requested creation of a new contact with the specified values.
*
* @param values ContentValues containing data rows for the new contact.
- * @param account Account where the new contact should be created
+ * @param account Account where the new contact should be created.
*/
- public void onCreateRawContactRequested(ArrayList<ContentValues> values, Account account);
+ public void onCreateRawContactRequested(ArrayList<ContentValues> values,
+ AccountWithDataSet account);
}
/**
@@ -1914,12 +2020,12 @@
mContext = context;
mInflater = LayoutInflater.from(context);
mContactData = contactData;
- final List<String> types = contactData.getInvitableAccontTypes();
+ final List<AccountType> types = contactData.getInvitableAccountTypes();
mAccountTypes = new ArrayList<AccountType>(types.size());
AccountTypeManager manager = AccountTypeManager.getInstance(context);
for (int i = 0; i < types.size(); i++) {
- mAccountTypes.add(manager.getAccountType(types.get(i)));
+ mAccountTypes.add(types.get(i));
}
Collections.sort(mAccountTypes, new AccountType.DisplayLabelComparator(mContext));
diff --git a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
index da64c34..95cf055 100644
--- a/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailFragmentCarousel.java
@@ -46,8 +46,8 @@
private int mCurrentPage = ABOUT_PAGE;
private int mLastScrollPosition;
- private FragmentOverlay mAboutFragment;
- private FragmentOverlay mUpdatesFragment;
+ private ViewOverlay mAboutFragment;
+ private ViewOverlay mUpdatesFragment;
private static final float MAX_ALPHA = 0.5f;
@@ -69,7 +69,7 @@
setOnTouchListener(this);
}
- public void setAboutFragment(FragmentOverlay fragment) {
+ public void setAboutFragment(ViewOverlay fragment) {
// TODO: We can't always assume the "about" page will be the current page.
mAboutFragment = fragment;
mAboutFragment.enableAlphaLayer();
@@ -77,7 +77,7 @@
mAboutFragment.disableTouchInterceptor();
}
- public void setUpdatesFragment(FragmentOverlay fragment) {
+ public void setUpdatesFragment(ViewOverlay fragment) {
mUpdatesFragment = fragment;
mUpdatesFragment.enableAlphaLayer();
mUpdatesFragment.setAlphaLayerValue(MAX_ALPHA);
diff --git a/src/com/android/contacts/detail/ContactDetailTabCarousel.java b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
index 26987f6..79ac5fb 100644
--- a/src/com/android/contacts/detail/ContactDetailTabCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
@@ -24,6 +24,7 @@
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
@@ -49,15 +50,22 @@
private Listener mListener;
- private final View[] mTabs = new View[TAB_COUNT];
+ private int mCurrentTab = TAB_INDEX_ABOUT;
+
+ private CarouselTab mAboutTab;
+ private CarouselTab mUpdatesTab;
private int mTabWidth;
private int mTabHeight;
private int mTabDisplayLabelHeight;
+ private int mLastScrollPosition;
+
private int mAllowedHorizontalScrollLength = Integer.MIN_VALUE;
private int mAllowedVerticalScrollLength = Integer.MIN_VALUE;
+ private static final float MAX_ALPHA = 0.5f;
+
/**
* Interface for callbacks invoked when the user interacts with the carousel.
*/
@@ -83,40 +91,28 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- View aboutView = findViewById(R.id.tab_about);
- View updateView = findViewById(R.id.tab_update);
+ mAboutTab = (CarouselTab) findViewById(R.id.tab_about);
+ mAboutTab.setLabel(mContext.getString(R.string.contactDetailAbout));
- TextView aboutTab = (TextView) aboutView.findViewById(R.id.label);
- aboutTab.setText(mContext.getString(R.string.contactDetailAbout));
- aboutTab.setClickable(true);
- aboutTab.setSelected(true);
+ mUpdatesTab = (CarouselTab) findViewById(R.id.tab_update);
+ mUpdatesTab.setLabel(mContext.getString(R.string.contactDetailUpdates));
- TextView updatesTab = (TextView) updateView.findViewById(R.id.label);
- updatesTab.setText(mContext.getString(R.string.contactDetailUpdates));
- updatesTab.setClickable(true);
+ // TODO: We can't always assume the "about" page will be the current page.
+ mAboutTab.showSelectedState();
+ mAboutTab.enableAlphaLayer();
+ mAboutTab.setAlphaLayerValue(0);
+ mAboutTab.enableTouchInterceptor(mAboutTabTouchInterceptListener);
- aboutTab.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mListener.onTabSelected(TAB_INDEX_ABOUT);
- }
- });
- updatesTab.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mListener.onTabSelected(TAB_INDEX_ABOUT);
- }
- });
-
- mTabs[TAB_INDEX_ABOUT] = aboutTab;
- mTabs[TAB_INDEX_UPDATES] = updatesTab;
+ mUpdatesTab.enableAlphaLayer();
+ mUpdatesTab.setAlphaLayerValue(MAX_ALPHA);
+ mUpdatesTab.enableTouchInterceptor(mUpdatesTabTouchInterceptListener);
// Retrieve the photo view for the "about" tab
- mPhotoView = (ImageView) aboutView.findViewById(R.id.photo);
+ mPhotoView = (ImageView) mAboutTab.findViewById(R.id.photo);
// Retrieve the social update views for the "updates" tab
- mStatusView = (TextView) updateView.findViewById(R.id.status);
- mStatusPhotoView = (ImageView) updateView.findViewById(R.id.status_photo);
+ mStatusView = (TextView) mUpdatesTab.findViewById(R.id.status);
+ mStatusPhotoView = (ImageView) mUpdatesTab.findViewById(R.id.status_photo);
}
@Override
@@ -143,10 +139,33 @@
}
}
+ private final OnClickListener mAboutTabTouchInterceptListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mListener.onTabSelected(TAB_INDEX_ABOUT);
+ }
+ };
+
+ private final OnClickListener mUpdatesTabTouchInterceptListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mListener.onTabSelected(TAB_INDEX_UPDATES);
+ }
+ };
+
+ private void updateAlphaLayers() {
+ mAboutTab.setAlphaLayerValue(mLastScrollPosition * MAX_ALPHA /
+ mAllowedHorizontalScrollLength);
+ mUpdatesTab.setAlphaLayerValue(MAX_ALPHA - mLastScrollPosition * MAX_ALPHA /
+ mAllowedVerticalScrollLength);
+ }
+
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
mListener.onScrollChanged(l, t, oldl, oldt);
+ mLastScrollPosition = l;
+ updateAlphaLayers();
}
/**
@@ -168,24 +187,21 @@
* Updates the tab selection.
*/
public void setCurrentTab(int position) {
- if (position < 0 || position > mTabs.length) {
- throw new IllegalStateException("Invalid position in array of tabs: " + position);
- }
// TODO: Handle device rotation (saving and restoring state of the selected tab)
// This will take more work because there is no tab carousel in phone landscape
- if (mTabs[position] == null) {
- return;
+ switch (position) {
+ case TAB_INDEX_ABOUT:
+ mAboutTab.showSelectedState();
+ mUpdatesTab.showDeselectedState();
+ break;
+ case TAB_INDEX_UPDATES:
+ mUpdatesTab.showSelectedState();
+ mAboutTab.showDeselectedState();
+ break;
+ default:
+ throw new IllegalStateException("Invalid tab position " + position);
}
- mTabs[position].setSelected(true);
- unselectAllOtherTabs(position);
- }
-
- private void unselectAllOtherTabs(int position) {
- for (int i = 0; i < mTabs.length; i++) {
- if (position != i) {
- mTabs[i].setSelected(false);
- }
- }
+ mCurrentTab = position;
}
/**
@@ -197,6 +213,8 @@
return;
}
+ // TODO: Move this into the {@link CarouselTab} class when the updates fragment code is more
+ // finalized
ContactDetailDisplayUtils.setPhoto(mContext, contactData, mPhotoView);
ContactDetailDisplayUtils.setSocialSnippet(mContext, contactData, mStatusView,
mStatusPhotoView);
diff --git a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
index daeae00..602958d 100644
--- a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
@@ -31,7 +31,7 @@
import android.widget.TextView;
public class ContactDetailUpdatesFragment extends Fragment
- implements FragmentKeyListener, FragmentOverlay {
+ implements FragmentKeyListener, ViewOverlay {
private static final String TAG = "ContactDetailUpdatesFragment";
diff --git a/src/com/android/contacts/detail/ContactLoaderFragment.java b/src/com/android/contacts/detail/ContactLoaderFragment.java
index 9085670..8d3cd75 100644
--- a/src/com/android/contacts/detail/ContactLoaderFragment.java
+++ b/src/com/android/contacts/detail/ContactLoaderFragment.java
@@ -17,8 +17,9 @@
package com.android.contacts.detail;
import com.android.contacts.ContactLoader;
-import com.android.contacts.ContactOptionsActivity;
+import com.android.contacts.ContactSaveService;
import com.android.contacts.R;
+import com.android.contacts.activities.ContactDetailActivity;
import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
import com.android.contacts.util.PhoneCapabilityTester;
import com.android.internal.util.Objects;
@@ -28,9 +29,11 @@
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.ActivityNotFoundException;
+import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
+import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.Contacts;
@@ -52,9 +55,15 @@
private static final String TAG = ContactLoaderFragment.class.getSimpleName();
+ /** The launch code when picking a ringtone */
+ private static final int REQUEST_CODE_PICK_RINGTONE = 1;
+
+
private boolean mOptionsMenuOptions;
private boolean mOptionsMenuEditable;
private boolean mOptionsMenuShareable;
+ private boolean mSendToVoicemailState;
+ private String mCustomRingtone;
/**
* This is a listener to the {@link ContactLoaderFragment} and will be notified when the
@@ -194,6 +203,8 @@
mListener.onDetailsLoaded(mContactData);
}
}
+ // Make sure the options menu is setup correctly with the loaded data.
+ getActivity().invalidateOptionsMenu();
}
@Override
@@ -222,11 +233,22 @@
mOptionsMenuOptions = isContactOptionsChangeEnabled();
mOptionsMenuEditable = isContactEditable();
mOptionsMenuShareable = isContactShareable();
+ if (mContactData != null) {
+ mSendToVoicemailState = mContactData.isSendToVoicemail();
+ mCustomRingtone = mContactData.getCustomRingtone();
+ }
- // Options only shows telephony-related settings (ringtone, send to voicemail).
- // ==> Hide if we don't have a telephone
- final MenuItem optionsMenu = menu.findItem(R.id.menu_options);
- optionsMenu.setVisible(mOptionsMenuOptions);
+ // Hide telephony-related settings (ringtone, send to voicemail)
+ // if we don't have a telephone
+ final MenuItem optionsSendToVoicemail = menu.findItem(R.id.menu_send_to_voicemail);
+ if (optionsSendToVoicemail != null) {
+ optionsSendToVoicemail.setChecked(mSendToVoicemailState);
+ optionsSendToVoicemail.setVisible(mOptionsMenuOptions);
+ }
+ final MenuItem optionsRingtone = menu.findItem(R.id.menu_set_ringtone);
+ if (optionsRingtone != null) {
+ optionsRingtone.setVisible(mOptionsMenuOptions);
+ }
final MenuItem editMenu = menu.findItem(R.id.menu_edit);
editMenu.setVisible(mOptionsMenuEditable);
@@ -262,11 +284,9 @@
if (mListener != null) mListener.onDeleteRequested(mLookupUri);
return true;
}
- case R.id.menu_options: {
+ case R.id.menu_set_ringtone: {
if (mContactData == null) return false;
- final Intent intent = new Intent(mContext, ContactOptionsActivity.class);
- intent.setData(mContactData.getLookupUri());
- mContext.startActivity(intent);
+ doPickRingtone();
return true;
}
case R.id.menu_share: {
@@ -290,6 +310,15 @@
}
return true;
}
+ case R.id.menu_send_to_voicemail: {
+ // Update state and save
+ mSendToVoicemailState = !mSendToVoicemailState;
+ item.setChecked(mSendToVoicemailState);
+ Intent intent = ContactSaveService.createSetSendToVoicemail(
+ mContext, mLookupUri, mSendToVoicemailState);
+ mContext.startService(intent);
+ return true;
+ }
}
return false;
}
@@ -304,4 +333,55 @@
}
return false;
}
+
+ private void doPickRingtone() {
+
+ Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
+ // Allow user to pick 'Default'
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
+ // Show only ringtones
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_RINGTONE);
+ // Don't show 'Silent'
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
+
+ Uri ringtoneUri;
+ if (mCustomRingtone != null) {
+ ringtoneUri = Uri.parse(mCustomRingtone);
+ } else {
+ // Otherwise pick default ringtone Uri so that something is selected.
+ ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
+ }
+
+ // Put checkmark next to the current ringtone for this contact
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, ringtoneUri);
+
+ // Launch!
+ startActivityForResult(intent, REQUEST_CODE_PICK_RINGTONE);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ switch (requestCode) {
+ case REQUEST_CODE_PICK_RINGTONE: {
+ Uri pickedUri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
+ handleRingtonePicked(pickedUri);
+ break;
+ }
+ }
+ }
+
+ private void handleRingtonePicked(Uri pickedUri) {
+ if (pickedUri == null || RingtoneManager.isDefault(pickedUri)) {
+ mCustomRingtone = null;
+ } else {
+ mCustomRingtone = pickedUri.toString();
+ }
+ Intent intent = ContactSaveService.createSetRingtone(
+ mContext, mLookupUri, mCustomRingtone);
+ mContext.startService(intent);
+ }
}
diff --git a/src/com/android/contacts/detail/FragmentOverlay.java b/src/com/android/contacts/detail/ViewOverlay.java
similarity index 88%
rename from src/com/android/contacts/detail/FragmentOverlay.java
rename to src/com/android/contacts/detail/ViewOverlay.java
index 6ef0846..4c14460 100644
--- a/src/com/android/contacts/detail/FragmentOverlay.java
+++ b/src/com/android/contacts/detail/ViewOverlay.java
@@ -19,12 +19,12 @@
import android.view.View.OnClickListener;
/**
- * This is implemented by {@link Fragment}s that contain an alpha layer and touch interceptor layer.
+ * This is implemented by {@link View}s that contain an alpha layer and touch interceptor layer.
* The alpha layer covers the entire fragment and has an alpha value which makes the fragment
* contents appear "dimmed" out. The touch interceptor layer covers the entire fragment so that
- * when visible, it intercepts all touch events on the {@link Fragment}.
+ * when visible, it intercepts all touch events on the {@link View}.
*/
-public interface FragmentOverlay {
+public interface ViewOverlay {
/**
* Sets the alpha value on the alpha layer (if there is one).
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index 6acbc85..add6b80 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -538,10 +538,8 @@
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
-
- // If the hardware doesn't have a hardware menu key, we'll show soft menu button on the
- // right side of digits EditText.
- if (ViewConfiguration.get(getActivity()).hasPermanentMenuKey()) {
+ if (mShowOptionsMenu && ViewConfiguration.get(getActivity()).hasPermanentMenuKey() &&
+ mDialpadChooser != null && mDigits != null) {
inflater.inflate(R.menu.dialpad_options, menu);
}
}
@@ -549,16 +547,9 @@
@Override
public void onPrepareOptionsMenu(Menu menu) {
// Hardware menu key should be available and Views should already be ready.
- if (ViewConfiguration.get(getActivity()).hasPermanentMenuKey() &&
+ if (mShowOptionsMenu && ViewConfiguration.get(getActivity()).hasPermanentMenuKey() &&
mDialpadChooser != null && mDigits != null) {
- if (mShowOptionsMenu) {
- setupMenuItems(menu);
- } else {
- menu.findItem(R.id.menu_call_settings_dialpad).setVisible(false);
- menu.findItem(R.id.menu_add_contacts).setVisible(false);
- menu.findItem(R.id.menu_2s_pause).setVisible(false);
- menu.findItem(R.id.menu_add_wait).setVisible(false);
- }
+ setupMenuItems(menu);
}
}
diff --git a/src/com/android/contacts/editor/AggregationSuggestionEngine.java b/src/com/android/contacts/editor/AggregationSuggestionEngine.java
index bb17bbb..0861d92 100644
--- a/src/com/android/contacts/editor/AggregationSuggestionEngine.java
+++ b/src/com/android/contacts/editor/AggregationSuggestionEngine.java
@@ -58,10 +58,12 @@
public long rawContactId;
public String accountType;
public String accountName;
+ public String dataSet;
@Override
public String toString() {
- return "ID: " + rawContactId + " account: " + accountType + "/" + accountName;
+ return "ID: " + rawContactId + " account: " + accountType + "/" + accountName
+ + " dataSet: " + dataSet;
}
}
@@ -277,6 +279,7 @@
Photo.PHOTO,
RawContacts.ACCOUNT_TYPE,
RawContacts.ACCOUNT_NAME,
+ RawContacts.DATA_SET
};
public static final int ID = 0;
@@ -291,6 +294,7 @@
public static final int PHOTO = 9;
public static final int ACCOUNT_TYPE = 10;
public static final int ACCOUNT_NAME = 11;
+ public static final int DATA_SET = 12;
}
private void loadAggregationSuggestions(Uri uri) {
@@ -390,6 +394,7 @@
rawContact.rawContactId = rawContactId;
rawContact.accountName = mDataCursor.getString(DataQuery.ACCOUNT_NAME);
rawContact.accountType = mDataCursor.getString(DataQuery.ACCOUNT_TYPE);
+ rawContact.dataSet = mDataCursor.getString(DataQuery.DATA_SET);
suggestion.rawContacts.add(rawContact);
}
diff --git a/src/com/android/contacts/editor/AggregationSuggestionView.java b/src/com/android/contacts/editor/AggregationSuggestionView.java
index af8e7f8..07e67e8 100644
--- a/src/com/android/contacts/editor/AggregationSuggestionView.java
+++ b/src/com/android/contacts/editor/AggregationSuggestionView.java
@@ -118,10 +118,11 @@
AccountTypeManager accountTypes = AccountTypeManager.getInstance(getContext());
for (RawContact rawContact : mRawContacts) {
String accountType = rawContact.accountType;
+ String dataSet = rawContact.dataSet;
if (accountType == null) {
return true;
}
- AccountType type = accountTypes.getAccountType(accountType);
+ AccountType type = accountTypes.getAccountType(accountType, dataSet);
if (!type.readOnly) {
return true;
}
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 5d9494c..e0f99ce 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -26,6 +26,7 @@
import com.android.contacts.editor.Editor.EditorListener;
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.model.EntityDeltaList;
@@ -316,11 +317,13 @@
if (mListener != null) mListener.setTitleTo(R.string.editContact_title_insert);
final Account account = mIntentExtras == null ? null :
- (Account) mIntentExtras.getParcelable(Intents.Insert.ACCOUNT);
+ (Account) mIntentExtras.getParcelable(Intents.Insert.ACCOUNT);
+ final String dataSet = mIntentExtras == null ? null :
+ mIntentExtras.getString(Intents.Insert.DATA_SET);
if (account != null) {
// Account specified in Intent
- createContact(account);
+ createContact(new AccountWithDataSet(account.name, account.type, dataSet));
} else {
// No Account specified. Let the user choose
// Load Accounts async so that we can present them
@@ -396,12 +399,15 @@
Entity entity = entities.get(0);
ContentValues entityValues = entity.getEntityValues();
String type = entityValues.getAsString(RawContacts.ACCOUNT_TYPE);
- AccountType accountType = AccountTypeManager.getInstance(mContext).getAccountType(type);
+ String dataSet = entityValues.getAsString(RawContacts.DATA_SET);
+ AccountType accountType = AccountTypeManager.getInstance(mContext).getAccountType(
+ type, dataSet);
if (accountType.getEditContactActivityClassName() != null) {
if (mListener != null) {
String name = entityValues.getAsString(RawContacts.ACCOUNT_NAME);
long rawContactId = entityValues.getAsLong(RawContacts.Entity._ID);
- mListener.onCustomEditContactActivityRequested(new Account(name, type),
+ mListener.onCustomEditContactActivityRequested(
+ new AccountWithDataSet(name, type, dataSet),
ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
mIntentExtras, true);
}
@@ -413,7 +419,7 @@
}
@Override
- public void onExternalEditorRequest(Account account, Uri uri) {
+ public void onExternalEditorRequest(AccountWithDataSet account, Uri uri) {
mListener.onCustomEditContactActivityRequested(account, uri, null, false);
}
@@ -440,7 +446,8 @@
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
for (EntityDelta state : mState) {
final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final AccountType type = accountTypes.getAccountType(accountType);
+ final String dataSet = state.getValues().getAsString(RawContacts.DATA_SET);
+ final AccountType type = accountTypes.getAccountType(accountType, dataSet);
if (!type.readOnly) {
// Apply extras to the first writable raw contact only
EntityModifier.parseExtras(mContext, type, state, extras);
@@ -454,7 +461,7 @@
* selected. If there's no available account, device-local contact should be created.
*/
private void createContact() {
- final ArrayList<Account> accounts =
+ final List<AccountWithDataSet> accounts =
AccountTypeManager.getInstance(mContext).getAccounts(true);
// No Accounts available. Create a phone-local contact.
if (accounts.isEmpty()) {
@@ -473,10 +480,11 @@
*
* @param account may be null to signal a device-local contact should be created.
*/
- private void createContact(Account account) {
+ private void createContact(AccountWithDataSet account) {
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
final AccountType accountType =
- accountTypes.getAccountType(account != null ? account.type : null);
+ accountTypes.getAccountType(account != null ? account.type : null,
+ account != null ? account.dataSet : null);
if (accountType.getCreateContactActivityClassName() != null) {
if (mListener != null) {
@@ -491,15 +499,17 @@
* Removes a current editor ({@link #mState}) and rebinds new editor for a new account.
* Some of old data are reused with new restriction enforced by the new account.
*
- * @param oldState Old data being editted.
+ * @param oldState Old data being edited.
* @param oldAccount Old account associated with oldState.
* @param newAccount New account to be used.
*/
private void rebindEditorsForNewContact(
- EntityDelta oldState, Account oldAccount, Account newAccount) {
+ EntityDelta oldState, AccountWithDataSet oldAccount, AccountWithDataSet newAccount) {
AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
- AccountType oldAccountType = accountTypes.getAccountType(oldAccount.type);
- AccountType newAccountType = accountTypes.getAccountType(newAccount.type);
+ AccountType oldAccountType = accountTypes.getAccountType(
+ oldAccount.type, oldAccount.dataSet);
+ AccountType newAccountType = accountTypes.getAccountType(
+ newAccount.type, newAccount.dataSet);
if (newAccountType.getCreateContactActivityClassName() != null) {
Log.w(TAG, "external activity called in rebind situation");
@@ -512,21 +522,24 @@
}
}
- private void bindEditorsForNewContact(Account account, final AccountType accountType) {
+ private void bindEditorsForNewContact(AccountWithDataSet account,
+ final AccountType accountType) {
bindEditorsForNewContact(account, accountType, null, null);
}
- private void bindEditorsForNewContact(Account newAccount, final AccountType newAccountType,
- EntityDelta oldState, AccountType oldAccountType) {
+ private void bindEditorsForNewContact(AccountWithDataSet newAccount,
+ final AccountType newAccountType, EntityDelta oldState, AccountType oldAccountType) {
mStatus = Status.EDITING;
final ContentValues values = new ContentValues();
if (newAccount != null) {
values.put(RawContacts.ACCOUNT_NAME, newAccount.name);
values.put(RawContacts.ACCOUNT_TYPE, newAccount.type);
+ values.put(RawContacts.DATA_SET, newAccount.dataSet);
} else {
values.putNull(RawContacts.ACCOUNT_NAME);
values.putNull(RawContacts.ACCOUNT_TYPE);
+ values.putNull(RawContacts.DATA_SET);
}
EntityDelta insert = new EntityDelta(ValuesDelta.fromAfter(values));
@@ -577,7 +590,8 @@
if (!values.isVisible()) continue;
final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
- final AccountType type = accountTypes.getAccountType(accountType);
+ final String dataSet = values.getAsString(RawContacts.DATA_SET);
+ final AccountType type = accountTypes.getAccountType(accountType, dataSet);
final long rawContactId = values.getAsLong(RawContacts._ID);
final BaseRawContactEditorView editor;
@@ -596,7 +610,7 @@
editor = rawContactEditor;
}
if (Intent.ACTION_INSERT.equals(mAction) && numRawContacts == 1) {
- final ArrayList<Account> accounts =
+ final List<AccountWithDataSet> accounts =
AccountTypeManager.getInstance(mContext).getAccounts(true);
if (accounts.size() > 1) {
addAccountSwitcher(mState.get(0), editor);
@@ -676,9 +690,10 @@
private void addAccountSwitcher(
final EntityDelta currentState, BaseRawContactEditorView editor) {
ValuesDelta values = currentState.getValues();
- final Account currentAccount = new Account(
+ final AccountWithDataSet currentAccount = new AccountWithDataSet(
values.getAsString(RawContacts.ACCOUNT_NAME),
- values.getAsString(RawContacts.ACCOUNT_TYPE));
+ values.getAsString(RawContacts.ACCOUNT_TYPE),
+ values.getAsString(RawContacts.DATA_SET));
final View accountView = editor.findViewById(R.id.account);
final View anchorView = editor.findViewById(R.id.anchor_for_account_switcher);
accountView.setOnClickListener(new View.OnClickListener() {
@@ -697,7 +712,7 @@
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
popup.dismiss();
- Account newAccount = adapter.getItem(position);
+ AccountWithDataSet newAccount = adapter.getItem(position);
if (!newAccount.equals(currentAccount)) {
rebindEditorsForNewContact(currentState, currentAccount, newAccount);
}
@@ -1059,7 +1074,8 @@
for (int i = 0; i < size; i++) {
ValuesDelta values = mState.get(i).getValues();
final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
- final AccountType type = accountTypes.getAccountType(accountType);
+ final String dataSet = values.getAsString(RawContacts.DATA_SET);
+ final AccountType type = accountTypes.getAccountType(accountType, dataSet);
if (!type.readOnly) {
return true;
}
@@ -1107,7 +1123,8 @@
* Contact is being created for an external account that provides its own
* new contact activity.
*/
- void onCustomCreateContactActivityRequested(Account account, Bundle intentExtras);
+ void onCustomCreateContactActivityRequested(AccountWithDataSet account,
+ Bundle intentExtras);
/**
* The edited raw contact belongs to an external account that provides
@@ -1116,7 +1133,7 @@
* @param redirect indicates that the current editor should be closed
* before the custom editor is shown.
*/
- void onCustomEditContactActivityRequested(Account account, Uri rawContactUri,
+ void onCustomEditContactActivityRequested(AccountWithDataSet account, Uri rawContactUri,
Bundle intentExtras, boolean redirect);
}
@@ -1132,10 +1149,12 @@
}
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(mContext);
- String accountType2 = one.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final AccountType type1 = accountTypes.getAccountType(accountType2);
- accountType2 = two.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final AccountType type2 = accountTypes.getAccountType(accountType2);
+ String accountType1 = one.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ String dataSet1 = one.getValues().getAsString(RawContacts.DATA_SET);
+ final AccountType type1 = accountTypes.getAccountType(accountType1, dataSet1);
+ String accountType2 = two.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+ String dataSet2 = two.getValues().getAsString(RawContacts.DATA_SET);
+ final AccountType type2 = accountTypes.getAccountType(accountType2, dataSet2);
// Check read-only
if (type1.readOnly && !type2.readOnly) {
@@ -1164,6 +1183,16 @@
value = type1.accountType.compareTo(type2.accountType);
if (value != 0) {
return value;
+ } else {
+ // Fall back to data set.
+ if (type1.dataSet != null) {
+ value = type1.dataSet.compareTo(type2.dataSet);
+ if (value != 0) {
+ return value;
+ }
+ } else if (type2.dataSet != null) {
+ return 1;
+ }
}
}
diff --git a/src/com/android/contacts/editor/ExternalRawContactEditorView.java b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
index e1a669b..734f013 100644
--- a/src/com/android/contacts/editor/ExternalRawContactEditorView.java
+++ b/src/com/android/contacts/editor/ExternalRawContactEditorView.java
@@ -18,14 +18,13 @@
import com.android.contacts.ContactsUtils;
import com.android.contacts.R;
-import com.android.contacts.editor.ExternalRawContactEditorView.Listener;
import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.model.DataKind;
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.model.EntityModifier;
-import android.accounts.Account;
import android.content.ContentUris;
import android.content.Context;
import android.net.Uri;
@@ -66,12 +65,13 @@
private String mAccountName;
private String mAccountType;
+ private String mDataSet;
private long mRawContactId = -1;
private Listener mListener;
public interface Listener {
- void onExternalEditorRequest(Account account, Uri uri);
+ void onExternalEditorRequest(AccountWithDataSet account, Uri uri);
}
public ExternalRawContactEditorView(Context context) {
@@ -127,6 +127,7 @@
ValuesDelta values = state.getValues();
mAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
mAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
+ mDataSet = values.getAsString(RawContacts.DATA_SET);
CharSequence accountType = type.getDisplayLabel(mContext);
if (TextUtils.isEmpty(accountType)) {
accountType = mContext.getString(R.string.account_phone);
@@ -136,6 +137,9 @@
mContext.getString(R.string.from_account_format, mAccountName));
}
mAccountTypeTextView.setText(mContext.getString(R.string.account_type_format, accountType));
+
+ // TODO: Expose data set in the UI somehow?
+
mAccountIcon.setImageDrawable(type.getDisplayIcon(mContext));
mRawContactId = values.getAsLong(RawContacts._ID);
@@ -221,7 +225,8 @@
public void onClick(View v) {
if (v.getId() == R.id.button_edit_externally) {
if (mListener != null) {
- mListener.onExternalEditorRequest(new Account(mAccountName, mAccountType),
+ mListener.onExternalEditorRequest(
+ new AccountWithDataSet(mAccountName, mAccountType, mDataSet),
ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactId));
}
}
diff --git a/src/com/android/contacts/editor/GroupMembershipView.java b/src/com/android/contacts/editor/GroupMembershipView.java
index a581b11..9693915 100644
--- a/src/com/android/contacts/editor/GroupMembershipView.java
+++ b/src/com/android/contacts/editor/GroupMembershipView.java
@@ -23,6 +23,7 @@
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.model.EntityModifier;
+import com.android.internal.util.Objects;
import android.app.Activity;
import android.content.Context;
@@ -84,6 +85,7 @@
private Cursor mGroupMetaData;
private String mAccountName;
private String mAccountType;
+ private String mDataSet;
private TextView mGroupList;
private ArrayAdapter<GroupSelectionItem> mAdapter;
private long mDefaultGroupId;
@@ -125,6 +127,7 @@
ValuesDelta values = state.getValues();
mAccountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
mAccountName = values.getAsString(RawContacts.ACCOUNT_NAME);
+ mDataSet = values.getAsString(RawContacts.DATA_SET);
mDefaultGroupVisibilityKnown = false;
updateView();
}
@@ -145,7 +148,9 @@
while (mGroupMetaData.moveToNext()) {
String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
- if (accountName.equals(mAccountName) && accountType.equals(mAccountType)) {
+ String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET);
+ if (accountName.equals(mAccountName) && accountType.equals(mAccountType)
+ && Objects.equal(dataSet, mDataSet)) {
long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
if (!mGroupMetaData.isNull(GroupMetaDataLoader.FAVORITES)
&& mGroupMetaData.getInt(GroupMetaDataLoader.FAVORITES) != 0) {
@@ -209,7 +214,9 @@
while (mGroupMetaData.moveToNext()) {
String accountName = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
String accountType = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
- if (accountName.equals(mAccountName) && accountType.equals(mAccountType)) {
+ String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET);
+ if (accountName.equals(mAccountName) && accountType.equals(mAccountType)
+ && Objects.equal(dataSet, mDataSet)) {
long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
if (groupId != mFavoritesGroupId
&& (groupId != mDefaultGroupId || mDefaultGroupVisible)) {
@@ -328,6 +335,7 @@
}
GroupCreationDialogFragment.show(
- ((Activity) getContext()).getFragmentManager(), mAccountType, mAccountName);
+ ((Activity) getContext()).getFragmentManager(), mAccountType, mAccountName,
+ mDataSet);
}
}
diff --git a/src/com/android/contacts/editor/RawContactEditorView.java b/src/com/android/contacts/editor/RawContactEditorView.java
index 499b86f..41f2ebd 100644
--- a/src/com/android/contacts/editor/RawContactEditorView.java
+++ b/src/com/android/contacts/editor/RawContactEditorView.java
@@ -24,6 +24,7 @@
import com.android.contacts.model.EntityDelta;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.model.EntityModifier;
+import com.android.internal.util.Objects;
import android.content.Context;
import android.content.Entity;
@@ -350,11 +351,14 @@
private long getDefaultGroupId() {
String accountType = mState.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
String accountName = mState.getValues().getAsString(RawContacts.ACCOUNT_NAME);
+ String accountDataSet = mState.getValues().getAsString(RawContacts.DATA_SET);
mGroupMetaData.moveToPosition(-1);
while (mGroupMetaData.moveToNext()) {
String name = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_NAME);
String type = mGroupMetaData.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
- if (name.equals(accountName) && type.equals(accountType)) {
+ String dataSet = mGroupMetaData.getString(GroupMetaDataLoader.DATA_SET);
+ if (name.equals(accountName) && type.equals(accountType)
+ && Objects.equal(dataSet, accountDataSet)) {
long groupId = mGroupMetaData.getLong(GroupMetaDataLoader.GROUP_ID);
if (!mGroupMetaData.isNull(GroupMetaDataLoader.AUTO_ADD)
&& mGroupMetaData.getInt(GroupMetaDataLoader.AUTO_ADD) != 0) {
diff --git a/src/com/android/contacts/editor/SelectAccountDialogFragment.java b/src/com/android/contacts/editor/SelectAccountDialogFragment.java
index 0a33f25..9dbe20a 100644
--- a/src/com/android/contacts/editor/SelectAccountDialogFragment.java
+++ b/src/com/android/contacts/editor/SelectAccountDialogFragment.java
@@ -17,9 +17,9 @@
package com.android.contacts.editor;
import com.android.contacts.R;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.util.AccountsListAdapter;
-import android.accounts.Account;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -80,10 +80,10 @@
}
/**
- * Calls {@link Listener#onAccountChosen(int, Account)} if the target fragment is castable
- * to {@link Listener}. Subclasses can also overide to directly perform an operation
+ * Calls {@link Listener#onAccountChosen(int, AccountWithDataSet)} if the target fragment is
+ * castable to {@link Listener}. Subclasses can also overide to directly perform an operation.
*/
- protected void onAccountSelected(Account account) {
+ protected void onAccountSelected(AccountWithDataSet account) {
final Fragment targetFragment = getTargetFragment();
if (targetFragment != null && targetFragment instanceof Listener) {
final Listener target = (Listener) targetFragment;
@@ -92,7 +92,7 @@
}
public interface Listener {
- void onAccountChosen(int requestCode, Account account);
+ void onAccountChosen(int requestCode, AccountWithDataSet account);
void onAccountSelectorCancelled();
}
}
diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java
index be99738..753261a 100644
--- a/src/com/android/contacts/group/GroupBrowseListAdapter.java
+++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java
@@ -20,6 +20,8 @@
import com.android.contacts.R;
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
+import com.android.internal.util.Objects;
import android.content.ContentUris;
import android.content.Context;
@@ -104,6 +106,7 @@
}
String accountName = mCursor.getString(GroupListLoader.ACCOUNT_NAME);
String accountType = mCursor.getString(GroupListLoader.ACCOUNT_TYPE);
+ String dataSet = mCursor.getString(GroupListLoader.DATA_SET);
long groupId = mCursor.getLong(GroupListLoader.GROUP_ID);
String title = mCursor.getString(GroupListLoader.TITLE);
int memberCount = mCursor.getInt(GroupListLoader.MEMBER_COUNT);
@@ -117,14 +120,17 @@
if (previousIndex >= 0 && mCursor.moveToPosition(previousIndex)) {
String previousGroupAccountName = mCursor.getString(GroupListLoader.ACCOUNT_NAME);
String previousGroupAccountType = mCursor.getString(GroupListLoader.ACCOUNT_TYPE);
+ String previousGroupDataSet = mCursor.getString(GroupListLoader.DATA_SET);
+
if (accountName.equals(previousGroupAccountName) &&
- accountType.equals(previousGroupAccountType)) {
+ accountType.equals(previousGroupAccountType) &&
+ Objects.equal(dataSet, previousGroupDataSet)) {
isFirstGroupInAccount = false;
}
}
- return new GroupListItem(accountName, accountType, groupId, title, isFirstGroupInAccount,
- memberCount, groupCountForThisAccount);
+ return new GroupListItem(accountName, accountType, dataSet, groupId, title,
+ isFirstGroupInAccount, memberCount, groupCountForThisAccount);
}
@Override
@@ -167,7 +173,8 @@
}
private void bindHeaderView(GroupListItem entry, GroupListItemViewCache viewCache) {
- AccountType accountType = mAccountTypeManager.getAccountType(entry.getAccountType());
+ AccountType accountType = mAccountTypeManager.getAccountType(
+ entry.getAccountType(), entry.getDataSet());
viewCache.accountType.setText(accountType.getDisplayLabel(mContext).toString());
viewCache.accountName.setText(entry.getAccountName());
diff --git a/src/com/android/contacts/group/GroupBrowseListFragment.java b/src/com/android/contacts/group/GroupBrowseListFragment.java
index d0d370e..a1544cf 100644
--- a/src/com/android/contacts/group/GroupBrowseListFragment.java
+++ b/src/com/android/contacts/group/GroupBrowseListFragment.java
@@ -27,15 +27,19 @@
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.CursorLoader;
+import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
+import android.provider.ContactsContract;
+import android.provider.Settings;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
@@ -61,6 +65,7 @@
* @param groupUri for the group that the user wishes to view.
*/
void onViewGroupAction(Uri groupUri);
+
}
private static final String TAG = "GroupBrowseListFragment";
@@ -77,6 +82,8 @@
private View mRootView;
private AutoScrollListView mListView;
private View mEmptyView;
+ private View mAddAccountsView;
+ private View mAddAccountButton;
private GroupBrowseListAdapter mAdapter;
private boolean mSelectionVisible;
@@ -113,6 +120,21 @@
}
});
+ mEmptyView = mRootView.findViewById(R.id.empty);
+ mAddAccountsView = mRootView.findViewById(R.id.add_accounts);
+ mAddAccountButton = mRootView.findViewById(R.id.add_account_button);
+ mAddAccountButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ intent.putExtra(Settings.EXTRA_AUTHORITIES,
+ new String[] { ContactsContract.AUTHORITY });
+ startActivity(intent);
+ }
+ });
+ setAddAccountsVisibility(false);
+
if (savedInstanceState != null) {
String groupUriString = savedInstanceState.getString(EXTRA_KEY_GROUP_URI);
if (groupUriString != null) {
@@ -281,4 +303,10 @@
}
}
}
+
+ public void setAddAccountsVisibility(boolean visible) {
+ if (mAddAccountsView != null) {
+ mAddAccountsView.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+ }
}
diff --git a/src/com/android/contacts/group/GroupDetailDisplayUtils.java b/src/com/android/contacts/group/GroupDetailDisplayUtils.java
index 56413b5..bb4cd5c 100644
--- a/src/com/android/contacts/group/GroupDetailDisplayUtils.java
+++ b/src/com/android/contacts/group/GroupDetailDisplayUtils.java
@@ -37,14 +37,15 @@
return inflater.inflate(R.layout.group_source_button, null);
}
- public static void bindGroupSourceView(Context context, View view, String accountTypeString) {
+ public static void bindGroupSourceView(Context context, View view, String accountTypeString,
+ String dataSet) {
ImageView accountIcon = (ImageView) view.findViewById(android.R.id.icon);
if (accountIcon == null) {
throw new IllegalStateException("Group source view must contain view with id"
+ "android.R.id.icon");
}
AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(context);
- AccountType accountType = accountTypeManager.getAccountType(accountTypeString);
+ AccountType accountType = accountTypeManager.getAccountType(accountTypeString, dataSet);
accountIcon.setImageDrawable(accountType.getDisplayIcon(context));
}
}
\ No newline at end of file
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index 576f6c1..019dc11 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -70,8 +70,8 @@
/**
* The group source (intent action and action URI) has been determined.
*/
- public void onGroupSourceUpdated(String accountTypeString, String groupSourceAction,
- String groupSourceUri);
+ public void onGroupSourceUpdated(String accountTypeString, String dataSet,
+ String groupSourceAction, String groupSourceUri);
/**
* User decided to go to Edit-Mode
@@ -108,6 +108,7 @@
private long mGroupId;
private String mGroupName;
private String mAccountTypeString;
+ private String mDataSet;
private boolean mIsReadOnly;
private boolean mShowGroupActionInActionBar;
@@ -271,6 +272,7 @@
cursor.moveToPosition(-1);
if (cursor.moveToNext()) {
mAccountTypeString = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+ mDataSet = cursor.getString(GroupMetaDataLoader.DATA_SET);
mGroupId = cursor.getLong(GroupMetaDataLoader.GROUP_ID);
mGroupName = cursor.getString(GroupMetaDataLoader.TITLE);
mIsReadOnly = cursor.getInt(GroupMetaDataLoader.IS_READ_ONLY) == 1;
@@ -279,9 +281,10 @@
getActivity().invalidateOptionsMenu ();
final String accountTypeString = cursor.getString(GroupMetaDataLoader.ACCOUNT_TYPE);
+ final String dataSet = cursor.getString(GroupMetaDataLoader.DATA_SET);
final String groupSourceAction = cursor.getString(GroupMetaDataLoader.ACTION);
final String groupSourceUri = cursor.getString(GroupMetaDataLoader.ACTION_URI);
- updateGroupSouce(accountTypeString, groupSourceAction, groupSourceUri);
+ updateGroupSource(accountTypeString, dataSet, groupSourceAction, groupSourceUri);
}
}
@@ -304,7 +307,8 @@
} else {
String groupSizeTemplateString = getResources().getQuantityString(
R.plurals.num_contacts_in_group, size);
- AccountType accountType = mAccountTypeManager.getAccountType(mAccountTypeString);
+ AccountType accountType = mAccountTypeManager.getAccountType(mAccountTypeString,
+ mDataSet);
groupSizeString = String.format(groupSizeTemplateString, size,
accountType.getDisplayLabel(mContext));
}
@@ -323,14 +327,15 @@
* a button in a static header on the page, or as a header that scrolls with the
* {@link ListView}.
*/
- private void updateGroupSouce(final String accountTypeString, final String groupSourceAction,
- final String groupSourceUri) {
+ private void updateGroupSource(final String accountTypeString, final String dataSet,
+ final String groupSourceAction, final String groupSourceUri) {
// If the group action should be shown in the action bar, then pass the data to the
// listener who will take care of setting up the view and click listener. There is nothing
// else to be done by this {@link Fragment}.
if (mShowGroupActionInActionBar) {
- mListener.onGroupSourceUpdated(accountTypeString, groupSourceAction, groupSourceUri);
+ mListener.onGroupSourceUpdated(accountTypeString, dataSet, groupSourceAction,
+ groupSourceUri);
return;
}
@@ -354,7 +359,7 @@
// Rebind the data since this action can change if the loader returns updated data
mGroupSourceView.setVisibility(View.VISIBLE);
GroupDetailDisplayUtils.bindGroupSourceView(mContext, mGroupSourceView,
- accountTypeString);
+ accountTypeString, dataSet);
mGroupSourceView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/src/com/android/contacts/group/GroupEditorFragment.java b/src/com/android/contacts/group/GroupEditorFragment.java
index f17ac60..9a09c77 100644
--- a/src/com/android/contacts/group/GroupEditorFragment.java
+++ b/src/com/android/contacts/group/GroupEditorFragment.java
@@ -27,6 +27,7 @@
import com.android.contacts.group.SuggestedMemberListAdapter.SuggestedMember;
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.internal.util.Objects;
import android.accounts.Account;
@@ -172,6 +173,7 @@
private boolean mGroupNameIsReadOnly;
private String mAccountName;
private String mAccountType;
+ private String mDataSet;
private String mOriginalGroupName = "";
private MemberListAdapter mMemberListAdapter;
@@ -221,11 +223,14 @@
final Account account = mIntentExtras == null ? null :
(Account) mIntentExtras.getParcelable(Intents.Insert.ACCOUNT);
+ final String dataSet = mIntentExtras == null ? null :
+ mIntentExtras.getString(Intents.Insert.DATA_SET);
if (account != null) {
- // Account specified in Intent
+ // Account specified in Intent - no data set can be specified in this manner.
mAccountName = account.name;
mAccountType = account.type;
+ mDataSet = dataSet;
setupEditorForAccount();
} else {
// No Account specified. Let the user choose from a disambiguation dialog.
@@ -245,7 +250,7 @@
}
private void selectAccountAndCreateGroup() {
- final ArrayList<Account> accounts =
+ final List<AccountWithDataSet> accounts =
AccountTypeManager.getInstance(mContext).getAccounts(true /* writeable */);
// No Accounts available
if (accounts.isEmpty()) {
@@ -257,6 +262,7 @@
if (accounts.size() == 1) {
mAccountName = accounts.get(0).name;
mAccountType = accounts.get(0).type;
+ mDataSet = accounts.get(0).dataSet;
setupEditorForAccount();
return; // Don't show a dialog.
}
@@ -268,9 +274,10 @@
}
@Override
- public void onAccountChosen(int requestCode, Account account) {
+ public void onAccountChosen(int requestCode, AccountWithDataSet account) {
mAccountName = account.name;
mAccountType = account.type;
+ mDataSet = account.dataSet;
setupEditorForAccount();
}
@@ -287,7 +294,7 @@
*/
private void setupEditorForAccount() {
final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(mContext);
- final AccountType accountType = accountTypeManager.getAccountType(mAccountType);
+ final AccountType accountType = accountTypeManager.getAccountType(mAccountType, mDataSet);
final boolean editable = accountType.isGroupMembershipEditable();
mMemberListAdapter.setIsGroupMembershipEditable(editable);
@@ -388,6 +395,10 @@
mListener = value;
}
+ public void doSaveAction() {
+ save(SaveMode.CLOSE);
+ }
+
@Override
public void onCreateOptionsMenu(Menu menu, final MenuInflater inflater) {
inflater.inflate(R.menu.edit_group, menu);
@@ -396,8 +407,6 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.menu_done:
- return save(SaveMode.CLOSE);
case R.id.menu_discard:
return revert();
}
@@ -476,16 +485,17 @@
return false;
}
Intent saveIntent = null;
- if (mAction == Intent.ACTION_INSERT) {
+ if (Intent.ACTION_INSERT.equals(mAction)) {
// Create array of raw contact IDs for contacts to add to the group
long[] membersToAddArray = convertToArray(mListMembersToAdd);
// Create the save intent to create the group and add members at the same time
saveIntent = ContactSaveService.createNewGroupIntent(activity,
- new Account(mAccountName, mAccountType), mGroupNameView.getText().toString(),
+ new AccountWithDataSet(mAccountName, mAccountType, mDataSet),
+ mGroupNameView.getText().toString(),
membersToAddArray, activity.getClass(),
GroupEditorActivity.ACTION_SAVE_COMPLETED);
- } else if (mAction == Intent.ACTION_EDIT) {
+ } else if (Intent.ACTION_EDIT.equals(mAction)) {
// Create array of raw contact IDs for contacts to add to the group
long[] membersToAddArray = convertToArray(mListMembersToAdd);
diff --git a/src/com/android/contacts/group/GroupListItem.java b/src/com/android/contacts/group/GroupListItem.java
index 349b86e..c707ea7 100644
--- a/src/com/android/contacts/group/GroupListItem.java
+++ b/src/com/android/contacts/group/GroupListItem.java
@@ -22,6 +22,7 @@
public final class GroupListItem {
private final String mAccountName;
private final String mAccountType;
+ private final String mDataSet;
private final long mGroupId;
private final String mTitle;
private final boolean mIsFirstGroupInAccount;
@@ -30,10 +31,12 @@
/** Number of groups in the account that this group belongs to */
private final int mGroupCountForThisAccount;
- public GroupListItem(String accountName, String accountType, long groupId, String title,
- boolean isFirstGroupInAccount, int memberCount, int groupCountForThisAccount) {
+ public GroupListItem(String accountName, String accountType, String dataSet, long groupId,
+ String title, boolean isFirstGroupInAccount, int memberCount,
+ int groupCountForThisAccount) {
mAccountName = accountName;
mAccountType = accountType;
+ mDataSet = dataSet;
mGroupId = groupId;
mTitle = title;
mIsFirstGroupInAccount = isFirstGroupInAccount;
@@ -49,6 +52,10 @@
return mAccountType;
}
+ public String getDataSet() {
+ return mDataSet;
+ }
+
public long getGroupId() {
return mGroupId;
}
diff --git a/src/com/android/contacts/group/SuggestedMemberListAdapter.java b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
index 5abb5fb..4f4413c 100644
--- a/src/com/android/contacts/group/SuggestedMemberListAdapter.java
+++ b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
@@ -78,6 +78,7 @@
private String mAccountType = "";
private String mAccountName = "";
+ private String mDataSet = "";
// TODO: Make this a Map for better performance when we check if a new contact is in the list
// or not
@@ -98,6 +99,10 @@
mAccountName = accountName;
}
+ public void setDataSet(String dataSet) {
+ mDataSet = dataSet;
+ }
+
public void setContentResolver(ContentResolver resolver) {
mContentResolver = resolver;
}
@@ -171,12 +176,25 @@
// First query for all the raw contacts that match the given search query
// and have the same account name and type as specified in this adapter
String searchQuery = prefix.toString() + "%";
+ String accountClause = RawContacts.ACCOUNT_NAME + "=? AND " +
+ RawContacts.ACCOUNT_TYPE + "=?";
+ String[] args;
+ if (mDataSet == null) {
+ accountClause += " AND " + RawContacts.DATA_SET + " IS NULL";
+ args = new String[] {mAccountName, mAccountType, searchQuery, searchQuery};
+ } else {
+ accountClause += " AND " + RawContacts.DATA_SET + "=?";
+ args = new String[] {
+ mAccountName, mAccountType, mDataSet, searchQuery, searchQuery
+ };
+ }
+
Cursor cursor = mContentResolver.query(
RawContacts.CONTENT_URI, PROJECTION_FILTERED_MEMBERS,
- RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=? AND (" +
+ accountClause + " AND (" +
RawContacts.DISPLAY_NAME_PRIMARY + " LIKE ? OR " +
RawContacts.DISPLAY_NAME_ALTERNATIVE + " LIKE ? )",
- new String[] {mAccountName, mAccountType, searchQuery, searchQuery}, null);
+ args, null);
if (cursor == null) {
return results;
diff --git a/src/com/android/contacts/interactions/ContactDeletionInteraction.java b/src/com/android/contacts/interactions/ContactDeletionInteraction.java
index dfcd7b6..93bd10e 100644
--- a/src/com/android/contacts/interactions/ContactDeletionInteraction.java
+++ b/src/com/android/contacts/interactions/ContactDeletionInteraction.java
@@ -59,14 +59,16 @@
private static final String[] ENTITY_PROJECTION = new String[] {
Entity.RAW_CONTACT_ID, //0
Entity.ACCOUNT_TYPE, //1
- Entity.CONTACT_ID, // 2
- Entity.LOOKUP_KEY, // 3
+ Entity.DATA_SET, // 2
+ Entity.CONTACT_ID, // 3
+ Entity.LOOKUP_KEY, // 4
};
private static final int COLUMN_INDEX_RAW_CONTACT_ID = 0;
private static final int COLUMN_INDEX_ACCOUNT_TYPE = 1;
- private static final int COLUMN_INDEX_CONTACT_ID = 2;
- private static final int COLUMN_INDEX_LOOKUP_KEY = 3;
+ private static final int COLUMN_INDEX_DATA_SET = 2;
+ private static final int COLUMN_INDEX_CONTACT_ID = 3;
+ private static final int COLUMN_INDEX_LOOKUP_KEY = 4;
private boolean mActive;
private Uri mContactUri;
@@ -233,9 +235,10 @@
while (cursor.moveToNext()) {
final long rawContactId = cursor.getLong(COLUMN_INDEX_RAW_CONTACT_ID);
final String accountType = cursor.getString(COLUMN_INDEX_ACCOUNT_TYPE);
+ final String dataSet = cursor.getString(COLUMN_INDEX_DATA_SET);
contactId = cursor.getLong(COLUMN_INDEX_CONTACT_ID);
lookupKey = cursor.getString(COLUMN_INDEX_LOOKUP_KEY);
- AccountType type = accountTypes.getAccountType(accountType);
+ AccountType type = accountTypes.getAccountType(accountType, dataSet);
boolean readonly = type != null && type.readOnly;
if (readonly) {
readOnlyRawContacts.add(rawContactId);
diff --git a/src/com/android/contacts/interactions/GroupCreationDialogFragment.java b/src/com/android/contacts/interactions/GroupCreationDialogFragment.java
index 87d83b4..224b4a0 100644
--- a/src/com/android/contacts/interactions/GroupCreationDialogFragment.java
+++ b/src/com/android/contacts/interactions/GroupCreationDialogFragment.java
@@ -17,8 +17,8 @@
import com.android.contacts.ContactSaveService;
import com.android.contacts.R;
+import com.android.contacts.model.AccountWithDataSet;
-import android.accounts.Account;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.Intent;
@@ -31,13 +31,16 @@
public class GroupCreationDialogFragment extends GroupNameDialogFragment {
private static final String ARG_ACCOUNT_TYPE = "accountType";
private static final String ARG_ACCOUNT_NAME = "accountName";
+ private static final String ARG_DATA_SET = "dataSet";
public static void show(
- FragmentManager fragmentManager, String accountType, String accountName) {
+ FragmentManager fragmentManager, String accountType, String accountName,
+ String dataSet) {
GroupCreationDialogFragment dialog = new GroupCreationDialogFragment();
Bundle args = new Bundle();
args.putString(ARG_ACCOUNT_TYPE, accountType);
args.putString(ARG_ACCOUNT_NAME, accountName);
+ args.putString(ARG_DATA_SET, dataSet);
dialog.setArguments(args);
dialog.show(fragmentManager, "createGroup");
}
@@ -56,10 +59,11 @@
Bundle arguments = getArguments();
String accountType = arguments.getString(ARG_ACCOUNT_TYPE);
String accountName = arguments.getString(ARG_ACCOUNT_NAME);
+ String dataSet = arguments.getString(ARG_DATA_SET);
Activity activity = getActivity();
activity.startService(ContactSaveService.createNewGroupIntent(activity,
- new Account(accountName, accountType), groupLabel,
+ new AccountWithDataSet(accountName, accountType, dataSet), groupLabel,
null /* no new members to add */,
activity.getClass(), Intent.ACTION_EDIT));
}
diff --git a/src/com/android/contacts/interactions/ImportExportDialogFragment.java b/src/com/android/contacts/interactions/ImportExportDialogFragment.java
index 4e22fa2..a6a37ef 100644
--- a/src/com/android/contacts/interactions/ImportExportDialogFragment.java
+++ b/src/com/android/contacts/interactions/ImportExportDialogFragment.java
@@ -19,10 +19,10 @@
import com.android.contacts.R;
import com.android.contacts.editor.SelectAccountDialogFragment;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.util.AccountSelectionUtil;
import com.android.contacts.vcard.ExportVCardActivity;
-import android.accounts.Account;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -169,7 +169,7 @@
// - just one account -> use the account without asking the user
// - no account -> use phone-local storage without asking the user
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(getActivity());
- final List<Account> accountList = accountTypes.getAccounts(true);
+ final List<AccountWithDataSet> accountList = accountTypes.getAccounts(true);
final int size = accountList.size();
if (size > 1) {
// Send over to the account selector
@@ -196,7 +196,7 @@
}
@Override
- protected void onAccountSelected(Account account) {
+ protected void onAccountSelected(AccountWithDataSet account) {
final int resourceId = getArguments().getInt(BUNDLE_RES_ID);
AccountSelectionUtil.doImport(getActivity(), resourceId, account);
}
diff --git a/src/com/android/contacts/interactions/PhoneNumberInteraction.java b/src/com/android/contacts/interactions/PhoneNumberInteraction.java
index 9762e3d..9442f5e 100644
--- a/src/com/android/contacts/interactions/PhoneNumberInteraction.java
+++ b/src/com/android/contacts/interactions/PhoneNumberInteraction.java
@@ -85,6 +85,7 @@
long id;
String phoneNumber;
String accountType;
+ String dataSet;
long type;
String label;
@@ -92,6 +93,7 @@
dest.writeLong(id);
dest.writeString(phoneNumber);
dest.writeString(accountType);
+ dest.writeString(dataSet);
dest.writeLong(type);
dest.writeString(label);
}
@@ -148,7 +150,8 @@
final View view = super.getView(position, convertView, parent);
final PhoneItem item = getItem(position);
- final AccountType accountType = mAccountTypeManager.getAccountType(item.accountType);
+ final AccountType accountType = mAccountTypeManager.getAccountType(
+ item.accountType, item.dataSet);
final TextView typeView = (TextView) view.findViewById(android.R.id.text1);
final DataKind kind = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
if (kind != null) {
@@ -237,6 +240,7 @@
Phone.NUMBER,
Phone.IS_SUPER_PRIMARY,
RawContacts.ACCOUNT_TYPE,
+ RawContacts.DATA_SET,
Phone.TYPE,
Phone.LABEL
};
@@ -335,6 +339,7 @@
item.phoneNumber = cursor.getString(cursor.getColumnIndex(Phone.NUMBER));
item.accountType =
cursor.getString(cursor.getColumnIndex(RawContacts.ACCOUNT_TYPE));
+ item.dataSet = cursor.getString(cursor.getColumnIndex(RawContacts.DATA_SET));
item.type = cursor.getInt(cursor.getColumnIndex(Phone.TYPE));
item.label = cursor.getString(cursor.getColumnIndex(Phone.LABEL));
diff --git a/src/com/android/contacts/list/AccountFilterActivity.java b/src/com/android/contacts/list/AccountFilterActivity.java
index 24eab23..f99b14e 100644
--- a/src/com/android/contacts/list/AccountFilterActivity.java
+++ b/src/com/android/contacts/list/AccountFilterActivity.java
@@ -22,8 +22,8 @@
import com.android.contacts.activities.PeopleActivity;
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
-import android.accounts.Account;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
@@ -81,12 +81,12 @@
private void loadAccountFilters() {
ArrayList<ContactListFilter> accountFilters = new ArrayList<ContactListFilter>();
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this);
- ArrayList<Account> accounts = accountTypes.getAccounts(false);
- for (Account account : accounts) {
- AccountType accountType = accountTypes.getAccountType(account.type);
+ List<AccountWithDataSet> accounts = accountTypes.getAccounts(false);
+ for (AccountWithDataSet account : accounts) {
+ AccountType accountType = accountTypes.getAccountType(account.type, account.dataSet);
Drawable icon = accountType != null ? accountType.getDisplayIcon(this) : null;
accountFilters.add(ContactListFilter.createAccountFilter(account.type, account.name,
- icon, account.name));
+ account.dataSet, icon, account.name));
}
int count = accountFilters.size();
diff --git a/src/com/android/contacts/list/ContactListFilter.java b/src/com/android/contacts/list/ContactListFilter.java
index 0651986..cfcde2a 100644
--- a/src/com/android/contacts/list/ContactListFilter.java
+++ b/src/com/android/contacts/list/ContactListFilter.java
@@ -40,6 +40,7 @@
private static final String KEY_FILTER_TYPE = "filter.type";
private static final String KEY_ACCOUNT_NAME = "filter.accountName";
private static final String KEY_ACCOUNT_TYPE = "filter.accountType";
+ private static final String KEY_DATA_SET = "filter.dataSet";
private static final String KEY_GROUP_ID = "filter.groupId";
private static final String KEY_GROUP_SOURCE_ID = "filter.groupSourceId";
private static final String KEY_GROUP_READ_ONLY = "filter.groupReadOnly";
@@ -48,6 +49,7 @@
public final int filterType;
public final String accountType;
public final String accountName;
+ public final String dataSet;
public final Drawable icon;
public long groupId;
public String groupSourceId;
@@ -55,11 +57,13 @@
public final String title;
private String mId;
- public ContactListFilter(int filterType, String accountType, String accountName, Drawable icon,
- long groupId, String groupSourceId, boolean groupReadOnly, String title) {
+ public ContactListFilter(int filterType, String accountType, String accountName, String dataSet,
+ Drawable icon, long groupId, String groupSourceId, boolean groupReadOnly,
+ String title) {
this.filterType = filterType;
this.accountType = accountType;
this.accountName = accountName;
+ this.dataSet = dataSet;
this.icon = icon;
this.groupId = groupId;
this.groupSourceId = groupSourceId;
@@ -68,24 +72,25 @@
}
public static ContactListFilter createFilterWithType(int filterType) {
- return new ContactListFilter(filterType, null, null, null, 0, null, false, null);
+ return new ContactListFilter(filterType, null, null, null, null, 0, null, false, null);
}
public static ContactListFilter createGroupFilter(long groupId) {
- return new ContactListFilter(ContactListFilter.FILTER_TYPE_GROUP, null, null, null, groupId,
- null, false, null);
+ return new ContactListFilter(ContactListFilter.FILTER_TYPE_GROUP, null, null, null, null,
+ groupId, null, false, null);
}
public static ContactListFilter createGroupFilter(String accountType, String accountName,
- long groupId, String groupSourceId, boolean groupReadOnly, String title) {
+ String dataSet, long groupId, String groupSourceId, boolean groupReadOnly,
+ String title) {
return new ContactListFilter(ContactListFilter.FILTER_TYPE_GROUP, accountType, accountName,
- null, groupId, groupSourceId, groupReadOnly, title);
+ dataSet, null, groupId, groupSourceId, groupReadOnly, title);
}
public static ContactListFilter createAccountFilter(String accountType, String accountName,
- Drawable icon, String title) {
+ String dataSet, Drawable icon, String title) {
return new ContactListFilter(ContactListFilter.FILTER_TYPE_ACCOUNT, accountType,
- accountName, icon, 0, null, false, title);
+ accountName, dataSet, icon, 0, null, false, title);
}
/**
@@ -111,10 +116,11 @@
case FILTER_TYPE_SINGLE_CONTACT:
return "single";
case FILTER_TYPE_ACCOUNT:
- return "account: " + accountType + " " + accountName;
+ return "account: " + accountType + (dataSet != null ? "/" + dataSet : "")
+ + " " + accountName;
case FILTER_TYPE_GROUP:
- return "group: " + accountType + " " + accountName + " " + title + "(" + groupId
- + ")";
+ return "group: " + accountType + (dataSet != null ? "/" + dataSet : "")
+ + " " + accountName + " " + title + "(" + groupId + ")";
}
return super.toString();
}
@@ -147,6 +153,9 @@
code = code * 31 + accountType.hashCode();
code = code * 31 + accountName.hashCode();
}
+ if (dataSet != null) {
+ code = code * 31 + dataSet.hashCode();
+ }
if (groupSourceId != null) {
code = code * 31 + groupSourceId.hashCode();
} else if (groupId != 0) {
@@ -168,7 +177,8 @@
ContactListFilter otherFilter = (ContactListFilter) other;
if (filterType != otherFilter.filterType
|| !TextUtils.equals(accountName, otherFilter.accountName)
- || !TextUtils.equals(accountType, otherFilter.accountType)) {
+ || !TextUtils.equals(accountType, otherFilter.accountType)
+ || !TextUtils.equals(dataSet, otherFilter.dataSet)) {
return false;
}
@@ -184,6 +194,7 @@
.putInt(KEY_FILTER_TYPE, filter == null ? FILTER_TYPE_DEFAULT : filter.filterType)
.putString(KEY_ACCOUNT_NAME, filter == null ? null : filter.accountName)
.putString(KEY_ACCOUNT_TYPE, filter == null ? null : filter.accountType)
+ .putString(KEY_DATA_SET, filter == null ? null : filter.dataSet)
.putLong(KEY_GROUP_ID, filter == null ? -1 : filter.groupId)
.putString(KEY_GROUP_SOURCE_ID, filter == null ? null : filter.groupSourceId)
.putBoolean(KEY_GROUP_READ_ONLY, filter == null ? false : filter.groupReadOnly)
@@ -199,11 +210,12 @@
String accountName = prefs.getString(KEY_ACCOUNT_NAME, null);
String accountType = prefs.getString(KEY_ACCOUNT_TYPE, null);
+ String dataSet = prefs.getString(KEY_DATA_SET, null);
long groupId = prefs.getLong(KEY_GROUP_ID, -1);
String groupSourceId = prefs.getString(KEY_GROUP_SOURCE_ID, null);
boolean groupReadOnly = prefs.getBoolean(KEY_GROUP_READ_ONLY, false);
String title = prefs.getString(KEY_GROUP_TITLE, "group");
- return new ContactListFilter(filterType, accountType, accountName, null, groupId,
+ return new ContactListFilter(filterType, accountType, accountName, dataSet, null, groupId,
groupSourceId, groupReadOnly, title);
}
@@ -213,6 +225,7 @@
dest.writeInt(filterType);
dest.writeString(accountName);
dest.writeString(accountType);
+ dest.writeString(dataSet);
dest.writeLong(groupId);
dest.writeString(groupSourceId);
dest.writeInt(groupReadOnly ? 1 : 0);
@@ -225,11 +238,12 @@
int filterType = source.readInt();
String accountName = source.readString();
String accountType = source.readString();
+ String dataSet = source.readString();
long groupId = source.readLong();
String groupSourceId = source.readString();
boolean groupReadOnly = source.readInt() != 0;
- return new ContactListFilter(filterType, accountType, accountName, null, groupId,
- groupSourceId, groupReadOnly, null);
+ return new ContactListFilter(filterType, accountType, accountName, dataSet, null,
+ groupId, groupSourceId, groupReadOnly, null);
}
@Override
@@ -253,6 +267,9 @@
if (accountType != null) {
sb.append('-').append(accountType);
}
+ if (dataSet != null) {
+ sb.append('/').append(dataSet);
+ }
if (accountName != null) {
sb.append('-').append(accountName.replace('-', '_'));
}
diff --git a/src/com/android/contacts/list/ContactTileAdapter.java b/src/com/android/contacts/list/ContactTileAdapter.java
index 975e3f6..8993cdd 100644
--- a/src/com/android/contacts/list/ContactTileAdapter.java
+++ b/src/com/android/contacts/list/ContactTileAdapter.java
@@ -19,11 +19,16 @@
import com.android.contacts.ContactTileLoaderFactory;
import com.android.contacts.GroupMemberLoader;
import com.android.contacts.R;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.DataKind;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts;
import android.view.View;
import android.view.View.OnClickListener;
@@ -62,6 +67,13 @@
private int mPresenceIndex;
private int mStatusIndex;
+ /**
+ * Only valid when {@link DisplayType#STREQUENT_PHONE_ONLY} is true
+ */
+ private int mPhoneNumberIndex;
+ private int mPhoneNumberTypeIndex;
+ private int mPhoneNumberLabelIndex;
+
private boolean mIsQuickContactEnabled = false;
/**
@@ -151,6 +163,10 @@
mStarredIndex = ContactTileLoaderFactory.STARRED;
mPresenceIndex = ContactTileLoaderFactory.CONTACT_PRESENCE;
mStatusIndex = ContactTileLoaderFactory.CONTACT_STATUS;
+
+ mPhoneNumberIndex = ContactTileLoaderFactory.PHONE_NUMBER;
+ mPhoneNumberTypeIndex = ContactTileLoaderFactory.PHONE_NUMBER_TYPE;
+ mPhoneNumberLabelIndex = ContactTileLoaderFactory.PHONE_NUMBER_LABEL;
}
}
@@ -168,7 +184,7 @@
/**
* Iterates over the {@link Cursor}
* Returns position of the first NON Starred Contact
- * Returns -1 if not {@link DisplayType#}
+ * Returns -1 if not {@link DisplayType#STREQUENT} or {@link DisplayType#STREQUENT_PHONE_ONLY}
*/
private int getDividerPosition(Cursor cursor) {
if (cursor == null || cursor.isClosed() || (mDisplayType != DisplayType.STREQUENT
@@ -204,6 +220,17 @@
Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey), id);
contact.presence = cursor.isNull(mPresenceIndex) ? null : cursor.getInt(mPresenceIndex);
+ if (mDisplayType == DisplayType.STREQUENT_PHONE_ONLY) {
+ int phoneNumberType = cursor.getInt(mPhoneNumberTypeIndex);
+ String phoneNumberCustomLabel = cursor.getString(mPhoneNumberLabelIndex);
+ contact.phoneLabel = (String) Phone.getTypeLabel(mContext.getResources(),
+ phoneNumberType, phoneNumberCustomLabel);
+ contact.phoneNumber = cursor.getString(mPhoneNumberIndex);
+ } else {
+ contact.status = cursor.getString(mStatusIndex);
+ contact.presence = cursor.isNull(mPresenceIndex) ? null : cursor.getInt(mPresenceIndex);
+ }
+
return contact;
}
@@ -294,11 +321,9 @@
@Override
public long getItemId(int position) {
- /*
- * As we show several selectable items for each ListView row,
- * we can not determine a stable id. But as we don't rely on ListView's selection,
- * this should not be a problem.
- */
+ // As we show several selectable items for each ListView row,
+ // we can not determine a stable id. But as we don't rely on ListView's selection,
+ // this should not be a problem.
return position;
}
@@ -350,7 +375,8 @@
return mIsQuickContactEnabled ?
R.layout.contact_tile_starred_quick_contact : R.layout.contact_tile_starred;
case ViewTypes.FREQUENT:
- return R.layout.contact_tile_frequent;
+ return mDisplayType == DisplayType.STREQUENT_PHONE_ONLY ?
+ R.layout.contact_tile_frequent_phone : R.layout.contact_tile_frequent;
case ViewTypes.STARRED_WITH_SECONDARY_ACTION:
return R.layout.contact_tile_starred_secondary_target;
default:
@@ -471,6 +497,8 @@
public static class ContactEntry {
public String name;
public String status;
+ public String phoneLabel;
+ public String phoneNumber;
public Uri photoUri;
public Uri lookupKey;
public Integer presence;
diff --git a/src/com/android/contacts/list/ContactTileView.java b/src/com/android/contacts/list/ContactTileView.java
index 6374c23..3839573 100644
--- a/src/com/android/contacts/list/ContactTileView.java
+++ b/src/com/android/contacts/list/ContactTileView.java
@@ -21,9 +21,8 @@
import com.android.contacts.list.ContactTileAdapter.ContactEntry;
import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.provider.ContactsContract.StatusUpdates;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
@@ -32,9 +31,6 @@
import android.widget.QuickContactBadge;
import android.widget.TextView;
-import android.provider.ContactsContract.StatusUpdates;
-
-
/**
* A ContactTile displays the contact's picture overlayed with their name
*/
@@ -47,6 +43,8 @@
private QuickContactBadge mQuickContact;
private TextView mName;
private TextView mStatus;
+ private TextView mPhoneLabel;
+ private TextView mPhoneNumber;
private ContactPhotoManager mPhotoManager = null;
public ContactTileView(Context context, AttributeSet attrs) {
@@ -63,6 +61,8 @@
mPhoto = (ImageView) findViewById(R.id.contact_tile_image);
mPresence = (ImageView) findViewById(R.id.contact_tile_presence);
mStatus = (TextView) findViewById(R.id.contact_tile_status);
+ mPhoneLabel = (TextView) findViewById(R.id.contact_tile_phone_type);
+ mPhoneNumber = (TextView) findViewById(R.id.contact_tile_phone_number);
}
public void setPhotoManager(ContactPhotoManager photoManager) {
@@ -97,6 +97,15 @@
mStatus.setText(statusText);
}
+ if (mPhoneLabel != null) {
+ mPhoneLabel.setText(entry.phoneLabel);
+ }
+
+ if (mPhoneNumber != null) {
+ // TODO: Format number correctly
+ mPhoneNumber.setText(entry.phoneNumber);
+ }
+
if (mQuickContact != null) {
mQuickContact.assignContactUri(mLookupUri);
mQuickContact.setImageBitmap(null);
diff --git a/src/com/android/contacts/list/ContactsUnavailableFragment.java b/src/com/android/contacts/list/ContactsUnavailableFragment.java
index e93687f..3bab3fd 100644
--- a/src/com/android/contacts/list/ContactsUnavailableFragment.java
+++ b/src/com/android/contacts/list/ContactsUnavailableFragment.java
@@ -45,6 +45,7 @@
private Button mUninstallAppsButton;
private Button mRetryUpgradeButton;
private ProgressBar mProgress;
+ private int mNoContactsMsgResId = -1;
private OnContactsUnavailableActionListener mListener;
@@ -81,8 +82,14 @@
int providerStatus = mProviderStatusLoader.getProviderStatus();
switch (providerStatus) {
case ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS:
- mMessageView.setGravity(Gravity.LEFT);
- mMessageView.setVisibility(View.GONE);
+ if (mNoContactsMsgResId != -1) {
+ mMessageView.setText(mNoContactsMsgResId);
+ mMessageView.setGravity(Gravity.CENTER_HORIZONTAL);
+ mMessageView.setVisibility(View.VISIBLE);
+ } else {
+ mMessageView.setGravity(Gravity.LEFT);
+ mMessageView.setVisibility(View.GONE);
+ }
mCreateContactButton.setVisibility(View.VISIBLE);
mAddAccountButton.setVisibility(View.VISIBLE);
mImportContactsButton.setVisibility(View.VISIBLE);
@@ -154,4 +161,19 @@
break;
}
}
+ /**
+ * Set the message to be shown if data is available for the selected tab
+ *
+ * @param resId - String resource ID of the message
+ */
+ public void setMessageText(int resId) {
+ mNoContactsMsgResId = resId;
+ if (mMessageView != null &&
+ mProviderStatusLoader.getProviderStatus() ==
+ ProviderStatus.STATUS_NO_ACCOUNTS_NO_CONTACTS) {
+ mMessageView.setText(mNoContactsMsgResId);
+ mMessageView.setGravity(Gravity.CENTER_HORIZONTAL);
+ mMessageView.setVisibility(View.VISIBLE);
+ }
+ }
}
diff --git a/src/com/android/contacts/list/CustomContactListFilterActivity.java b/src/com/android/contacts/list/CustomContactListFilterActivity.java
index 0f6aaad..ae21824 100644
--- a/src/com/android/contacts/list/CustomContactListFilterActivity.java
+++ b/src/com/android/contacts/list/CustomContactListFilterActivity.java
@@ -21,6 +21,7 @@
import com.android.contacts.R;
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.model.EntityDelta.ValuesDelta;
import com.android.contacts.model.GoogleAccountType;
import com.android.contacts.util.EmptyService;
@@ -28,7 +29,6 @@
import com.android.contacts.util.WeakAsyncTask;
import com.google.android.collect.Lists;
-import android.accounts.Account;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.LoaderManager.LoaderCallbacks;
@@ -124,13 +124,14 @@
final ContentResolver resolver = context.getContentResolver();
final AccountSet accounts = new AccountSet();
- for (Account account : accountTypes.getAccounts(false)) {
+ for (AccountWithDataSet account : accountTypes.getAccounts(false)) {
AccountDisplay accountDisplay =
- new AccountDisplay(resolver, account.name, account.type);
+ new AccountDisplay(resolver, account.name, account.type, account.dataSet);
final Uri groupsUri = Groups.CONTENT_URI.buildUpon()
.appendQueryParameter(Groups.ACCOUNT_NAME, account.name)
- .appendQueryParameter(Groups.ACCOUNT_TYPE, account.type).build();
+ .appendQueryParameter(Groups.ACCOUNT_TYPE, account.type)
+ .appendQueryParameter(Groups.DATA_SET, account.dataSet).build();
EntityIterator iterator = ContactsContract.Groups.newEntityIterator(resolver.query(
groupsUri, null, null, null, null));
try {
@@ -417,12 +418,13 @@
}
/**
- * {@link GroupDelta} details for a single {@link Account}, usually shown as
+ * {@link GroupDelta} details for a single {@link AccountWithDataSet}, usually shown as
* children under a single expandable group.
*/
protected static class AccountDisplay {
- public String mName;
- public String mType;
+ public final String mName;
+ public final String mType;
+ public final String mDataSet;
public GroupDelta mUngrouped;
public ArrayList<GroupDelta> mSyncedGroups = Lists.newArrayList();
@@ -430,11 +432,13 @@
/**
* Build an {@link AccountDisplay} covering all {@link Groups} under the
- * given {@link Account}.
+ * given {@link AccountWithDataSet}.
*/
- public AccountDisplay(ContentResolver resolver, String accountName, String accountType) {
+ public AccountDisplay(ContentResolver resolver, String accountName, String accountType,
+ String dataSet) {
mName = accountName;
mType = accountType;
+ mDataSet = dataSet;
}
/**
@@ -489,7 +493,7 @@
/**
* Build set of {@link ContentProviderOperation} to persist any user
- * changes to {@link GroupDelta} rows under this {@link Account}.
+ * changes to {@link GroupDelta} rows under this {@link AccountWithDataSet}.
*/
public void buildDiff(ArrayList<ContentProviderOperation> diff) {
for (GroupDelta group : mSyncedGroups) {
@@ -505,7 +509,7 @@
/**
* {@link ExpandableListAdapter} that shows {@link GroupDelta} settings,
- * grouped by {@link Account} type. Shows footer row when any groups are
+ * grouped by {@link AccountWithDataSet} type. Shows footer row when any groups are
* unsynced, as determined through {@link AccountDisplay#mUnsyncedGroups}.
*/
protected static class DisplayAdapter extends BaseExpandableListAdapter {
@@ -548,7 +552,8 @@
final AccountDisplay account = (AccountDisplay)this.getGroup(groupPosition);
- final AccountType accountType = mAccountTypes.getAccountType(account.mType);
+ final AccountType accountType = mAccountTypes.getAccountType(
+ account.mType, account.mDataSet);
text1.setText(account.mName);
text1.setVisibility(account.mName == null ? View.GONE : View.VISIBLE);
@@ -693,7 +698,7 @@
protected int getSyncMode(AccountDisplay account) {
// TODO: read sync mode through <sync-adapter> definition
- if (GoogleAccountType.ACCOUNT_TYPE.equals(account.mType)) {
+ if (GoogleAccountType.ACCOUNT_TYPE.equals(account.mType) && account.mDataSet == null) {
return SYNC_MODE_EVERYTHING;
} else {
return SYNC_MODE_UNSUPPORTED;
diff --git a/src/com/android/contacts/list/DefaultContactListAdapter.java b/src/com/android/contacts/list/DefaultContactListAdapter.java
index 8b93888..8e96690 100644
--- a/src/com/android/contacts/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/list/DefaultContactListAdapter.java
@@ -196,9 +196,15 @@
+ "SELECT DISTINCT " + RawContacts.CONTACT_ID
+ " FROM raw_contacts"
+ " WHERE " + RawContacts.ACCOUNT_TYPE + "=?"
- + " AND " + RawContacts.ACCOUNT_NAME + "=?)");
+ + " AND " + RawContacts.ACCOUNT_NAME + "=?");
selectionArgs.add(filter.accountType);
selectionArgs.add(filter.accountName);
+ if (filter.dataSet != null) {
+ selection.append(" AND " + RawContacts.DATA_SET + "=?)");
+ selectionArgs.add(filter.dataSet);
+ } else {
+ selection.append(" AND " + RawContacts.DATA_SET + " IS NULL)");
+ }
break;
}
case ContactListFilter.FILTER_TYPE_GROUP: {
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index 608fca3..59b9f45 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -38,6 +38,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
/**
* Internal structure that represents constraints and styles for a specific data
@@ -49,12 +50,19 @@
public abstract class AccountType {
private static final String TAG = "AccountType";
+ private static final String ACCOUNT_TYPE_DATA_SET_DELIMITER = "/";
+
/**
* The {@link RawContacts#ACCOUNT_TYPE} these constraints apply to.
*/
public String accountType = null;
/**
+ * The {@link RawContacts#DATA_SET} these constraints apply to.
+ */
+ public String dataSet = null;
+
+ /**
* Package that resources should be loaded from, either defined through an
* {@link Account} or for matching against {@link Data#RES_PACKAGE}.
*/
@@ -116,6 +124,34 @@
}
/**
+ * Returns the account type with the data set (if any) appended after a delimiter.
+ * If the data set is null, this will simply return the account type.
+ */
+ public String getAccountTypeAndDataSet() {
+ return getAccountTypeAndDataSet(accountType, dataSet);
+ }
+
+ /**
+ * Utility method to concatenate the given account type with a data set with a delimiter.
+ * If the data set is null, this will simply return the account type.
+ */
+ public static String getAccountTypeAndDataSet(String accountType, String dataSet) {
+ return dataSet == null
+ ? accountType
+ : accountType + ACCOUNT_TYPE_DATA_SET_DELIMITER + dataSet;
+ }
+
+ /**
+ * Returns a list of additional package names that should be inspected as additional
+ * external account types. This allows for a primary account type to indicate other packages
+ * that may not be sync adapters but which still provide contact data, perhaps under a
+ * separate data set within the account.
+ */
+ public List<String> getExtensionPackageNames() {
+ return new ArrayList<String>();
+ }
+
+ /**
* Returns an optional custom label for the "invite contact" action, which will be shown on
* the contact card. (If not defined, returns null.)
*/
@@ -186,7 +222,7 @@
/**
* Find the {@link DataKind} for a specific MIME-type, if it's handled by
* this data source. If you may need a fallback {@link DataKind}, use
- * {@link AccountTypeManager#getKindOrFallback(String, String)}.
+ * {@link AccountTypeManager#getKindOrFallback(String, String, String)}.
*/
public DataKind getKindForMimetype(String mimeType) {
return this.mMimeKinds.get(mimeType);
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index ea2568f..7232a26 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -16,8 +16,10 @@
package com.android.contacts.model;
+import com.android.internal.util.Objects;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import com.google.android.collect.Sets;
import com.google.common.annotations.VisibleForTesting;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
@@ -43,13 +45,14 @@
import android.text.TextUtils;
import android.util.Log;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
/**
@@ -79,9 +82,9 @@
return new AccountTypeManagerImpl(context);
}
- public abstract ArrayList<Account> getAccounts(boolean writableOnly);
+ public abstract List<AccountWithDataSet> getAccounts(boolean writableOnly);
- public abstract AccountType getAccountType(String accountType);
+ public abstract AccountType getAccountType(String accountType, String dataSet);
/**
* @return Unmodifiable map from account type strings to {@link AccountType}s which support
@@ -91,11 +94,11 @@
/**
* Find the best {@link DataKind} matching the requested
- * {@link AccountType#accountType} and {@link DataKind#mimeType}. If no
- * direct match found, we try searching {@link FallbackAccountType}.
+ * {@link AccountType#accountType}, {@link AccountType#dataSet}, and {@link DataKind#mimeType}.
+ * If no direct match found, we try searching {@link FallbackAccountType}.
*/
- public DataKind getKindOrFallback(String accountType, String mimeType) {
- final AccountType type = getAccountType(accountType);
+ public DataKind getKindOrFallback(String accountType, String dataSet, String mimeType) {
+ final AccountType type = getAccountType(accountType, dataSet);
return type == null ? null : type.getKindForMimetype(mimeType);
}
}
@@ -108,9 +111,9 @@
private AccountType mFallbackAccountType;
- private ArrayList<Account> mAccounts = Lists.newArrayList();
- private ArrayList<Account> mWritableAccounts = Lists.newArrayList();
- private HashMap<String, AccountType> mAccountTypes = Maps.newHashMap();
+ private List<AccountWithDataSet> mAccounts = Lists.newArrayList();
+ private List<AccountWithDataSet> mWritableAccounts = Lists.newArrayList();
+ private Map<String, AccountType> mAccountTypesWithDataSets = Maps.newHashMap();
private Map<String, AccountType> mInvitableAccountTypes = Collections.unmodifiableMap(
new HashMap<String, AccountType>());
@@ -134,14 +137,41 @@
private volatile CountDownLatch mInitializationLatch = new CountDownLatch(1);
private static final Comparator<Account> ACCOUNT_COMPARATOR = new Comparator<Account>() {
-
@Override
- public int compare(Account account1, Account account2) {
- int diff = account1.name.compareTo(account2.name);
- if (diff != 0) {
- return diff;
+ public int compare(Account a, Account b) {
+ String aDataSet = null;
+ String bDataSet = null;
+ if (a instanceof AccountWithDataSet) {
+ aDataSet = ((AccountWithDataSet) a).dataSet;
}
- return account1.type.compareTo(account2.type);
+ if (b instanceof AccountWithDataSet) {
+ bDataSet = ((AccountWithDataSet) b).dataSet;
+ }
+
+ if (Objects.equal(a.name, b.name) && Objects.equal(a.type, b.type)
+ && Objects.equal(aDataSet, bDataSet)) {
+ return 0;
+ } else if (b.name == null || b.type == null) {
+ return -1;
+ } else if (a.name == null || a.type == null) {
+ return 1;
+ } else {
+ int diff = a.name.compareTo(b.name);
+ if (diff != 0) {
+ return diff;
+ }
+ diff = a.type.compareTo(b.type);
+ if (diff != 0) {
+ return diff;
+ }
+
+ // Accounts without data sets get sorted before those that have them.
+ if (aDataSet != null) {
+ return bDataSet == null ? 1 : aDataSet.compareTo(bDataSet);
+ } else {
+ return -1;
+ }
+ }
}
};
@@ -228,15 +258,23 @@
}
/**
- * Loads account list and corresponding account types. Always called on a
- * background thread.
+ * Loads account list and corresponding account types (potentially with data sets). Always
+ * called on a background thread.
*/
protected void loadAccountsInBackground() {
long startTime = SystemClock.currentThreadTimeMillis();
- HashMap<String, AccountType> accountTypes = Maps.newHashMap();
- ArrayList<Account> allAccounts = Lists.newArrayList();
- ArrayList<Account> writableAccounts = Lists.newArrayList();
+ // Account types, keyed off the account type and data set concatenation.
+ Map<String, AccountType> accountTypesByTypeAndDataSet = Maps.newHashMap();
+
+ // The same AccountTypes, but keyed off {@link RawContacts#ACCOUNT_TYPE}. Since there can
+ // be multiple account types (with different data sets) for the same type of account, each
+ // type string may have multiple AccountType entries.
+ Map<String, List<AccountType>> accountTypesByType = Maps.newHashMap();
+
+ List<AccountWithDataSet> allAccounts = Lists.newArrayList();
+ List<AccountWithDataSet> writableAccounts = Lists.newArrayList();
+ Set<String> extensionPackages = Sets.newHashSet();
final AccountManager am = mAccountManager;
final IContentService cs = ContentResolver.getContentService();
@@ -245,6 +283,7 @@
final SyncAdapterType[] syncs = cs.getSyncAdapterTypes();
final AuthenticatorDescription[] auths = am.getAuthenticatorTypes();
+ // First process sync adapters to find any that provide contact data.
for (SyncAdapterType sync : syncs) {
if (!ContactsContract.AUTHORITY.equals(sync.authority)) {
// Skip sync adapters that don't provide contact data.
@@ -277,31 +316,52 @@
accountType.titleRes = auth.labelId;
accountType.iconRes = auth.iconId;
- accountTypes.put(accountType.accountType, accountType);
+ addAccountType(accountType, accountTypesByTypeAndDataSet, accountTypesByType);
+
+ // Check to see if the account type knows of any other non-sync-adapter packages
+ // that may provide other data sets of contact data.
+ extensionPackages.addAll(accountType.getExtensionPackageNames());
+ }
+
+ // If any extension packages were specified, process them as well.
+ if (!extensionPackages.isEmpty()) {
+ Log.d(TAG, "Registering " + extensionPackages.size() + " extension packages");
+ for (String extensionPackage : extensionPackages) {
+ ExternalAccountType accountType =
+ new ExternalAccountType(mContext, extensionPackage);
+ Log.d(TAG, "Registering extension package account type="
+ + accountType.accountType + ", dataSet=" + accountType.dataSet
+ + ", packageName=" + extensionPackage);
+
+ addAccountType(accountType, accountTypesByTypeAndDataSet, accountTypesByType);
+ }
}
} catch (RemoteException e) {
Log.w(TAG, "Problem loading accounts: " + e.toString());
}
+ // Map in accounts to associate the account names with each account type entry.
Account[] accounts = mAccountManager.getAccounts();
for (Account account : accounts) {
boolean syncable = false;
try {
- int isSyncable = cs.getIsSyncable(account, ContactsContract.AUTHORITY);
- if (isSyncable > 0) {
- syncable = true;
- }
+ syncable = cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
} catch (RemoteException e) {
Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
}
if (syncable) {
- // Ensure we have details loaded for each account
- final AccountType accountType = accountTypes.get(account.type);
- if (accountType != null) {
- allAccounts.add(account);
- if (!accountType.readOnly) {
- writableAccounts.add(account);
+ List<AccountType> accountTypes = accountTypesByType.get(account.type);
+ if (accountTypes != null) {
+ // Add an account-with-data-set entry for each account type that is
+ // authenticated by this account.
+ for (AccountType accountType : accountTypes) {
+ AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
+ account.name, account.type, accountType.dataSet);
+ allAccounts.add(accountWithDataSet);
+ if (!accountType.readOnly) {
+ writableAccounts.add(accountWithDataSet);
+ }
}
}
}
@@ -317,13 +377,14 @@
long endTime = SystemClock.currentThreadTimeMillis();
synchronized (this) {
- mAccountTypes = accountTypes;
+ mAccountTypesWithDataSets = accountTypesByTypeAndDataSet;
mAccounts = allAccounts;
mWritableAccounts = writableAccounts;
- mInvitableAccountTypes = findInvitableAccountTypes(mContext, allAccounts, accountTypes);
+ mInvitableAccountTypes = findInvitableAccountTypes(
+ mContext, allAccounts, accountTypesByTypeAndDataSet);
}
- Log.i(TAG, "Loaded meta-data for " + mAccountTypes.size() + " account types, "
+ Log.i(TAG, "Loaded meta-data for " + mAccountTypesWithDataSets.size() + " account types, "
+ mAccounts.size() + " accounts in " + (endTime - startTime) + "ms");
if (mInitializationLatch != null) {
@@ -332,6 +393,19 @@
}
}
+ // Bookkeeping method for tracking the known account types in the given maps.
+ private void addAccountType(AccountType accountType,
+ Map<String, AccountType> accountTypesByTypeAndDataSet,
+ Map<String, List<AccountType>> accountTypesByType) {
+ accountTypesByTypeAndDataSet.put(accountType.getAccountTypeAndDataSet(), accountType);
+ List<AccountType> accountsForType = accountTypesByType.get(accountType.accountType);
+ if (accountsForType == null) {
+ accountsForType = Lists.newArrayList();
+ }
+ accountsForType.add(accountType);
+ accountTypesByType.put(accountType.accountType, accountsForType);
+ }
+
/**
* Find a specific {@link AuthenticatorDescription} in the provided list
* that matches the given account type.
@@ -347,26 +421,27 @@
}
/**
- * Return list of all known, writable {@link Account}'s.
+ * Return list of all known, writable {@link AccountWithDataSet}'s.
*/
@Override
- public ArrayList<Account> getAccounts(boolean writableOnly) {
+ public List<AccountWithDataSet> getAccounts(boolean writableOnly) {
ensureAccountsLoaded();
return writableOnly ? mWritableAccounts : mAccounts;
}
/**
* Find the best {@link DataKind} matching the requested
- * {@link AccountType#accountType} and {@link DataKind#mimeType}. If no
- * direct match found, we try searching {@link FallbackAccountType}.
+ * {@link AccountType#accountType}, {@link AccountType#dataSet}, and {@link DataKind#mimeType}.
+ * If no direct match found, we try searching {@link FallbackAccountType}.
*/
@Override
- public DataKind getKindOrFallback(String accountType, String mimeType) {
+ public DataKind getKindOrFallback(String accountType, String dataSet, String mimeType) {
ensureAccountsLoaded();
DataKind kind = null;
// Try finding account type and kind matching request
- final AccountType type = mAccountTypes.get(accountType);
+ final AccountType type = mAccountTypesWithDataSets.get(
+ AccountType.getAccountTypeAndDataSet(accountType, dataSet));
if (type != null) {
kind = type.getKindForMimetype(mimeType);
}
@@ -384,13 +459,14 @@
}
/**
- * Return {@link AccountType} for the given account type.
+ * Return {@link AccountType} for the given account type and data set.
*/
@Override
- public AccountType getAccountType(String accountType) {
+ public AccountType getAccountType(String accountType, String dataSet) {
ensureAccountsLoaded();
synchronized (this) {
- AccountType type = mAccountTypes.get(accountType);
+ AccountType type = mAccountTypesWithDataSets.get(
+ AccountType.getAccountTypeAndDataSet(accountType, dataSet));
return type != null ? type : mFallbackAccountType;
}
}
@@ -406,20 +482,23 @@
*/
@VisibleForTesting
static Map<String, AccountType> findInvitableAccountTypes(Context context,
- Collection<Account> accounts, Map<String, AccountType> accountTypes) {
+ Collection<AccountWithDataSet> accounts,
+ Map<String, AccountType> accountTypesByTypeAndDataSet) {
HashMap<String, AccountType> result = Maps.newHashMap();
- for (Account account : accounts) {
- AccountType type = accountTypes.get(account.type);
+ for (AccountWithDataSet account : accounts) {
+ String accountTypeWithDataSet = account.getAccountTypeWithDataSet();
+ AccountType type = accountTypesByTypeAndDataSet.get(
+ account.getAccountTypeWithDataSet());
if (type == null) continue; // just in case
- if (result.containsKey(type.accountType)) continue;
+ if (result.containsKey(accountTypeWithDataSet)) continue;
if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Type " + type.accountType
+ Log.d(TAG, "Type " + accountTypeWithDataSet
+ " inviteClass=" + type.getInviteContactActivityClassName()
+ " inviteAction=" + type.getInviteContactActionLabel(context));
}
if (!TextUtils.isEmpty(type.getInviteContactActivityClassName())) {
- result.put(type.accountType, type);
+ result.put(accountTypeWithDataSet, type);
}
}
return Collections.unmodifiableMap(result);
diff --git a/src/com/android/contacts/model/AccountWithDataSet.java b/src/com/android/contacts/model/AccountWithDataSet.java
new file mode 100644
index 0000000..1d97614
--- /dev/null
+++ b/src/com/android/contacts/model/AccountWithDataSet.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.model;
+
+import com.android.internal.util.Objects;
+
+import android.accounts.Account;
+import android.os.Parcel;
+
+/**
+ * Wrapper for an account that includes a data set (which may be null).
+ */
+public class AccountWithDataSet extends Account {
+
+ public final String dataSet;
+
+ public AccountWithDataSet(String name, String type, String dataSet) {
+ super(name, type);
+ this.dataSet = dataSet;
+ }
+
+ public AccountWithDataSet(Parcel in, String dataSet) {
+ super(in);
+ this.dataSet = dataSet;
+ }
+
+ public String getAccountTypeWithDataSet() {
+ return dataSet == null ? type : AccountType.getAccountTypeAndDataSet(type, dataSet);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof AccountWithDataSet) && super.equals(o)
+ && Objects.equal(((AccountWithDataSet) o).dataSet, dataSet);
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * super.hashCode()
+ + (dataSet == null ? 0 : dataSet.hashCode());
+ }
+
+ @Override
+ public String toString() {
+ return "AccountWithDataSet {name=" + name + ", type=" + type + ", dataSet=" + dataSet + "}";
+ }
+}
diff --git a/src/com/android/contacts/model/BaseAccountType.java b/src/com/android/contacts/model/BaseAccountType.java
index 06cb039..aca2d3c 100644
--- a/src/com/android/contacts/model/BaseAccountType.java
+++ b/src/com/android/contacts/model/BaseAccountType.java
@@ -64,6 +64,7 @@
public BaseAccountType() {
this.accountType = null;
+ this.dataSet = null;
this.titleRes = R.string.account_phone;
this.iconRes = R.mipmap.ic_launcher_contacts;
}
@@ -205,6 +206,7 @@
android.R.drawable.sym_action_call, 10, true,
R.layout.text_fields_editor_view, android.R.style.TextAppearance_Medium));
kind.iconAltRes = R.drawable.sym_action_sms;
+ kind.iconAltResDark = R.drawable.ic_text_holo_dark;
kind.actionHeader = new PhoneActionInflater();
kind.actionAltHeader = new PhoneActionAltInflater();
kind.actionBody = new SimpleInflater(Phone.NUMBER);
diff --git a/src/com/android/contacts/model/DataKind.java b/src/com/android/contacts/model/DataKind.java
index 40f6f99..a3b31e3 100644
--- a/src/com/android/contacts/model/DataKind.java
+++ b/src/com/android/contacts/model/DataKind.java
@@ -27,7 +27,10 @@
public String mimeType;
public int titleRes;
public int iconRes;
+ /** Icon used for secondary action when shown on top of a bright background */
public int iconAltRes;
+ /** Icon used for secondary action when shown on top of a dark background */
+ public int iconAltResDark;
public int weight;
public boolean editable;
diff --git a/src/com/android/contacts/model/EntityModifier.java b/src/com/android/contacts/model/EntityModifier.java
index fb05162..fdac645 100644
--- a/src/com/android/contacts/model/EntityModifier.java
+++ b/src/com/android/contacts/model/EntityModifier.java
@@ -378,8 +378,10 @@
*/
public static void trimEmpty(EntityDeltaList set, AccountTypeManager accountTypes) {
for (EntityDelta state : set) {
- final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final AccountType type = accountTypes.getAccountType(accountType);
+ ValuesDelta values = state.getValues();
+ final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
+ final String dataSet = values.getAsString(RawContacts.DATA_SET);
+ final AccountType type = accountTypes.getAccountType(accountType, dataSet);
trimEmpty(state, type);
}
}
@@ -390,8 +392,10 @@
}
for (EntityDelta state : set) {
- final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
- final AccountType type = accountTypes.getAccountType(accountType);
+ ValuesDelta values = state.getValues();
+ final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
+ final String dataSet = values.getAsString(RawContacts.DATA_SET);
+ final AccountType type = accountTypes.getAccountType(accountType, dataSet);
if (hasChanges(state, type)) {
return true;
}
@@ -678,8 +682,8 @@
DataKind kind = accountType.getKindForMimetype(mimeType);
if (kind == null) {
- Log.e(TAG, "Mimetype not supported for account type " + accountType.accountType
- + ". Ignoring: " + values);
+ Log.e(TAG, "Mimetype not supported for account type "
+ + accountType.getAccountTypeAndDataSet() + ". Ignoring: " + values);
continue;
}
diff --git a/src/com/android/contacts/model/ExternalAccountType.java b/src/com/android/contacts/model/ExternalAccountType.java
index 4e6add0..caf311b 100644
--- a/src/com/android/contacts/model/ExternalAccountType.java
+++ b/src/com/android/contacts/model/ExternalAccountType.java
@@ -36,6 +36,7 @@
import android.util.Xml;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -55,12 +56,24 @@
private static final String ATTR_CREATE_CONTACT_ACTIVITY = "createContactActivity";
private static final String ATTR_INVITE_CONTACT_ACTIVITY = "inviteContactActivity";
private static final String ATTR_INVITE_CONTACT_ACTION_LABEL = "inviteContactActionLabel";
+ private static final String ATTR_DATA_SET = "dataSet";
+ private static final String ATTR_EXTENSION_PACKAGE_NAMES = "extensionPackageNames";
+
+ // The following attributes should only be set in non-sync-adapter account types. They allow
+ // for the account type and resource IDs to be specified without an associated authenticator.
+ private static final String ATTR_ACCOUNT_TYPE = "accountType";
+ private static final String ATTR_READ_ONLY = "readOnly";
+ private static final String ATTR_ACCOUNT_LABEL = "accountTypeLabel";
+ private static final String ATTR_ACCOUNT_ICON = "accountTypeIcon";
private String mEditContactActivityClassName;
private String mCreateContactActivityClassName;
private String mInviteContactActivity;
private String mInviteActionLabelAttribute;
+ private List<String> mExtensionPackageNames;
private int mInviteActionLabelResId;
+ private String mAccountTypeLabelAttribute;
+ private String mAccountTypeIconAttribute;
public ExternalAccountType(Context context, String resPackageName) {
this.resPackageName = resPackageName;
@@ -81,8 +94,13 @@
}
}
+ mExtensionPackageNames = new ArrayList<String>();
mInviteActionLabelResId = resolveExternalResId(context, mInviteActionLabelAttribute,
summaryResPackageName, ATTR_INVITE_CONTACT_ACTION_LABEL);
+ titleRes = resolveExternalResId(context, mAccountTypeLabelAttribute,
+ this.resPackageName, ATTR_ACCOUNT_LABEL);
+ iconRes = resolveExternalResId(context, mAccountTypeIconAttribute,
+ this.resPackageName, ATTR_ACCOUNT_ICON);
// Bring in name and photo from fallback source, which are non-optional
addDataKindStructuredName(context);
@@ -116,6 +134,11 @@
return mInviteActionLabelResId;
}
+ @Override
+ public List<String> getExtensionPackageNames() {
+ return mExtensionPackageNames;
+ }
+
/**
* Inflate this {@link AccountType} from the given parser. This may only
* load details matching the publicly-defined schema.
@@ -156,6 +179,18 @@
mInviteContactActivity = value;
} else if (ATTR_INVITE_CONTACT_ACTION_LABEL.equals(attr)) {
mInviteActionLabelAttribute = value;
+ } else if (ATTR_DATA_SET.equals(attr)) {
+ dataSet = value;
+ } else if (ATTR_EXTENSION_PACKAGE_NAMES.equals(attr)) {
+ mExtensionPackageNames.add(value);
+ } else if (ATTR_ACCOUNT_TYPE.equals(attr)) {
+ accountType = value;
+ } else if (ATTR_READ_ONLY.equals(attr)) {
+ readOnly = !"0".equals(value) && !"false".equals(value);
+ } else if (ATTR_ACCOUNT_LABEL.equals(attr)) {
+ mAccountTypeLabelAttribute = value;
+ } else if (ATTR_ACCOUNT_ICON.equals(attr)) {
+ mAccountTypeIconAttribute = value;
} else {
Log.e(TAG, "Unsupported attribute " + attr);
}
diff --git a/src/com/android/contacts/model/FallbackAccountType.java b/src/com/android/contacts/model/FallbackAccountType.java
index 13540be..8bb3992 100644
--- a/src/com/android/contacts/model/FallbackAccountType.java
+++ b/src/com/android/contacts/model/FallbackAccountType.java
@@ -24,6 +24,7 @@
public FallbackAccountType(Context context) {
this.accountType = null;
+ this.dataSet = null;
this.titleRes = R.string.account_phone;
this.iconRes = R.mipmap.ic_launcher_contacts;
diff --git a/src/com/android/contacts/model/GoogleAccountType.java b/src/com/android/contacts/model/GoogleAccountType.java
index fd0eea4..c3dbd79 100644
--- a/src/com/android/contacts/model/GoogleAccountType.java
+++ b/src/com/android/contacts/model/GoogleAccountType.java
@@ -28,6 +28,8 @@
import android.provider.ContactsContract.CommonDataKinds.Relation;
import android.view.inputmethod.EditorInfo;
+import java.util.List;
+
public class GoogleAccountType extends BaseAccountType {
public static final String ACCOUNT_TYPE = "com.google";
protected static final int FLAGS_RELATION = EditorInfo.TYPE_CLASS_TEXT
@@ -57,6 +59,12 @@
}
@Override
+ public List<String> getExtensionPackageNames() {
+ // TODO: Return the Google+ package name once it has the XML for an ExternalAccountType.
+ return super.getExtensionPackageNames();
+ }
+
+ @Override
protected DataKind addDataKindPhone(Context context) {
final DataKind kind = super.addDataKindPhone(context);
diff --git a/src/com/android/contacts/quickcontact/Action.java b/src/com/android/contacts/quickcontact/Action.java
index bdfbe48..f6e282c 100644
--- a/src/com/android/contacts/quickcontact/Action.java
+++ b/src/com/android/contacts/quickcontact/Action.java
@@ -27,20 +27,21 @@
* string description and icon.
*/
public interface Action extends Collapser.Collapsible<Action> {
- public CharSequence getHeader();
public CharSequence getBody();
+ public CharSequence getSubtitle();
public String getMimeType();
- public Drawable getFallbackIcon();
- /**
- * Build an {@link Intent} that will perform this action.
- */
+ /** Returns an icon that can be clicked for the alternate action. */
+ public Drawable getAlternateIcon();
+
+ /** Build an {@link Intent} that will perform this action. */
public Intent getIntent();
- /**
- * Checks if the contact data for this action is primary.
- */
+ /** Build an {@link Intent} that will perform the alternate action. */
+ public Intent getAlternateIntent();
+
+ /** Checks if the contact data for this action is primary. */
public Boolean isPrimary();
/**
diff --git a/src/com/android/contacts/quickcontact/ClearDefaultsAction.java b/src/com/android/contacts/quickcontact/ClearDefaultsAction.java
deleted file mode 100644
index d1acc6b..0000000
--- a/src/com/android/contacts/quickcontact/ClearDefaultsAction.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts.quickcontact;
-
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-
-/**
- * Action that expands to show and allow clearing the currently selected defaults.
- */
-public class ClearDefaultsAction implements Action {
- /**
- * This is a pseudo-mimetype that is only needed for the action list. It has to be
- * different from the real mime-types used
- */
- public static final String PSEUDO_MIME_TYPE = "__clear_defaults_mime_type";
-
- @Override
- public boolean collapseWith(Action t) {
- return false;
- }
-
- @Override
- public boolean shouldCollapseWith(Action t) {
- return false;
- }
-
- @Override
- public CharSequence getHeader() {
- return null;
- }
-
- @Override
- public CharSequence getBody() {
- return null;
- }
-
- @Override
- public String getMimeType() {
- return PSEUDO_MIME_TYPE;
- }
-
- @Override
- public Drawable getFallbackIcon() {
- return null;
- }
-
- @Override
- public Intent getIntent() {
- return null;
- }
-
- @Override
- public Boolean isPrimary() {
- return null;
- }
-
- @Override
- public Uri getDataUri() {
- return null;
- }
-
- @Override
- public long getDataId() {
- return -1;
- }
-}
diff --git a/src/com/android/contacts/quickcontact/DataAction.java b/src/com/android/contacts/quickcontact/DataAction.java
index 787d482..6f719af 100644
--- a/src/com/android/contacts/quickcontact/DataAction.java
+++ b/src/com/android/contacts/quickcontact/DataAction.java
@@ -3,6 +3,7 @@
import com.android.contacts.ContactsUtils;
import com.android.contacts.R;
import com.android.contacts.model.DataKind;
+import com.android.contacts.model.AccountType.EditType;
import com.android.contacts.util.Constants;
import com.android.contacts.util.PhoneCapabilityTester;
@@ -34,11 +35,12 @@
private final DataKind mKind;
private final String mMimeType;
- private CharSequence mHeader;
private CharSequence mBody;
+ private CharSequence mSubtitle;
private Intent mIntent;
+ private Intent mAlternateIntent;
+ private int mAlternateIconRes;
- private boolean mAlternate;
private Uri mDataUri;
private long mDataId;
private boolean mIsPrimary;
@@ -46,18 +48,33 @@
/**
* Create an action from common {@link Data} elements.
*/
- public DataAction(Context context, String mimeType, DataKind kind,
- long dataId, Cursor cursor) {
+ public DataAction(Context context, String mimeType, DataKind kind, long dataId, Cursor cursor) {
mContext = context;
mKind = kind;
mMimeType = mimeType;
- // Inflate strings from cursor
- mAlternate = Constants.MIME_TYPE_SMS_ADDRESS.equals(mimeType);
- if (mAlternate && mKind.actionAltHeader != null) {
- mHeader = mKind.actionAltHeader.inflateUsing(context, cursor);
- } else if (mKind.actionHeader != null) {
- mHeader = mKind.actionHeader.inflateUsing(context, cursor);
+ // Determine type for subtitle
+ mSubtitle = "";
+ if (kind.typeColumn != null) {
+ final int typeColumnIndex = cursor.getColumnIndex(kind.typeColumn);
+ if (typeColumnIndex != -1) {
+ final int typeValue = cursor.getInt(typeColumnIndex);
+
+ // get type string
+ for (EditType type : kind.typeList) {
+ if (type.rawValue == typeValue) {
+ if (type.customColumn == null) {
+ // Non-custom type. Get its description from the resource
+ mSubtitle = context.getString(type.labelRes);
+ } else {
+ // Custom type. Read it from the database
+ mSubtitle = cursor.getString(cursor.getColumnIndexOrThrow(
+ type.customColumn));
+ }
+ break;
+ }
+ }
+ }
}
if (getAsInt(cursor, Data.IS_SUPER_PRIMARY) != 0) {
@@ -71,13 +88,30 @@
mDataId = dataId;
mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
+ final boolean hasPhone = PhoneCapabilityTester.isPhone(mContext);
+ final boolean hasSms = PhoneCapabilityTester.isSmsIntentRegistered(mContext);
+
// Handle well-known MIME-types with special care
if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
if (PhoneCapabilityTester.isPhone(mContext)) {
final String number = getAsString(cursor, Phone.NUMBER);
if (!TextUtils.isEmpty(number)) {
- final Uri callUri = Uri.fromParts(Constants.SCHEME_TEL, number, null);
- mIntent = new Intent(Intent.ACTION_CALL_PRIVILEGED, callUri);
+
+ final Intent phoneIntent = hasPhone ? new Intent(Intent.ACTION_CALL_PRIVILEGED,
+ Uri.fromParts(Constants.SCHEME_TEL, number, null)) : null;
+ final Intent smsIntent = hasSms ? new Intent(Intent.ACTION_SENDTO,
+ Uri.fromParts(Constants.SCHEME_SMSTO, number, null)) : null;
+
+ // Configure Icons and Intents. Notice actionIcon is already set to the phone
+ if (hasPhone && hasSms) {
+ mIntent = phoneIntent;
+ mAlternateIntent = smsIntent;
+ mAlternateIconRes = kind.iconAltResDark;
+ } else if (hasPhone) {
+ mIntent = phoneIntent;
+ } else if (hasSms) {
+ mIntent = smsIntent;
+ }
}
}
} else if (SipAddress.CONTENT_ITEM_TYPE.equals(mimeType)) {
@@ -94,14 +128,6 @@
// for the SIP-related intent-filters in its manifest.
}
}
- } else if (Constants.MIME_TYPE_SMS_ADDRESS.equals(mimeType)) {
- if (PhoneCapabilityTester.isSmsIntentRegistered(mContext)) {
- final String number = getAsString(cursor, Phone.NUMBER);
- if (!TextUtils.isEmpty(number)) {
- final Uri smsUri = Uri.fromParts(Constants.SCHEME_SMSTO, number, null);
- mIntent = new Intent(Intent.ACTION_SENDTO, smsUri);
- }
- }
} else if (Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
final String address = getAsString(cursor, Email.DATA);
if (!TextUtils.isEmpty(address)) {
@@ -127,7 +153,7 @@
if (isEmail) {
// Use Google Talk string when using Email, and clear data
// Uri so we don't try saving Email as primary.
- mHeader = context.getText(R.string.chat_gtalk);
+ mSubtitle = context.getText(R.string.chat_gtalk);
mDataUri = null;
}
@@ -189,66 +215,59 @@
return true;
}
- /** {@inheritDoc} */
@Override
- public CharSequence getHeader() {
- return mHeader;
+ public CharSequence getSubtitle() {
+ return mSubtitle;
}
- /** {@inheritDoc} */
@Override
public CharSequence getBody() {
return mBody;
}
- /** {@inheritDoc} */
@Override
public String getMimeType() {
return mMimeType;
}
- /** {@inheritDoc} */
@Override
public Uri getDataUri() {
return mDataUri;
}
- /** {@inheritDoc} */
@Override
public long getDataId() {
return mDataId;
}
- /** {@inheritDoc} */
@Override
public Boolean isPrimary() {
return mIsPrimary;
}
- /** {@inheritDoc} */
@Override
- public Drawable getFallbackIcon() {
- // Bail early if no valid resources
+ public Drawable getAlternateIcon() {
+ if (mAlternateIconRes == 0) return null;
+
final String resPackageName = mKind.resPackageName;
- if (resPackageName == null) return null;
+ if (resPackageName == null) {
+ return mContext.getResources().getDrawable(mAlternateIconRes);
+ }
final PackageManager pm = mContext.getPackageManager();
- if (mAlternate && mKind.iconAltRes != -1) {
- return pm.getDrawable(resPackageName, mKind.iconAltRes, null);
- } else if (mKind.iconRes != -1) {
- return pm.getDrawable(resPackageName, mKind.iconRes, null);
- } else {
- return null;
- }
+ return pm.getDrawable(resPackageName, mAlternateIconRes, null);
}
- /** {@inheritDoc} */
@Override
public Intent getIntent() {
return mIntent;
}
- /** {@inheritDoc} */
+ @Override
+ public Intent getAlternateIntent() {
+ return mAlternateIntent;
+ }
+
@Override
public boolean collapseWith(Action other) {
if (!shouldCollapseWith(other)) {
@@ -257,7 +276,6 @@
return true;
}
- /** {@inheritDoc} */
@Override
public boolean shouldCollapseWith(Action t) {
if (t == null) {
@@ -276,8 +294,7 @@
return false;
}
if (!TextUtils.equals(mMimeType, other.mMimeType)
- || !ContactsUtils.areIntentActionEqual(mIntent, other.mIntent)
- ) {
+ || !ContactsUtils.areIntentActionEqual(mIntent, other.mIntent)) {
return false;
}
return true;
diff --git a/src/com/android/contacts/quickcontact/FloatingChildLayout.java b/src/com/android/contacts/quickcontact/FloatingChildLayout.java
index ddba609..11d3176 100644
--- a/src/com/android/contacts/quickcontact/FloatingChildLayout.java
+++ b/src/com/android/contacts/quickcontact/FloatingChildLayout.java
@@ -18,15 +18,16 @@
import com.android.contacts.R;
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.Animation.AnimationListener;
+import android.view.ViewPropertyAnimator;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.PopupWindow;
@@ -46,22 +47,28 @@
*/
public class FloatingChildLayout extends FrameLayout {
private static final String TAG = "FloatingChild";
+ private int mFixedTopPosition;
+ private View mChild;
+ private Rect mTargetScreen = new Rect();
+ private final int mAnimationDuration;
public FloatingChildLayout(Context context, AttributeSet attrs) {
super(context, attrs);
+ final Resources resources = getResources();
+ mFixedTopPosition =
+ resources.getDimensionPixelOffset(R.dimen.quick_contact_top_position);
+ mAnimationDuration = resources.getInteger(android.R.integer.config_shortAnimTime);
}
- private View mChild;
-
- private Rect mTargetScreen = new Rect();
-
- private int mCalloutState = 0;
- private int mCalloutLeft;
-
@Override
protected void onFinishInflate() {
mChild = findViewById(android.R.id.content);
mChild.setDuplicateParentStateEnabled(true);
+
+ // this will be expanded in showChild()
+ mChild.setScaleX(0.0f);
+ mChild.setScaleY(0.0f);
+ mChild.setAlpha(0.0f);
}
public View getChild() {
@@ -90,26 +97,6 @@
return target;
}
- private void updateCallout(int calloutState, int calloutLeft) {
- if (mCalloutState != calloutState) {
- mCalloutState = calloutState;
- mChild.refreshDrawableState();
- }
-
- final Drawable background = mChild.getBackground();
- if (background != null && mCalloutLeft != calloutLeft) {
- mCalloutLeft = calloutLeft;
- background.setLevel(calloutLeft);
- }
- }
-
- @Override
- protected int[] onCreateDrawableState(int extraSpace) {
- final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
- mergeDrawableStates(drawableState, new int[] { mCalloutState });
- return drawableState;
- }
-
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
@@ -119,38 +106,23 @@
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight();
- // default is no callout, left-aligned, and vertically centered
- int calloutState = 0;
- int childLeft = target.left;
- int childTop = target.centerY() - (childHeight / 2);
+ if (mFixedTopPosition != -1) {
+ // Horizontally centered, vertically fixed position
+ final int childLeft = (getWidth() - childWidth) / 2;
+ final int childTop = mFixedTopPosition;
+ layoutChild(child, childLeft, childTop);
+ } else {
+ // default is centered horizontally around target...
+ final int childLeft = target.centerX() - (childWidth / 2);
+ // ... and vertically aligned at the top
+ final int childTop = target.top;
- // when target is wide, horizontally center instead of left-align
- if (target.width() > childWidth / 2) {
- childLeft = target.centerX() - (childWidth / 2);
+ // when child is outside bounds, nudge back inside
+ final int clampedChildLeft = clampDimension(childLeft, childWidth, getWidth());
+ final int clampedChildTop = clampDimension(childTop, childHeight, getHeight());
+
+ layoutChild(child, clampedChildLeft, clampedChildTop);
}
-
- final int areaAboveTarget = target.top;
- final int areaBelowTarget = getHeight() - target.bottom;
-
- if (areaAboveTarget >= childHeight) {
- // enough room above target, place above and callout down
- calloutState = android.R.attr.state_first;
- childTop = target.top - childHeight;
-
- } else if (areaBelowTarget >= childHeight) {
- // enough room below target, place below and callout up
- calloutState = android.R.attr.state_last;
- childTop = target.bottom;
- }
-
- // when child is outside bounds, nudge back inside
- childLeft = clampDimension(childLeft, childWidth, getWidth());
- childTop = clampDimension(childTop, childHeight, getHeight());
-
- final int calloutLeft = target.centerX() - childLeft;
- updateCallout(calloutState, calloutLeft);
- layoutChild(child, childLeft, childTop);
-
}
private static int clampDimension(int value, int size, int max) {
@@ -159,58 +131,55 @@
return (max - size) / 2;
}
- // clamp to lower bound
- value = Math.max(value, 0);
- // clamp to higher bound
- value = Math.min(value, max - size);
-
- return value;
+ // clamp to bounds
+ return Math.min(Math.max(value, 0), max - size);
}
private static void layoutChild(View child, int left, int top) {
child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
}
- /**
- * Begin animating {@link #getChild()} visible.
- */
- public void showChild() {
- final boolean calloutAbove = mCalloutState == android.R.attr.state_first;
- final Animation anim = AnimationUtils.loadAnimation(getContext(),
- calloutAbove ? R.anim.quickcontact_above_enter : R.anim.quickcontact_below_enter);
- mChild.startAnimation(anim);
- mChild.setVisibility(View.VISIBLE);
+ /** Begin animating {@link #getChild()} visible. */
+ public void showChild(Runnable onAnimationEndRunnable) {
+ animateScale(false, onAnimationEndRunnable);
}
- /**
- * Begin animating {@link #getChild()} invisible.
- */
- public void hideChild(final Runnable onAnimationEnd) {
- final boolean calloutAbove = mCalloutState == android.R.attr.state_first;
- final Animation anim = AnimationUtils.loadAnimation(getContext(),
- calloutAbove ? R.anim.quickcontact_above_exit : R.anim.quickcontact_below_exit);
+ /** Begin animating {@link #getChild()} invisible. */
+ public void hideChild(Runnable onAnimationEndRunnable) {
+ animateScale(true, onAnimationEndRunnable);
+ }
- if (onAnimationEnd != null) {
- anim.setAnimationListener(new AnimationListener() {
- /** {@inheritDoc} */
- public void onAnimationStart(Animation animation) {
- // ignored
- }
+ /** Creates the open/close animation */
+ private void animateScale(boolean isExitAnimation, final Runnable onAnimationEndRunnable) {
+ mChild.setPivotX(mTargetScreen.centerX() - mChild.getLeft());
+ mChild.setPivotY(mTargetScreen.centerY() - mChild.getTop());
+ ViewPropertyAnimator animator = mChild.animate();
+ animator.setDuration(mAnimationDuration);
+ final int scaleInterpolator = isExitAnimation ? android.R.interpolator.accelerate_quint
+ : android.R.interpolator.decelerate_quint;
+ animator.setInterpolator(AnimationUtils.loadInterpolator(getContext(), scaleInterpolator));
+ final float scaleTarget = isExitAnimation ? 0.7f : 1.0f;
+ animator.scaleX(scaleTarget);
+ animator.scaleY(scaleTarget);
+ animator.alpha(isExitAnimation ? 0.0f : 1.0f);
- /** {@inheritDoc} */
- public void onAnimationRepeat(Animation animation) {
- // ignored
- }
+ if (onAnimationEndRunnable != null) {
+ animator.setListener(new AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {}
- /** {@inheritDoc} */
- public void onAnimationEnd(Animation animation) {
- onAnimationEnd.run();
+ @Override
+ public void onAnimationRepeat(Animator animation) {}
+
+ @Override
+ public void onAnimationCancel(Animator animation) {}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onAnimationEndRunnable.run();
}
});
}
-
- mChild.startAnimation(anim);
- mChild.setVisibility(View.INVISIBLE);
}
private View.OnTouchListener mOutsideTouchListener;
@@ -224,8 +193,7 @@
// at this point, touch wasn't handled by child view; assume outside
if (mOutsideTouchListener != null) {
return mOutsideTouchListener.onTouch(this, event);
- } else {
- return false;
}
+ return false;
}
}
diff --git a/src/com/android/contacts/quickcontact/ProfileAction.java b/src/com/android/contacts/quickcontact/ProfileAction.java
deleted file mode 100644
index a04e522..0000000
--- a/src/com/android/contacts/quickcontact/ProfileAction.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts.quickcontact;
-
-import com.android.contacts.R;
-
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.provider.ContactsContract.Contacts;
-
-/**
- * Specific action that launches the profile card.
- */
-public class ProfileAction implements Action {
- private final Context mContext;
- private final Uri mLookupUri;
-
- public ProfileAction(Context context, Uri lookupUri) {
- mContext = context;
- mLookupUri = lookupUri;
- }
-
- /** {@inheritDoc} */
- @Override
- public CharSequence getHeader() {
- return null;
- }
-
- /** {@inheritDoc} */
- @Override
- public CharSequence getBody() {
- return null;
- }
-
- /** {@inheritDoc} */
- @Override
- public String getMimeType() {
- return Contacts.CONTENT_ITEM_TYPE;
- }
-
- /** {@inheritDoc} */
- @Override
- public Drawable getFallbackIcon() {
- return mContext.getResources().getDrawable(R.drawable.ic_contacts_details);
- }
-
- /** {@inheritDoc} */
- @Override
- public Intent getIntent() {
- final Intent intent = new Intent(Intent.ACTION_VIEW, mLookupUri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- return intent;
- }
-
- /** {@inheritDoc} */
- @Override
- public Boolean isPrimary() {
- return null;
- }
-
- /** {@inheritDoc} */
- @Override
- public Uri getDataUri() {
- return null;
- }
-
- /** {@inheritDoc} */
- @Override
- public long getDataId() {
- return -1;
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean collapseWith(Action t) {
- return false; // Never dup.
- }
-
- /** {@inheritDoc} */
- @Override
- public boolean shouldCollapseWith(Action t) {
- return false; // Never dup.
- }
-}
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
new file mode 100644
index 0000000..47e7173
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -0,0 +1,725 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.quickcontact;
+
+import com.android.contacts.Collapser;
+import com.android.contacts.ContactPresenceIconUtil;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.DataKind;
+import com.android.contacts.util.Constants;
+import com.android.contacts.util.ContactBadgeUtil;
+import com.android.contacts.util.DataStatus;
+import com.android.contacts.util.NotifyingAsyncQueryHandler;
+import com.android.contacts.util.NotifyingAsyncQueryHandler.AsyncQueryListener;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.ActivityNotFoundException;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.DisplayPhoto;
+import android.provider.ContactsContract.QuickContact;
+import android.provider.ContactsContract.RawContacts;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.SimpleOnPageChangeListener;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.HorizontalScrollView;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+// TODO: Save selected tab index during rotation
+// TODO: Handle GTalk Audio/Videochat secondary actions
+// TODO: Don't do a query in QuickContactBadge
+// TODO: Fix bug when QuickContact is dismissed using HOME or task switching
+// (it will relaunch with the previous contact)
+
+// Missing assets and specs:
+// Pushed states for list items
+
+/**
+ * Mostly translucent {@link Activity} that shows QuickContact dialog. It loads
+ * data asynchronously, and then shows a popup with details centered around
+ * {@link Intent#getSourceBounds()}.
+ */
+public class QuickContactActivity extends Activity {
+ private static final String TAG = "QuickContact";
+
+ private static final boolean TRACE_LAUNCH = false;
+ private static final String TRACE_TAG = "quickcontact";
+
+ @SuppressWarnings("deprecation")
+ private static final String LEGACY_AUTHORITY = android.provider.Contacts.AUTHORITY;
+
+ private NotifyingAsyncQueryHandler mHandler;
+
+ private Uri mLookupUri;
+ private String[] mExcludeMimes;
+ private List<String> mSortedActionMimeTypes = Lists.newArrayList();
+
+ private boolean mHasFinishedAnimatingIn = false;
+ private boolean mHasStartedAnimatingOut = false;
+
+ private FloatingChildLayout mFloatingLayout;
+
+ private View mPhotoContainer;
+ private ViewGroup mTrack;
+ private HorizontalScrollView mTrackScroller;
+ private View mSelectedTabRectangle;
+ /** Line before the track. Depending on the layout, this can be null */
+ private View mLineBeforeTrack;
+
+ private ImageButton mOpenDetailsButton;
+ private ImageButton mOpenDetailsPushLayerButton;
+ private ViewPager mListPager;
+
+ /**
+ * Keeps the default action per mimetype. Empty if no default actions are set
+ */
+ private HashMap<String, Action> mDefaultsMap = new HashMap<String, Action>();
+
+ /**
+ * Set of {@link Action} that are associated with the aggregate currently
+ * displayed by this dialog, represented as a map from {@link String}
+ * MIME-type to a list of {@link Action}.
+ */
+ private ActionMultiMap mActions = new ActionMultiMap();
+
+ /**
+ * {@link #LEADING_MIMETYPES} and {@link #TRAILING_MIMETYPES} are used to sort MIME-types.
+ *
+ * <p>The MIME-types in {@link #LEADING_MIMETYPES} appear in the front of the dialog,
+ * in the order specified here.</p>
+ *
+ * <p>The ones in {@link #TRAILING_MIMETYPES} appear in the end of the dialog, in the order
+ * specified here.</p>
+ *
+ * <p>The rest go between them, in the order in the array.</p>
+ */
+ private static final List<String> LEADING_MIMETYPES = Lists.newArrayList(
+ Phone.CONTENT_ITEM_TYPE, SipAddress.CONTENT_ITEM_TYPE, Email.CONTENT_ITEM_TYPE);
+
+ /** See {@link #LEADING_MIMETYPES}. */
+ private static final List<String> TRAILING_MIMETYPES = Lists.newArrayList(
+ StructuredPostal.CONTENT_ITEM_TYPE, Website.CONTENT_ITEM_TYPE);
+
+ /** Id for the background handler that loads the data */
+ private static final int HANDLER_ID_DATA = 1;
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setContentView(R.layout.quickcontact_activity);
+
+ mFloatingLayout = (FloatingChildLayout) findViewById(R.id.floating_layout);
+ mTrack = (ViewGroup) findViewById(R.id.track);
+ mTrackScroller = (HorizontalScrollView) findViewById(R.id.track_scroller);
+ mOpenDetailsButton = (ImageButton) findViewById(R.id.open_details_button);
+ mOpenDetailsPushLayerButton = (ImageButton) findViewById(R.id.open_details_push_layer);
+ mListPager = (ViewPager) findViewById(R.id.item_list_pager);
+ mSelectedTabRectangle = findViewById(R.id.selected_tab_rectangle);
+ mLineBeforeTrack = findViewById(R.id.line_before_track);
+
+ mFloatingLayout.setOnOutsideTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ return handleOutsideTouch();
+ }
+ });
+
+ final OnClickListener openDetailsClickHandler = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Intent intent = new Intent(Intent.ACTION_VIEW, mLookupUri);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(intent);
+ hide(false);
+ }
+ };
+ mOpenDetailsButton.setOnClickListener(openDetailsClickHandler);
+ mOpenDetailsPushLayerButton.setOnClickListener(openDetailsClickHandler);
+ mListPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));
+ mListPager.setOnPageChangeListener(new PageChangeListener());
+
+ mHandler = new NotifyingAsyncQueryHandler(this, mQueryListener);
+
+ show();
+ }
+
+ private void show() {
+
+ if (TRACE_LAUNCH) {
+ android.os.Debug.startMethodTracing(TRACE_TAG);
+ }
+
+ final Intent intent = getIntent();
+
+ Uri lookupUri = intent.getData();
+
+ // Check to see whether it comes from the old version.
+ if (LEGACY_AUTHORITY.equals(lookupUri.getAuthority())) {
+ final long rawContactId = ContentUris.parseId(lookupUri);
+ lookupUri = RawContacts.getContactLookupUri(getContentResolver(),
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
+ }
+
+ mLookupUri = Preconditions.checkNotNull(lookupUri, "missing lookupUri");
+
+ // Read requested parameters for displaying
+ final Rect targetScreen = intent.getSourceBounds();
+ Preconditions.checkNotNull(targetScreen, "missing targetScreen");
+ mFloatingLayout.setChildTargetScreen(targetScreen);
+
+ mExcludeMimes = intent.getStringArrayExtra(QuickContact.EXTRA_EXCLUDE_MIMES);
+
+ // find and prepare correct header view
+ mPhotoContainer = findViewById(R.id.photo_container);
+ setHeaderText(R.id.name, R.string.quickcontact_missing_name);
+ setHeaderText(R.id.status, null);
+ setHeaderText(R.id.timestamp, null);
+ setHeaderImage(R.id.presence, null);
+
+ // Start background query for data, but only select photo rows when they
+ // directly match the super-primary PHOTO_ID.
+ final Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
+ mHandler.cancelOperation(HANDLER_ID_DATA);
+
+ // Select all data items of the contact (except for photos, where we only select the display
+ // photo)
+ mHandler.startQuery(HANDLER_ID_DATA, lookupUri, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
+ + "!=? OR (" + Data.MIMETYPE + "=? AND " + Data._ID + "=" + Contacts.PHOTO_ID
+ + ")", new String[] { Photo.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE }, null);
+ }
+
+ private boolean handleOutsideTouch() {
+ if (!mHasFinishedAnimatingIn) return false;
+ if (mHasStartedAnimatingOut) return false;
+
+ mHasStartedAnimatingOut = true;
+ hide(true);
+ return true;
+ }
+
+ private void hide(boolean withAnimation) {
+ // cancel any pending queries
+ mHandler.cancelOperation(HANDLER_ID_DATA);
+
+ if (withAnimation) {
+ mFloatingLayout.hideChild(new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ });
+ } else {
+ mFloatingLayout.hideChild(null);
+ finish();
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ hide(true);
+ }
+
+ private final AsyncQueryListener mQueryListener = new AsyncQueryListener() {
+ @Override
+ public synchronized void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ try {
+ if (isFinishing()) {
+ hide(false);
+ return;
+ } else if (cursor == null || cursor.getCount() == 0) {
+ Toast.makeText(QuickContactActivity.this, R.string.invalidContactMessage,
+ Toast.LENGTH_LONG).show();
+ hide(false);
+ return;
+ }
+
+ bindData(cursor);
+
+ if (TRACE_LAUNCH) {
+ android.os.Debug.stopMethodTracing();
+ }
+
+ // Data bound and ready, pull curtain to show. Put this on the Handler to ensure
+ // that the layout passes are completed
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mFloatingLayout.showChild(new Runnable() {
+ @Override
+ public void run() {
+ mHasFinishedAnimatingIn = true;
+ }
+ });
+ }
+ });
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+ };
+
+ /** Assign this string to the view, if found in {@link #mPhotoContainer}. */
+ private void setHeaderText(int id, int resId) {
+ setHeaderText(id, getText(resId));
+ }
+
+ /** Assign this string to the view, if found in {@link #mPhotoContainer}. */
+ private void setHeaderText(int id, CharSequence value) {
+ final View view = mPhotoContainer.findViewById(id);
+ if (view instanceof TextView) {
+ ((TextView)view).setText(value);
+ view.setVisibility(TextUtils.isEmpty(value) ? View.GONE : View.VISIBLE);
+ }
+ }
+
+ /** Assign this image to the view, if found in {@link #mPhotoContainer}. */
+ private void setHeaderImage(int id, Drawable drawable) {
+ final View view = mPhotoContainer.findViewById(id);
+ if (view instanceof ImageView) {
+ ((ImageView)view).setImageDrawable(drawable);
+ view.setVisibility(drawable == null ? View.GONE : View.VISIBLE);
+ }
+ }
+
+ /**
+ * Check if the given MIME-type appears in the list of excluded MIME-types
+ * that the most-recent caller requested.
+ */
+ private boolean isMimeExcluded(String mimeType) {
+ if (mExcludeMimes == null) return false;
+ for (String excludedMime : mExcludeMimes) {
+ if (TextUtils.equals(excludedMime, mimeType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Handle the result from the {@link #TOKEN_DATA} query.
+ */
+ private void bindData(Cursor cursor) {
+ final ResolveCache cache = ResolveCache.getInstance(this);
+ final Context context = this;
+
+ mOpenDetailsButton.setVisibility(isMimeExcluded(Contacts.CONTENT_ITEM_TYPE) ? View.GONE
+ : View.VISIBLE);
+
+ mDefaultsMap.clear();
+
+ final DataStatus status = new DataStatus();
+ final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
+ context.getApplicationContext());
+ final ImageView photoView = (ImageView) mPhotoContainer.findViewById(R.id.photo);
+
+ Bitmap photoBitmap = null;
+ while (cursor.moveToNext()) {
+ // Handle any social status updates from this row
+ status.possibleUpdate(cursor);
+
+ final String mimeType = cursor.getString(DataQuery.MIMETYPE);
+
+ // Skip this data item if MIME-type excluded
+ if (isMimeExcluded(mimeType)) continue;
+
+ final long dataId = cursor.getLong(DataQuery._ID);
+ final String accountType = cursor.getString(DataQuery.ACCOUNT_TYPE);
+ final String dataSet = cursor.getString(DataQuery.DATA_SET);
+ final boolean isPrimary = cursor.getInt(DataQuery.IS_PRIMARY) != 0;
+ final boolean isSuperPrimary = cursor.getInt(DataQuery.IS_SUPER_PRIMARY) != 0;
+
+ // Handle photos included as data row
+ if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ final int displayPhotoColumnIndex = cursor.getColumnIndex(Photo.PHOTO_FILE_ID);
+ final boolean hasDisplayPhoto = !cursor.isNull(displayPhotoColumnIndex);
+ if (hasDisplayPhoto) {
+ final long displayPhotoId = cursor.getLong(displayPhotoColumnIndex);
+ final Uri displayPhotoUri = ContentUris.withAppendedId(
+ DisplayPhoto.CONTENT_URI, displayPhotoId);
+ // Fetch and JPEG uncompress on the background thread
+ new AsyncTask<Void, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(Void... params) {
+ try {
+ AssetFileDescriptor fd = getContentResolver()
+ .openAssetFileDescriptor(displayPhotoUri, "r");
+ return BitmapFactory.decodeStream(fd.createInputStream());
+ } catch (IOException e) {
+ Log.e(TAG, "Error getting display photo. Ignoring, as we already " +
+ "have the thumbnail", e);
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ if (result == null) return;
+ photoView.setImageBitmap(result);
+ }
+ }.execute();
+ }
+ final int photoColumnIndex = cursor.getColumnIndex(Photo.PHOTO);
+ final byte[] photoBlob = cursor.getBlob(photoColumnIndex);
+ if (photoBlob != null) {
+ photoBitmap = BitmapFactory.decodeByteArray(photoBlob, 0, photoBlob.length);
+ }
+ continue;
+ }
+
+ final DataKind kind = accountTypes.getKindOrFallback(accountType, dataSet, mimeType);
+
+ if (kind != null) {
+ // Build an action for this data entry, find a mapping to a UI
+ // element, build its summary from the cursor, and collect it
+ // along with all others of this MIME-type.
+ final Action action = new DataAction(context, mimeType, kind, dataId, cursor);
+ final boolean wasAdded = considerAdd(action, cache);
+ if (wasAdded) {
+ // Remember the default
+ if (isSuperPrimary || (isPrimary && (mDefaultsMap.get(mimeType) == null))) {
+ mDefaultsMap.put(mimeType, action);
+ }
+ }
+ }
+
+ boolean isIm = Im.CONTENT_ITEM_TYPE.equals(mimeType);
+
+ // Handle Email rows with presence data as Im entry
+ final boolean hasPresence = !cursor.isNull(DataQuery.PRESENCE);
+ if (hasPresence && Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
+ final DataKind imKind = accountTypes.getKindOrFallback(accountType, dataSet,
+ Im.CONTENT_ITEM_TYPE);
+ if (imKind != null) {
+ final DataAction action = new DataAction(context, Im.CONTENT_ITEM_TYPE, imKind,
+ dataId, cursor);
+ considerAdd(action, cache);
+ isIm = true;
+ }
+ }
+
+ if (hasPresence && isIm) {
+ int chatCapability = cursor.getInt(DataQuery.CHAT_CAPABILITY);
+ if ((chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) {
+ final DataKind imKind = accountTypes.getKindOrFallback(accountType, dataSet,
+ Im.CONTENT_ITEM_TYPE);
+ if (imKind != null) {
+ final DataAction chatAction = new DataAction(context,
+ Constants.MIME_TYPE_VIDEO_CHAT, imKind, dataId, cursor);
+ considerAdd(chatAction, cache);
+ }
+ }
+ }
+ }
+
+ // Collapse Action Lists (remove e.g. duplicate e-mail addresses from different sources)
+ for (List<Action> actionChildren : mActions.values()) {
+ Collapser.collapseList(actionChildren);
+ }
+
+ if (cursor.moveToLast()) {
+ // Read contact information from last data row
+ final String name = cursor.getString(DataQuery.DISPLAY_NAME);
+ final int presence = cursor.getInt(DataQuery.CONTACT_PRESENCE);
+ final int chatCapability = cursor.getInt(DataQuery.CONTACT_CHAT_CAPABILITY);
+ final Drawable statusIcon = ContactPresenceIconUtil.getChatCapabilityIcon(
+ context, presence, chatCapability);
+
+ setHeaderText(R.id.name, name);
+ // TODO: Bring this back once we have a design
+// setHeaderImage(R.id.presence, statusIcon);
+ }
+
+ if (photoView != null) {
+ // Place photo when discovered in data, otherwise show generic avatar
+ photoView.setImageBitmap(photoBitmap != null ? photoBitmap
+ : ContactBadgeUtil.loadPlaceholderPhoto(context));
+ }
+
+ // TODO: Bring this back once we have a design
+// if (status.isValid()) {
+// // Update status when valid was found
+// setHeaderText(R.id.status, status.getStatus());
+// setHeaderText(R.id.timestamp, status.getTimestampLabel(context));
+// }
+
+ // All the mime-types to add.
+ final Set<String> containedTypes = new HashSet<String>(mActions.keySet());
+ mSortedActionMimeTypes.clear();
+ // First, add LEADING_MIMETYPES, which are most common.
+ for (String mimeType : LEADING_MIMETYPES) {
+ if (containedTypes.contains(mimeType)) {
+ mSortedActionMimeTypes.add(mimeType);
+ containedTypes.remove(mimeType);
+ }
+ }
+
+ // Add all the remaining ones that are not TRAILING
+ for (String mimeType : containedTypes.toArray(new String[containedTypes.size()])) {
+ if (!TRAILING_MIMETYPES.contains(mimeType)) {
+ mSortedActionMimeTypes.add(mimeType);
+ containedTypes.remove(mimeType);
+ }
+ }
+
+ // Then, add TRAILING_MIMETYPES, which are least common.
+ for (String mimeType : TRAILING_MIMETYPES) {
+ if (containedTypes.contains(mimeType)) {
+ containedTypes.remove(mimeType);
+ mSortedActionMimeTypes.add(mimeType);
+ }
+ }
+
+ // Add buttons for each mimetype
+ for (String mimeType : mSortedActionMimeTypes) {
+ final View actionView = inflateAction(mimeType, cache, mTrack);
+ mTrack.addView(actionView);
+ }
+
+ final boolean hasData = !mSortedActionMimeTypes.isEmpty();
+ if (mLineBeforeTrack != null) {
+ mLineBeforeTrack.setVisibility(hasData ? View.VISIBLE : View.GONE);
+ }
+ mTrackScroller.setVisibility(hasData ? View.VISIBLE : View.GONE);
+ mSelectedTabRectangle.setVisibility(hasData ? View.VISIBLE : View.GONE);
+ mListPager.setVisibility(hasData ? View.VISIBLE : View.GONE);
+ }
+
+ /**
+ * Consider adding the given {@link Action}, which will only happen if
+ * {@link PackageManager} finds an application to handle
+ * {@link Action#getIntent()}.
+ * @return true if action has been added
+ */
+ private boolean considerAdd(Action action, ResolveCache resolveCache) {
+ if (resolveCache.hasResolve(action)) {
+ mActions.put(action.getMimeType(), action);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Inflate the in-track view for the action of the given MIME-type, collapsing duplicate values.
+ * Will use the icon provided by the {@link DataKind}.
+ */
+ private View inflateAction(String mimeType, ResolveCache resolveCache, ViewGroup root) {
+ final CheckableImageView typeView = (CheckableImageView) getLayoutInflater().inflate(
+ R.layout.quickcontact_track_button, root, false);
+
+ List<Action> children = mActions.get(mimeType);
+ typeView.setTag(mimeType);
+ final Action firstInfo = children.get(0);
+
+ // Set icon and listen for clicks
+ final CharSequence descrip = resolveCache.getDescription(firstInfo);
+ final Drawable icon = resolveCache.getIcon(firstInfo);
+ typeView.setChecked(false);
+ typeView.setContentDescription(descrip);
+ typeView.setImageDrawable(icon);
+ typeView.setOnClickListener(mTypeViewClickListener);
+
+ return typeView;
+ }
+
+ private CheckableImageView getActionViewAt(int position) {
+ return (CheckableImageView) mTrack.getChildAt(position);
+ }
+
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ final QuickContactListFragment listFragment = (QuickContactListFragment) fragment;
+ listFragment.setListener(mListFragmentListener);
+ }
+
+ /** A type (e.g. Call/Addresses was clicked) */
+ private final OnClickListener mTypeViewClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ final CheckableImageView actionView = (CheckableImageView)view;
+ final String mimeType = (String) actionView.getTag();
+ int index = mSortedActionMimeTypes.indexOf(mimeType);
+ mListPager.setCurrentItem(index, true);
+ }
+ };
+
+ private class ViewPagerAdapter extends FragmentPagerAdapter {
+ public ViewPagerAdapter(FragmentManager fragmentManager) {
+ super(fragmentManager);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ QuickContactListFragment fragment = new QuickContactListFragment();
+ final String mimeType = mSortedActionMimeTypes.get(position);
+ final List<Action> actions = mActions.get(mimeType);
+ fragment.setActions(actions);
+ return fragment;
+ }
+
+ @Override
+ public int getCount() {
+ return mSortedActionMimeTypes.size();
+ }
+ }
+
+ private class PageChangeListener extends SimpleOnPageChangeListener {
+ @Override
+ public void onPageSelected(int position) {
+ final CheckableImageView actionView = getActionViewAt(position);
+ mTrackScroller.requestChildRectangleOnScreen(actionView,
+ new Rect(0, 0, actionView.getWidth(), actionView.getHeight()), false);
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ final RelativeLayout.LayoutParams layoutParams =
+ (RelativeLayout.LayoutParams) mSelectedTabRectangle.getLayoutParams();
+ final int width = mSelectedTabRectangle.getWidth();
+ layoutParams.leftMargin = (int) ((position + positionOffset) * width);
+ mSelectedTabRectangle.setLayoutParams(layoutParams);
+ }
+ }
+
+ private final QuickContactListFragment.Listener mListFragmentListener =
+ new QuickContactListFragment.Listener() {
+ @Override
+ public void onOutsideClick() {
+ // If there is no background, we want to dismiss, because to the user it seems
+ // like he had touched outside. If the ViewPager is solid however, those taps
+ // must be ignored
+ final boolean isTransparent = mListPager.getBackground() == null;
+ if (isTransparent) handleOutsideTouch();
+ }
+
+ @Override
+ public void onItemClicked(final Action action, final boolean alternate) {
+ final Runnable startAppRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ startActivity(alternate ? action.getAlternateIntent() : action.getIntent());
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(QuickContactActivity.this, R.string.quickcontact_missing_app,
+ Toast.LENGTH_SHORT).show();
+ }
+
+ hide(false);
+ }
+ };
+ // Defer the action to make the window properly repaint
+ new Handler().post(startAppRunnable);
+ }
+ };
+
+ private interface DataQuery {
+ final String[] PROJECTION = new String[] {
+ Data._ID,
+
+ RawContacts.ACCOUNT_TYPE,
+ RawContacts.DATA_SET,
+ Contacts.STARRED,
+ Contacts.DISPLAY_NAME,
+ Contacts.CONTACT_PRESENCE,
+ Contacts.CONTACT_CHAT_CAPABILITY,
+
+ Data.STATUS,
+ Data.STATUS_RES_PACKAGE,
+ Data.STATUS_ICON,
+ Data.STATUS_LABEL,
+ Data.STATUS_TIMESTAMP,
+ Data.PRESENCE,
+ Data.CHAT_CAPABILITY,
+
+ Data.RES_PACKAGE,
+ Data.MIMETYPE,
+ Data.IS_PRIMARY,
+ Data.IS_SUPER_PRIMARY,
+ Data.RAW_CONTACT_ID,
+
+ Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, Data.DATA5,
+ Data.DATA6, Data.DATA7, Data.DATA8, Data.DATA9, Data.DATA10, Data.DATA11,
+ Data.DATA12, Data.DATA13, Data.DATA14, Data.DATA15,
+ };
+
+ final int _ID = 0;
+
+ final int ACCOUNT_TYPE = 1;
+ final int DATA_SET = 2;
+ final int STARRED = 3;
+ final int DISPLAY_NAME = 4;
+ final int CONTACT_PRESENCE = 5;
+ final int CONTACT_CHAT_CAPABILITY = 6;
+
+ final int STATUS = 7;
+ final int STATUS_RES_PACKAGE = 8;
+ final int STATUS_ICON = 9;
+ final int STATUS_LABEL = 10;
+ final int STATUS_TIMESTAMP = 11;
+ final int PRESENCE = 12;
+ final int CHAT_CAPABILITY = 13;
+
+ final int RES_PACKAGE = 14;
+ final int MIMETYPE = 15;
+ final int IS_PRIMARY = 16;
+ final int IS_SUPER_PRIMARY = 17;
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/QuickContactBackgroundDrawable.java b/src/com/android/contacts/quickcontact/QuickContactBackgroundDrawable.java
deleted file mode 100644
index 15311f9..0000000
--- a/src/com/android/contacts/quickcontact/QuickContactBackgroundDrawable.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts.quickcontact;
-
-import com.android.contacts.R;
-
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-
-/**
- * Background {@link Drawable} for {@link QuickContactWindow} that draws arrow
- * centered around requested position.
- */
-public class QuickContactBackgroundDrawable extends Drawable {
- private Drawable mLeftDrawable;
- private Drawable mMiddleDrawable;
- private Drawable mRightDrawable;
-
- private int mBottomOverride = Integer.MIN_VALUE;
-
- public QuickContactBackgroundDrawable(Resources res) {
- mLeftDrawable = res.getDrawable(R.drawable.quickactions_arrow_left_holo_light);
- mMiddleDrawable = res.getDrawable(R.drawable.quickactions_arrow_middle_holo_light);
- mRightDrawable = res.getDrawable(R.drawable.quickactions_arrow_right_holo_light);
- }
-
- @Override
- public void setAlpha(int alpha) {
- mLeftDrawable.setAlpha(alpha);
- mMiddleDrawable.setAlpha(alpha);
- mRightDrawable.setAlpha(alpha);
- }
-
- /**
- * Overrides the bottom bounds. This is used for the animation when the
- * QuickContact expands/collapses options
- */
- public void setBottomOverride(int value) {
- mBottomOverride = value;
- onBoundsChange(getBounds());
- invalidateSelf();
- }
-
- public void clearBottomOverride() {
- mBottomOverride = Integer.MIN_VALUE;
- onBoundsChange(getBounds());
- invalidateSelf();
- }
-
- public float getBottomOverride() {
- return mBottomOverride;
- }
-
- @Override
- public boolean isStateful() {
- return true;
- }
-
- @Override
- protected boolean onStateChange(int[] state) {
- super.onStateChange(state);
- mLeftDrawable.setState(state);
- mMiddleDrawable.setState(state);
- mRightDrawable.setState(state);
- return true;
- }
-
- @Override
- protected boolean onLevelChange(int level) {
- return true;
- }
-
- @Override
- public void setColorFilter(ColorFilter cf) {
- mLeftDrawable.setColorFilter(cf);
- mMiddleDrawable.setColorFilter(cf);
- mRightDrawable.setColorFilter(cf);
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- @Override
- protected void onBoundsChange(Rect bounds) {
- final int requestedX = getLevel();
-
- int middleLeft = requestedX - mMiddleDrawable.getIntrinsicWidth() / 2;
- int middleRight = requestedX + mMiddleDrawable.getIntrinsicWidth() / 2;
-
- // ensure left drawable is not smaller than its Intrinsic Width
- final int leftExtra = (middleLeft - bounds.left) - mLeftDrawable.getIntrinsicWidth();
- if (leftExtra < 0) {
- middleLeft -= leftExtra;
- middleRight -= leftExtra;
- }
-
- // ensure right drawable is not smaller than its Intrinsic Width
- final int rightExtra = (bounds.right - middleRight) - mRightDrawable.getIntrinsicWidth();
- if (rightExtra < 0) {
- middleLeft += rightExtra;
- middleRight += rightExtra;
- }
-
- final int bottom = mBottomOverride == Integer.MIN_VALUE ? bounds.bottom : mBottomOverride;
- mLeftDrawable.setBounds(bounds.left, bounds.top, middleLeft, bottom);
- mMiddleDrawable.setBounds(middleLeft, bounds.top, middleRight, bottom);
- mRightDrawable.setBounds(middleRight, bounds.top, bounds.right, bottom);
- }
-
- @Override
- public void draw(Canvas canvas) {
- mLeftDrawable.draw(canvas);
- mMiddleDrawable.draw(canvas);
- mRightDrawable.draw(canvas);
- }
-}
diff --git a/src/com/android/contacts/quickcontact/QuickContactListFragment.java b/src/com/android/contacts/quickcontact/QuickContactListFragment.java
new file mode 100644
index 0000000..910fede
--- /dev/null
+++ b/src/com/android/contacts/quickcontact/QuickContactListFragment.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.quickcontact;
+
+import com.android.contacts.R;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.List;
+
+/** A fragment that shows the list of resolve items below a tab */
+public class QuickContactListFragment extends Fragment {
+ private ListView mListView;
+ private List<Action> mActions;
+ private LinearLayout mFragmentContainer;
+ private Listener mListener;
+
+ public QuickContactListFragment() {
+ setRetainInstance(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ mFragmentContainer = (LinearLayout) inflater.inflate(R.layout.quickcontact_list_fragment,
+ container, false);
+ mListView = (ListView) mFragmentContainer.findViewById(R.id.list);
+ mListView.setOnItemClickListener(mItemClickListener);
+ mFragmentContainer.setOnClickListener(mOutsideClickListener);
+ configureAdapter();
+ return mFragmentContainer;
+ }
+
+ public void setActions(List<Action> actions) {
+ mActions = actions;
+ configureAdapter();
+ }
+
+ public void setListener(Listener value) {
+ mListener = value;
+ }
+
+ private void configureAdapter() {
+ if (mActions == null || mListView == null) return;
+
+ mListView.setAdapter(new BaseAdapter() {
+ @Override
+ public int getCount() {
+ return mActions.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mActions.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final View resultView = convertView != null ? convertView
+ : getActivity().getLayoutInflater()
+ .inflate(R.layout.quickcontact_list_item, parent, false);
+
+ // Set action title based on summary value
+ final Action action = mActions.get(position);
+
+ // TODO: Put those findViewByIds in a container
+ final TextView text1 = (TextView) resultView.findViewById(
+ android.R.id.text1);
+ final TextView text2 = (TextView) resultView.findViewById(
+ android.R.id.text2);
+ final ImageView alternateActionButton = (ImageView) resultView.findViewById(
+ R.id.secondary_action_button);
+ final View alternateActionDivider = resultView.findViewById(R.id.vertical_divider);
+
+ alternateActionButton.setOnClickListener(mSecondaryActionClickListener);
+ alternateActionButton.setTag(action);
+
+ final boolean hasAlternateAction = action.getAlternateIntent() != null;
+ alternateActionDivider.setVisibility(hasAlternateAction ? View.VISIBLE : View.GONE);
+ alternateActionButton.setImageDrawable(action.getAlternateIcon());
+
+ text1.setText(action.getBody());
+ text2.setText(action.getSubtitle().toString().toUpperCase());
+
+ resultView.setTag(action);
+ return resultView;
+ }
+ });
+ }
+
+ /** A secondary action (SMS) was clicked */
+ protected final OnClickListener mSecondaryActionClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Action action = (Action) v.getTag();
+ if (mListener != null) mListener.onItemClicked(action, true);
+ }
+ };
+
+ /** A data item (e.g. phone number) was clicked */
+ private final AbsListView.OnItemClickListener mItemClickListener =
+ new AbsListView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ final Action action = (Action) view.getTag();
+ if (mListener != null) mListener.onItemClicked(action, false);
+ }
+ };
+
+ private final OnClickListener mOutsideClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mListener != null) mListener.onOutsideClick();
+ }
+ };
+
+ public interface Listener {
+ void onOutsideClick();
+ void onItemClicked(Action action, boolean alternate);
+ }
+}
diff --git a/src/com/android/contacts/quickcontact/QuickContactWindow.java b/src/com/android/contacts/quickcontact/QuickContactWindow.java
deleted file mode 100644
index 5f4bcc9..0000000
--- a/src/com/android/contacts/quickcontact/QuickContactWindow.java
+++ /dev/null
@@ -1,1026 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.contacts.quickcontact;
-
-import com.android.contacts.Collapser;
-import com.android.contacts.ContactPresenceIconUtil;
-import com.android.contacts.ContactSaveService;
-import com.android.contacts.R;
-import com.android.contacts.model.AccountTypeManager;
-import com.android.contacts.model.DataKind;
-import com.android.contacts.util.Constants;
-import com.android.contacts.util.DataStatus;
-import com.android.contacts.util.NotifyingAsyncQueryHandler;
-import com.google.android.collect.Lists;
-import com.google.common.base.Preconditions;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.app.Activity;
-import android.content.ActivityNotFoundException;
-import android.content.ContentUris;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Im;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.SipAddress;
-import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.ContactsContract.CommonDataKinds.Website;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.QuickContact;
-import android.provider.ContactsContract.RawContacts;
-import android.text.TextUtils;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewStub;
-import android.widget.AbsListView;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Mostly translucent {@link Activity} that shows QuickContact dialog. It loads
- * data asynchronously, and then shows a popup with details centered around
- * {@link Intent#getSourceBounds()}.
- */
-public class QuickContactWindow extends Activity implements
- NotifyingAsyncQueryHandler.AsyncQueryListener, View.OnClickListener,
- AbsListView.OnItemClickListener {
- private static final String TAG = "QuickContact";
-
- private static final boolean TRACE_LAUNCH = false;
- private static final String TRACE_TAG = "quickcontact";
-
- private static final int ANIMATION_FADE_IN_TIME = 100;
- private static final int ANIMATION_FADE_OUT_TIME = 100;
- private static final int ANIMATION_EXPAND_TIME = 100;
- private static final int ANIMATION_COLLAPSE_TIME = 100;
-
- private NotifyingAsyncQueryHandler mHandler;
-
- private Uri mLookupUri;
- private int mMode;
- private String[] mExcludeMimes;
-
- private boolean mHasValidSocial = false;
-
- private FloatingChildLayout mFloatingLayout;
- private QuickContactBackgroundDrawable mBackground;
-
- private View mHeader;
- private ViewGroup mTrack;
- private FrameLayout mFooter;
- private LinearLayout mFooterDisambig;
- private LinearLayout mFooterClearDefaults;
-
- private ListView mResolveList;
- private CheckableImageView mLastAction;
- private CheckBox mSetPrimaryCheckBox;
- private ListView mDefaultsListView;
- private Button mClearDefaultsButton;
-
- /**
- * Keeps the default action per mimetype. Empty if no default actions are set
- */
- private HashMap<String, Action> mDefaultsMap = new HashMap<String, Action>();
-
- /**
- * Set of {@link Action} that are associated with the aggregate currently
- * displayed by this dialog, represented as a map from {@link String}
- * MIME-type to a list of {@link Action}.
- */
- private ActionMultiMap mActions = new ActionMultiMap();
-
- /**
- * {@link #PRECEDING_MIMETYPES} and {@link #FOLLOWING_MIMETYPES} are used to sort MIME-types.
- *
- * <p>The MIME-types in {@link #PRECEDING_MIMETYPES} appear in the front of the dialog,
- * in the order in the array.
- *
- * <p>The ones in {@link #FOLLOWING_MIMETYPES} appear in the end of the dialog, in alphabetical
- * order.
- *
- * <p>The rest go between them, in the order in the array.
- */
- private static final String[] PRECEDING_MIMETYPES = new String[] {
- Phone.CONTENT_ITEM_TYPE,
- SipAddress.CONTENT_ITEM_TYPE,
- Contacts.CONTENT_ITEM_TYPE,
- Constants.MIME_TYPE_SMS_ADDRESS,
- Email.CONTENT_ITEM_TYPE,
- };
-
- /**
- * See {@link #PRECEDING_MIMETYPES}.
- */
- private static final String[] FOLLOWING_MIMETYPES = new String[] {
- StructuredPostal.CONTENT_ITEM_TYPE,
- Website.CONTENT_ITEM_TYPE,
- };
-
- /**
- * List of MIMETYPES that do not represent real data rows and can therefore not be set
- * as defaults
- */
- private static final ArrayList<String> VIRTUAL_MIMETYPES = Lists.newArrayList(new String[] {
- Im.CONTENT_ITEM_TYPE,
- Constants.MIME_TYPE_SMS_ADDRESS,
- Constants.MIME_TYPE_VIDEO_CHAT,
- });
- private static final int TOKEN_DATA = 1;
-
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- setContentView(R.layout.quickcontact_activity);
-
- mBackground = new QuickContactBackgroundDrawable(getResources());
-
- mFloatingLayout = findTypedViewById(R.id.floating_layout);
- mFloatingLayout.getChild().setBackgroundDrawable(mBackground);
- mFloatingLayout.setOnOutsideTouchListener(mOnOutsideTouchListener);
-
- mTrack = findTypedViewById(R.id.quickcontact);
- mFooter = findTypedViewById(R.id.footer);
- mFooterDisambig = findTypedViewById(R.id.footer_disambig);
- mFooterClearDefaults = findTypedViewById(R.id.footer_clear_defaults);
- mResolveList = findTypedViewById(android.R.id.list);
- mSetPrimaryCheckBox = findTypedViewById(android.R.id.checkbox);
-
- mDefaultsListView = findTypedViewById(R.id.defaults_list);
-
- mClearDefaultsButton = findTypedViewById(R.id.clear_defaults_button);
- mClearDefaultsButton.setOnClickListener(mOnClearDefaultsClickListener);
-
- mResolveList.setOnItemClickListener(this);
-
- mHandler = new NotifyingAsyncQueryHandler(this, this);
-
- show();
- }
-
- private View getHeaderView(int mode) {
- View header = null;
- switch (mode) {
- case QuickContact.MODE_SMALL:
- header = findViewById(R.id.header_small);
- break;
- case QuickContact.MODE_MEDIUM:
- header = findViewById(R.id.header_medium);
- break;
- case QuickContact.MODE_LARGE:
- header = findViewById(R.id.header_large);
- break;
- }
-
- if (header instanceof ViewStub) {
- // Inflate actual header if we picked a stub
- final ViewStub stub = (ViewStub)header;
- header = stub.inflate();
- } else if (header != null) {
- header.setVisibility(View.VISIBLE);
- }
-
- return header;
- }
-
- private void show() {
-
- if (TRACE_LAUNCH) {
- android.os.Debug.startMethodTracing(TRACE_TAG);
- }
-
- final Intent intent = getIntent();
-
- Uri lookupUri = intent.getData();
-
- // Check to see whether it comes from the old version.
- if (android.provider.Contacts.AUTHORITY.equals(lookupUri.getAuthority())) {
- final long rawContactId = ContentUris.parseId(lookupUri);
- lookupUri = RawContacts.getContactLookupUri(getContentResolver(),
- ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId));
- }
-
- mLookupUri = Preconditions.checkNotNull(lookupUri, "missing lookupUri");
-
- // Read requested parameters for displaying
- final Rect targetScreen = intent.getSourceBounds();
- Preconditions.checkNotNull(targetScreen, "missing targetScreen");
- mFloatingLayout.setChildTargetScreen(targetScreen);
-
- mMode = intent.getIntExtra(QuickContact.EXTRA_MODE, QuickContact.MODE_MEDIUM);
- mExcludeMimes = intent.getStringArrayExtra(QuickContact.EXTRA_EXCLUDE_MIMES);
-
- switch (mMode) {
- case QuickContact.MODE_SMALL:
- case QuickContact.MODE_MEDIUM:
- case QuickContact.MODE_LARGE:
- break;
- default:
- throw new IllegalArgumentException("Unexpected mode: " + mMode);
- }
-
- // find and prepare correct header view
- mHeader = getHeaderView(mMode);
- setHeaderText(R.id.name, R.string.quickcontact_missing_name);
- setHeaderText(R.id.status, null);
- setHeaderText(R.id.timestamp, null);
- setHeaderImage(R.id.presence, null);
-
- // Start background query for data, but only select photo rows when they
- // directly match the super-primary PHOTO_ID.
- final Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY);
- mHandler.cancelOperation(TOKEN_DATA);
-
- // Only request photo data when required by mode
- if (mMode == QuickContact.MODE_LARGE) {
- // Select photos, but only super-primary
- mHandler.startQuery(TOKEN_DATA, lookupUri, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
- + "!=? OR (" + Data.MIMETYPE + "=? AND " + Data._ID + "=" + Contacts.PHOTO_ID
- + ")", new String[] { Photo.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE }, null);
- } else {
- // Exclude all photos from cursor
- mHandler.startQuery(TOKEN_DATA, lookupUri, dataUri, DataQuery.PROJECTION, Data.MIMETYPE
- + "!=?", new String[] { Photo.CONTENT_ITEM_TYPE }, null);
- }
- }
-
- @SuppressWarnings("unchecked")
- private <T> T findTypedViewById(int id) {
- return (T) super.findViewById(id);
- }
-
- private View.OnTouchListener mOnOutsideTouchListener = new View.OnTouchListener() {
- /** {@inheritDoc} */
- public boolean onTouch(View v, MotionEvent event) {
- hide(true);
- return true;
- }
- };
-
- private View.OnClickListener mOnClearDefaultsClickListener = new View.OnClickListener() {
- /** {@inheritDoc} */
- public void onClick(View v) {
- clearDefaults();
- }
- };
-
- private void hide(boolean withAnimation) {
- // cancel any pending queries
- mHandler.cancelOperation(TOKEN_DATA);
-
- if (withAnimation) {
- mFloatingLayout.hideChild(new Runnable() {
- /** {@inheritDoc} */
- public void run() {
- finish();
- }
- });
- } else {
- mFloatingLayout.hideChild(null);
- finish();
- }
- }
-
- @Override
- public void onBackPressed() {
- hide(true);
- }
-
- /** {@inheritDoc} */
- public synchronized void onQueryComplete(int token, Object cookie, Cursor cursor) {
- try {
- if (isFinishing()) {
- hide(false);
- return;
- } else if (cursor == null || cursor.getCount() == 0) {
- Toast.makeText(this, R.string.invalidContactMessage, Toast.LENGTH_LONG).show();
- hide(false);
- return;
- }
-
- bindData(cursor);
-
- if (mMode == QuickContact.MODE_MEDIUM && !mHasValidSocial) {
- // Missing valid social, swap medium for small header
- mHeader.setVisibility(View.GONE);
- mHeader = getHeaderView(QuickContact.MODE_SMALL);
- }
-
- if (TRACE_LAUNCH) {
- android.os.Debug.stopMethodTracing();
- }
-
- // data bound and ready, pull curtain to show
- mFloatingLayout.showChild();
-
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-
- /** Assign this string to the view, if found in {@link #mHeader}. */
- private void setHeaderText(int id, int resId) {
- setHeaderText(id, getText(resId));
- }
-
- /** Assign this string to the view, if found in {@link #mHeader}. */
- private void setHeaderText(int id, CharSequence value) {
- final View view = mHeader.findViewById(id);
- if (view instanceof TextView) {
- ((TextView)view).setText(value);
- view.setVisibility(TextUtils.isEmpty(value) ? View.GONE : View.VISIBLE);
- }
- }
-
- /** Assign this image to the view, if found in {@link #mHeader}. */
- private void setHeaderImage(int id, Drawable drawable) {
- final View view = mHeader.findViewById(id);
- if (view instanceof ImageView) {
- ((ImageView)view).setImageDrawable(drawable);
- view.setVisibility(drawable == null ? View.GONE : View.VISIBLE);
- }
- }
-
- /**
- * Check if the given MIME-type appears in the list of excluded MIME-types
- * that the most-recent caller requested.
- */
- private boolean isMimeExcluded(String mimeType) {
- if (mExcludeMimes == null) return false;
- for (String excludedMime : mExcludeMimes) {
- if (TextUtils.equals(excludedMime, mimeType)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Handle the result from the {@link #TOKEN_DATA} query.
- */
- private void bindData(Cursor cursor) {
- final ResolveCache cache = ResolveCache.getInstance(this);
- final Context context = this;
-
- if (!isMimeExcluded(Contacts.CONTENT_ITEM_TYPE)) {
- // Add the profile shortcut action
- final Action action = new ProfileAction(context, mLookupUri);
- mActions.put(Contacts.CONTENT_ITEM_TYPE, action);
- }
-
- mDefaultsMap.clear();
-
- final DataStatus status = new DataStatus();
- final AccountTypeManager accountTypes = AccountTypeManager.getInstance(
- context.getApplicationContext());
- final ImageView photoView = (ImageView) mHeader.findViewById(R.id.photo);
-
- Bitmap photoBitmap = null;
- while (cursor.moveToNext()) {
- final long dataId = cursor.getLong(DataQuery._ID);
- final String accountType = cursor.getString(DataQuery.ACCOUNT_TYPE);
- final String mimeType = cursor.getString(DataQuery.MIMETYPE);
- final boolean isPrimary = cursor.getInt(DataQuery.IS_PRIMARY) != 0;
- final boolean isSuperPrimary = cursor.getInt(DataQuery.IS_SUPER_PRIMARY) != 0;
-
- // Handle any social status updates from this row
- status.possibleUpdate(cursor);
-
- // Skip this data item if MIME-type excluded
- if (isMimeExcluded(mimeType)) continue;
-
- // Handle photos included as data row
- if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final int colPhoto = cursor.getColumnIndex(Photo.PHOTO);
- final byte[] photoBlob = cursor.getBlob(colPhoto);
- if (photoBlob != null) {
- photoBitmap = BitmapFactory.decodeByteArray(photoBlob, 0, photoBlob.length);
- }
- continue;
- }
-
- final DataKind kind = accountTypes.getKindOrFallback(accountType, mimeType);
-
- if (kind != null) {
- // Build an action for this data entry, find a mapping to a UI
- // element, build its summary from the cursor, and collect it
- // along with all others of this MIME-type.
- final Action action = new DataAction(context, mimeType, kind, dataId, cursor);
- final boolean wasAdded = considerAdd(action, cache);
- if (wasAdded) {
- // Remember the default
- if (isSuperPrimary || (isPrimary && (mDefaultsMap.get(mimeType) == null))) {
- mDefaultsMap.put(mimeType, action);
- }
- }
- }
-
- // If phone number, also insert as text message action
- if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && kind != null) {
- final DataAction action = new DataAction(context, Constants.MIME_TYPE_SMS_ADDRESS,
- kind, dataId, cursor);
- considerAdd(action, cache);
- }
-
- boolean isIm = Im.CONTENT_ITEM_TYPE.equals(mimeType);
-
- // Handle Email rows with presence data as Im entry
- final boolean hasPresence = !cursor.isNull(DataQuery.PRESENCE);
- if (hasPresence && Email.CONTENT_ITEM_TYPE.equals(mimeType)) {
- final DataKind imKind = accountTypes.getKindOrFallback(accountType,
- Im.CONTENT_ITEM_TYPE);
- if (imKind != null) {
- final DataAction action = new DataAction(context, Im.CONTENT_ITEM_TYPE, imKind,
- dataId, cursor);
- considerAdd(action, cache);
- isIm = true;
- }
- }
-
- if (hasPresence && isIm) {
- int chatCapability = cursor.getInt(DataQuery.CHAT_CAPABILITY);
- if ((chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) {
- final DataKind imKind = accountTypes.getKindOrFallback(accountType,
- Im.CONTENT_ITEM_TYPE);
- if (imKind != null) {
- final DataAction chatAction = new DataAction(context,
- Constants.MIME_TYPE_VIDEO_CHAT, imKind, dataId, cursor);
- considerAdd(chatAction, cache);
- }
- }
- }
- }
-
- // Collapse Action Lists (remove e.g. duplicate e-mail addresses from different sources)
- for (ArrayList<Action> actionChildren : mActions.values()) {
- Collapser.collapseList(actionChildren);
- }
-
- // Make sure that we only display the "clear default" action if there
- // are actually several items to chose from
- boolean shouldDisplayClearDefaults = false;
- for (String mimetype : mDefaultsMap.keySet()) {
- if (mActions.get(mimetype).size() > 1) {
- shouldDisplayClearDefaults = true;
- break;
- }
- }
-
- if (shouldDisplayClearDefaults) {
- final Action action = new ClearDefaultsAction();
- mActions.put(action.getMimeType(), action);
- }
-
- if (cursor.moveToLast()) {
- // Read contact information from last data row
- final String name = cursor.getString(DataQuery.DISPLAY_NAME);
- final int presence = cursor.getInt(DataQuery.CONTACT_PRESENCE);
- final int chatCapability = cursor.getInt(DataQuery.CONTACT_CHAT_CAPABILITY);
- final Drawable statusIcon = ContactPresenceIconUtil.getChatCapabilityIcon(
- context, presence, chatCapability);
-
- setHeaderText(R.id.name, name);
- setHeaderImage(R.id.presence, statusIcon);
- }
-
- if (photoView != null) {
- // Place photo when discovered in data, otherwise hide
- photoView.setVisibility(photoBitmap != null ? View.VISIBLE : View.GONE);
- photoView.setImageBitmap(photoBitmap);
- }
-
- mHasValidSocial = status.isValid();
- if (mHasValidSocial && mMode != QuickContact.MODE_SMALL) {
- // Update status when valid was found
- setHeaderText(R.id.status, status.getStatus());
- setHeaderText(R.id.timestamp, status.getTimestampLabel(context));
- }
-
- // Turn our list of actions into UI elements
-
- // All the mime-types to add.
- final Set<String> containedTypes = new HashSet<String>(mActions.keySet());
-
- boolean hasData = false;
-
- // First, add PRECEDING_MIMETYPES, which are most common.
- for (String mimeType : PRECEDING_MIMETYPES) {
- if (containedTypes.contains(mimeType)) {
- hasData = true;
- mTrack.addView(inflateAction(mimeType, cache, mTrack));
- containedTypes.remove(mimeType);
- }
- }
-
- // Keep the current index to append non PRECEDING/FOLLOWING items.
- final int indexAfterPreceding = mTrack.getChildCount() - 1;
-
- // Then, add FOLLOWING_MIMETYPES, which are least common.
- for (String mimeType : FOLLOWING_MIMETYPES) {
- if (containedTypes.contains(mimeType)) {
- hasData = true;
- mTrack.addView(inflateAction(mimeType, cache, mTrack));
- containedTypes.remove(mimeType);
- }
- }
-
- // Show the clear-defaults button? If yes, it goes to the end of the list
- if (containedTypes.contains(ClearDefaultsAction.PSEUDO_MIME_TYPE)) {
- final ClearDefaultsAction action = (ClearDefaultsAction) mActions.get(
- ClearDefaultsAction.PSEUDO_MIME_TYPE).get(0);
- final CheckableImageView view = (CheckableImageView) getLayoutInflater().inflate(
- R.layout.quickcontact_item, mTrack, false);
-
- view.setChecked(false);
- final String description = context.getResources().getString(
- R.string.quickcontact_clear_defaults_description);
- view.setContentDescription(description);
- view.setImageResource(R.drawable.ic_menu_settings_holo_light);
- view.setOnClickListener(this);
- view.setTag(action);
- mTrack.addView(view);
- containedTypes.remove(ClearDefaultsAction.PSEUDO_MIME_TYPE);
- }
-
- // Go back to just after PRECEDING_MIMETYPES, and append the rest.
- int index = indexAfterPreceding;
- final String[] remainingTypes = containedTypes.toArray(new String[containedTypes.size()]);
- if (remainingTypes.length > 0) hasData = true;
- Arrays.sort(remainingTypes);
- for (String mimeType : remainingTypes) {
- mTrack.addView(inflateAction(mimeType, cache, mTrack), index++);
- }
-
- if (!hasData) {
- // When there is no data to display, add a TextView to show the user there's no data
- View view = getLayoutInflater().inflate(
- R.layout.quickcontact_item_nodata, mTrack, false);
- mTrack.addView(view, index++);
- }
- }
-
- /**
- * Clears the defaults currently set on the Contact
- */
- private void clearDefaults() {
- final Context context = this;
- final Set<String> mimeTypesKeySet = mDefaultsMap.keySet();
-
- // Copy to array so that we can modify the HashMap below
- final String[] mimeTypes = new String[mimeTypesKeySet.size()];
- mimeTypesKeySet.toArray(mimeTypes);
-
- // Send clear default Intents, one by one
- for (String mimeType : mimeTypes) {
- final Action action = mDefaultsMap.get(mimeType);
- final Intent intent = ContactSaveService.createClearPrimaryIntent(
- context, action.getDataId());
- context.startService(intent);
- mDefaultsMap.remove(mimeType);
- }
-
- // Close up and remove the configure default button
- animateCollapse(new Runnable() {
- @Override
- public void run() {
- for (int i = mTrack.getChildCount() - 1; i >= 0; i--) {
- final CheckableImageView button = (CheckableImageView) mTrack.getChildAt(i);
- if (button.getTag() instanceof ClearDefaultsAction) {
- mTrack.removeViewAt(i);
- break;
- }
- }
- }
- });
- }
-
- /**
- * Consider adding the given {@link Action}, which will only happen if
- * {@link PackageManager} finds an application to handle
- * {@link Action#getIntent()}.
- * @return true if action has been added
- */
- private boolean considerAdd(Action action, ResolveCache resolveCache) {
- if (resolveCache.hasResolve(action)) {
- mActions.put(action.getMimeType(), action);
- return true;
- }
- return false;
- }
-
- /**
- * Inflate the in-track view for the action of the given MIME-type, collapsing duplicate values.
- * Will use the icon provided by the {@link DataKind}.
- */
- private View inflateAction(String mimeType, ResolveCache resolveCache, ViewGroup root) {
- final CheckableImageView view = (CheckableImageView) getLayoutInflater().inflate(
- R.layout.quickcontact_item, root, false);
-
- // Add direct intent if single child, otherwise flag for multiple
- List<Action> children = mActions.get(mimeType);
- view.setTag(mimeType);
- final Action firstInfo = children.get(0);
-
- // Set icon and listen for clicks
- final CharSequence descrip = resolveCache.getDescription(firstInfo);
- final Drawable icon = resolveCache.getIcon(firstInfo);
- view.setChecked(false);
- view.setContentDescription(descrip);
- view.setImageDrawable(icon);
- view.setOnClickListener(this);
- return view;
- }
-
- /** {@inheritDoc} */
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- // Pass list item clicks along so that Intents are handled uniformly
- onClick(view);
- }
-
- /**
- * Helper for checking an action view
- */
- private void setNewActionViewChecked(CheckableImageView actionView) {
- if (mLastAction != null) mLastAction.setChecked(false);
- if (actionView != null) actionView.setChecked(true);
- mLastAction = actionView;
- }
-
- /**
- * Animates collpasing of the disambig area. When done, it expands again to the new size
- */
- private void animateCollapse(final Runnable whenDone) {
- final int oldBottom = mBackground.getBounds().bottom;
- mBackground.setBottomOverride(oldBottom);
-
- final ObjectAnimator fadeOutAnimator = ObjectAnimator.ofFloat(mFooter, "alpha",
- 1.0f, 0.0f);
- fadeOutAnimator.setDuration(ANIMATION_FADE_OUT_TIME);
- fadeOutAnimator.start();
-
- final ObjectAnimator collapseAnimator = ObjectAnimator.ofInt(mBackground,
- "bottomOverride", oldBottom, oldBottom - mFooter.getHeight());
- collapseAnimator.setDuration(ANIMATION_COLLAPSE_TIME);
- collapseAnimator.setStartDelay(ANIMATION_FADE_OUT_TIME);
- collapseAnimator.start();
-
- collapseAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mFooter.setVisibility(View.GONE);
- new Handler().post(whenDone);
- }
- });
- }
-
- /**
- * Animates expansion of the disambig area.
- * @param showClearDefaults true to expand to clear defaults. false to expand to intent disambig
- */
- private void animateExpand(boolean showClearDefaults) {
- mFooter.setVisibility(View.VISIBLE);
- mFooterDisambig.setVisibility(showClearDefaults ? View.GONE : View.VISIBLE);
- mFooterClearDefaults.setVisibility(showClearDefaults ? View.VISIBLE : View.GONE);
- final int oldBottom = mBackground.getBounds().bottom;
- mBackground.setBottomOverride(oldBottom);
- mFooter.setAlpha(0.0f);
- new Handler().post(new Runnable() {
- @Override
- public void run() {
- final int newBottom = mBackground.getBounds().bottom;
- final ObjectAnimator expandAnimator = ObjectAnimator.ofInt(mBackground,
- "bottomOverride", oldBottom, newBottom);
- expandAnimator.setDuration(ANIMATION_EXPAND_TIME);
- expandAnimator.start();
-
- expandAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mBackground.clearBottomOverride();
- }
- });
-
- final ObjectAnimator fadeInAnimator = ObjectAnimator.ofFloat(mFooter,
- "alpha", 0.0f, 1.0f);
- fadeInAnimator.setDuration(ANIMATION_FADE_IN_TIME);
- fadeInAnimator.setStartDelay(ANIMATION_EXPAND_TIME);
- fadeInAnimator.start();
- }
- });
- }
-
- /** {@inheritDoc} */
- @Override
- public void onClick(View view) {
- final Context context = this;
-
- final boolean isActionView = (view instanceof CheckableImageView);
- final CheckableImageView actionView = isActionView ? (CheckableImageView)view : null;
- final Object tag = view.getTag();
-
- if (tag instanceof ClearDefaultsAction) {
- // Do nothing if already open
- if (actionView == mLastAction) return;
- // collapse any disambig that may already be open. the open clearing-defaults
- final Runnable expandClearDefaultsRunnable = new Runnable() {
- @Override
- public void run() {
- // Show resolution list and set adapter
- setNewActionViewChecked(actionView);
- final Action[] actions = new Action[mDefaultsMap.size()];
- mDefaultsMap.values().toArray(actions);
-
- mDefaultsListView.setAdapter(new BaseAdapter() {
- @Override
- public int getCount() {
- return actions.length;
- }
-
- @Override
- public Object getItem(int position) {
- return actions[position];
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public boolean areAllItemsEnabled() {
- return false;
- }
-
- @Override
- public boolean isEnabled(int position) {
- return false;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = getLayoutInflater().inflate(
- R.layout.quickcontact_default_item, parent, false);
- }
-
- // Set action title based on summary value
- final Action defaultAction = actions[position];
-
- final TextView text1 = (TextView) convertView.findViewById(
- android.R.id.text1);
- final TextView text2 = (TextView) convertView.findViewById(
- android.R.id.text2);
-
- text1.setText(defaultAction.getHeader());
- text2.setText(defaultAction.getBody());
-
- convertView.setTag(defaultAction);
- return convertView;
- }
- });
-
- animateExpand(true);
- }
- };
- if (mFooter.getVisibility() == View.VISIBLE) {
- animateCollapse(expandClearDefaultsRunnable);
- } else {
- new Handler().post(expandClearDefaultsRunnable);
- }
- return;
- }
-
- // Determine whether to launch a specific Action or show a disambig-List
- final Action action;
- final List<Action> actionList;
- final boolean fromDisambigList;
- final String mimeType;
- if (tag instanceof Action) {
- action = (Action) tag;
- actionList = null;
- fromDisambigList = true;
- mimeType = action.getMimeType();
- } else if (tag instanceof String) {
- mimeType = (String) tag;
- actionList = mActions.get(mimeType);
-
- if (actionList.size() == 1) {
- // Just one item? Pick that one
- action = actionList.get(0);
- } else if (mDefaultsMap.containsKey(mimeType)) {
- // Default item? pick that one
- action = mDefaultsMap.get(mimeType);
- } else {
- // Several actions and none is default.
- action = null;
- }
- fromDisambigList = false;
- } else {
- throw new IllegalStateException("tag is neither Action nor (mimetype-) String");
- }
-
- if (action != null) {
- final boolean isVirtual = VIRTUAL_MIMETYPES.contains(mimeType);
- final boolean makePrimary = fromDisambigList && mSetPrimaryCheckBox.isChecked() &&
- !isVirtual;
- final Runnable startAppRunnable = new Runnable() {
- @Override
- public void run() {
- // Incoming tag is concrete intent, so try launching
- try {
- context.startActivity(action.getIntent());
- } catch (ActivityNotFoundException e) {
- Toast.makeText(context, R.string.quickcontact_missing_app,
- Toast.LENGTH_SHORT).show();
- }
-
- // Hide the resolution list, if present
- setNewActionViewChecked(null);
-
- // Set default?
- final long dataId = action.getDataId();
- if (makePrimary && dataId != -1) {
- Intent serviceIntent = ContactSaveService.createSetSuperPrimaryIntent(
- context, dataId);
- context.startService(serviceIntent);
- }
-
- hide(false);
- }
- };
- if (isActionView && mFooter.getVisibility() == View.VISIBLE) {
- // If the footer is currently opened, animate its collapse and then
- // execute the target app
- animateCollapse(startAppRunnable);
- } else {
- // Defer the action to make the window properly repaint
- new Handler().post(startAppRunnable);
- }
- return;
- }
-
- // This was not a specific Action. Expand the disambig-list
- if (actionList == null) throw new IllegalStateException();
-
- // Don't do anything if already open
- if (actionView == mLastAction) return;
- final Runnable configureListRunnable = new Runnable() {
- @Override
- public void run() {
- // Show resolution list and set adapter
- setNewActionViewChecked(actionView);
- final boolean isVirtual = VIRTUAL_MIMETYPES.contains(mimeType);
- mSetPrimaryCheckBox.setVisibility(isVirtual ? View.GONE : View.VISIBLE);
-
- mResolveList.setAdapter(new BaseAdapter() {
- @Override
- public int getCount() {
- return actionList.size();
- }
-
- @Override
- public Object getItem(int position) {
- return actionList.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = getLayoutInflater().inflate(
- R.layout.quickcontact_resolve_item, parent, false);
- }
-
- // Set action title based on summary value
- final Action listAction = actionList.get(position);
-
- final TextView text1 = (TextView) convertView.findViewById(
- android.R.id.text1);
- final TextView text2 = (TextView) convertView.findViewById(
- android.R.id.text2);
-
- text1.setText(listAction.getHeader());
- text2.setText(listAction.getBody());
-
- convertView.setTag(listAction);
- return convertView;
- }
- });
-
- animateExpand(false);
- }
- };
-
- if (mFooter.getVisibility() == View.VISIBLE) {
- // If the expansion list is currently opened, animate its collapse and then
- // execute the target app
- animateCollapse(configureListRunnable);
- } else {
- // Defer the action to make the window properly repaint
- configureListRunnable.run();
- }
- }
-
-
- private interface DataQuery {
- final String[] PROJECTION = new String[] {
- Data._ID,
-
- RawContacts.ACCOUNT_TYPE,
- Contacts.STARRED,
- Contacts.DISPLAY_NAME,
- Contacts.CONTACT_PRESENCE,
- Contacts.CONTACT_CHAT_CAPABILITY,
-
- Data.STATUS,
- Data.STATUS_RES_PACKAGE,
- Data.STATUS_ICON,
- Data.STATUS_LABEL,
- Data.STATUS_TIMESTAMP,
- Data.PRESENCE,
- Data.CHAT_CAPABILITY,
-
- Data.RES_PACKAGE,
- Data.MIMETYPE,
- Data.IS_PRIMARY,
- Data.IS_SUPER_PRIMARY,
- Data.RAW_CONTACT_ID,
-
- Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, Data.DATA5,
- Data.DATA6, Data.DATA7, Data.DATA8, Data.DATA9, Data.DATA10, Data.DATA11,
- Data.DATA12, Data.DATA13, Data.DATA14, Data.DATA15,
- };
-
- final int _ID = 0;
-
- final int ACCOUNT_TYPE = 1;
- final int STARRED = 2;
- final int DISPLAY_NAME = 3;
- final int CONTACT_PRESENCE = 4;
- final int CONTACT_CHAT_CAPABILITY = 5;
-
- final int STATUS = 6;
- final int STATUS_RES_PACKAGE = 7;
- final int STATUS_ICON = 8;
- final int STATUS_LABEL = 9;
- final int STATUS_TIMESTAMP = 10;
- final int PRESENCE = 11;
- final int CHAT_CAPABILITY = 12;
-
- final int RES_PACKAGE = 13;
- final int MIMETYPE = 14;
- final int IS_PRIMARY = 15;
- final int IS_SUPER_PRIMARY = 16;
- }
-}
diff --git a/src/com/android/contacts/quickcontact/ResolveCache.java b/src/com/android/contacts/quickcontact/ResolveCache.java
index 2b9e92f..71c4d0a 100644
--- a/src/com/android/contacts/quickcontact/ResolveCache.java
+++ b/src/com/android/contacts/quickcontact/ResolveCache.java
@@ -134,7 +134,7 @@
/**
* Best {@link ResolveInfo} when multiple found. Ties are broken by
- * selecting first from the {QuickContactWindow#sPreferResolve} list of
+ * selecting first from the {@link QuickContactActivity#sPreferResolve} list of
* preferred packages, second by apps that live on the system partition,
* otherwise the app from the top of the list. This is
* <strong>only</strong> used for selecting a default icon for
@@ -182,10 +182,10 @@
* for accessibility purposes.
*/
public CharSequence getDescription(Action action) {
- final CharSequence actionHeader = action.getHeader();
+ final CharSequence actionSubtitle = action.getSubtitle();
final ResolveInfo info = getEntry(action).bestResolve;
- if (!TextUtils.isEmpty(actionHeader)) {
- return actionHeader;
+ if (!TextUtils.isEmpty(actionSubtitle)) {
+ return actionSubtitle;
} else if (info != null) {
return info.loadLabel(mPackageManager);
} else {
diff --git a/src/com/android/contacts/util/AccountSelectionUtil.java b/src/com/android/contacts/util/AccountSelectionUtil.java
index 46d20b8..7317d21 100644
--- a/src/com/android/contacts/util/AccountSelectionUtil.java
+++ b/src/com/android/contacts/util/AccountSelectionUtil.java
@@ -16,7 +16,11 @@
package com.android.contacts.util;
-import android.accounts.Account;
+import com.android.contacts.R;
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
+
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
@@ -31,10 +35,6 @@
import android.widget.ArrayAdapter;
import android.widget.TextView;
-import com.android.contacts.R;
-import com.android.contacts.model.AccountType;
-import com.android.contacts.model.AccountTypeManager;
-
import java.util.List;
/**
@@ -54,9 +54,10 @@
final private Context mContext;
final private int mResId;
- final protected List<Account> mAccountList;
+ final protected List<AccountWithDataSet> mAccountList;
- public AccountSelectedListener(Context context, List<Account> accountList, int resId) {
+ public AccountSelectedListener(Context context, List<AccountWithDataSet> accountList,
+ int resId) {
if (accountList == null || accountList.size() == 0) {
Log.e(LOG_TAG, "The size of Account list is 0.");
}
@@ -88,7 +89,7 @@
DialogInterface.OnClickListener onClickListener,
DialogInterface.OnCancelListener onCancelListener) {
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(context);
- final List<Account> writableAccountList = accountTypes.getAccounts(true);
+ final List<AccountWithDataSet> writableAccountList = accountTypes.getAccounts(true);
Log.i(LOG_TAG, "The number of available accounts: " + writableAccountList.size());
@@ -99,8 +100,8 @@
context, android.R.style.Theme_Light);
final LayoutInflater dialogInflater = (LayoutInflater)dialogContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- final ArrayAdapter<Account> accountAdapter =
- new ArrayAdapter<Account>(context, android.R.layout.simple_list_item_2,
+ final ArrayAdapter<AccountWithDataSet> accountAdapter =
+ new ArrayAdapter<AccountWithDataSet>(context, android.R.layout.simple_list_item_2,
writableAccountList) {
@Override
@@ -117,8 +118,9 @@
final TextView text2 =
(TextView)convertView.findViewById(android.R.id.text2);
- final Account account = this.getItem(position);
- final AccountType accountType = accountTypes.getAccountType(account.type);
+ final AccountWithDataSet account = this.getItem(position);
+ final AccountType accountType = accountTypes.getAccountType(
+ account.type, account.dataSet);
final Context context = getContext();
text1.setText(account.name);
@@ -147,7 +149,7 @@
.create();
}
- public static void doImport(Context context, int resId, Account account) {
+ public static void doImport(Context context, int resId, AccountWithDataSet account) {
switch (resId) {
case R.string.import_from_sim: {
doImportFromSim(context, account);
@@ -160,23 +162,25 @@
}
}
- public static void doImportFromSim(Context context, Account account) {
+ public static void doImportFromSim(Context context, AccountWithDataSet account) {
Intent importIntent = new Intent(Intent.ACTION_VIEW);
importIntent.setType("vnd.android.cursor.item/sim-contact");
if (account != null) {
importIntent.putExtra("account_name", account.name);
importIntent.putExtra("account_type", account.type);
+ importIntent.putExtra("data_set", account.dataSet);
}
importIntent.setClassName("com.android.phone", "com.android.phone.SimContacts");
context.startActivity(importIntent);
}
- public static void doImportFromSdCard(Context context, Account account) {
+ public static void doImportFromSdCard(Context context, AccountWithDataSet account) {
Intent importIntent = new Intent(context,
com.android.contacts.vcard.ImportVCardActivity.class);
if (account != null) {
importIntent.putExtra("account_name", account.name);
importIntent.putExtra("account_type", account.type);
+ importIntent.putExtra("data_set", account.dataSet);
}
if (mVCardShare) {
diff --git a/src/com/android/contacts/util/AccountsListAdapter.java b/src/com/android/contacts/util/AccountsListAdapter.java
index 5448d1d..d065255 100644
--- a/src/com/android/contacts/util/AccountsListAdapter.java
+++ b/src/com/android/contacts/util/AccountsListAdapter.java
@@ -19,8 +19,8 @@
import com.android.contacts.R;
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
-import android.accounts.Account;
import android.content.Context;
import android.text.TextUtils.TruncateAt;
import android.view.LayoutInflater;
@@ -38,7 +38,7 @@
*/
public final class AccountsListAdapter extends BaseAdapter {
private final LayoutInflater mInflater;
- private final List<Account> mAccounts;
+ private final List<AccountWithDataSet> mAccounts;
private final AccountTypeManager mAccountTypes;
private final Context mContext;
@@ -51,11 +51,11 @@
* first in the list. Can be null.
*/
public AccountsListAdapter(Context context, boolean writableOnly,
- Account currentAccount) {
+ AccountWithDataSet currentAccount) {
mContext = context;
mAccountTypes = AccountTypeManager.getInstance(context);
// We don't want possible side-effect toward AccountTypeManager
- mAccounts = new ArrayList<Account>(mAccountTypes.getAccounts(writableOnly));
+ mAccounts = new ArrayList<AccountWithDataSet>(mAccountTypes.getAccounts(writableOnly));
if (currentAccount != null
&& !mAccounts.isEmpty()
&& !mAccounts.get(0).equals(currentAccount)
@@ -74,8 +74,8 @@
final TextView text2 = (TextView)resultView.findViewById(android.R.id.text2);
final ImageView icon = (ImageView)resultView.findViewById(android.R.id.icon);
- final Account account = mAccounts.get(position);
- final AccountType accountType = mAccountTypes.getAccountType(account.type);
+ final AccountWithDataSet account = mAccounts.get(position);
+ final AccountType accountType = mAccountTypes.getAccountType(account.type, account.dataSet);
text1.setText(account.name);
@@ -94,7 +94,7 @@
}
@Override
- public Account getItem(int position) {
+ public AccountWithDataSet getItem(int position) {
return mAccounts.get(position);
}
diff --git a/src/com/android/contacts/util/Constants.java b/src/com/android/contacts/util/Constants.java
index 2457600..a8ba059 100644
--- a/src/com/android/contacts/util/Constants.java
+++ b/src/com/android/contacts/util/Constants.java
@@ -16,15 +16,7 @@
package com.android.contacts.util;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-
public class Constants {
- /**
- * Specific MIME-type for {@link Phone#CONTENT_ITEM_TYPE} entries that
- * distinguishes actions that should initiate a text message.
- */
- public static final String MIME_TYPE_SMS_ADDRESS = "vnd.android.cursor.item/sms-address";
-
public static final String MIME_TYPE_VIDEO_CHAT = "vnd.android.cursor.item/video-chat-address";
public static final String SCHEME_TEL = "tel";
@@ -32,5 +24,4 @@
public static final String SCHEME_MAILTO = "mailto";
public static final String SCHEME_IMTO = "imto";
public static final String SCHEME_SIP = "sip";
-
}
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
index 0b84440..28b8540 100644
--- a/src/com/android/contacts/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -19,6 +19,7 @@
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.util.AccountSelectionUtil;
import com.android.vcard.VCardEntryCounter;
import com.android.vcard.VCardParser;
@@ -29,7 +30,6 @@
import com.android.vcard.exception.VCardNestedException;
import com.android.vcard.exception.VCardVersionException;
-import android.accounts.Account;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -118,7 +118,7 @@
private AccountSelectionUtil.AccountSelectedListener mAccountSelectionListener;
- private Account mAccount;
+ private AccountWithDataSet mAccount;
private ProgressDialog mProgressDialogForScanVCard;
private ProgressDialog mProgressDialogForCachingVCard;
@@ -839,19 +839,21 @@
String accountName = null;
String accountType = null;
+ String dataSet = null;
final Intent intent = getIntent();
if (intent != null) {
accountName = intent.getStringExtra(SelectAccountActivity.ACCOUNT_NAME);
accountType = intent.getStringExtra(SelectAccountActivity.ACCOUNT_TYPE);
+ dataSet = intent.getStringExtra(SelectAccountActivity.DATA_SET);
} else {
Log.e(LOG_TAG, "intent does not exist");
}
if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
- mAccount = new Account(accountName, accountType);
+ mAccount = new AccountWithDataSet(accountName, accountType, dataSet);
} else {
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this);
- final List<Account> accountList = accountTypes.getAccounts(true);
+ final List<AccountWithDataSet> accountList = accountTypes.getAccounts(true);
if (accountList.size() == 0) {
mAccount = null;
} else if (accountList.size() == 1) {
@@ -870,9 +872,10 @@
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == SELECT_ACCOUNT) {
if (resultCode == RESULT_OK) {
- mAccount = new Account(
+ mAccount = new AccountWithDataSet(
intent.getStringExtra(SelectAccountActivity.ACCOUNT_NAME),
- intent.getStringExtra(SelectAccountActivity.ACCOUNT_TYPE));
+ intent.getStringExtra(SelectAccountActivity.ACCOUNT_TYPE),
+ intent.getStringExtra(SelectAccountActivity.DATA_SET));
startImport();
} else {
if (resultCode != RESULT_CANCELED) {
diff --git a/src/com/android/contacts/vcard/SelectAccountActivity.java b/src/com/android/contacts/vcard/SelectAccountActivity.java
index 5f8aa28..f27331e 100644
--- a/src/com/android/contacts/vcard/SelectAccountActivity.java
+++ b/src/com/android/contacts/vcard/SelectAccountActivity.java
@@ -18,9 +18,9 @@
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.util.AccountSelectionUtil;
-import android.accounts.Account;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
@@ -34,6 +34,7 @@
public static final String ACCOUNT_NAME = "account_name";
public static final String ACCOUNT_TYPE = "account_type";
+ public static final String DATA_SET = "data_set";
private class CancelListener
implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
@@ -57,16 +58,17 @@
// - no account -> use phone-local storage without asking the user
final int resId = R.string.import_from_sdcard;
final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this);
- final List<Account> accountList = accountTypes.getAccounts(true);
+ final List<AccountWithDataSet> accountList = accountTypes.getAccounts(true);
if (accountList.size() == 0) {
Log.w(LOG_TAG, "Account does not exist");
finish();
return;
} else if (accountList.size() == 1) {
- final Account account = accountList.get(0);
+ final AccountWithDataSet account = accountList.get(0);
final Intent intent = new Intent();
intent.putExtra(ACCOUNT_NAME, account.name);
intent.putExtra(ACCOUNT_TYPE, account.type);
+ intent.putExtra(DATA_SET, account.dataSet);
setResult(RESULT_OK, intent);
finish();
return;
@@ -81,10 +83,11 @@
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
- final Account account = mAccountList.get(which);
+ final AccountWithDataSet account = mAccountList.get(which);
final Intent intent = new Intent();
intent.putExtra(ACCOUNT_NAME, account.name);
intent.putExtra(ACCOUNT_TYPE, account.type);
+ intent.putExtra(DATA_SET, account.dataSet);
setResult(RESULT_OK, intent);
finish();
}
diff --git a/tests/res/layout/quick_contact_tests.xml b/tests/res/layout/quick_contact_tests.xml
index fb2d518..aa0648e 100644
--- a/tests/res/layout/quick_contact_tests.xml
+++ b/tests/res/layout/quick_contact_tests.xml
@@ -29,19 +29,19 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/quickContactBadgeStyleWindowSmall"
- android:layout_marginLeft="32dip" />
+ android:layout_marginLeft="4dip" />
<QuickContactBadge
android:id="@+id/medium_badge1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/quickContactBadgeStyleWindowMedium"
- android:layout_marginLeft="64dip" />
+ android:layout_marginLeft="4dip" />
<QuickContactBadge
android:id="@+id/large_badge1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/quickContactBadgeStyleWindowLarge"
- android:layout_marginLeft="64dip" />
+ android:layout_marginLeft="4dip" />
</LinearLayout>
<View
android:layout_width="match_parent"
@@ -77,19 +77,19 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/quickContactBadgeStyleSmallWindowSmall"
- android:layout_marginLeft="32dip" />
+ android:layout_marginLeft="4dip" />
<QuickContactBadge
android:id="@+id/medium_badge2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/quickContactBadgeStyleSmallWindowMedium"
- android:layout_marginLeft="64dip" />
+ android:layout_marginLeft="4dip" />
<QuickContactBadge
android:id="@+id/large_badge2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:attr/quickContactBadgeStyleSmallWindowLarge"
- android:layout_marginLeft="64dip" />
+ android:layout_marginLeft="4dip" />
</LinearLayout>
</LinearLayout>
diff --git a/tests/src/com/android/contacts/ContactLoaderTest.java b/tests/src/com/android/contacts/ContactLoaderTest.java
index 1d3fb20..ac691bc 100644
--- a/tests/src/com/android/contacts/ContactLoaderTest.java
+++ b/tests/src/com/android/contacts/ContactLoaderTest.java
@@ -276,9 +276,10 @@
Contacts.Entity.CONTACT_ID,
Contacts.Entity.RAW_CONTACT_ID,
- RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE, RawContacts.DIRTY,
- RawContacts.VERSION, RawContacts.SOURCE_ID, RawContacts.SYNC1,
- RawContacts.SYNC2, RawContacts.SYNC3, RawContacts.SYNC4,
+ RawContacts.ACCOUNT_NAME, RawContacts.ACCOUNT_TYPE,
+ RawContacts.DATA_SET, RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
+ RawContacts.DIRTY, RawContacts.VERSION, RawContacts.SOURCE_ID,
+ RawContacts.SYNC1, RawContacts.SYNC2, RawContacts.SYNC3, RawContacts.SYNC4,
RawContacts.DELETED, RawContacts.NAME_VERIFIED,
Contacts.Entity.DATA_ID,
@@ -297,6 +298,9 @@
Data.STATUS_LABEL, Data.STATUS_TIMESTAMP,
Contacts.PHOTO_URI,
+
+ Contacts.SEND_TO_VOICEMAIL,
+ Contacts.CUSTOM_RINGTONE,
})
.withSortOrder(Contacts.Entity.RAW_CONTACT_ID)
.returnRow(
@@ -309,9 +313,9 @@
contactId,
rawContactId,
- "mockAccountName", "mockAccountType", 0,
- 1, 0, "sync1",
- "sync2", "sync3", "sync4",
+ "mockAccountName", "mockAccountType", null, "mockAccountType",
+ 0, 1, 0,
+ "sync1", "sync2", "sync3", "sync4",
0, 0,
dataId,
@@ -330,7 +334,10 @@
"Having dinner", "mockPkg3", 0,
20, 0,
- "content:some.photo.uri"
+ "content:some.photo.uri",
+
+ 0,
+ null
);
}
diff --git a/tests/src/com/android/contacts/activities/PeopleActivityTest.java b/tests/src/com/android/contacts/activities/PeopleActivityTest.java
index 43158e5..8cef001 100644
--- a/tests/src/com/android/contacts/activities/PeopleActivityTest.java
+++ b/tests/src/com/android/contacts/activities/PeopleActivityTest.java
@@ -24,6 +24,7 @@
import com.android.contacts.list.ContactBrowseListFragment;
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.model.BaseAccountType;
import com.android.contacts.test.InjectedServices;
import com.android.contacts.tests.mocks.ContactsMockContext;
@@ -34,7 +35,6 @@
import com.android.contacts.tests.mocks.MockSharedPreferences;
import com.android.contacts.util.PhoneCapabilityTester;
-import android.accounts.Account;
import android.content.ContentValues;
import android.content.Intent;
import android.content.Loader;
@@ -94,11 +94,11 @@
AccountType accountType = new BaseAccountType();
accountType.accountType = TEST_ACCOUNT_TYPE;
- Account account = new Account(TEST_ACCOUNT, TEST_ACCOUNT_TYPE);
+ AccountWithDataSet account = new AccountWithDataSet(TEST_ACCOUNT, TEST_ACCOUNT_TYPE, null);
services.setSystemService(AccountTypeManager.ACCOUNT_TYPE_SERVICE,
new MockAccountTypeManager(
- new AccountType[] { accountType }, new Account[] { account }));
+ new AccountType[] { accountType }, new AccountWithDataSet[] { account }));
ContactsApplication.injectServices(services);
}
diff --git a/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java b/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java
index fc3545b..67bf725 100644
--- a/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java
+++ b/tests/src/com/android/contacts/interactions/ContactDeletionInteractionTest.java
@@ -130,7 +130,8 @@
private Query expectQuery() {
return mContactsProvider.expectQuery(ENTITY_URI).withProjection(
- Entity.RAW_CONTACT_ID, Entity.ACCOUNT_TYPE, Entity.CONTACT_ID, Entity.LOOKUP_KEY);
+ Entity.RAW_CONTACT_ID, Entity.ACCOUNT_TYPE, Entity.DATA_SET, Entity.CONTACT_ID,
+ Entity.LOOKUP_KEY);
}
private void assertWithMessageId(int messageId) {
diff --git a/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java b/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
index d0e50c4..d70cb44 100644
--- a/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
+++ b/tests/src/com/android/contacts/interactions/PhoneNumberInteractionTest.java
@@ -222,6 +222,7 @@
Phone.NUMBER,
Phone.IS_SUPER_PRIMARY,
RawContacts.ACCOUNT_TYPE,
+ RawContacts.DATA_SET,
Phone.TYPE,
Phone.LABEL)
.withSelection("mimetype='vnd.android.cursor.item/phone_v2' AND data1 NOT NULL");
diff --git a/tests/src/com/android/contacts/model/AccountTypeManagerTest.java b/tests/src/com/android/contacts/model/AccountTypeManagerTest.java
index e5cf080..27b3e90 100644
--- a/tests/src/com/android/contacts/model/AccountTypeManagerTest.java
+++ b/tests/src/com/android/contacts/model/AccountTypeManagerTest.java
@@ -19,13 +19,12 @@
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
-import android.accounts.Account;
import android.content.Context;
import android.test.AndroidTestCase;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -45,10 +44,10 @@
final AccountType typeD = new MockAccountType("typeD", "d");
// Define users
- final Account accountA1 = new Account("a1", typeA.accountType);
- final Account accountC1 = new Account("c1", typeC.accountType);
- final Account accountC2 = new Account("c2", typeC.accountType);
- final Account accountD1 = new Account("d1", typeD.accountType);
+ final AccountWithDataSet accountA1 = new AccountWithDataSet("a1", typeA.accountType, null);
+ final AccountWithDataSet accountC1 = new AccountWithDataSet("c1", typeC.accountType, null);
+ final AccountWithDataSet accountC2 = new AccountWithDataSet("c2", typeC.accountType, null);
+ final AccountWithDataSet accountD1 = new AccountWithDataSet("d1", typeD.accountType, null);
// empty - empty
Map<String, AccountType> types = AccountTypeManagerImpl.findInvitableAccountTypes(c,
@@ -133,11 +132,11 @@
}
/**
- * Array of {@link Account} -> {@link Collection}
+ * Array of {@link AccountWithDataSet} -> {@link Collection}
*/
- private static Collection<Account> buildAccounts(Account... accounts) {
- final ArrayList<Account> result = Lists.newArrayList();
- for (Account account : accounts) {
+ private static Collection<AccountWithDataSet> buildAccounts(AccountWithDataSet... accounts) {
+ final List<AccountWithDataSet> result = Lists.newArrayList();
+ for (AccountWithDataSet account : accounts) {
result.add(account);
}
return result;
@@ -147,13 +146,13 @@
* Executes {@link AccountTypeManagerImpl#findInvitableAccountTypes} and verifies the
* result.
*/
- private void verifyAccountTypes(Collection<Account> accounts,
+ private void verifyAccountTypes(Collection<AccountWithDataSet> accounts,
Map<String, AccountType> types, AccountType... expectedTypes) {
Map<String, AccountType> result = AccountTypeManagerImpl.findInvitableAccountTypes(
getContext(), accounts, types);
for (AccountType type : expectedTypes) {
- if (!result.containsKey(type.accountType)) {
- fail("Result doesn't contain type=" + type.accountType);
+ if (!result.containsKey(type.getAccountTypeAndDataSet())) {
+ fail("Result doesn't contain type=" + type.getAccountTypeAndDataSet());
}
}
}
diff --git a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
index e5e6a1c..97816c2 100644
--- a/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
+++ b/tests/src/com/android/contacts/tests/allintents/AllIntentsActivity.java
@@ -16,10 +16,10 @@
package com.android.contacts.tests.allintents;
+import com.android.contacts.model.AccountWithDataSet;
import com.android.contacts.tests.R;
import com.google.android.collect.Lists;
-import android.accounts.Account;
import android.app.ListActivity;
import android.app.SearchManager;
import android.content.ComponentName;
@@ -625,11 +625,12 @@
}
@Override
- public void onAccountChosen(Account account, int tag) {
+ public void onAccountChosen(AccountWithDataSet account, int tag) {
switch (ContactsIntent.get(tag)) {
case EDIT_NEW_CONTACT_FOR_ACCOUNT: {
final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
intent.putExtra(Insert.ACCOUNT, account);
+ intent.putExtra(Insert.DATA_SET, account.dataSet);
startActivity(intent);
break;
}
@@ -637,6 +638,7 @@
final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
intent.putExtra(Insert.ACCOUNT, account);
+ intent.putExtra(Insert.DATA_SET, account.dataSet);
putDataExtra(intent);
startActivity(intent);
diff --git a/tests/src/com/android/contacts/tests/allintents/SelectAccountDialogFragment.java b/tests/src/com/android/contacts/tests/allintents/SelectAccountDialogFragment.java
index e074a0b..c261553 100644
--- a/tests/src/com/android/contacts/tests/allintents/SelectAccountDialogFragment.java
+++ b/tests/src/com/android/contacts/tests/allintents/SelectAccountDialogFragment.java
@@ -16,8 +16,9 @@
package com.android.contacts.tests.allintents;
-import android.accounts.Account;
-import android.accounts.AccountManager;
+import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
+
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
@@ -29,6 +30,8 @@
import android.widget.ArrayAdapter;
import android.widget.TextView;
+import java.util.List;
+
/**
* Shows a dialog asking the user which account to chose.
* The result is passed back to the owning Activity
@@ -43,13 +46,15 @@
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Bundle parameters = getArguments();
- final Account[] accounts = AccountManager.get(getActivity()).getAccounts();
+ final List<AccountWithDataSet> accounts =
+ AccountTypeManager.getInstance(getActivity()).getAccounts(false);
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
final LayoutInflater inflater = LayoutInflater.from(builder.getContext());
- final ArrayAdapter<Account> accountAdapter = new ArrayAdapter<Account>(builder.getContext(),
- android.R.layout.simple_list_item_2, accounts) {
+ final ArrayAdapter<AccountWithDataSet> accountAdapter =
+ new ArrayAdapter<AccountWithDataSet>(builder.getContext(),
+ android.R.layout.simple_list_item_2, accounts) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final View resultView = convertView == null
@@ -59,7 +64,7 @@
final TextView text1 = (TextView)resultView.findViewById(android.R.id.text1);
final TextView text2 = (TextView)resultView.findViewById(android.R.id.text2);
- final Account account = getItem(position);
+ final AccountWithDataSet account = getItem(position);
text1.setText("Name: " + account.name);
text2.setText("Type: " + account.type);
@@ -92,6 +97,6 @@
}
public interface Listener {
- void onAccountChosen(Account account, int tag);
+ void onAccountChosen(AccountWithDataSet account, int tag);
}
}
diff --git a/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java b/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java
index acfa7f5..e60391e 100644
--- a/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java
+++ b/tests/src/com/android/contacts/tests/mocks/MockAccountTypeManager.java
@@ -17,13 +17,11 @@
import com.android.contacts.model.AccountType;
import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.model.AccountWithDataSet;
import com.google.android.collect.Maps;
-import android.accounts.Account;
-
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -32,15 +30,15 @@
public class MockAccountTypeManager extends AccountTypeManager {
private final AccountType[] mTypes;
- private Account[] mAccounts;
+ private AccountWithDataSet[] mAccounts;
- public MockAccountTypeManager(AccountType[] types, Account[] accounts) {
+ public MockAccountTypeManager(AccountType[] types, AccountWithDataSet[] accounts) {
this.mTypes = types;
this.mAccounts = accounts;
}
@Override
- public AccountType getAccountType(String accountType) {
+ public AccountType getAccountType(String accountType, String dataSet) {
for (AccountType type : mTypes) {
if (accountType.equals(type.accountType)) {
return type;
@@ -50,8 +48,8 @@
}
@Override
- public ArrayList<Account> getAccounts(boolean writableOnly) {
- return new ArrayList<Account>(Arrays.asList(mAccounts));
+ public List<AccountWithDataSet> getAccounts(boolean writableOnly) {
+ return Arrays.asList(mAccounts);
}
@Override
diff --git a/tests/src/com/android/contacts/tests/streamitems/StreamItemPopulatorActivity.java b/tests/src/com/android/contacts/tests/streamitems/StreamItemPopulatorActivity.java
index 5613bc3..5acf83b 100644
--- a/tests/src/com/android/contacts/tests/streamitems/StreamItemPopulatorActivity.java
+++ b/tests/src/com/android/contacts/tests/streamitems/StreamItemPopulatorActivity.java
@@ -139,6 +139,7 @@
long rawContactId = -1;
String accountType = null;
String accountName = null;
+ String dataSet = null;
// Lookup the com.google raw contact for the contact.
Cursor c = getContentResolver().query(RawContacts.CONTENT_URI,