Merge "Shrink-wrapping model to spec (issue 10397399)" into jb-ub-now-indigo-rose
diff --git a/res/drawable-hdpi/flying_icon_bg_pressed.9.png b/res/drawable-hdpi/flying_icon_bg_pressed.9.png
deleted file mode 100644
index 700fadc..0000000
--- a/res/drawable-hdpi/flying_icon_bg_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-xxhdpi/bg_cling1.png b/res/drawable-land-xxhdpi/bg_cling1.png
new file mode 100644
index 0000000..78943f0
--- /dev/null
+++ b/res/drawable-land-xxhdpi/bg_cling1.png
Binary files differ
diff --git a/res/drawable-land-xxhdpi/bg_cling2.png b/res/drawable-land-xxhdpi/bg_cling2.png
new file mode 100644
index 0000000..98b6568
--- /dev/null
+++ b/res/drawable-land-xxhdpi/bg_cling2.png
Binary files differ
diff --git a/res/drawable-land-xxhdpi/bg_cling3.png b/res/drawable-land-xxhdpi/bg_cling3.png
new file mode 100644
index 0000000..e249fe5
--- /dev/null
+++ b/res/drawable-land-xxhdpi/bg_cling3.png
Binary files differ
diff --git a/res/drawable-land-xxhdpi/ic_home_voice_search_holo.png b/res/drawable-land-xxhdpi/ic_home_voice_search_holo.png
new file mode 100644
index 0000000..6ea7368
--- /dev/null
+++ b/res/drawable-land-xxhdpi/ic_home_voice_search_holo.png
Binary files differ
diff --git a/res/drawable-land-xxhdpi/workspace_bg.9.png b/res/drawable-land-xxhdpi/workspace_bg.9.png
new file mode 100644
index 0000000..b0b4561
--- /dev/null
+++ b/res/drawable-land-xxhdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-mdpi/flying_icon_bg_pressed.9.png b/res/drawable-mdpi/flying_icon_bg_pressed.9.png
deleted file mode 100644
index fb74449..0000000
--- a/res/drawable-mdpi/flying_icon_bg_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.png
new file mode 100644
index 0000000..c661f68
--- /dev/null
+++ b/res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.png
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.png
new file mode 100644
index 0000000..bf6ab97
--- /dev/null
+++ b/res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.png
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/ic_allapps.png b/res/drawable-sw600dp-xxhdpi/ic_allapps.png
new file mode 100644
index 0000000..2429656
--- /dev/null
+++ b/res/drawable-sw600dp-xxhdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.png
new file mode 100644
index 0000000..b93a51b
--- /dev/null
+++ b/res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.png
new file mode 100644
index 0000000..472c3f9
--- /dev/null
+++ b/res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.png
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.png
new file mode 100644
index 0000000..e30cc97
--- /dev/null
+++ b/res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.png
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.png
new file mode 100644
index 0000000..65b2541
--- /dev/null
+++ b/res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.png
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.png
new file mode 100644
index 0000000..5068646
--- /dev/null
+++ b/res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.png
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.png
new file mode 100644
index 0000000..6628425
--- /dev/null
+++ b/res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.png
Binary files differ
diff --git a/res/drawable-xhdpi/flying_icon_bg_pressed.9.png b/res/drawable-xhdpi/flying_icon_bg_pressed.9.png
deleted file mode 100644
index e678927..0000000
--- a/res/drawable-xhdpi/flying_icon_bg_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/arrow_dashed.png b/res/drawable-xxhdpi/arrow_dashed.png
new file mode 100644
index 0000000..b64f4d0
--- /dev/null
+++ b/res/drawable-xxhdpi/arrow_dashed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_cling1.png b/res/drawable-xxhdpi/bg_cling1.png
new file mode 100644
index 0000000..0777856
--- /dev/null
+++ b/res/drawable-xxhdpi/bg_cling1.png
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_cling2.png b/res/drawable-xxhdpi/bg_cling2.png
new file mode 100644
index 0000000..1797a1b
--- /dev/null
+++ b/res/drawable-xxhdpi/bg_cling2.png
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_cling3.png b/res/drawable-xxhdpi/bg_cling3.png
new file mode 100644
index 0000000..a87be63
--- /dev/null
+++ b/res/drawable-xxhdpi/bg_cling3.png
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_cling4.png b/res/drawable-xxhdpi/bg_cling4.png
new file mode 100644
index 0000000..cabe919
--- /dev/null
+++ b/res/drawable-xxhdpi/bg_cling4.png
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_cling_home.png b/res/drawable-xxhdpi/bg_cling_home.png
new file mode 100644
index 0000000..1ae93e7
--- /dev/null
+++ b/res/drawable-xxhdpi/bg_cling_home.png
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_cling_nakasi3.png b/res/drawable-xxhdpi/bg_cling_nakasi3.png
new file mode 100644
index 0000000..f47236c
--- /dev/null
+++ b/res/drawable-xxhdpi/bg_cling_nakasi3.png
Binary files differ
diff --git a/res/drawable-xxhdpi/btn_cling_normal.9.png b/res/drawable-xxhdpi/btn_cling_normal.9.png
new file mode 100644
index 0000000..f5e8032
--- /dev/null
+++ b/res/drawable-xxhdpi/btn_cling_normal.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/btn_cling_pressed.9.png b/res/drawable-xxhdpi/btn_cling_pressed.9.png
new file mode 100644
index 0000000..c507dd7
--- /dev/null
+++ b/res/drawable-xxhdpi/btn_cling_pressed.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/cling.png b/res/drawable-xxhdpi/cling.png
new file mode 100644
index 0000000..9446ea4
--- /dev/null
+++ b/res/drawable-xxhdpi/cling.png
Binary files differ
diff --git a/res/drawable-xxhdpi/default_widget_preview_holo.9.png b/res/drawable-xxhdpi/default_widget_preview_holo.9.png
new file mode 100644
index 0000000..0f62097
--- /dev/null
+++ b/res/drawable-xxhdpi/default_widget_preview_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/grid_focused.9.png b/res/drawable-xxhdpi/grid_focused.9.png
new file mode 100644
index 0000000..a8dff7a
--- /dev/null
+++ b/res/drawable-xxhdpi/grid_focused.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/grid_pressed.9.png b/res/drawable-xxhdpi/grid_pressed.9.png
new file mode 100644
index 0000000..cbc83d1
--- /dev/null
+++ b/res/drawable-xxhdpi/grid_pressed.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/grid_selected.9.png b/res/drawable-xxhdpi/grid_selected.9.png
new file mode 100644
index 0000000..f8cd673
--- /dev/null
+++ b/res/drawable-xxhdpi/grid_selected.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/hand.png b/res/drawable-xxhdpi/hand.png
new file mode 100644
index 0000000..88c2a88
--- /dev/null
+++ b/res/drawable-xxhdpi/hand.png
Binary files differ
diff --git a/res/drawable-xxhdpi/homescreen_blue_normal_holo.9.png b/res/drawable-xxhdpi/homescreen_blue_normal_holo.9.png
new file mode 100644
index 0000000..040748a
--- /dev/null
+++ b/res/drawable-xxhdpi/homescreen_blue_normal_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/homescreen_blue_strong_holo.9.png b/res/drawable-xxhdpi/homescreen_blue_strong_holo.9.png
new file mode 100644
index 0000000..b75b322
--- /dev/null
+++ b/res/drawable-xxhdpi/homescreen_blue_strong_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/hotseat_bg_panel.9.png b/res/drawable-xxhdpi/hotseat_bg_panel.9.png
new file mode 100644
index 0000000..40fc076
--- /dev/null
+++ b/res/drawable-xxhdpi/hotseat_bg_panel.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/hotseat_scrubber_holo.9.png b/res/drawable-xxhdpi/hotseat_scrubber_holo.9.png
new file mode 100644
index 0000000..8a77536
--- /dev/null
+++ b/res/drawable-xxhdpi/hotseat_scrubber_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/hotseat_track_holo.9.png b/res/drawable-xxhdpi/hotseat_track_holo.9.png
new file mode 100644
index 0000000..dd2216f
--- /dev/null
+++ b/res/drawable-xxhdpi/hotseat_track_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_allapps.png b/res/drawable-xxhdpi/ic_allapps.png
new file mode 100644
index 0000000..0cd0f14
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_allapps_pressed.png b/res/drawable-xxhdpi/ic_allapps_pressed.png
new file mode 100644
index 0000000..99494d8
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_home_all_apps_holo_dark.png b/res/drawable-xxhdpi/ic_home_all_apps_holo_dark.png
new file mode 100644
index 0000000..0b8e88c
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_home_all_apps_holo_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_home_google_logo_normal_holo.png b/res/drawable-xxhdpi/ic_home_google_logo_normal_holo.png
new file mode 100644
index 0000000..a47b2ba
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_home_google_logo_normal_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_home_google_logo_pressed_holo.png b/res/drawable-xxhdpi/ic_home_google_logo_pressed_holo.png
new file mode 100644
index 0000000..75625d1
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_home_google_logo_pressed_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_home_search_normal_holo.png b/res/drawable-xxhdpi/ic_home_search_normal_holo.png
new file mode 100644
index 0000000..a9523d3
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_home_search_normal_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_home_search_pressed_holo.png b/res/drawable-xxhdpi/ic_home_search_pressed_holo.png
new file mode 100644
index 0000000..800d994
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_home_search_pressed_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_home_voice_search_holo.png b/res/drawable-xxhdpi/ic_home_voice_search_holo.png
new file mode 100644
index 0000000..c9c0b50
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_home_voice_search_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_home_voice_search_pressed_holo.png b/res/drawable-xxhdpi/ic_home_voice_search_pressed_holo.png
new file mode 100644
index 0000000..27a5897
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_home_voice_search_pressed_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_clear_active_holo.png b/res/drawable-xxhdpi/ic_launcher_clear_active_holo.png
new file mode 100644
index 0000000..95cf841
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_launcher_clear_active_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_clear_normal_holo.png b/res/drawable-xxhdpi/ic_launcher_clear_normal_holo.png
new file mode 100644
index 0000000..b0f5a27
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_launcher_clear_normal_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_info_active_holo.png b/res/drawable-xxhdpi/ic_launcher_info_active_holo.png
new file mode 100644
index 0000000..57f332a
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_launcher_info_active_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_info_normal_holo.png b/res/drawable-xxhdpi/ic_launcher_info_normal_holo.png
new file mode 100644
index 0000000..94f0955
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_launcher_info_normal_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_trashcan_active_holo.png b/res/drawable-xxhdpi/ic_launcher_trashcan_active_holo.png
new file mode 100644
index 0000000..3bb098c
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_launcher_trashcan_active_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_trashcan_normal_holo.png b/res/drawable-xxhdpi/ic_launcher_trashcan_normal_holo.png
new file mode 100644
index 0000000..550cc5b
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_launcher_trashcan_normal_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_qs_remote_display.png b/res/drawable-xxhdpi/ic_qs_remote_display.png
new file mode 100644
index 0000000..25ea9fa
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_qs_remote_display.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_qs_remote_display_connected.png b/res/drawable-xxhdpi/ic_qs_remote_display_connected.png
new file mode 100644
index 0000000..33a8d2d
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_qs_remote_display_connected.png
Binary files differ
diff --git a/res/drawable-xxhdpi/overscroll_glow_left.9.png b/res/drawable-xxhdpi/overscroll_glow_left.9.png
new file mode 100644
index 0000000..ae20624
--- /dev/null
+++ b/res/drawable-xxhdpi/overscroll_glow_left.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/overscroll_glow_right.9.png b/res/drawable-xxhdpi/overscroll_glow_right.9.png
new file mode 100644
index 0000000..b083720
--- /dev/null
+++ b/res/drawable-xxhdpi/overscroll_glow_right.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left_holo.9.png b/res/drawable-xxhdpi/page_hover_left_holo.9.png
new file mode 100644
index 0000000..cfe5a53
--- /dev/null
+++ b/res/drawable-xxhdpi/page_hover_left_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right_holo.9.png b/res/drawable-xxhdpi/page_hover_right_holo.9.png
new file mode 100644
index 0000000..481e791
--- /dev/null
+++ b/res/drawable-xxhdpi/page_hover_right_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_container_holo.9.png b/res/drawable-xxhdpi/portal_container_holo.9.png
new file mode 100644
index 0000000..2a28f4a
--- /dev/null
+++ b/res/drawable-xxhdpi/portal_container_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_ring_inner_holo.png b/res/drawable-xxhdpi/portal_ring_inner_holo.png
new file mode 100644
index 0000000..3b93f83
--- /dev/null
+++ b/res/drawable-xxhdpi/portal_ring_inner_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_ring_inner_nolip_holo.png b/res/drawable-xxhdpi/portal_ring_inner_nolip_holo.png
new file mode 100644
index 0000000..01f330c
--- /dev/null
+++ b/res/drawable-xxhdpi/portal_ring_inner_nolip_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_ring_outer_holo.png b/res/drawable-xxhdpi/portal_ring_outer_holo.png
new file mode 100644
index 0000000..281e9b5
--- /dev/null
+++ b/res/drawable-xxhdpi/portal_ring_outer_holo.png
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_ring_rest.png b/res/drawable-xxhdpi/portal_ring_rest.png
new file mode 100644
index 0000000..947d7db
--- /dev/null
+++ b/res/drawable-xxhdpi/portal_ring_rest.png
Binary files differ
diff --git a/res/drawable-xxhdpi/search_bg_panel.9.png b/res/drawable-xxhdpi/search_bg_panel.9.png
new file mode 100644
index 0000000..85cae17
--- /dev/null
+++ b/res/drawable-xxhdpi/search_bg_panel.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/search_frame.9.png b/res/drawable-xxhdpi/search_frame.9.png
new file mode 100644
index 0000000..f297bf1
--- /dev/null
+++ b/res/drawable-xxhdpi/search_frame.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected_focused_holo.9.png b/res/drawable-xxhdpi/tab_selected_focused_holo.9.png
new file mode 100644
index 0000000..2400c65
--- /dev/null
+++ b/res/drawable-xxhdpi/tab_selected_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected_holo.9.png b/res/drawable-xxhdpi/tab_selected_holo.9.png
new file mode 100644
index 0000000..5067cbb
--- /dev/null
+++ b/res/drawable-xxhdpi/tab_selected_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_selected_pressed_holo.9.png b/res/drawable-xxhdpi/tab_selected_pressed_holo.9.png
new file mode 100644
index 0000000..84c246d
--- /dev/null
+++ b/res/drawable-xxhdpi/tab_selected_pressed_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected_focused_holo.9.png b/res/drawable-xxhdpi/tab_unselected_focused_holo.9.png
new file mode 100644
index 0000000..939e0c3
--- /dev/null
+++ b/res/drawable-xxhdpi/tab_unselected_focused_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected_holo.9.png b/res/drawable-xxhdpi/tab_unselected_holo.9.png
new file mode 100644
index 0000000..62ca6cf
--- /dev/null
+++ b/res/drawable-xxhdpi/tab_unselected_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/tab_unselected_pressed_holo.9.png b/res/drawable-xxhdpi/tab_unselected_pressed_holo.9.png
new file mode 100644
index 0000000..58ac0d6
--- /dev/null
+++ b/res/drawable-xxhdpi/tab_unselected_pressed_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_container_holo.9.png b/res/drawable-xxhdpi/widget_container_holo.9.png
new file mode 100644
index 0000000..8f79920
--- /dev/null
+++ b/res/drawable-xxhdpi/widget_container_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_frame_holo.9.png b/res/drawable-xxhdpi/widget_resize_frame_holo.9.png
new file mode 100644
index 0000000..a38e7d5
--- /dev/null
+++ b/res/drawable-xxhdpi/widget_resize_frame_holo.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_handle_bottom.png b/res/drawable-xxhdpi/widget_resize_handle_bottom.png
new file mode 100644
index 0000000..a241c59
--- /dev/null
+++ b/res/drawable-xxhdpi/widget_resize_handle_bottom.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_handle_left.png b/res/drawable-xxhdpi/widget_resize_handle_left.png
new file mode 100644
index 0000000..24046ea
--- /dev/null
+++ b/res/drawable-xxhdpi/widget_resize_handle_left.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_handle_right.png b/res/drawable-xxhdpi/widget_resize_handle_right.png
new file mode 100644
index 0000000..968ffe2
--- /dev/null
+++ b/res/drawable-xxhdpi/widget_resize_handle_right.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_handle_top.png b/res/drawable-xxhdpi/widget_resize_handle_top.png
new file mode 100644
index 0000000..a2bab29
--- /dev/null
+++ b/res/drawable-xxhdpi/widget_resize_handle_top.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_tile_jb.png b/res/drawable-xxhdpi/widget_tile_jb.png
new file mode 100644
index 0000000..363fe84
--- /dev/null
+++ b/res/drawable-xxhdpi/widget_tile_jb.png
Binary files differ
diff --git a/res/drawable-xxhdpi/workspace_bg.9.png b/res/drawable-xxhdpi/workspace_bg.9.png
new file mode 100644
index 0000000..efc9b04
--- /dev/null
+++ b/res/drawable-xxhdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable/flying_icon_bg.xml b/res/drawable/flying_icon_bg.xml
deleted file mode 100644
index 167c3ba..0000000
--- a/res/drawable/flying_icon_bg.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:drawable="@drawable/flying_icon_bg_pressed" />
-    <item android:drawable="@android:color/transparent" />
-</selector>
diff --git a/res/drawable/wallpaper_gallery_item.xml b/res/drawable/wallpaper_gallery_item.xml
index b7052bd..9ac931b 100644
--- a/res/drawable/wallpaper_gallery_item.xml
+++ b/res/drawable/wallpaper_gallery_item.xml
@@ -15,6 +15,7 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true" android:drawable="@drawable/grid_pressed" />
     <item android:state_focused="true" android:drawable="@drawable/grid_focused" />
     <item android:state_pressed="true" android:drawable="@drawable/grid_pressed" />
     <item android:state_selected="true" android:drawable="@drawable/grid_selected" />
