Merge change 26655 into eclair

* changes:
  clear PHOTO column instead of deleting the row for focus
diff --git a/res/drawable-finger/ic_tab_selected_contacts.png b/res/drawable-finger/ic_tab_selected_contacts.png
deleted file mode 100644
index 9f11fc6..0000000
--- a/res/drawable-finger/ic_tab_selected_contacts.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-finger/ic_tab_selected_recent.png b/res/drawable-finger/ic_tab_selected_recent.png
deleted file mode 100644
index b87424b..0000000
--- a/res/drawable-finger/ic_tab_selected_recent.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-finger/ic_tab_selected_starred.png b/res/drawable-finger/ic_tab_selected_starred.png
deleted file mode 100644
index 89004a3..0000000
--- a/res/drawable-finger/ic_tab_selected_starred.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-finger/ic_tab_unselected_contacts.png b/res/drawable-finger/ic_tab_unselected_contacts.png
deleted file mode 100644
index 93ff795..0000000
--- a/res/drawable-finger/ic_tab_unselected_contacts.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-finger/ic_tab_unselected_recent.png b/res/drawable-finger/ic_tab_unselected_recent.png
deleted file mode 100644
index 3da6583..0000000
--- a/res/drawable-finger/ic_tab_unselected_recent.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-finger/ic_tab_unselected_starred.png b/res/drawable-finger/ic_tab_unselected_starred.png
deleted file mode 100644
index f2ebc3b..0000000
--- a/res/drawable-finger/ic_tab_unselected_starred.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_left_disable.9.png b/res/drawable-hdpi-finger/btn_dial_action_left_disable.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_left_disable_focused.9.png b/res/drawable-hdpi-finger/btn_dial_action_left_disable_focused.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_left_normal.9.png b/res/drawable-hdpi-finger/btn_dial_action_left_normal.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_left_pressed.9.png b/res/drawable-hdpi-finger/btn_dial_action_left_pressed.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_left_selected.9.png b/res/drawable-hdpi-finger/btn_dial_action_left_selected.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_middle_disable.9.png b/res/drawable-hdpi-finger/btn_dial_action_middle_disable.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_middle_disable_focused.9.png b/res/drawable-hdpi-finger/btn_dial_action_middle_disable_focused.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_middle_normal.9.png b/res/drawable-hdpi-finger/btn_dial_action_middle_normal.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_middle_pressed.9.png b/res/drawable-hdpi-finger/btn_dial_action_middle_pressed.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_middle_selected.9.png b/res/drawable-hdpi-finger/btn_dial_action_middle_selected.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_right_disable.9.png b/res/drawable-hdpi-finger/btn_dial_action_right_disable.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_right_disable_focused.9.png b/res/drawable-hdpi-finger/btn_dial_action_right_disable_focused.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_right_normal.9.png b/res/drawable-hdpi-finger/btn_dial_action_right_normal.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_right_pressed.9.png b/res/drawable-hdpi-finger/btn_dial_action_right_pressed.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_action_right_selected.9.png b/res/drawable-hdpi-finger/btn_dial_action_right_selected.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_normal.9.png b/res/drawable-hdpi-finger/btn_dial_normal.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_normal_blue.9.png b/res/drawable-hdpi-finger/btn_dial_normal_blue.9.png
new file mode 100644
index 0000000..ddad155
--- /dev/null
+++ b/res/drawable-hdpi-finger/btn_dial_normal_blue.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_normal_green.9.png b/res/drawable-hdpi-finger/btn_dial_normal_green.9.png
new file mode 100644
index 0000000..dbb0f97
--- /dev/null
+++ b/res/drawable-hdpi-finger/btn_dial_normal_green.9.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_pressed.9.png b/res/drawable-hdpi-finger/btn_dial_pressed.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_selected.9.png b/res/drawable-hdpi-finger/btn_dial_selected.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_textfield_activated.9.png b/res/drawable-hdpi-finger/btn_dial_textfield_activated.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_textfield_normal.9.png b/res/drawable-hdpi-finger/btn_dial_textfield_normal.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_textfield_pressed.9.png b/res/drawable-hdpi-finger/btn_dial_textfield_pressed.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_dial_textfield_selected.9.png b/res/drawable-hdpi-finger/btn_dial_textfield_selected.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_0_blk.png b/res/drawable-hdpi-finger/dial_num_0_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_0_wht.png b/res/drawable-hdpi-finger/dial_num_0_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_1_no_vm_blk.png b/res/drawable-hdpi-finger/dial_num_1_no_vm_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_1_no_vm_wht.png b/res/drawable-hdpi-finger/dial_num_1_no_vm_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_2_blk.png b/res/drawable-hdpi-finger/dial_num_2_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_2_wht.png b/res/drawable-hdpi-finger/dial_num_2_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_3_blk.png b/res/drawable-hdpi-finger/dial_num_3_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_3_wht.png b/res/drawable-hdpi-finger/dial_num_3_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_4_blk.png b/res/drawable-hdpi-finger/dial_num_4_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_4_wht.png b/res/drawable-hdpi-finger/dial_num_4_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_5_blk.png b/res/drawable-hdpi-finger/dial_num_5_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_5_wht.png b/res/drawable-hdpi-finger/dial_num_5_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_6_blk.png b/res/drawable-hdpi-finger/dial_num_6_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_6_wht.png b/res/drawable-hdpi-finger/dial_num_6_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_7_blk.png b/res/drawable-hdpi-finger/dial_num_7_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_7_wht.png b/res/drawable-hdpi-finger/dial_num_7_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_8_blk.png b/res/drawable-hdpi-finger/dial_num_8_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_8_wht.png b/res/drawable-hdpi-finger/dial_num_8_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_9_blk.png b/res/drawable-hdpi-finger/dial_num_9_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_9_wht.png b/res/drawable-hdpi-finger/dial_num_9_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_pound_blk.png b/res/drawable-hdpi-finger/dial_num_pound_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_pound_wht.png b/res/drawable-hdpi-finger/dial_num_pound_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_star_blk.png b/res/drawable-hdpi-finger/dial_num_star_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/dial_num_star_wht.png b/res/drawable-hdpi-finger/dial_num_star_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_dial_action_call.png b/res/drawable-hdpi-finger/ic_dial_action_call.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_dial_action_delete.png b/res/drawable-hdpi-finger/ic_dial_action_delete.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_dial_action_voice_mail.png b/res/drawable-hdpi-finger/ic_dial_action_voice_mail.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_selected_contacts.png b/res/drawable-hdpi-finger/ic_tab_selected_contacts.png
new file mode 100644
index 0000000..7c92375
--- /dev/null
+++ b/res/drawable-hdpi-finger/ic_tab_selected_contacts.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_selected_dialer.png b/res/drawable-hdpi-finger/ic_tab_selected_dialer.png
index a84f8ab..44b4db0 100644
--- a/res/drawable-hdpi-finger/ic_tab_selected_dialer.png
+++ b/res/drawable-hdpi-finger/ic_tab_selected_dialer.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_selected_recent.png b/res/drawable-hdpi-finger/ic_tab_selected_recent.png
new file mode 100644
index 0000000..dfa268b
--- /dev/null
+++ b/res/drawable-hdpi-finger/ic_tab_selected_recent.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_selected_starred.png b/res/drawable-hdpi-finger/ic_tab_selected_starred.png
new file mode 100644
index 0000000..d9182b5
--- /dev/null
+++ b/res/drawable-hdpi-finger/ic_tab_selected_starred.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_unselected_contacts.png b/res/drawable-hdpi-finger/ic_tab_unselected_contacts.png
new file mode 100644
index 0000000..ee09bf1
--- /dev/null
+++ b/res/drawable-hdpi-finger/ic_tab_unselected_contacts.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_unselected_dialer.png b/res/drawable-hdpi-finger/ic_tab_unselected_dialer.png
index 51b0656..4151cd4 100644
--- a/res/drawable-hdpi-finger/ic_tab_unselected_dialer.png
+++ b/res/drawable-hdpi-finger/ic_tab_unselected_dialer.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_unselected_recent.png b/res/drawable-hdpi-finger/ic_tab_unselected_recent.png
new file mode 100644
index 0000000..4ecfa21
--- /dev/null
+++ b/res/drawable-hdpi-finger/ic_tab_unselected_recent.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_tab_unselected_starred.png b/res/drawable-hdpi-finger/ic_tab_unselected_starred.png
new file mode 100644
index 0000000..259d2d3
--- /dev/null
+++ b/res/drawable-hdpi-finger/ic_tab_unselected_starred.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_left_disable.9.png b/res/drawable-mdpi-finger/btn_dial_action_left_disable.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_left_disable_focused.9.png b/res/drawable-mdpi-finger/btn_dial_action_left_disable_focused.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_left_normal.9.png b/res/drawable-mdpi-finger/btn_dial_action_left_normal.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_left_pressed.9.png b/res/drawable-mdpi-finger/btn_dial_action_left_pressed.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_left_selected.9.png b/res/drawable-mdpi-finger/btn_dial_action_left_selected.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_middle_disable.9.png b/res/drawable-mdpi-finger/btn_dial_action_middle_disable.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_middle_disable_focused.9.png b/res/drawable-mdpi-finger/btn_dial_action_middle_disable_focused.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_middle_normal.9.png b/res/drawable-mdpi-finger/btn_dial_action_middle_normal.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_middle_pressed.9.png b/res/drawable-mdpi-finger/btn_dial_action_middle_pressed.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_middle_selected.9.png b/res/drawable-mdpi-finger/btn_dial_action_middle_selected.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_right_disable.9.png b/res/drawable-mdpi-finger/btn_dial_action_right_disable.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_right_disable_focused.9.png b/res/drawable-mdpi-finger/btn_dial_action_right_disable_focused.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_right_normal.9.png b/res/drawable-mdpi-finger/btn_dial_action_right_normal.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_right_pressed.9.png b/res/drawable-mdpi-finger/btn_dial_action_right_pressed.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_action_right_selected.9.png b/res/drawable-mdpi-finger/btn_dial_action_right_selected.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_normal.9.png b/res/drawable-mdpi-finger/btn_dial_normal.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_normal_blue.9.png b/res/drawable-mdpi-finger/btn_dial_normal_blue.9.png
new file mode 100644
index 0000000..7c8c2ca
--- /dev/null
+++ b/res/drawable-mdpi-finger/btn_dial_normal_blue.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_normal_green.9.png b/res/drawable-mdpi-finger/btn_dial_normal_green.9.png
new file mode 100644
index 0000000..b220650
--- /dev/null
+++ b/res/drawable-mdpi-finger/btn_dial_normal_green.9.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_pressed.9.png b/res/drawable-mdpi-finger/btn_dial_pressed.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_selected.9.png b/res/drawable-mdpi-finger/btn_dial_selected.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_textfield_activated.9.png b/res/drawable-mdpi-finger/btn_dial_textfield_activated.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_textfield_normal.9.png b/res/drawable-mdpi-finger/btn_dial_textfield_normal.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_textfield_pressed.9.png b/res/drawable-mdpi-finger/btn_dial_textfield_pressed.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_dial_textfield_selected.9.png b/res/drawable-mdpi-finger/btn_dial_textfield_selected.9.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_0_blk.png b/res/drawable-mdpi-finger/dial_num_0_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_0_wht.png b/res/drawable-mdpi-finger/dial_num_0_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_1_no_vm_blk.png b/res/drawable-mdpi-finger/dial_num_1_no_vm_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_1_no_vm_wht.png b/res/drawable-mdpi-finger/dial_num_1_no_vm_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_2_blk.png b/res/drawable-mdpi-finger/dial_num_2_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_2_wht.png b/res/drawable-mdpi-finger/dial_num_2_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_3_blk.png b/res/drawable-mdpi-finger/dial_num_3_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_3_wht.png b/res/drawable-mdpi-finger/dial_num_3_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_4_blk.png b/res/drawable-mdpi-finger/dial_num_4_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_4_wht.png b/res/drawable-mdpi-finger/dial_num_4_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_5_blk.png b/res/drawable-mdpi-finger/dial_num_5_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_5_wht.png b/res/drawable-mdpi-finger/dial_num_5_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_6_blk.png b/res/drawable-mdpi-finger/dial_num_6_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_6_wht.png b/res/drawable-mdpi-finger/dial_num_6_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_7_blk.png b/res/drawable-mdpi-finger/dial_num_7_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_7_wht.png b/res/drawable-mdpi-finger/dial_num_7_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_8_blk.png b/res/drawable-mdpi-finger/dial_num_8_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_8_wht.png b/res/drawable-mdpi-finger/dial_num_8_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_9_blk.png b/res/drawable-mdpi-finger/dial_num_9_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_9_wht.png b/res/drawable-mdpi-finger/dial_num_9_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_pound_blk.png b/res/drawable-mdpi-finger/dial_num_pound_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_pound_wht.png b/res/drawable-mdpi-finger/dial_num_pound_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_star_blk.png b/res/drawable-mdpi-finger/dial_num_star_blk.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/dial_num_star_wht.png b/res/drawable-mdpi-finger/dial_num_star_wht.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_dial_action_call.png b/res/drawable-mdpi-finger/ic_dial_action_call.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_dial_action_delete.png b/res/drawable-mdpi-finger/ic_dial_action_delete.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_dial_action_voice_mail.png b/res/drawable-mdpi-finger/ic_dial_action_voice_mail.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_selected_contacts.png b/res/drawable-mdpi-finger/ic_tab_selected_contacts.png
new file mode 100644
index 0000000..b3e23b7
--- /dev/null
+++ b/res/drawable-mdpi-finger/ic_tab_selected_contacts.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_selected_dialer.png b/res/drawable-mdpi-finger/ic_tab_selected_dialer.png
index 6ac51ad..71739a6 100644
--- a/res/drawable-mdpi-finger/ic_tab_selected_dialer.png
+++ b/res/drawable-mdpi-finger/ic_tab_selected_dialer.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_selected_recent.png b/res/drawable-mdpi-finger/ic_tab_selected_recent.png
new file mode 100644
index 0000000..a6cbc2a
--- /dev/null
+++ b/res/drawable-mdpi-finger/ic_tab_selected_recent.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_selected_starred.png b/res/drawable-mdpi-finger/ic_tab_selected_starred.png
new file mode 100644
index 0000000..089dfe9
--- /dev/null
+++ b/res/drawable-mdpi-finger/ic_tab_selected_starred.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_unselected_contacts.png b/res/drawable-mdpi-finger/ic_tab_unselected_contacts.png
new file mode 100644
index 0000000..b3f5ae0
--- /dev/null
+++ b/res/drawable-mdpi-finger/ic_tab_unselected_contacts.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_unselected_dialer.png b/res/drawable-mdpi-finger/ic_tab_unselected_dialer.png
index c33c658..f008813 100644
--- a/res/drawable-mdpi-finger/ic_tab_unselected_dialer.png
+++ b/res/drawable-mdpi-finger/ic_tab_unselected_dialer.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_unselected_recent.png b/res/drawable-mdpi-finger/ic_tab_unselected_recent.png
new file mode 100644
index 0000000..8f81688
--- /dev/null
+++ b/res/drawable-mdpi-finger/ic_tab_unselected_recent.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_tab_unselected_starred.png b/res/drawable-mdpi-finger/ic_tab_unselected_starred.png
new file mode 100644
index 0000000..32fe67d
--- /dev/null
+++ b/res/drawable-mdpi-finger/ic_tab_unselected_starred.png
Binary files differ
diff --git a/res/layout-finger/contacts_list_item.xml b/res/layout-finger/contacts_list_item.xml
index f2c3204..ccc53e7 100644
--- a/res/layout-finger/contacts_list_item.xml
+++ b/res/layout-finger/contacts_list_item.xml
@@ -30,7 +30,7 @@
     <View android:id="@+id/list_divider"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