diff --git a/res/layout/wallpaper_picker_gallery_item.xml b/res/layout/wallpaper_picker_gallery_item.xml
index b42b98a..7f30f80 100644
--- a/res/layout/wallpaper_picker_gallery_item.xml
+++ b/res/layout/wallpaper_picker_gallery_item.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.CheckableFrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="@dimen/wallpaperThumbnailWidth"
     android:layout_height="@dimen/wallpaperThumbnailHeight"
     android:focusable="true"
@@ -33,4 +34,4 @@
         android:layout_height="wrap_content"
         android:layout_gravity="center"
         android:textColor="#FFFFFFFF"/>
-</FrameLayout>
+</com.android.launcher3.CheckableFrameLayout>
diff --git a/res/layout/wallpaper_picker_item.xml b/res/layout/wallpaper_picker_item.xml
index c73bd73..06f01c9 100644
--- a/res/layout/wallpaper_picker_item.xml
+++ b/res/layout/wallpaper_picker_item.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.CheckableFrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="@dimen/wallpaperThumbnailWidth"
     android:layout_height="@dimen/wallpaperThumbnailHeight"
     android:focusable="true"
@@ -26,4 +27,4 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:scaleType="fitXY" />
-</FrameLayout>
+</com.android.launcher3.CheckableFrameLayout>
diff --git a/res/menu/cab_delete_wallpapers.xml b/res/menu/cab_delete_wallpapers.xml
new file mode 100644
index 0000000..38ac5c4
--- /dev/null
+++ b/res/menu/cab_delete_wallpapers.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+  <item
+     android:id="@+id/menu_delete"
+     android:title="@string/wallpaper_delete"
+     android:showAsAction="always"
+     android:icon="@android:drawable/ic_menu_delete" />
+</menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index aeae48b..bd803c6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -30,6 +30,17 @@
     <string name="folder_name"></string>
     <!-- Button label on Wallpaper picker screen; user selects this button to set a specific wallpaper -->
     <string name="wallpaper_instructions">Set wallpaper</string>