-        android:background="@android:drawable/divider_horizontal_dark"
+        android:background="@*android:drawable/divider_horizontal_dark_opaque"
     />
 
     <RelativeLayout
@@ -38,17 +38,49 @@
         android:layout_height="?android:attr/listPreferredItemHeight"
         android:paddingLeft="14dip"
     >
-
-        <ImageView android:id="@+id/presence"
-            android:layout_width="32dip"
-            android:layout_height="32dip"
-            android:layout_alignParentRight="true"
-            android:layout_marginLeft="5dip"
-            android:layout_centerVertical="true"
-
-            android:gravity="center"
-            android:scaleType="centerInside"
-        />
+    
+        <LinearLayout android:id="@+id/right_side"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent"
+            android:orientation="horizontal"
+            android:layout_marginLeft="11dip"
+            android:layout_alignParentRight="true">
+            
+            <ImageView android:id="@+id/presence"
+                android:layout_width="32dip"
+                android:layout_height="32dip"
+                android:layout_marginLeft="5dip"
+                android:layout_marginRight="5dip"
+    
+                android:layout_gravity="center_vertical"
+                android:scaleType="centerInside"
+            />
+            
+            <LinearLayout android:id="@+id/call_view"
+                android:layout_width="wrap_content"
+                android:layout_height="fill_parent"
+                android:orientation="horizontal">
+    
+                <View android:id="@+id/divider"
+                    android:layout_width="1px"
+                    android:layout_height="fill_parent"
+                    android:background="@*android:drawable/divider_vertical_dark_opaque"
+                />
+    
+                <ImageView android:id="@+id/call_button"
+                    android:layout_width="wrap_content"
+                    android:layout_height="fill_parent"
+                    android:paddingLeft="14dip"
+                    android:paddingRight="14dip"
+                    android:layout_centerVertical="true"
+                    android:gravity="center"
+                    android:src="@android:drawable/sym_action_call"
+                    android:background="@android:drawable/list_selector_background"
+                />
+    
+            </LinearLayout>
+    
+        </LinearLayout>
 
         <TextView android:id="@+id/label"
             android:layout_width="wrap_content"
@@ -70,7 +102,7 @@
             android:layout_marginLeft="5dip"
             android:layout_toRightOf="@id/label"
             android:layout_alignBaseline="@id/label"
-            android:layout_toLeftOf="@id/presence"
+            android:layout_toLeftOf="@id/right_side"
             android:layout_alignWithParentIfMissing="true"
 
             android:singleLine="true"
@@ -85,7 +117,7 @@
             android:layout_alignWithParentIfMissing="true"
             android:layout_alignParentTop="true"
             android:layout_alignParentLeft="true"
-            android:layout_toLeftOf="@id/presence"
+            android:layout_toLeftOf="@id/right_side"
             android:layout_marginBottom="1dip"
 
             android:singleLine="true"
@@ -94,31 +126,5 @@
             android:textAppearance="?android:attr/textAppearanceLarge"
         />
 
-        <LinearLayout android:id="@+id/call_view"
-            android:layout_width="wrap_content"
-            android:layout_height="fill_parent"
-            android:orientation="horizontal"
-            android:layout_marginLeft="11dip"
-            android:layout_alignParentRight="true">
-
-            <View android:id="@+id/divider"
-                android:layout_width="1dip"
-                android:layout_height="fill_parent"
-                android:background="@drawable/divider_vertical_dark"
-            />
-
-            <ImageView android:id="@+id/call_button"
-                android:layout_width="wrap_content"
-                android:layout_height="fill_parent"
-                android:paddingLeft="14dip"
-                android:paddingRight="14dip"
-                android:layout_centerVertical="true"
-                android:gravity="center"
-                android:src="@android:drawable/sym_action_call"
-                android:background="@android:drawable/list_selector_background"
-            />
-
-        </LinearLayout>
-
     </RelativeLayout>
 </LinearLayout>
diff --git a/res/layout-finger/contacts_list_item_photo.xml b/res/layout-finger/contacts_list_item_photo.xml
index 9f762ab..7fecada 100644
--- a/res/layout-finger/contacts_list_item_photo.xml
+++ b/res/layout-finger/contacts_list_item_photo.xml
@@ -30,26 +30,56 @@
     <View android:id="@+id/list_divider"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