+    <!-- Shown when wallpapers are selected in Wallpaper picker -->
+    <!-- String indicating how many media item(s) is(are) selected
+            eg. 1 selected [CHAR LIMIT=30] -->
+    <plurals name="number_of_items_selected">
+        <item quantity="zero">%1$d selected</item>
+        <item quantity="one">%1$d selected</item>
+        <item quantity="other">%1$d selected</item>
+    </plurals>
+
+    <!-- Label on button to delete wallpaper(s) -->
+    <string name="wallpaper_delete">Delete</string>
     <!-- Label on button in Wallpaper Picker that launches Gallery app -->
     <string name="gallery">Gallery</string>
     <!-- Option in "Select wallpaper from" dialog box -->
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 682c2ed..04f4d81 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -64,6 +64,8 @@
     private Launcher mLauncher;
     private int mCellWidth;
     private int mCellHeight;
+    private int mFixedCellWidth;
+    private int mFixedCellHeight;
 
     private int mCountX;
     private int mCountY;
@@ -193,8 +195,8 @@
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
 
-        mCellWidth = -1;
-        mCellHeight = -1;
+        mCellWidth = mCellHeight = -1;
+        mFixedCellHeight = mFixedCellHeight = -1;
         mWidthGap = mOriginalWidthGap = 0;
         mHeightGap = mOriginalHeightGap = 0;
         mMaxGap = Integer.MAX_VALUE;
@@ -310,8 +312,8 @@
     }
 
     public void setCellDimensions(int width, int height) {
-        mCellWidth = width;
-        mCellHeight = height;
+        mFixedCellWidth = mCellWidth = width;
+        mFixedCellHeight = mCellHeight = height;
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
                 mCountX, mCountY);
     }
@@ -947,13 +949,15 @@
         int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
         int heightSize =  MeasureSpec.getSize(heightMeasureSpec);
-        int cw = grid.calculateCellWidth(widthSize, mCountX);
-        int ch = grid.calculateCellHeight(heightSize, mCountY);
-        if (cw != mCellWidth || ch != mCellHeight) {
-            mCellWidth = cw;
-            mCellHeight = ch;
-            mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap,
-                    mHeightGap, mCountX, mCountY);
+        if (mFixedCellWidth < 0 || mFixedCellHeight < 0) {
+            int cw = grid.calculateCellWidth(widthSize, mCountX);
+            int ch = grid.calculateCellHeight(heightSize, mCountY);
+            if (cw != mCellWidth || ch != mCellHeight) {
+                mCellWidth = cw;
+                mCellHeight = ch;
+                mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap,
+                        mHeightGap, mCountX, mCountY);
+            }
         }
 
         int newWidth = widthSize;