-        android:background="@android:drawable/divider_horizontal_dark"
+        android:background="@*android:drawable/divider_horizontal_dark_opaque"
     />
 
     <RelativeLayout
         android:layout_width="fill_parent"
         android:layout_height="?android:attr/listPreferredItemHeight"
         android:paddingLeft="5dip"
-        android:paddingRight="5dip"
     >
 
-        <ImageView android:id="@+id/presence"
-            android:layout_width="32dip"
-            android:layout_height="32dip"
-            android:layout_alignParentRight="true"
-            android:layout_marginLeft="5dip"
-            android:layout_centerVertical="true"
-
-            android:gravity="center"
-            android:scaleType="centerInside"
-        />
+        <LinearLayout android:id="@+id/right_side"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent"
+            android:orientation="horizontal"
+            android:layout_marginLeft="11dip"
+            android:layout_alignParentRight="true">
+            
+            <ImageView android:id="@+id/presence"
+                android:layout_width="32dip"
+                android:layout_height="32dip"
+                android:layout_marginLeft="5dip"
+                android:layout_marginRight="5dip"
+    
+                android:layout_gravity="center_vertical"
+                android:scaleType="centerInside"
+            />
+            
+            <LinearLayout android:id="@+id/call_view"
+                android:layout_width="wrap_content"
+                android:layout_height="fill_parent"
+                android:orientation="horizontal">
+    
+                <View android:id="@+id/divider"
+                    android:layout_width="1px"
+                    android:layout_height="fill_parent"
+                    android:background="@*android:drawable/divider_vertical_dark_opaque"
+                />
+    
+                <ImageView android:id="@+id/call_button"
+                    android:layout_width="wrap_content"
+                    android:layout_height="fill_parent"
+                    android:paddingLeft="14dip"
+                    android:paddingRight="14dip"
+                    android:layout_centerVertical="true"
+                    android:gravity="center"
+                    android:src="@android:drawable/sym_action_call"
+                    android:background="@android:drawable/list_selector_background"
+                />
+    
+            </LinearLayout>
+        </LinearLayout>
 
         <android.widget.FasttrackBadgeWidget android:id="@+id/photo"
             android:layout_alignParentLeft="true"
@@ -85,7 +115,7 @@
             android:layout_height="wrap_content"
             android:layout_marginLeft="5dip"
             android:layout_toRightOf="@id/label"
-            android:layout_toLeftOf="@id/presence"
+            android:layout_toLeftOf="@id/right_side"
             android:layout_alignBaseline="@id/label"
             android:layout_alignWithParentIfMissing="true"
 
@@ -98,7 +128,7 @@
             android:layout_width="0dip"
             android:layout_height="0dip"
             android:layout_toRightOf="@id/photo"
-            android:layout_toLeftOf="@id/presence"
+            android:layout_toLeftOf="@id/right_side"
             android:layout_alignParentTop="true"
             android:layout_above="@id/label"
             android:layout_alignWithParentIfMissing="true"
@@ -109,30 +139,5 @@
             android:textAppearance="?android:attr/textAppearanceLarge"
         />
 
-        <LinearLayout android:id="@+id/call_view"
-            android:layout_width="wrap_content"
-            android:layout_height="fill_parent"
-            android:orientation="horizontal"
-            android:layout_marginLeft="11dip"
-            android:layout_alignParentRight="true">
-
-            <View android:id="@+id/divider"
-                android:layout_width="1dip"
-                android:layout_height="fill_parent"
-                android:background="@drawable/divider_vertical_dark"
-            />
-
-            <ImageView android:id="@+id/call_button"
-                android:layout_width="wrap_content"
-                android:layout_height="fill_parent"
-                android:paddingLeft="14dip"
-                android:paddingRight="14dip"
-                android:layout_centerVertical="true"
-                android:gravity="center"
-                android:src="@android:drawable/sym_action_call"
-                android:background="@android:drawable/list_selector_background"
-            />
-
-        </LinearLayout>
     </RelativeLayout>
 </LinearLayout>
diff --git a/res/layout-finger/recent_calls_list_item.xml b/res/layout-finger/recent_calls_list_item.xml
index 275dea2..0f53d87 100644
--- a/res/layout-finger/recent_calls_list_item.xml
+++ b/res/layout-finger/recent_calls_list_item.xml
@@ -33,12 +33,12 @@
     />
 
     <View android:id="@+id/divider"
-        android:layout_width="1dip"
+        android:layout_width="1px"
         android:layout_height="fill_parent"
         android:layout_toLeftOf="@id/call_icon"
         android:layout_marginLeft="11dip"
 
-        android:background="@drawable/divider_vertical_dark"
+        android:background="@*android:drawable/divider_vertical_dark_opaque"
     />
 
     <ImageView android:id="@+id/call_type_icon"
diff --git a/res/values/config.xml b/res/values/config.xml
index 81c3d67..93d19e9 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -35,14 +35,12 @@
     <bool name="config_allow_export_to_sdcard">true</bool>
 
     <!-- If true, enable vibration (haptic feedback) for dialer key presses.
+         The pattern is set on a per-platform basis using config_virtualKeyVibePattern.
          TODO: If enough users are annoyed by this, we might eventually
          need to make it a user preference rather than a per-platform
          resource. -->
     <bool name="config_enable_dialer_key_vibration">true</bool>
 
-    <!-- How long to vibrate (in msec), if dialer key vibration is enabled. -->
-    <integer name="config_dialer_key_vibrate_duration">40</integer>
-
     <!-- The type of vcard for improt. If the vcard importer cannot guess the exact type
     of a vCard type, the improter uses this type. -->
     <string name="config_import_vcard_type" translatable="false">default</string>
diff --git a/res/values/ids.xml b/res/values/ids.xml
index 5746232..1af6162 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -39,6 +39,7 @@
     <item type="id" name="dialog_select_multiple_vcard" />
     <item type="id" name="dialog_reading_vcard" />
     <item type="id" name="dialog_io_exception" />
+    <item type="id" name="dialog_error_with_message" />
 
     <!-- For ExportVCard -->
     <item type="id" name="dialog_confirm_export_vcard" />
diff --git a/src/com/android/contacts/ImportVCardActivity.java b/src/com/android/contacts/ImportVCardActivity.java
index 027c70e..e77a3e8 100644
--- a/src/com/android/contacts/ImportVCardActivity.java
+++ b/src/com/android/contacts/ImportVCardActivity.java
@@ -104,6 +104,25 @@
     private VCardReadThread mVCardReadThread;
     private ProgressDialog mProgressDialogForReadVCard;
 
+    private String mErrorMessage;
+
+    private class DialogDisplayer implements Runnable {
+        private final int mResId;
+        public DialogDisplayer(int resId) {
+            mResId = resId;
+        }
+        public DialogDisplayer(String errorMessage) {
+            mResId = R.id.dialog_error_with_message;
+            mErrorMessage = errorMessage;
+        }
+        public void run() {
+            // Show the Dialog only when the parent Activity is still alive.
+            if (!ImportVCardActivity.this.isFinishing()) {
+                showDialog(mResId);
+            }
+        }
+    }
+
     private class CancelListener
         implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
         public void onClick(DialogInterface dialog, int which) {
@@ -117,27 +136,6 @@
 
     private CancelListener mCancelListener = new CancelListener();
 
-    private class ErrorDisplayer implements Runnable {
-        private String mErrorMessage;
-
-        public ErrorDisplayer(String errorMessage) {
-            mErrorMessage = errorMessage;
-        }
-
-        public void run() {
-            String message =
-                getString(R.string.reading_vcard_failed_message, mErrorMessage);
-            AlertDialog.Builder builder =
-                new AlertDialog.Builder(ImportVCardActivity.this)
-                    .setTitle(getString(R.string.reading_vcard_failed_title))
-                    .setIcon(android.R.drawable.ic_dialog_alert)
-                    .setMessage(message)
-                    .setOnCancelListener(mCancelListener)
-                    .setPositiveButton(android.R.string.ok, mCancelListener);
-            builder.show();
-        }
-    }
-
     private class VCardReadThread extends Thread
             implements DialogInterface.OnCancelListener {
         private ContentResolver mResolver;
@@ -263,7 +261,7 @@
             } finally {
                 mWakeLock.release();
                 mProgressDialogForReadVCard.dismiss();
-                // finish() is called via ErrorDisplayer() on failure.
+                // finish() is called via mCancelListener, which is used in DialogDisplayer.
                 if (shouldCallFinish && !isFinishing()) {
                     if (mErrorFileNameList == null || mErrorFileNameList.isEmpty()) {
                         finish();
@@ -279,7 +277,7 @@
                             builder.append(fileName);
                         }
                         
-                        mHandler.post(new ErrorDisplayer(
+                        mHandler.post(new DialogDisplayer(
                                 getString(R.string.fail_reason_failed_to_read_files,
                                         builder.toString())));
                     }
@@ -359,7 +357,7 @@
                 if (errorFileNameList != null) {
                     errorFileNameList.add(canonicalPath);
                 } else {
-                    mHandler.post(new ErrorDisplayer(
+                    mHandler.post(new DialogDisplayer(
                             getString(R.string.fail_reason_io_error,
                                     e.getMessage())));
                 }
@@ -371,7 +369,7 @@
                 if (errorFileNameList != null) {
                     errorFileNameList.add(canonicalPath);
                 } else {
-                    mHandler.post(new ErrorDisplayer(
+                    mHandler.post(new DialogDisplayer(
                             getString(R.string.fail_reason_vcard_not_supported_error) +
                             " (" + e.getMessage() + ")"));
                 }
@@ -380,7 +378,7 @@
                 if (errorFileNameList != null) {
                     errorFileNameList.add(canonicalPath);
                 } else {
-                    mHandler.post(new ErrorDisplayer(
+                    mHandler.post(new DialogDisplayer(
                             getString(R.string.fail_reason_vcard_parse_error) +
                             " (" + e.getMessage() + ")"));
                 }
@@ -602,17 +600,9 @@
         } else if (size == 1) {
             importOneVCardFromSDCard(mAllVCardFileList.get(0).getCanonicalPath());
         } else if (getResources().getBoolean(R.bool.config_allow_users_select_all_vcard_import)) {
-            mHandler.post(new Runnable() {
-                public void run() {
-                    showDialog(R.id.dialog_select_import_type);
-                }
-            });
+            mHandler.post(new DialogDisplayer(R.id.dialog_select_import_type));
         } else {
-            mHandler.post(new Runnable() {
-                public void run() {
-                    showDialog(R.id.dialog_select_one_vcard);
-                }
-            });
+            mHandler.post(new DialogDisplayer(R.id.dialog_select_one_vcard));
         }
     }
     
@@ -779,6 +769,20 @@
                     .setPositiveButton(android.R.string.ok, mCancelListener);
                 return builder.create();
             }
+            case R.id.dialog_error_with_message: {
+                String message = mErrorMessage;
+                if (TextUtils.isEmpty(message)) {
+                    Log.e(LOG_TAG, "Error message is null while it must not.");
+                    message = getString(R.string.fail_reason_unknown);
+                }
+                AlertDialog.Builder builder = new AlertDialog.Builder(this)
+                    .setTitle(getString(R.string.reading_vcard_failed_title))
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setMessage(message)
+                    .setOnCancelListener(mCancelListener)
+                    .setPositiveButton(android.R.string.ok, mCancelListener);
+                return builder.create();
+            }
         }
 
         return super.onCreateDialog(resId);
diff --git a/src/com/android/contacts/TwelveKeyDialer.java b/src/com/android/contacts/TwelveKeyDialer.java
index bdc6b6f..d07d785 100644
--- a/src/com/android/contacts/TwelveKeyDialer.java
+++ b/src/com/android/contacts/TwelveKeyDialer.java
@@ -82,6 +82,9 @@
     /** The DTMF tone volume relative to other sounds in the stream */
     private static final int TONE_RELATIVE_VOLUME = 50;
 
+    /** Play the vibrate pattern only once. */
+    private static final int VIBRATE_NO_REPEAT = -1;
+
     private EditText mDigits;
     private View mDelete;
     private MenuItem mAddToContactMenuItem;
@@ -108,7 +111,7 @@
     // Vibration (haptic feedback) for dialer key presses.
     private Vibrator mVibrator;
     private boolean mVibrateOn;
-    private long mVibrateDuration;
+    private long[] mVibratePattern;
 
 
     /** Identifier for the "Add Call" intent extra. */
@@ -218,12 +221,10 @@
             super.onRestoreInstanceState(icicle);
         }
 
-        // Initialize vibration parameters.
         // TODO: We might eventually need to make mVibrateOn come from a
         // user preference rather than a per-platform resource, in which
         // case we would need to update it in onResume() rather than here.
-        mVibrateOn = r.getBoolean(R.bool.config_enable_dialer_key_vibration);
-        mVibrateDuration = (long) r.getInteger(R.integer.config_dialer_key_vibrate_duration);
+        initVibrationPattern(r);
     }
 
     @Override
@@ -984,7 +985,7 @@
         if (mVibrator == null) {
             mVibrator = new Vibrator();
         }
-        mVibrator.vibrate(mVibrateDuration);
+        mVibrator.vibrate(mVibratePattern, VIBRATE_NO_REPEAT);
     }
 
     /**
@@ -1067,6 +1068,35 @@
     }
 
     /**
+     * Initialize the vibration parameters.
+     * @param r The Resources with the vibration parameters.
+     */
+    private void initVibrationPattern(Resources r) {
+        int[] pattern = null;
+        try {
+            mVibrateOn = r.getBoolean(R.bool.config_enable_dialer_key_vibration);
+            pattern = r.getIntArray(com.android.internal.R.array.config_virtualKeyVibePattern);
+            if (null == pattern) {
+                Log.e(TAG, "Vibrate pattern is null.");
+                mVibrateOn = false;
+            }
+        } catch (Resources.NotFoundException nfe) {
+            Log.e(TAG, "Vibrate control bool or pattern missing.", nfe);
+            mVibrateOn = false;
+        }
+
+        if (!mVibrateOn) {
+            return;
+        }
+
+        // int[] to long[] conversion.
+        mVibratePattern = new long[pattern.length];
+        for (int i = 0; i < pattern.length; i++) {
+            mVibratePattern[i] = pattern[i];
+        }
+    }
+
+    /**
      * This function return true if Wait menu item can be shown
      * otherwise returns false. Assumes the passed string is non-empty
      * and the 0th index check is not required.
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
index 14039f0..70c1867 100644
--- a/src/com/android/contacts/ViewContactActivity.java
+++ b/src/com/android/contacts/ViewContactActivity.java
@@ -963,8 +963,7 @@
 
                             if (protocolObj instanceof Number) {
                                 int protocol = ((Number) protocolObj).intValue();
-                                host = ContactsUtils.lookupProviderNameFromId(
-                                        protocol).toLowerCase();
+                                host = ContactsUtils.lookupProviderNameFromId(protocol);
                                 if (protocol == CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK
                                         || protocol == CommonDataKinds.Im.PROTOCOL_MSN) {
                                     entry.maxLabelLines = 2;
@@ -975,9 +974,10 @@
                             }
 
                             // Only add the intent if there is a valid host
+                            //  host is null for CommonDataKinds.Im.PROTOCOL_CUSTOM
                             if (!TextUtils.isEmpty(host)) {
                                 entry.intent = new Intent(Intent.ACTION_SENDTO,
-                                        constructImToUrl(host, entry.data));
+                                        constructImToUrl(host.toLowerCase(), entry.data));
                             }
                             //TODO(emillar) Add in presence info
                             /*if (!aggCursor.isNull(METHODS_STATUS_COLUMN)) {
diff --git a/src/com/android/contacts/model/EntityDelta.java b/src/com/android/contacts/model/EntityDelta.java
index f51ea34..8e0cf9f 100644
--- a/src/com/android/contacts/model/EntityDelta.java
+++ b/src/com/android/contacts/model/EntityDelta.java
@@ -59,6 +59,9 @@
 public class EntityDelta implements Parcelable {
     // TODO: optimize by using contentvalues pool, since we allocate so many of them
 
+    private static final String TAG = "EntityDelta";
+    private static final boolean LOGV = true;
+
     /**
      * Direct values from {@link Entity#getEntityValues()}.
      */
@@ -97,28 +100,41 @@
      * existing "after" states. This is typically used when re-parenting changes
      * onto an updated {@link Entity}.
      */
-    public void mergeAfter(EntityDelta remote) {
-        // Always take after values from new state
-        this.mValues.mAfter = remote.mValues.mAfter;
+    public static EntityDelta mergeAfter(EntityDelta local, EntityDelta remote) {
+        // Bail early if trying to merge delete with missing local
+        if (local == null && remote.mValues.isDelete()) return null;
 
-        // TODO: log before/after versions to track re-parenting
+        // Create local version if none exists yet
+        if (local == null) local = new EntityDelta();
+
+        if (LOGV) {
+            final Long localVersion = (local.mValues == null) ? -1 : local.mValues
+                    .getAsLong(RawContacts.VERSION);
+            final Long remoteVersion = remote.mValues.getAsLong(RawContacts.VERSION);
+            Log.d(TAG, "Re-parenting from original version " + remoteVersion + " to "
+                    + localVersion);
+        }
+
+        // Create values if needed, and merge "after" changes
+        local.mValues = ValuesDelta.mergeAfter(local.mValues, remote.mValues);
 
         // Find matching local entry for each remote values, or create
         for (ArrayList<ValuesDelta> mimeEntries : remote.mEntries.values()) {
             for (ValuesDelta remoteEntry : mimeEntries) {
                 final Long childId = remoteEntry.getId();
 
-                ValuesDelta localEntry = this.getEntry(childId);
-                if (localEntry == null) {
-                    // Is "insert", or "before" record is missing, so now "insert"
-                    localEntry = ValuesDelta.fromAfter(remoteEntry.mAfter);
-                    this.addEntry(localEntry);
-                } else {
-                    // Existing entry "update"
-                    localEntry.mAfter = remoteEntry.mAfter;
+                // Find or create local match and merge
+                final ValuesDelta localEntry = local.getEntry(childId);
+                final ValuesDelta merged = ValuesDelta.mergeAfter(localEntry, remoteEntry);
+
+                if (localEntry == null && merged != null) {
+                    // No local entry before, so insert
+                    local.addEntry(merged);
                 }
             }
         }
+
+        return local;
     }
 
     public ValuesDelta getValues() {
@@ -181,9 +197,10 @@
         return mEntries.containsKey(mimeType);
     }
 
-    public void addEntry(ValuesDelta entry) {
+    public ValuesDelta addEntry(ValuesDelta entry) {
         final String mimeType = entry.getMimetype();
         getMimeEntries(mimeType, true).add(entry);
+        return entry;
     }
 
     /**
@@ -587,6 +604,44 @@
             return keys;
         }
 
+        /**
+         * Return complete set of "before" and "after" values mixed together,
+         * giving full state regardless of edits.
+         */
+        public ContentValues getCompleteValues() {
+            final ContentValues values = new ContentValues();
+            if (mBefore != null) {
+                values.putAll(mBefore);
+            }
+            if (mAfter != null) {
+                values.putAll(mAfter);
+            }
+            return values;
+        }
+
+        /**
+         * Merge the "after" values from the given {@link ValuesDelta},
+         * discarding any existing "after" state. This is typically used when
+         * re-parenting changes onto an updated {@link Entity}.
+         */
+        public static ValuesDelta mergeAfter(ValuesDelta local, ValuesDelta remote) {
+            // Bail early if trying to merge delete with missing local
+            if (local == null && remote.isDelete()) return null;
+
+            // Create local version if none exists yet
+            if (local == null) local = new ValuesDelta();
+
+            if (!local.beforeExists()) {
+                // Any "before" record is missing, so take all values as "insert"
+                local.mAfter = remote.getCompleteValues();
+            } else {
+                // Existing "update" with only "after" values
+                local.mAfter = remote.mAfter;
+            }
+
+            return local;
+        }
+
         @Override
         public boolean equals(Object object) {
             if (object instanceof ValuesDelta) {
diff --git a/src/com/android/contacts/model/EntitySet.java b/src/com/android/contacts/model/EntitySet.java
index 75c9dc3..02a127e 100644
--- a/src/com/android/contacts/model/EntitySet.java
+++ b/src/com/android/contacts/model/EntitySet.java
@@ -21,6 +21,7 @@
 
 import android.content.ContentProviderOperation;
 import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Entity;
 import android.content.EntityIterator;
 import android.content.ContentProviderOperation.Builder;
@@ -30,6 +31,7 @@
 import android.provider.ContactsContract.AggregationExceptions;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.RawContacts;
+import android.util.Log;
 
 import java.util.ArrayList;
 
@@ -82,11 +84,28 @@
     }
 
     /**
-     * Merge the "after" values from the given {@link EntitySet}.
+     * Merge the "after" values from the given {@link EntitySet}, discarding any
+     * previous "after" states. This is typically used when re-parenting user
+     * edits onto an updated {@link EntitySet}.
      */
-    public void mergeAfter(EntitySet remote) {
-        // TODO: write this folding logic to re-parent
-        throw new UnsupportedOperationException();
+    public static EntitySet mergeAfter(EntitySet local, EntitySet remote) {
+        if (local == null) local = new EntitySet();
+
+        // For each entity in the remote set, try matching over existing
+        for (EntityDelta remoteEntity : remote) {
+            final Long rawContactId = remoteEntity.getValues().getId();
+
+            // Find or create local match and merge
+            final EntityDelta localEntity = local.getByRawContactId(rawContactId);
+            final EntityDelta merged = EntityDelta.mergeAfter(localEntity, remoteEntity);
+
+            if (localEntity == null && merged != null) {
+                // No local entry before, so insert
+                local.add(merged);
+            }
+        }
+
+        return local;
     }
 
     /**
@@ -173,7 +192,7 @@
     /**
      * Find {@link RawContacts#_ID} of the requested {@link EntityDelta}.
      */
-    public long getRawContactId(int index) {
+    public Long getRawContactId(int index) {
         if (index >= 0 && index < this.size()) {
             final EntityDelta delta = this.get(index);
             final ValuesDelta values = delta.getValues();
@@ -181,16 +200,22 @@
                 return values.getAsLong(RawContacts._ID);
             }
         }
-        return 0;
+        return null;
+    }
+
+    public EntityDelta getByRawContactId(Long rawContactId) {
+        final int index = this.indexOfRawContactId(rawContactId);
+        return (index == -1) ? null : this.get(index);
     }
 
     /**
      * Find index of given {@link RawContacts#_ID} when present.
      */
     public int indexOfRawContactId(Long rawContactId) {
+        if (rawContactId == null) return -1;
         final int size = this.size();
         for (int i = 0; i < size; i++) {
-            final long currentId = getRawContactId(i);
+            final Long currentId = getRawContactId(i);
             if (currentId == rawContactId) {
                 return i;
             }
diff --git a/src/com/android/contacts/model/ExchangeSource.java b/src/com/android/contacts/model/ExchangeSource.java
index c017037..5c2d024 100644
--- a/src/com/android/contacts/model/ExchangeSource.java
+++ b/src/com/android/contacts/model/ExchangeSource.java
@@ -212,6 +212,8 @@
         final DataKind kind = super.inflateOrganization(ContactsSource.LEVEL_MIMETYPES);
 
         if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
+            kind.typeOverallMax = 1;
+
             kind.typeColumn = Organization.TYPE;
             kind.typeList = Lists.newArrayList();
             kind.typeList.add(buildOrgType(Organization.TYPE_WORK).setSpecificMax(1));
diff --git a/src/com/android/contacts/model/FallbackSource.java b/src/com/android/contacts/model/FallbackSource.java
index 85aedd7..fa545be 100644
--- a/src/com/android/contacts/model/FallbackSource.java
+++ b/src/com/android/contacts/model/FallbackSource.java
@@ -140,6 +140,9 @@
         }
 
         if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
+            kind.defaultValues = new ContentValues();
+            kind.defaultValues.put(Nickname.TYPE, Nickname.TYPE_DEFAULT);
+
             kind.fieldList = Lists.newArrayList();
             kind.fieldList.add(new EditField(Nickname.NAME, R.string.nicknameLabelsGroup,
                     FLAGS_PERSON_NAME));
@@ -345,7 +348,6 @@
         }
 
         if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
-
             kind.fieldList = Lists.newArrayList();
             kind.fieldList.add(new EditField(Note.NOTE, R.string.label_notes, FLAGS_NOTE));
         }
@@ -364,6 +366,9 @@
         }
 
         if (inflateLevel >= ContactsSource.LEVEL_CONSTRAINTS) {
+            kind.defaultValues = new ContentValues();
+            kind.defaultValues.put(Website.TYPE, Website.TYPE_OTHER);
+
             kind.fieldList = Lists.newArrayList();
             kind.fieldList.add(new EditField(Website.URL, R.string.websiteLabelsGroup, FLAGS_WEBSITE));
         }
@@ -437,6 +442,14 @@
             return type == BaseTypes.TYPE_CUSTOM;
         }
 
+        protected String getTypeColumn() {
+            return Phone.TYPE;
+        }
+
+        protected String getLabelColumn() {
+            return Phone.LABEL;
+        }
+
         protected CharSequence getTypeLabel(Resources res, Integer type, CharSequence label) {
             final int labelRes = getTypeLabelResource(type);
             if (type == null) {
@@ -449,14 +462,14 @@
         }
 
         public CharSequence inflateUsing(Context context, Cursor cursor) {
-            final Integer type = cursor.getInt(cursor.getColumnIndex(Phone.TYPE));
-            final String label = cursor.getString(cursor.getColumnIndex(Phone.LABEL));
+            final Integer type = cursor.getInt(cursor.getColumnIndex(getTypeColumn()));
+            final String label = cursor.getString(cursor.getColumnIndex(getLabelColumn()));
             return getTypeLabel(context.getResources(), type, label);
         }
 
         public CharSequence inflateUsing(Context context, ContentValues values) {
-            final Integer type = values.getAsInteger(Phone.TYPE);
-            final String label = values.getAsString(Phone.LABEL);
+            final Integer type = values.getAsInteger(getTypeColumn());
+            final String label = values.getAsString(getLabelColumn());
             return getTypeLabel(context.getResources(), type, label);
         }
     }
@@ -560,6 +573,16 @@
 
     public static class ImActionInflater extends CommonInflater {
         @Override
+        protected String getTypeColumn() {
+            return Im.PROTOCOL;
+        }
+
+        @Override
+        protected String getLabelColumn() {
+            return Im.CUSTOM_PROTOCOL;
+        }
+
+        @Override
         protected int getTypeLabelResource(Integer type) {
             if (type == null) return R.string.chat;
             switch (type) {
diff --git a/src/com/android/contacts/ui/EditContactActivity.java b/src/com/android/contacts/ui/EditContactActivity.java
index c02ee80..c3a3935 100644
--- a/src/com/android/contacts/ui/EditContactActivity.java
+++ b/src/com/android/contacts/ui/EditContactActivity.java
@@ -279,8 +279,7 @@
      */
     protected EntityDelta getSelectedEntityDelta() {
         final Long rawContactId = getSelectedRawContactId();
-        final int stateIndex = mState.indexOfRawContactId(rawContactId);
-        return mState.get(stateIndex);
+        return mState.getByRawContactId(rawContactId);
     }
 
     /**
@@ -391,9 +390,10 @@
 
         // Find entity and source for selected tab
         final EntityDelta entity = this.getSelectedEntityDelta();
-        final String accountType = entity.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
+        if (entity == null) return;
 
         final Sources sources = Sources.getInstance(this);
+        final String accountType = entity.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
         final ContactsSource source = sources.getInflatedSource(accountType,
                 ContactsSource.LEVEL_CONSTRAINTS);
 
@@ -574,8 +574,7 @@
                     Log.w(TAG, "Version consistency failed, re-parenting", e);
                     final EntitySet newState = EntitySet.fromQuery(resolver,
                             target.mQuerySelection, null, null);
-                    newState.mergeAfter(state);
-                    state = newState;
+                    state = EntitySet.mergeAfter(newState, state);
                 }
             }
 
@@ -886,6 +885,7 @@
             public void onClick(DialogInterface dialog, int which) {
                 // Mark the currently selected contact for deletion
                 final EntityDelta delta = getSelectedEntityDelta();
+                if (delta == null) return;
                 delta.markDeleted();
 
                 bindTabs();
diff --git a/src/com/android/contacts/ui/FastTrackWindow.java b/src/com/android/contacts/ui/FastTrackWindow.java
index 8626818..9d5740a 100644
--- a/src/com/android/contacts/ui/FastTrackWindow.java
+++ b/src/com/android/contacts/ui/FastTrackWindow.java
@@ -26,6 +26,7 @@
 import com.android.internal.policy.PolicyManager;
 
 import android.content.ActivityNotFoundException;
+import android.content.ContentValues;
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.EntityIterator;
@@ -66,6 +67,8 @@
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.ListView;
@@ -85,7 +88,7 @@
  */
 public class FastTrackWindow implements Window.Callback,
         NotifyingAsyncQueryHandler.AsyncQueryListener, View.OnClickListener,
-        AbsListView.OnItemClickListener {
+        AbsListView.OnItemClickListener, CompoundButton.OnCheckedChangeListener {
     private static final String TAG = "FastTrackWindow";
 
     /**
@@ -119,6 +122,7 @@
     private boolean mHasSocial = false;
     private boolean mHasValidSocial = false;
     private boolean mHasActions = false;
+    private boolean mMakePrimary = false;
 
     private ImageView mArrowUp;
     private ImageView mArrowDown;
@@ -133,6 +137,7 @@
     private View mFooterDisambig;
     private ListView mResolveList;
     private CheckableImageView mLastAction;
+    private CheckBox mSetPrimaryCheckBox;
 
     /**
      * Set of {@link Action} that are associated with the aggregate currently
@@ -189,6 +194,9 @@
         mFooter = mWindow.findViewById(R.id.footer);
         mFooterDisambig = mWindow.findViewById(R.id.footer_disambig);
         mResolveList = (ListView)mWindow.findViewById(android.R.id.list);
+        mSetPrimaryCheckBox = (CheckBox)mWindow.findViewById(android.R.id.checkbox);
+
+        mSetPrimaryCheckBox.setOnCheckedChangeListener(this);
 
         // Prepare track entrance animation
         mTrackAnim = AnimationUtils.loadAnimation(mContext, R.anim.fasttrack);
@@ -563,6 +571,12 @@
         return cursor.getString(index);
     }
 
+    /** Read {@link int} from the given {@link Cursor}. */
+    private static int getAsInt(Cursor cursor, String columnName) {
+        final int index = cursor.getColumnIndex(columnName);
+        return cursor.getInt(index);
+    }
+
     /**
      * Abstract definition of an action that could be performed, along with
      * string description and icon.
@@ -578,6 +592,17 @@
          * Build an {@link Intent} that will perform this action.
          */
         public Intent getIntent();
+
+        /**
+         * Checks if the contact data for this action is primary.
+         */
+        public Boolean isPrimary();
+
+        /**
+         * Returns a lookup (@link Uri) for the contact data item.
+         */
+        public Uri getDataUri();
+
     }
 
     /**
@@ -594,11 +619,14 @@
         private Intent mIntent;
 
         private boolean mAlternate;
+        private Uri mDataUri;
+        private boolean mIsPrimary;
 
         /**
          * Create an action from common {@link Data} elements.
          */
-        public DataAction(Context context, String mimeType, DataKind kind, Cursor cursor) {
+        public DataAction(Context context, String mimeType, DataKind kind,
+                long dataId, Cursor cursor) {
             mContext = context;
             mKind = kind;
             mMimeType = mimeType;
@@ -611,10 +639,16 @@
                 mHeader = mKind.actionHeader.inflateUsing(context, cursor);
             }
 
+            if (getAsInt(cursor, Data.IS_SUPER_PRIMARY) != 0) {
+                mIsPrimary = true;
+            }
+
             if (mKind.actionBody != null) {
                 mBody = mKind.actionBody.inflateUsing(context, cursor);
             }
 
+            mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
+
             // Handle well-known MIME-types with special care
             if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
                 final String number = getAsString(cursor, Phone.NUMBER);
@@ -639,9 +673,7 @@
 
             } else {
                 // Otherwise fall back to default VIEW action
-                final long dataId = cursor.getLong(DataQuery._ID);
-                final Uri dataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
-                mIntent = new Intent(Intent.ACTION_VIEW, dataUri);
+                mIntent = new Intent(Intent.ACTION_VIEW, mDataUri);
             }
         }
 
@@ -661,6 +693,16 @@
         }
 
         /** {@inheritDoc} */
+        public Uri getDataUri() {
+            return mDataUri;
+        }
+
+        /** {@inheritDoc} */
+        public Boolean isPrimary() {
+            return mIsPrimary;
+        }
+
+        /** {@inheritDoc} */
         public Drawable getFallbackIcon() {
             // Bail early if no valid resources
             final String resPackageName = mKind.resPackageName;
@@ -719,6 +761,17 @@
             final Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, mId);
             return new Intent(Intent.ACTION_VIEW, contactUri);
         }
+
+        /** {@inheritDoc} */
+        public Boolean isPrimary() {
+            return null;
+        }
+
+        /** {@inheritDoc} */
+        public Uri getDataUri() {
+            return null;
+        }
+
     }
 
     /**
@@ -868,6 +921,7 @@
             final String accountType = cursor.getString(DataQuery.ACCOUNT_TYPE);
             final String resPackage = cursor.getString(DataQuery.RES_PACKAGE);
             final String mimeType = cursor.getString(DataQuery.MIMETYPE);
+            final long dataId = cursor.getLong(DataQuery._ID);
 
             // Skip this data item if MIME-type excluded
             if (isMimeExcluded(mimeType)) continue;
@@ -895,14 +949,14 @@
                 // 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(mContext, mimeType, kind, cursor);
+                final Action action = new DataAction(mContext, mimeType, kind, dataId, cursor);
                 considerAdd(action, mimeType);
             }
 
             // If phone number, also insert as text message action
             if (Phone.CONTENT_ITEM_TYPE.equals(mimeType) && kind != null) {
-                final Action action = new DataAction(mContext, Constants.MIME_SMS_ADDRESS, kind,
-                        cursor);
+                final Action action = new DataAction(mContext, Constants.MIME_SMS_ADDRESS,
+                        kind, dataId, cursor);
                 considerAdd(action, Constants.MIME_SMS_ADDRESS);
             }
         }
@@ -943,14 +997,24 @@
      */
     private View inflateAction(String mimeType) {
         CheckableImageView view = (CheckableImageView)mInflater.inflate(R.layout.fasttrack_item, mTrack, false);
+        boolean isActionSet = false;
 
         // Add direct intent if single child, otherwise flag for multiple
         ActionList children = mActions.get(mimeType);
         Action firstInfo = children.get(0);
         if (children.size() == 1) {
-            view.setTag(firstInfo.getIntent());
+            view.setTag(firstInfo);
         } else {
-            view.setTag(children);
+            for (Action action: children) {
+              if (action.isPrimary()) {
+                  view.setTag(action);
+                  isActionSet = true;
+                  break;
+              }
+            }
+            if (!isActionSet) {
+                view.setTag(children);
+            }
         }
 
         // Set icon and listen for clicks
@@ -1007,16 +1071,26 @@
     public void onClick(View view) {
         final boolean isActionView = (view instanceof CheckableImageView);
         final CheckableImageView actionView = isActionView ? (CheckableImageView)view : null;
-
         final Object tag = view.getTag();
-        if (tag instanceof Intent) {
+        if (tag instanceof Action) {
             // Hide the resolution list, if present
             setResolveVisible(false, actionView);
             this.dismiss();
 
             try {
                 // Incoming tag is concrete intent, so try launching
-                mContext.startActivity((Intent)tag);
+                final Action action = (Action)tag;
+                mContext.startActivity(action.getIntent());
+
+                if (mMakePrimary) {
+                    ContentValues values = new ContentValues(1);
+                    values.put(Data.IS_SUPER_PRIMARY, 1);
+                    final Uri dataUri = action.getDataUri();
+                    if (dataUri != null) {
+                        mContext.getContentResolver().update(dataUri, values, null, null);
+                    }
+                }
+
             } catch (ActivityNotFoundException e) {
                 Toast.makeText(mContext, R.string.fasttrack_missing_app, Toast.LENGTH_SHORT).show();
             }
@@ -1056,7 +1130,7 @@
                     text1.setText(action.getHeader());
                     text2.setText(action.getBody());
 
-                    convertView.setTag(action.getIntent());
+                    convertView.setTag(action);
                     return convertView;
                 }
             });
@@ -1067,6 +1141,10 @@
         }
     }
 
+    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+        mMakePrimary = isChecked;
+    }
+
     /** {@inheritDoc} */
     public boolean dispatchKeyEvent(KeyEvent event) {
         if (event.getAction() == KeyEvent.ACTION_DOWN
diff --git a/src/com/android/contacts/ui/ShowOrCreateActivity.java b/src/com/android/contacts/ui/ShowOrCreateActivity.java
index 8d3ed01..7728b36 100755
--- a/src/com/android/contacts/ui/ShowOrCreateActivity.java
+++ b/src/com/android/contacts/ui/ShowOrCreateActivity.java
@@ -23,6 +23,7 @@
 
 import android.app.Activity;
 import android.app.AlertDialog;
+import android.app.Dialog;
 import android.content.ComponentName;
 import android.content.ContentUris;
 import android.content.DialogInterface;
@@ -68,6 +69,8 @@
 
     static final int CONTACT_ID_INDEX = 0;
 
+    static final int CREATE_CONTACT_DIALOG = 1;
+
     static final int QUERY_TOKEN = 42;
 
     private NotifyingAsyncQueryHandler mQueryHandler;
@@ -189,24 +192,33 @@
                 finish();
 
             } else {
+	        showDialog(CREATE_CONTACT_DIALOG);
+           }
+        }
+    }
+
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        switch(id) {
+	    case CREATE_CONTACT_DIALOG:
                 // Prompt user to insert or edit contact
-                Intent createIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+                final Intent createIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
                 createIntent.putExtras(mCreateExtras);
                 createIntent.setType(RawContacts.CONTENT_ITEM_TYPE);
 
-                CharSequence message = getResources().getString(
+                final CharSequence message = getResources().getString(
                         R.string.add_contact_dlg_message_fmt, mCreateDescrip);
 
-                new AlertDialog.Builder(this)
+                return new AlertDialog.Builder(this)
                         .setTitle(R.string.add_contact_dlg_title)
                         .setMessage(message)
                         .setPositiveButton(android.R.string.ok,
                                 new IntentClickListener(this, createIntent))
                         .setNegativeButton(android.R.string.cancel,
                                 new IntentClickListener(this, null))
-                        .show();
-            }
+                        .create();
         }
+	return super.onCreateDialog(id);
     }
 
     /** {@inheritDoc} */
diff --git a/src/com/android/contacts/ui/widget/GenericEditorView.java b/src/com/android/contacts/ui/widget/GenericEditorView.java
index 3522258..4490fbf 100644
--- a/src/com/android/contacts/ui/widget/GenericEditorView.java
+++ b/src/com/android/contacts/ui/widget/GenericEditorView.java
@@ -215,7 +215,11 @@
     }
 
     /**
-     * Prepare dialog for entering a custom label.
+     * Prepare dialog for entering a custom label. The input value is trimmed: white spaces before
+     * and after the input text is removed.
+     * <p>
+     * If the final value is empty, this change request is ignored;
+     * no empty text is allowed in any custom label.
      */
     public Dialog createCustomDialog() {
         final EditText customType = new EditText(mContext);
@@ -228,13 +232,14 @@
 
         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
             public void onClick(DialogInterface dialog, int which) {
-                final String customText = customType.getText().toString();
-                mEntry.put(mType.customColumn, customText);
-                rebuildLabel();
+                final String customText = customType.getText().toString().trim();
+                if (!TextUtils.isEmpty(customText)) {
+                    mEntry.put(mType.customColumn, customText);
+                    rebuildLabel();
+                }
             }
         });
 