diff --git a/src/com/android/launcher3/CheckableFrameLayout.java b/src/com/android/launcher3/CheckableFrameLayout.java
new file mode 100644
index 0000000..5b7d824
--- /dev/null
+++ b/src/com/android/launcher3/CheckableFrameLayout.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.Checkable;
+import android.widget.FrameLayout;
+
+public class CheckableFrameLayout extends FrameLayout implements Checkable {
+    private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
+    boolean mChecked;
+
+    public CheckableFrameLayout(Context context) {
+        super(context);
+    }
+
+    public CheckableFrameLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CheckableFrameLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public boolean isChecked() {
+        return mChecked;
+    }
+
+    public void setChecked(boolean checked) {
+        if (checked != mChecked) {
+            mChecked = checked;
+            refreshDrawableState();
+        }
+    }
+
+    public void toggle() {
+        setChecked(!mChecked);
+    }
+
+    @Override
+    protected int[] onCreateDrawableState(int extraSpace) {
+        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+        if (isChecked()) {
+            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
+        }
+        return drawableState;
+    }
+}
diff --git a/src/com/android/launcher3/CropView.java b/src/com/android/launcher3/CropView.java
index 6d29be2..32c590d 100644
--- a/src/com/android/launcher3/CropView.java
+++ b/src/com/android/launcher3/CropView.java
@@ -140,12 +140,12 @@
     public void onScaleEnd(ScaleGestureDetector detector) {
     }
 
-    public void moveToUpperLeft() {
+    public void moveToLeft() {
         if (getWidth() == 0 || getHeight() == 0) {
             final ViewTreeObserver observer = getViewTreeObserver();
             observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                     public void onGlobalLayout() {
-                        moveToUpperLeft();
+                        moveToLeft();
                         getViewTreeObserver().removeOnGlobalLayoutListener(this);
                     }
                 });
@@ -154,7 +154,6 @@
         getEdgesHelper(edges);
         final float scale = mRenderer.scale;
         mRenderer.centerX += Math.ceil(edges.left / scale);
-        mRenderer.centerY += Math.ceil(edges.top / scale);
     }
 
     public void setTouchEnabled(boolean enabled) {
diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java
index f43af61..70f0000 100644
--- a/src/com/android/launcher3/DynamicGrid.java
+++ b/src/com/android/launcher3/DynamicGrid.java
@@ -223,17 +223,16 @@
         availableWidthPx = awPx;
         availableHeightPx = ahPx;
 
-        if (isLandscape) {
-            allAppsNumRows = (int) numRows - 1;
-        } else {
-            allAppsNumRows = (int) numRows + 1;
-        }
         Rect padding = getWorkspacePadding(isLandscape ?
                 CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
         int pageIndicatorOffset =
             resources.getDimensionPixelSize(R.dimen.apps_customize_page_indicator_offset);
-        allAppsNumRows = (availableHeightPx - pageIndicatorOffset - 4 * edgeMarginPx) /
-                (iconSizePx + iconTextSizePx + 2 * edgeMarginPx);
+        if (isLandscape) {
+            allAppsNumRows = (availableHeightPx - pageIndicatorOffset - 4 * edgeMarginPx) /
+                    (iconSizePx + iconTextSizePx + 2 * edgeMarginPx);
+        } else {
+            allAppsNumRows = (int) numRows + 1;
+        }
         allAppsNumCols = (availableWidthPx - padding.left - padding.right - 2 * edgeMarginPx) /
                 (iconSizePx + 2 * edgeMarginPx);
     }
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index ec78761..fbbb09f 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -69,6 +69,14 @@
     CellLayout getLayout() {
         return mContent;
     }
+
+    /**
+     * Registers the specified listener on the cell layout of the hotseat.
+     */
+    @Override
+    public void setOnLongClickListener(OnLongClickListener l) {
+        mContent.setOnLongClickListener(l);
+    }
   
     private boolean hasVerticalHotseat() {
         return (mIsLandscape && mTransposeLayoutWithOrientation);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index fbc6819..c282890 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1114,6 +1114,7 @@
         mHotseat = (Hotseat) findViewById(R.id.hotseat);
         if (mHotseat != null) {
             mHotseat.setup(this);
+            mHotseat.setOnLongClickListener(this);
         }
 
         mOverviewPanel = findViewById(R.id.overview_panel);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 18e1c85..179c8aa 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -2428,9 +2428,6 @@
         private void loadAllApps() {
             final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
 
-            // "two variables"?
-            // Don't use these two variables in any of the callback runnables.
-            // Otherwise we hold a reference to them.
             final Callbacks oldCallbacks = mCallbacks.get();
             if (oldCallbacks == null) {
                 // This launcher has exited and nobody bothered to tell us.  Just bail.
@@ -2477,7 +2474,6 @@
             }
 
             // Huh? Shouldn't this be inside the Runnable below?
-            final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
             final ArrayList<AppInfo> added = mBgAllAppsList.added;
             mBgAllAppsList.added = new ArrayList<AppInfo>();
 
@@ -2485,6 +2481,7 @@
             mHandler.post(new Runnable() {
                 public void run() {
                     final long bindTime = SystemClock.uptimeMillis();
+                    final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                     if (callbacks != null) {
                         callbacks.bindAllApplications(added);
                         if (DEBUG_LOADERS) {
diff --git a/src/com/android/launcher3/SavedWallpaperImages.java b/src/com/android/launcher3/SavedWallpaperImages.java
new file mode 100644
index 0000000..afeea2d
--- /dev/null
+++ b/src/com/android/launcher3/SavedWallpaperImages.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+
+public class SavedWallpaperImages {
+    private static String TAG = "Launcher3.SavedWallpaperImages";
+    private ImageDb mDb;
+    ArrayList<Integer> mIds;
+    ArrayList<Drawable> mThumbs;
+    Context mContext;
+
+    public SavedWallpaperImages(Context context) {
+        mDb = new ImageDb(context);
+        mContext = context;
+    }
+
+    public void loadThumbnailsAndImageIdList() {
+        mIds = new ArrayList<Integer>();
+        mThumbs = new ArrayList<Drawable>();
+        SQLiteDatabase db = mDb.getReadableDatabase();
+        Cursor result = db.query(ImageDb.TABLE_NAME,
+                new String[] { ImageDb.COLUMN_ID,
+                    ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME }, // cols to return
+                null, // select query
+                null, // args to select query
+                null,
+                null,
+                null,
+                null);
+
+        while (result.moveToNext()) {
+            String filename = result.getString(1);
+            File file = new File(mContext.getFilesDir(), filename);
+            Bitmap thumb = BitmapFactory.decodeFile(file.getAbsolutePath());
+            if (thumb != null) {
+                mIds.add(result.getInt(0));
+                mThumbs.add(new BitmapDrawable(thumb));
+            }
+        }
+        result.close();
+    }
+
+    public ArrayList<Drawable> getThumbnails() {
+        return mThumbs;
+    }
+
+    public ArrayList<Integer> getImageIds() {
+        return mIds;
+    }
+
+    public String getImageFilename(int id) {
+        Pair<String, String> filenames = getImageFilenames(id);
+        if (filenames != null) {
+            return filenames.second;
+        }
+        return null;
+    }
+
+    private Pair<String, String> getImageFilenames(int id) {
+        SQLiteDatabase db = mDb.getReadableDatabase();
+        Cursor result = db.query(ImageDb.TABLE_NAME,
+                new String[] { ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME,
+                    ImageDb.COLUMN_IMAGE_FILENAME }, // cols to return
+                ImageDb.COLUMN_ID + " = ?", // select query
+                new String[] { Integer.toString(id) }, // args to select query
+                null,
+                null,
+                null,
+                null);
+        if (result.getCount() > 0) {
+            result.moveToFirst();
+            String thumbFilename = result.getString(0);
+            String imageFilename = result.getString(1);
+            result.close();
+            return new Pair<String, String>(thumbFilename, imageFilename);
+        } else {
+            return null;
+        }
+    }
+
+    public void deleteImage(int id) {
+        Pair<String, String> filenames = getImageFilenames(id);
+        File imageFile = new File(mContext.getFilesDir(), filenames.first);
+        imageFile.delete();
+        File thumbFile = new File(mContext.getFilesDir(), filenames.second);
+        thumbFile.delete();
+        SQLiteDatabase db = mDb.getWritableDatabase();
+        db.delete(ImageDb.TABLE_NAME,
+                ImageDb.COLUMN_ID + " = ?", // SELECT query
+                new String[] {
+                    Integer.toString(id) // args to SELECT query
+                });
+    }
+
+    public void writeImage(Bitmap thumbnail, byte[] imageBytes) {
+        try {
+            File imageFile = File.createTempFile("wallpaper", "", mContext.getFilesDir());
+            FileOutputStream imageFileStream =
+                    mContext.openFileOutput(imageFile.getName(), Context.MODE_PRIVATE);
+            imageFileStream.write(imageBytes);
+            imageFileStream.close();
+
+            ByteArrayOutputStream stream = new ByteArrayOutputStream();
+            thumbnail.compress(Bitmap.CompressFormat.JPEG, 95, stream);
+            File thumbFile = File.createTempFile("wallpaperthumb", "", mContext.getFilesDir());
+            FileOutputStream thumbFileStream =
+                    mContext.openFileOutput(thumbFile.getName(), Context.MODE_PRIVATE);
+            thumbFileStream.write(stream.toByteArray());
+
+            SQLiteDatabase db = mDb.getWritableDatabase();
+            ContentValues values = new ContentValues();
+            values.put(ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME, thumbFile.getName());
+            values.put(ImageDb.COLUMN_IMAGE_FILENAME, imageFile.getName());
+            db.insert(ImageDb.TABLE_NAME, null, values);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed writing images to storage " + e);
+        }
+    }
+
+    static class ImageDb extends SQLiteOpenHelper {
+        final static int DB_VERSION = 1;
+        final static String DB_NAME = "saved_wallpaper_images.db";
+        final static String TABLE_NAME = "saved_wallpaper_images";
+        final static String COLUMN_ID = "id";
+        final static String COLUMN_IMAGE_THUMBNAIL_FILENAME = "image_thumbnail";
+        final static String COLUMN_IMAGE_FILENAME = "image";
+
+        Context mContext;
+
+        public ImageDb(Context context) {
+            super(context, new File(context.getCacheDir(), DB_NAME).getPath(), null, DB_VERSION);
+            // Store the context for later use
+            mContext = context;
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase database) {
+            database.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
+                    COLUMN_ID + " INTEGER NOT NULL, " +
+                    COLUMN_IMAGE_THUMBNAIL_FILENAME + " TEXT NOT NULL, " +
+                    COLUMN_IMAGE_FILENAME + " TEXT NOT NULL, " +
+                    "PRIMARY KEY (" + COLUMN_ID + " ASC) " +
+                    ");");
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            if (oldVersion != newVersion) {
+                // Delete all the records; they'll be repopulated as this is a cache
+                db.execSQL("DELETE FROM " + TABLE_NAME);
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/WallpaperCropActivity.java b/src/com/android/launcher3/WallpaperCropActivity.java
index 6832b0c..dee9fe9 100644
--- a/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/src/com/android/launcher3/WallpaperCropActivity.java
@@ -95,7 +95,7 @@
                     @Override
                     public void onClick(View v) {
                         boolean finishActivityWhenDone = true;
-                        cropImageAndSetWallpaper(imageUri, finishActivityWhenDone);
+                        cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
                     }
                 });
     }
@@ -155,15 +155,34 @@
         return new Point(defaultWidth, defaultHeight);
     }
 
-    protected void cropImageAndSetWallpaper(Resources res,
-            int resId, final boolean finishActivityWhenDone) {
+    protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) {
+
+        BitmapCropTask cropTask = new BitmapCropTask(this,
+                filePath, null, 0, 0, true, false, null);
+        final Point bounds = cropTask.getImageBounds();
+        Runnable onEndCrop = new Runnable() {
+            public void run() {
+                updateWallpaperDimensions(bounds.x, bounds.y);
+                if (finishActivityWhenDone) {
+                    setResult(Activity.RESULT_OK);
+                    finish();
+                }
+            }
+        };
+        cropTask.setOnEndRunnable(onEndCrop);
+        cropTask.setNoCrop(true);
+        cropTask.execute();
+    }
+
+    protected void cropImageAndSetWallpaper(
+            Resources res, int resId, final boolean finishActivityWhenDone) {
         // crop this image and scale it down to the default wallpaper size for
         // this device
         Point inSize = mCropView.getSourceDimensions();
         Point outSize = getDefaultWallpaperSize(getResources(),
                 getWindowManager());
         RectF crop = getMaxCropRect(
-                inSize.x, inSize.y, outSize.x, outSize.y);
+                inSize.x, inSize.y, outSize.x, outSize.y, false);
         Runnable onEndCrop = new Runnable() {
             public void run() {
                 // Passing 0, 0 will cause launcher to revert to using the
@@ -181,7 +200,8 @@
         cropTask.execute();
     }
 
-    protected void cropImageAndSetWallpaper(Uri uri, final boolean finishActivityWhenDone) {
+    protected void cropImageAndSetWallpaper(Uri uri,
+            OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) {
      // Get the crop
         Point inSize = mCropView.getSourceDimensions();
 
@@ -258,11 +278,21 @@
         };
         BitmapCropTask cropTask = new BitmapCropTask(uri,
                 cropRect, outWidth, outHeight, true, false, onEndCrop);
+        if (onBitmapCroppedHandler != null) {
+            cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
+        }
         cropTask.execute();
     }
 
+    public interface OnBitmapCroppedHandler {
+        public void onBitmapCropped(byte[] imageBytes);
+    }
+
     protected class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
         Uri mInUri = null;
+        Context mContext;
+        String mInFilePath;
+        byte[] mInImageBytes;
         int mInResId = 0;
         InputStream mInStream;
         RectF mCropBounds = null;
@@ -275,44 +305,81 @@
         Bitmap mCroppedBitmap;
         Runnable mOnEndRunnable;
         Resources mResources;
+        OnBitmapCroppedHandler mOnBitmapCroppedHandler;
+        boolean mNoCrop;
+
+        public BitmapCropTask(Context c, String filePath,
+                RectF cropBounds, int outWidth, int outHeight,
+                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
+            mContext = c;
+            mInFilePath = filePath;
+            mWPManager = WallpaperManager.getInstance(getApplicationContext());
+            init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
+        }
+
+        public BitmapCropTask(byte[] imageBytes,
+                RectF cropBounds, int outWidth, int outHeight,
+                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
+            mInImageBytes = imageBytes;
+            mWPManager = WallpaperManager.getInstance(getApplicationContext());
+            init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
+        }
 
         public BitmapCropTask(Uri inUri,
                 RectF cropBounds, int outWidth, int outHeight,
                 boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
             mInUri = inUri;
-            mCropBounds = cropBounds;
-            mOutWidth = outWidth;
-            mOutHeight = outHeight;
             mWPManager = WallpaperManager.getInstance(getApplicationContext());
-            mSetWallpaper = setWallpaper;
-            mSaveCroppedBitmap = saveCroppedBitmap;
-            mOnEndRunnable = onEndRunnable;
+            init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
         }
 
         public BitmapCropTask(Resources res, int inResId,
                 RectF cropBounds, int outWidth, int outHeight,
                 boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
             mInResId = inResId;
+            mResources = res;
+            mWPManager = WallpaperManager.getInstance(getApplicationContext());
+            init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
+        }
+
+        private void init(RectF cropBounds, int outWidth, int outHeight,
+                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
             mCropBounds = cropBounds;
             mOutWidth = outWidth;
             mOutHeight = outHeight;
-            mWPManager = WallpaperManager.getInstance(getApplicationContext());
             mSetWallpaper = setWallpaper;
             mSaveCroppedBitmap = saveCroppedBitmap;
             mOnEndRunnable = onEndRunnable;
-            mResources = res;
+        }
+
+        public void setOnBitmapCropped(OnBitmapCroppedHandler handler) {
+            mOnBitmapCroppedHandler = handler;
+        }
+
+        public void setNoCrop(boolean value) {
+            mNoCrop = value;
+        }
+
+        public void setOnEndRunnable(Runnable onEndRunnable) {
+            mOnEndRunnable = onEndRunnable;
         }
 
         // Helper to setup input stream
         private void regenerateInputStream() {
-            if (mInUri == null && mInResId == 0) {
-                Log.w(LOGTAG, "cannot read original file, no input URI or resource ID given");
+            if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
+                Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
+                        "image byte array given");
             } else {
                 Utils.closeSilently(mInStream);
                 try {
                     if (mInUri != null) {
                         mInStream = new BufferedInputStream(
                                 getContentResolver().openInputStream(mInUri));
+                    } else if (mInFilePath != null) {
+                        mInStream = mContext.openFileInput(mInFilePath);
+                    } else if (mInImageBytes != null) {
+                        mInStream = new BufferedInputStream(
+                                new ByteArrayInputStream(mInImageBytes));
                     } else {
                         mInStream = new BufferedInputStream(
                                 mResources.openRawResource(mInResId));
@@ -348,6 +415,18 @@
 
             regenerateInputStream();
 
+            if (mNoCrop && mInStream != null) {
+                try {
+                    mWPManager.setStream(mInStream);
+                } catch (IOException e) {
+                    Log.w(LOGTAG, "cannot write stream to wallpaper", e);
+                    failure = true;
+                }
+                if (mOnEndRunnable != null) {
+                    mOnEndRunnable.run();
+                }
+                return !failure;
+            }
             if (mInStream != null) {
                 // Find crop bounds (scaled to original image size)
                 Rect roundedTrueCrop = new Rect();
@@ -450,8 +529,11 @@
                             failure = true;
                         } else {
                             try {
-                                mWPManager.setStream(new ByteArrayInputStream(tmpOut
-                                        .toByteArray()));
+                                byte[] outByteArray = tmpOut.toByteArray();
+                                mWPManager.setStream(new ByteArrayInputStream(outByteArray));
+                                if (mOnBitmapCroppedHandler != null) {
+                                    mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
+                                }
                             } catch (IOException e) {
                                 Log.w(LOGTAG, "cannot write stream to wallpaper", e);
                                 failure = true;
@@ -516,7 +598,8 @@
     }
 
 
-    protected static RectF getMaxCropRect(int inWidth, int inHeight, int outWidth, int outHeight) {
+    protected static RectF getMaxCropRect(
+            int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) {
         RectF cropRect = new RectF();
         // Get a crop rect that will fit this
         if (inWidth / (float) inHeight > outWidth / (float) outHeight) {
@@ -524,6 +607,10 @@
              cropRect.bottom = inHeight;
              cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2;
              cropRect.right = inWidth - cropRect.left;
+             if (leftAligned) {
+                 cropRect.right -= cropRect.left;
+                 cropRect.left = 0;
+             }
         } else {
             cropRect.left = 0;
             cropRect.right = inWidth;
diff --git a/src/com/android/launcher3/WallpaperPickerActivity.java b/src/com/android/launcher3/WallpaperPickerActivity.java
index ec0106f..fe7525a 100644
--- a/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.animation.LayoutTransition;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.content.ComponentName;
@@ -25,16 +26,20 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LevelListDrawable;
 import android.net.Uri;
+import android.os.Bundle;
 import android.util.Log;
 import android.util.Pair;
+import android.view.ActionMode;
 import android.view.LayoutInflater;
 import android.view.Menu;
+import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.SubMenu;
 import android.view.View;
@@ -51,26 +56,39 @@
 
 import com.android.photos.BitmapRegionTileSource;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
 public class WallpaperPickerActivity extends WallpaperCropActivity {
-    private static final String TAG = "Launcher.WallpaperPickerActivity";
+    static final String TAG = "Launcher.WallpaperPickerActivity";
 
     private static final int IMAGE_PICK = 5;
     private static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6;
+    private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES";
 
-    private ArrayList<Drawable> mThumbs;
-    private ArrayList<Integer> mImages;
+    private ArrayList<Drawable> mBundledWallpaperThumbs;
+    private ArrayList<Integer> mBundledWallpaperResIds;
     private Resources mWallpaperResources;
 
     private View mSelectedThumb;
     private boolean mIgnoreNextTap;
     private OnClickListener mThumbnailOnClickListener;
 
+    private LinearLayout mWallpapersView;
+
+    private ActionMode.Callback mActionModeCallback;
+    private ActionMode mActionMode;
+
+    private View.OnLongClickListener mLongClickListener;
+
+    ArrayList<Uri> mTempWallpaperTiles = new ArrayList<Uri>();
+    private SavedWallpaperImages mSavedImages;
+
     private static class ThumbnailMetaData {
         public boolean mLaunchesGallery;
-        public Uri mGalleryImageUri;
+        public Uri mWallpaperUri;
+        public int mSavedWallpaperDbId;
         public int mWallpaperResId;
     }
 
@@ -110,6 +128,13 @@
 
         mThumbnailOnClickListener = new OnClickListener() {
             public void onClick(View v) {
+                if (mActionMode != null) {
+                    // When CAB is up, clicking toggles the item instead
+                    if (v.isLongClickable()) {
+                        mLongClickListener.onLongClick(v);
+                    }
+                    return;
+                }
                 if (mSelectedThumb != null) {
                     mSelectedThumb.setSelected(false);
                 }
@@ -126,11 +151,18 @@
                     intent.setType("image/*");
                     Utilities.startActivityForResultSafely(
                             WallpaperPickerActivity.this, intent, IMAGE_PICK);
-                } else if (meta.mGalleryImageUri != null) {
+                } else if (meta.mWallpaperUri != null) {
                     mCropView.setTileSource(new BitmapRegionTileSource(WallpaperPickerActivity.this,
-                            meta.mGalleryImageUri, 1024, 0), null);
+                            meta.mWallpaperUri, 1024, 0), null);
                     mCropView.setTouchEnabled(true);
-                } else {
+                } else if (meta.mSavedWallpaperDbId != 0) {
+                    String imageFilename = mSavedImages.getImageFilename(meta.mSavedWallpaperDbId);
+                    File file = new File(getFilesDir(), imageFilename);
+                    mCropView.setTileSource(new BitmapRegionTileSource(WallpaperPickerActivity.this,
+                            file.getAbsolutePath(), 1024, 0), null);
+                    mCropView.moveToLeft();
+                    mCropView.setTouchEnabled(false);
+                } else if (meta.mWallpaperResId != 0) {
                     BitmapRegionTileSource source = new BitmapRegionTileSource(mWallpaperResources,
                             WallpaperPickerActivity.this, meta.mWallpaperResId, 1024, 0);
                     mCropView.setTileSource(source, null);
@@ -138,45 +170,68 @@
                             getResources(), getWindowManager());
                     RectF crop = WallpaperCropActivity.getMaxCropRect(
                             source.getImageWidth(), source.getImageHeight(),
-                            wallpaperSize.x, wallpaperSize.y);
+                            wallpaperSize.x, wallpaperSize.y, false);
                     mCropView.setScale(wallpaperSize.x / crop.width());
                     mCropView.setTouchEnabled(false);
                 }
             }
         };
+        mLongClickListener = new View.OnLongClickListener() {
+            // Called when the user long-clicks on someView
+            public boolean onLongClick(View view) {
+                CheckableFrameLayout c = (CheckableFrameLayout) view;
+                c.toggle();
+
+                if (mActionMode != null) {
+                    mActionMode.invalidate();
+                } else {
+                    // Start the CAB using the ActionMode.Callback defined below
+                    mActionMode = startActionMode(mActionModeCallback);
+                    int childCount = mWallpapersView.getChildCount();
+                    for (int i = 0; i < childCount; i++) {
+                        mWallpapersView.getChildAt(i).setSelected(false);
+                    }
+                }
+                return true;
+            }
+        };
 
         // Populate the built-in wallpapers
-        findWallpapers();
+        findBundledWallpapers();
+        mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list);
+        ImageAdapter ia = new ImageAdapter(this, mBundledWallpaperThumbs);
+        populateWallpapersFromAdapter(mWallpapersView, ia, mBundledWallpaperResIds, true, false);
 
-        LinearLayout wallpapers = (LinearLayout) findViewById(R.id.wallpaper_list);
-        ImageAdapter ia = new ImageAdapter(this);
-        for (int i = 0; i < ia.getCount(); i++) {
-            FrameLayout thumbnail = (FrameLayout) ia.getView(i, null, wallpapers);
-            wallpapers.addView(thumbnail, i);
+        // Populate the saved wallpapers
+        mSavedImages = new SavedWallpaperImages(this);
+        mSavedImages.loadThumbnailsAndImageIdList();
+        ArrayList<Drawable> savedWallpaperThumbs = mSavedImages.getThumbnails();
+        ArrayList<Integer > savedWallpaperIds = mSavedImages.getImageIds();
+        ia = new ImageAdapter(this, savedWallpaperThumbs);
+        populateWallpapersFromAdapter(mWallpapersView, ia, savedWallpaperIds, false, true);
 
-            ThumbnailMetaData meta = new ThumbnailMetaData();
-            meta.mWallpaperResId = mImages.get(i);
-            thumbnail.setTag(meta);
-            thumbnail.setOnClickListener(mThumbnailOnClickListener);
-            if (i == 0) {
-                mThumbnailOnClickListener.onClick(thumbnail);
-            }
-        }
         // Add a tile for the Gallery
         FrameLayout galleryThumbnail = (FrameLayout) getLayoutInflater().
-                inflate(R.layout.wallpaper_picker_gallery_item, wallpapers, false);
+                inflate(R.layout.wallpaper_picker_gallery_item, mWallpapersView, false);
         setWallpaperItemPaddingToZero(galleryThumbnail);
 
         TextView galleryLabel =
                 (TextView) galleryThumbnail.findViewById(R.id.wallpaper_item_label);
         galleryLabel.setText(R.string.gallery);
-        wallpapers.addView(galleryThumbnail, 0);
+        mWallpapersView.addView(galleryThumbnail, 0);
 
         ThumbnailMetaData meta = new ThumbnailMetaData();
         meta.mLaunchesGallery = true;
         galleryThumbnail.setTag(meta);
         galleryThumbnail.setOnClickListener(mThumbnailOnClickListener);
 
+        // Create smooth layout transitions for when items are deleted
+        final LayoutTransition transitioner = new LayoutTransition();
+        transitioner.setDuration(200);
+        transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
+        transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
+        mWallpapersView.setLayoutTransition(transitioner);
+
         // Action bar
         // Show the custom action bar view
         final ActionBar actionBar = getActionBar();
@@ -188,9 +243,20 @@
                         ThumbnailMetaData meta = (ThumbnailMetaData) mSelectedThumb.getTag();
                         if (meta.mLaunchesGallery) {
                             // shouldn't be selected, but do nothing
-                        } else if (meta.mGalleryImageUri != null) {
+                        } else if (meta.mWallpaperUri != null) {
                             boolean finishActivityWhenDone = true;
-                            cropImageAndSetWallpaper(meta.mGalleryImageUri, finishActivityWhenDone);
+                            OnBitmapCroppedHandler h = new OnBitmapCroppedHandler() {
+                                public void onBitmapCropped(byte[] imageBytes) {
+                                    Bitmap thumb = createThumbnail(null, imageBytes, true);
+                                    mSavedImages.writeImage(thumb, imageBytes);
+                                }
+                            };
+                            cropImageAndSetWallpaper(meta.mWallpaperUri, h, finishActivityWhenDone);
+                        } else if (meta.mSavedWallpaperDbId != 0) {
+                            boolean finishActivityWhenDone = true;
+                            String imageFilename =
+                                    mSavedImages.getImageFilename(meta.mSavedWallpaperDbId);
+                            setWallpaper(imageFilename, finishActivityWhenDone);
                         } else if (meta.mWallpaperResId != 0) {
                             boolean finishActivityWhenDone = true;
                             cropImageAndSetWallpaper(mWallpaperResources,
@@ -198,47 +264,176 @@
                         }
                     }
                 });
+
+        // CAB for deleting items
+        mActionModeCallback = new ActionMode.Callback() {
+            // Called when the action mode is created; startActionMode() was called
+            @Override
+            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+                // Inflate a menu resource providing context menu items
+                MenuInflater inflater = mode.getMenuInflater();
+                inflater.inflate(R.menu.cab_delete_wallpapers, menu);
+                return true;
+            }
+
+            private int numCheckedItems() {
+                int childCount = mWallpapersView.getChildCount();
+                int numCheckedItems = 0;
+                for (int i = 0; i < childCount; i++) {
+                    CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
+                    if (c.isChecked()) {
+                        numCheckedItems++;
+                    }
+                }
+                return numCheckedItems;
+            }
+
+            // Called each time the action mode is shown. Always called after onCreateActionMode,
+            // but may be called multiple times if the mode is invalidated.
+            @Override
+            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+                int numCheckedItems = numCheckedItems();
+                if (numCheckedItems == 0) {
+                    mode.finish();
+                    return true;
+                } else {
+                    mode.setTitle(getResources().getQuantityString(
+                            R.plurals.number_of_items_selected, numCheckedItems, numCheckedItems));
+                    return true;
+                }
+            }
+
+            // Called when the user selects a contextual menu item
+            @Override
+            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+                int itemId = item.getItemId();
+                if (itemId == R.id.menu_delete) {
+                    int childCount = mWallpapersView.getChildCount();
+                    ArrayList<View> viewsToRemove = new ArrayList<View>();
+                    for (int i = 0; i < childCount; i++) {
+                        CheckableFrameLayout c =
+                                (CheckableFrameLayout) mWallpapersView.getChildAt(i);
+                        if (c.isChecked()) {
+                            ThumbnailMetaData meta = (ThumbnailMetaData) c.getTag();
+                            mSavedImages.deleteImage(meta.mSavedWallpaperDbId);
+                            viewsToRemove.add(c);
+                        }
+                    }
+                    for (View v : viewsToRemove) {
+                        mWallpapersView.removeView(v);
+                    }
+                    ///xxxxx DESTROYING
+                    mode.finish(); // Action picked, so close the CAB
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+
+            // Called when the user exits the action mode
+            @Override
+            public void onDestroyActionMode(ActionMode mode) {
+                int childCount = mWallpapersView.getChildCount();
+                for (int i = 0; i < childCount; i++) {
+                    CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
+                    c.setChecked(false);
+                }
+                mSelectedThumb.setSelected(true);
+                mActionMode = null;
+            }
+        };
+    }
+    protected void onSaveInstanceState(Bundle outState) {
+        outState.putParcelableArrayList(TEMP_WALLPAPER_TILES, mTempWallpaperTiles);
+    }
+
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        ArrayList<Uri> uris = savedInstanceState.getParcelableArrayList(TEMP_WALLPAPER_TILES);
+        for (Uri uri : uris) {
+            addTemporaryWallpaperTile(uri);
+        }
+    }
+
+    private void populateWallpapersFromAdapter(ViewGroup parent, ImageAdapter ia,
+            ArrayList<Integer> imageIds, boolean imagesAreResources, boolean addLongPressHandler) {
+        for (int i = 0; i < ia.getCount(); i++) {
+            FrameLayout thumbnail = (FrameLayout) ia.getView(i, null, parent);
+            parent.addView(thumbnail, i);
+
+            ThumbnailMetaData meta = new ThumbnailMetaData();
+            if (imagesAreResources) {
+                meta.mWallpaperResId = imageIds.get(i);
+            } else {
+                meta.mSavedWallpaperDbId = imageIds.get(i);
+            }
+            thumbnail.setTag(meta);
+            if (addLongPressHandler) {
+                addLongPressHandler(thumbnail);
+            }
+            thumbnail.setOnClickListener(mThumbnailOnClickListener);
+            if (i == 0) {
+                mThumbnailOnClickListener.onClick(thumbnail);
+            }
+        }
+    }
+
+    private Bitmap createThumbnail(Uri uri, byte[] imageBytes, boolean leftAligned) {
+        Resources res = getResources();
+        int width = res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth);
+        int height = res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight);
+
+        BitmapCropTask cropTask;
+        if (uri != null) {
+            cropTask = new BitmapCropTask(uri, null, width, height, false, true, null);
+        } else {
+            cropTask = new BitmapCropTask(imageBytes, null, width, height, false, true, null);
+        }
+        Point bounds = cropTask.getImageBounds();
+        if (bounds == null) {
+            return null;
+        }
+
+        RectF cropRect = WallpaperCropActivity.getMaxCropRect(
+                bounds.x, bounds.y, width, height, leftAligned);
+        cropTask.setCropBounds(cropRect);
+
+        if (cropTask.cropBitmap()) {
+            return cropTask.getCroppedBitmap();
+        } else {
+            return null;
+        }
+    }
+
+    private void addTemporaryWallpaperTile(Uri uri) {
+        mTempWallpaperTiles.add(uri);
+        // Add a tile for the image picked from Gallery
+        FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
+                inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
+        setWallpaperItemPaddingToZero(pickedImageThumbnail);
+
+        // Load the thumbnail
+        ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
+        Bitmap thumb = createThumbnail(uri, null, false);
+        if (thumb != null) {
+            image.setImageBitmap(thumb);
+            Drawable thumbDrawable = image.getDrawable();
+            thumbDrawable.setDither(true);
+        } else {
+            Log.e(TAG, "Error loading thumbnail for uri=" + uri);
+        }
+        mWallpapersView.addView(pickedImageThumbnail, 1);
+
+        ThumbnailMetaData meta = new ThumbnailMetaData();
+        meta.mWallpaperUri = uri;
+        pickedImageThumbnail.setTag(meta);
+        pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener);
+        mThumbnailOnClickListener.onClick(pickedImageThumbnail);
     }
 
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == IMAGE_PICK && resultCode == RESULT_OK) {
             Uri uri = data.getData();
-
-            // Add a tile for the image picked from Gallery
-            LinearLayout wallpapers = (LinearLayout) findViewById(R.id.wallpaper_list);
-            FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
-                    inflate(R.layout.wallpaper_picker_item, wallpapers, false);
-            setWallpaperItemPaddingToZero(pickedImageThumbnail);
-
-            // Load the thumbnail
-            ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
-
-            Resources res = getResources();
-            int width = res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth);
-            int height = res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight);
-
-            BitmapCropTask cropTask =
-                    new BitmapCropTask(uri, null, width, height, false, true, null);
-            Point bounds = cropTask.getImageBounds();
-
-            RectF cropRect = WallpaperCropActivity.getMaxCropRect(
-                    bounds.x, bounds.y, width, height);
-            cropTask.setCropBounds(cropRect);
-
-            if (cropTask.cropBitmap()) {
-                image.setImageBitmap(cropTask.getCroppedBitmap());
-                Drawable thumbDrawable = image.getDrawable();
-                thumbDrawable.setDither(true);
-            } else {
-                Log.e(TAG, "Error loading thumbnail for uri=" + uri);
-            }
-            wallpapers.addView(pickedImageThumbnail, 0);
-
-            ThumbnailMetaData meta = new ThumbnailMetaData();
-            meta.mGalleryImageUri = uri;
-            pickedImageThumbnail.setTag(meta);
-            pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener);
-            mThumbnailOnClickListener.onClick(pickedImageThumbnail);
+            addTemporaryWallpaperTile(uri);
         } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY) {
             // No result code is returned; just return
             setResult(RESULT_OK);
@@ -251,6 +446,11 @@
         frameLayout.setForeground(new ZeroPaddingDrawable(frameLayout.getForeground()));
     }
 