-        // TODO: handle canceled case by reverting to previous type?
         builder.setNegativeButton(android.R.string.cancel, null);
 
         return builder.create();
diff --git a/tests/src/com/android/contacts/EntityDeltaTests.java b/tests/src/com/android/contacts/EntityDeltaTests.java
index 73318cb..70a506b 100644
--- a/tests/src/com/android/contacts/EntityDeltaTests.java
+++ b/tests/src/com/android/contacts/EntityDeltaTests.java
@@ -93,8 +93,8 @@
         final EntityDelta dest = EntityDelta.fromBefore(before);
 
         // Merge modified values and assert they match
-        dest.mergeAfter(source);
-        assertEquals("Unexpected change when merging", source, dest);
+        final EntityDelta merged = EntityDelta.mergeAfter(dest, source);
+        assertEquals("Unexpected change when merging", source, merged);
     }
 
     public void testParcelChangesInsert() {
@@ -104,14 +104,14 @@
 
         // Add a new row and pass across parcel, should be same
         final ContentValues phone = new ContentValues();
-        phone.put(Data.MIMETYPE, Phone.MIMETYPE);
+        phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
         phone.put(Phone.TYPE, Phone.TYPE_WORK);
         source.addEntry(ValuesDelta.fromAfter(phone));
 
         // Merge modified values and assert they match
-        dest.mergeAfter(source);
-        assertEquals("Unexpected change when merging", source, dest);
+        final EntityDelta merged = EntityDelta.mergeAfter(dest, source);
+        assertEquals("Unexpected change when merging", source, merged);
     }
 
     public void testParcelChangesUpdate() {
@@ -124,8 +124,8 @@
         child.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
 
         // Merge modified values and assert they match
-        dest.mergeAfter(source);
-        assertEquals("Unexpected change when merging", source, dest);
+        final EntityDelta merged = EntityDelta.mergeAfter(dest, source);
+        assertEquals("Unexpected change when merging", source, merged);
     }
 
     public void testParcelChangesDelete() {
@@ -138,8 +138,8 @@
         child.markDeleted();
 
         // Merge modified values and assert they match
-        dest.mergeAfter(source);
-        assertEquals("Unexpected change when merging", source, dest);
+        final EntityDelta merged = EntityDelta.mergeAfter(dest, source);
+        assertEquals("Unexpected change when merging", source, merged);
     }
 
     /**
@@ -221,7 +221,7 @@
 
         // Insert a new phone number
         final ContentValues phone = new ContentValues();
-        phone.put(Data.MIMETYPE, Phone.MIMETYPE);
+        phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
         phone.put(Phone.TYPE, Phone.TYPE_WORK);
         source.addEntry(ValuesDelta.fromAfter(phone));
@@ -261,7 +261,7 @@
 
         // Insert a new phone number
         final ContentValues phone = new ContentValues();
-        phone.put(Data.MIMETYPE, Phone.MIMETYPE);
+        phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
         phone.put(Phone.TYPE, Phone.TYPE_WORK);
         source.addEntry(ValuesDelta.fromAfter(phone));
@@ -386,7 +386,7 @@
 
         // Insert a new phone number
         final ContentValues phone = new ContentValues();
-        phone.put(Data.MIMETYPE, Phone.MIMETYPE);
+        phone.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
         phone.put(Phone.NUMBER, TEST_PHONE_NUMBER_2);
         phone.put(Phone.TYPE, Phone.TYPE_WORK);
         source.addEntry(ValuesDelta.fromAfter(phone));
diff --git a/tests/src/com/android/contacts/EntityModifierTests.java b/tests/src/com/android/contacts/EntityModifierTests.java
index 6bc3005..1de3936 100644
--- a/tests/src/com/android/contacts/EntityModifierTests.java
+++ b/tests/src/com/android/contacts/EntityModifierTests.java
@@ -26,7 +26,6 @@
 import com.android.contacts.model.EntitySet;
 import com.android.contacts.model.Sources;
 import com.android.contacts.model.ContactsSource.DataKind;
-import com.android.contacts.model.ContactsSource.EditField;
 import com.android.contacts.model.ContactsSource.EditType;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
 import com.google.android.collect.Lists;
@@ -40,7 +39,6 @@
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -99,7 +97,9 @@
      * testing purposes.
      */
     protected ContactsSource getSource() {
-        return new MockContactsSource();
+        final ContactsSource source = new MockContactsSource();
+        source.ensureInflated(getContext(), ContactsSource.LEVEL_CONSTRAINTS);
+        return source;
     }
 
     /**
diff --git a/tests/src/com/android/contacts/EntitySetTests.java b/tests/src/com/android/contacts/EntitySetTests.java
index 89fb455..cf0257f 100644
--- a/tests/src/com/android/contacts/EntitySetTests.java
+++ b/tests/src/com/android/contacts/EntitySetTests.java
@@ -16,6 +16,11 @@
 
 package com.android.contacts;
 
+import static android.content.ContentProviderOperation.TYPE_ASSERT;
+import static android.content.ContentProviderOperation.TYPE_DELETE;
+import static android.content.ContentProviderOperation.TYPE_INSERT;
+import static android.content.ContentProviderOperation.TYPE_UPDATE;
+
 import com.android.contacts.model.EntityDelta;
 import com.android.contacts.model.EntitySet;
 import com.android.contacts.model.EntityDelta.ValuesDelta;
@@ -23,10 +28,15 @@
 import android.content.ContentProviderOperation;
 import android.content.ContentValues;
 import android.content.Entity;
+import android.net.Uri;
+import android.provider.BaseColumns;
 import android.provider.ContactsContract.AggregationExceptions;
+import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
 
 import java.util.ArrayList;
 
@@ -41,6 +51,16 @@
     private static final long CONTACT_FIRST = 1;
     private static final long CONTACT_SECOND = 2;
 
+    public static final long CONTACT_BOB = 10;
+    public static final long CONTACT_MARY = 11;
+
+    public static final long PHONE_RED = 20;
+    public static final long PHONE_GREEN = 21;
+    public static final long PHONE_BLUE = 22;
+
+    public static final long VER_FIRST = 100;
+    public static final long VER_SECOND = 200;
+
     public EntitySetTests() {
         super();
     }
@@ -50,13 +70,13 @@
         mContext = getContext();
     }
 
-    protected EntityDelta getUpdate(long rawContactId) {
+    protected static EntityDelta getUpdate(long rawContactId) {
         final Entity before = EntityDeltaTests.getEntity(rawContactId,
                 EntityDeltaTests.TEST_PHONE_ID);
         return EntityDelta.fromBefore(before);
     }
 
-    protected EntityDelta getInsert() {
+    protected static EntityDelta getInsert() {
         final ContentValues after = new ContentValues();
         after.put(RawContacts.ACCOUNT_NAME, EntityDeltaTests.TEST_ACCOUNT_NAME);
         after.put(RawContacts.SEND_TO_VOICEMAIL, 1);
@@ -65,7 +85,7 @@
         return new EntityDelta(values);
     }
 
-    protected EntitySet setFrom(EntityDelta... deltas) {
+    protected static EntitySet buildSet(EntityDelta... deltas) {
         final EntitySet set = EntitySet.fromSingle(deltas[0]);
         for (int i = 1; i < deltas.length; i++) {
             set.add(deltas[i]);
@@ -73,11 +93,104 @@
         return set;
     }
 
+    protected static EntityDelta buildBeforeEntity(long rawContactId, long version,
+            ContentValues... entries) {
+        // Build an existing contact read from database
+        final ContentValues contact = new ContentValues();
+        contact.put(RawContacts.VERSION, version);
+        contact.put(RawContacts._ID, rawContactId);
+        final Entity before = new Entity(contact);
+        for (ContentValues entry : entries) {
+            before.addSubValue(Data.CONTENT_URI, entry);
+        }
+        return EntityDelta.fromBefore(before);
+    }
+
+    protected static EntityDelta buildAfterEntity(ContentValues... entries) {
+        // Build an existing contact read from database
+        final ContentValues contact = new ContentValues();
+        contact.putNull(RawContacts.ACCOUNT_NAME);
+        final EntityDelta after = new EntityDelta(ValuesDelta.fromAfter(contact));
+        for (ContentValues entry : entries) {
+            after.addEntry(ValuesDelta.fromAfter(entry));
+        }
+        return after;
+    }
+
+    protected static ContentValues buildPhone(long phoneId) {
+        return buildPhone(phoneId, Long.toString(phoneId));
+    }
+
+    protected static ContentValues buildPhone(long phoneId, String value) {
+        final ContentValues values = new ContentValues();
+        values.put(Data._ID, phoneId);
+        values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+        values.put(Phone.NUMBER, value);
+        values.put(Phone.TYPE, Phone.TYPE_HOME);
+        return values;
+    }
+
+    protected static void insertPhone(EntitySet set, long rawContactId, ContentValues values) {
+        final EntityDelta match = set.getByRawContactId(rawContactId);
+        match.addEntry(ValuesDelta.fromAfter(values));
+    }
+
+    protected static ValuesDelta getPhone(EntitySet set, long rawContactId, long dataId) {
+        final EntityDelta match = set.getByRawContactId(rawContactId);
+        return match.getEntry(dataId);
+    }
+
+    protected void assertDiffPattern(EntitySet set, ContentProviderOperation... pattern) {
+        final ArrayList<ContentProviderOperation> diff = set.buildDiff();
+
+        assertEquals("Unexpected operations", pattern.length, diff.size());
+        for (int i = 0; i < pattern.length; i++) {
+            final ContentProviderOperation expected = pattern[i];
+            final ContentProviderOperation found = diff.get(i);
+
+            final String expectedType = getStringForType(expected.getType());
+            final String foundType = getStringForType(found.getType());
+
+            assertEquals("Unexpected type", expectedType, foundType);
+            assertEquals("Unexpected uri", expected.getUri(), found.getUri());
+        }
+    }
+
+    protected static String getStringForType(int type) {
+        switch (type) {
+            case TYPE_ASSERT: return "TYPE_ASSERT";
+            case TYPE_INSERT: return "TYPE_INSERT";
+            case TYPE_UPDATE: return "TYPE_UPDATE";
+            case TYPE_DELETE: return "TYPE_DELETE";
+            default: return Integer.toString(type);
+        }
+    }
+
+    protected static ContentProviderOperation buildOper(Uri uri, int type) {
+        final ContentValues values = new ContentValues();
+        values.put(BaseColumns._ID, 4);
+        switch (type) {
+            case TYPE_ASSERT:
+                return ContentProviderOperation.newAssertQuery(uri).withValues(values).build();
+            case TYPE_INSERT:
+                return ContentProviderOperation.newInsert(uri).withValues(values).build();
+            case TYPE_UPDATE:
+                return ContentProviderOperation.newUpdate(uri).withValues(values).build();
+            case TYPE_DELETE:
+                return ContentProviderOperation.newDelete(uri).build();
+        }
+        return null;
+    }
+
+    protected static Long getVersion(EntitySet set, Long rawContactId) {
+        return set.getByRawContactId(rawContactId).getValues().getAsLong(RawContacts.VERSION);
+    }
+
     /**
      * Count number of {@link AggregationExceptions} updates contained in the
      * given list of {@link ContentProviderOperation}.
      */