+    private void addLongPressHandler(View v) {
+        v.setOnLongClickListener(mLongClickListener);
+    }
+
+
     public boolean onMenuItemSelected(int featureId, MenuItem item) {
         if (item.getIntent() == null) {
             return super.onMenuItemSelected(featureId, item);
@@ -312,9 +512,9 @@
         return super.onCreateOptionsMenu(menu);
     }
 
-    private void findWallpapers() {
-        mThumbs = new ArrayList<Drawable>(24);
-        mImages = new ArrayList<Integer>(24);
+    private void findBundledWallpapers() {
+        mBundledWallpaperThumbs = new ArrayList<Drawable>(24);
+        mBundledWallpaperResIds = new ArrayList<Integer>(24);
 
         Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId();
         if (r != null) {
@@ -349,8 +549,8 @@
                         "drawable", packageName);
 
                 if (thumbRes != 0) {
-                    mThumbs.add(resources.getDrawable(thumbRes));
-                    mImages.add(res);
+                    mBundledWallpaperThumbs.add(resources.getDrawable(thumbRes));
+                    mBundledWallpaperResIds.add(res);
                     // Log.d(TAG, "add: [" + packageName + "]: " + extra + " (" + res + ")");
                 }
             } else {
@@ -373,11 +573,13 @@
         }
     }
 
-    private class ImageAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter {
+    private static class ImageAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter {
         private LayoutInflater mLayoutInflater;
+        private ArrayList<Drawable> mThumbs;
 
-        ImageAdapter(Activity activity) {
+        ImageAdapter(Activity activity, ArrayList<Drawable> thumbs) {
             mLayoutInflater = activity.getLayoutInflater();
+            mThumbs = thumbs;
         }
 
         public int getCount() {