-    protected int countExceptionUpdates(ArrayList<ContentProviderOperation> diff) {
+    protected static int countExceptionUpdates(ArrayList<ContentProviderOperation> diff) {
         int updateCount = 0;
         for (ContentProviderOperation oper : diff) {
             if (AggregationExceptions.CONTENT_URI.equals(oper.getUri())
@@ -90,7 +203,7 @@
 
     public void testInsert() {
         final EntityDelta insert = getInsert();
-        final EntitySet set = setFrom(insert);
+        final EntitySet set = buildSet(insert);
 
         // Inserting single shouldn't create rules
         final ArrayList<ContentProviderOperation> diff = set.buildDiff();
@@ -101,7 +214,7 @@
     public void testUpdateUpdate() {
         final EntityDelta updateFirst = getUpdate(CONTACT_FIRST);
         final EntityDelta updateSecond = getUpdate(CONTACT_SECOND);
-        final EntitySet set = setFrom(updateFirst, updateSecond);
+        final EntitySet set = buildSet(updateFirst, updateSecond);
 
         // Updating two existing shouldn't create rules
         final ArrayList<ContentProviderOperation> diff = set.buildDiff();
@@ -112,7 +225,7 @@
     public void testUpdateInsert() {
         final EntityDelta update = getUpdate(CONTACT_FIRST);
         final EntityDelta insert = getInsert();
-        final EntitySet set = setFrom(update, insert);
+        final EntitySet set = buildSet(update, insert);
 
         // New insert should only create one rule
         final ArrayList<ContentProviderOperation> diff = set.buildDiff();
@@ -124,7 +237,7 @@
         final EntityDelta insertFirst = getInsert();
         final EntityDelta update = getUpdate(CONTACT_FIRST);
         final EntityDelta insertSecond = getInsert();
-        final EntitySet set = setFrom(insertFirst, update, insertSecond);
+        final EntitySet set = buildSet(insertFirst, update, insertSecond);
 
         // Two inserts should create two rules to bind against single existing
         final ArrayList<ContentProviderOperation> diff = set.buildDiff();
@@ -136,11 +249,189 @@
         final EntityDelta insertFirst = getInsert();
         final EntityDelta insertSecond = getInsert();
         final EntityDelta insertThird = getInsert();
-        final EntitySet set = setFrom(insertFirst, insertSecond, insertThird);
+        final EntitySet set = buildSet(insertFirst, insertSecond, insertThird);
 
         // Three new inserts should create only two binding rules
         final ArrayList<ContentProviderOperation> diff = set.buildDiff();
         final int exceptionCount = countExceptionUpdates(diff);
         assertEquals("Unexpected exception updates", 2, exceptionCount);
     }
+
+    public void testMergeDataRemoteInsert() {
+        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)));
+        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+                buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
+
+        // Merge in second version, verify they match
+        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        assertEquals("Unexpected change when merging", second, merged);
+    }
+
+    public void testMergeDataLocalUpdateRemoteInsert() {
+        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)));
+        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+                buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
+
+        // Change the local number to trigger update
+        getPhone(first, CONTACT_BOB, PHONE_RED).put(Phone.NUMBER, "555-1212");
+        assertDiffPattern(first,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE),
+                buildOper(Data.CONTENT_URI, TYPE_UPDATE),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE));
+
+        // Merge in the second version, verify diff matches
+        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        assertDiffPattern(merged,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE),
+                buildOper(Data.CONTENT_URI, TYPE_UPDATE),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE));
+    }
+
+    public void testMergeDataLocalUpdateRemoteDelete() {
+        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)));
+        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND, buildPhone(PHONE_GREEN)));
+
+        // Change the local number to trigger update
+        getPhone(first, CONTACT_BOB, PHONE_RED).put(Phone.NUMBER, "555-1212");
+        assertDiffPattern(first,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE),
+                buildOper(Data.CONTENT_URI, TYPE_UPDATE),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE));
+
+        // Merge in the second version, verify that our update changed to
+        // insert, since RED was deleted on remote side
+        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        assertDiffPattern(merged,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE),
+                buildOper(Data.CONTENT_URI, TYPE_INSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE));
+    }
+
+    public void testMergeDataLocalDeleteRemoteUpdate() {
+        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)));
+        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND, buildPhone(
+                PHONE_RED, "555-1212")));
+
+        // Delete phone locally
+        getPhone(first, CONTACT_BOB, PHONE_RED).markDeleted();
+        assertDiffPattern(first,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE),
+                buildOper(Data.CONTENT_URI, TYPE_DELETE),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE));
+
+        // Merge in the second version, verify that our delete remains
+        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        assertDiffPattern(merged,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE),
+                buildOper(Data.CONTENT_URI, TYPE_DELETE),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE));
+    }
+
+    public void testMergeDataLocalInsertRemoteInsert() {
+        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)));
+        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND,
+                buildPhone(PHONE_RED), buildPhone(PHONE_GREEN)));
+
+        // Insert new phone locally
+        final ValuesDelta bluePhone = ValuesDelta.fromAfter(buildPhone(PHONE_BLUE));
+        first.getByRawContactId(CONTACT_BOB).addEntry(bluePhone);
+        assertDiffPattern(first,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE),
+                buildOper(Data.CONTENT_URI, TYPE_INSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE));
+
+        // Merge in the second version, verify that our insert remains
+        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        assertDiffPattern(merged,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE),
+                buildOper(Data.CONTENT_URI, TYPE_INSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE));
+    }
+
+    public void testMergeRawContactLocalInsertRemoteInsert() {
+        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)));
+        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)),
+                buildBeforeEntity(CONTACT_MARY, VER_SECOND, buildPhone(PHONE_RED)));
+
+        // Add new contact locally, should remain insert
+        final EntityDelta joeContact = buildAfterEntity(buildPhone(PHONE_BLUE));
+        first.add(joeContact);
+        assertDiffPattern(first,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_INSERT),
+                buildOper(Data.CONTENT_URI, TYPE_INSERT),
+                buildOper(AggregationExceptions.CONTENT_URI, TYPE_UPDATE));
+
+        // Merge in the second version, verify that our insert remains
+        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        assertDiffPattern(merged,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_INSERT),
+                buildOper(Data.CONTENT_URI, TYPE_INSERT),
+                buildOper(AggregationExceptions.CONTENT_URI, TYPE_UPDATE));
+    }
+
+    public void testMergeRawContactLocalDeleteRemoteDelete() {
+        final EntitySet first = buildSet(
+                buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)),
+                buildBeforeEntity(CONTACT_MARY, VER_SECOND, buildPhone(PHONE_RED)));
+        final EntitySet second = buildSet(
+                buildBeforeEntity(CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
+
+        // Remove contact locally
+        first.getByRawContactId(CONTACT_MARY).markDeleted();
+        assertDiffPattern(first,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_DELETE));
+
+        // Merge in the second version, verify that our delete isn't needed
+        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        assertDiffPattern(merged);
+    }
+
+    public void testMergeRawContactLocalUpdateRemoteDelete() {
+        final EntitySet first = buildSet(
+                buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)),
+                buildBeforeEntity(CONTACT_MARY, VER_SECOND, buildPhone(PHONE_RED)));
+        final EntitySet second = buildSet(
+                buildBeforeEntity(CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
+
+        // Perform local update
+        getPhone(first, CONTACT_MARY, PHONE_RED).put(Phone.NUMBER, "555-1212");
+        assertDiffPattern(first,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE),
+                buildOper(Data.CONTENT_URI, TYPE_UPDATE),
+                buildOper(RawContacts.CONTENT_URI, TYPE_UPDATE));
+
+        // Merge and verify that update turned into insert
+        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        assertDiffPattern(merged,
+                buildOper(RawContacts.CONTENT_URI, TYPE_ASSERT),
+                buildOper(RawContacts.CONTENT_URI, TYPE_INSERT),
+                buildOper(Data.CONTENT_URI, TYPE_INSERT),
+                buildOper(AggregationExceptions.CONTENT_URI, TYPE_UPDATE));
+    }
+
+    public void testMergeUsesNewVersion() {
+        final EntitySet first = buildSet(buildBeforeEntity(CONTACT_BOB, VER_FIRST, buildPhone(PHONE_RED)));
+        final EntitySet second = buildSet(buildBeforeEntity(CONTACT_BOB, VER_SECOND, buildPhone(PHONE_RED)));
+
+        assertEquals((Long)VER_FIRST, getVersion(first, CONTACT_BOB));
+        assertEquals((Long)VER_SECOND, getVersion(second, CONTACT_BOB));
+
+        final EntitySet merged = EntitySet.mergeAfter(second, first);
+        assertEquals((Long)VER_SECOND, getVersion(merged, CONTACT_BOB));
+    }
 }