Merge "Revert "Play the video on top of lock screen"" into gb-ub-photos-carlsbad
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c2f93f3..8f48edf 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -42,7 +42,8 @@
             android:logo="@mipmap/ic_launcher_gallery"
             android:hardwareAccelerated="true"
             android:largeHeap="true"
-            android:restoreAnyVersion="true">
+            android:restoreAnyVersion="true"
+            android:supportsRtl="true">
         <uses-library android:name="com.google.android.media.effects" android:required="false" />
         <activity android:name="com.android.gallery3d.app.MovieActivity"
                 android:label="@string/movie_view_label"
@@ -84,7 +85,7 @@
              </intent-filter>
         </activity>
 
-        <activity android:name="com.android.gallery3d.app.Gallery" android:label="@string/app_name"
+        <activity android:name="com.android.gallery3d.app.GalleryActivity" android:label="@string/app_name"
                 android:configChanges="keyboardHidden|orientation|screenSize">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -160,10 +161,21 @@
             </intent-filter>
         </activity>
 
+        <!-- This activity acts as a trampoline to the new Gallery activity,
+             so that existing shortcuts are preserved. -->
+        <activity android:name="com.android.gallery3d.app.Gallery"
+            android:excludeFromRecents="true"
+            android:theme="@style/android:Theme.NoDisplay">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <!-- we add this activity-alias for shortcut backward compatibility -->
         <!-- Note: The alias must put after the target activity -->
         <activity-alias android:name="com.cooliris.media.Gallery"
-                android:targetActivity="com.android.gallery3d.app.Gallery"
+                android:targetActivity="com.android.gallery3d.app.GalleryActivity"
                 android:configChanges="keyboardHidden|orientation|screenSize"
                 android:label="@string/app_name">
             <intent-filter>
@@ -177,6 +189,7 @@
         <activity android:name="com.android.camera.CameraActivity"
             android:icon="@mipmap/ic_launcher_camera"
             android:label="@string/camera_label"
+            android:taskAffinity="com.android.camera.CameraActivity"
             android:theme="@style/android:Theme.NoDisplay" />
         <activity-alias android:name="com.android.camera.CameraLauncher"
             android:label="@string/camera_label"
@@ -264,7 +277,7 @@
         <activity
             android:name="com.android.gallery3d.filtershow.crop.CropActivity"
             android:label="@string/crop"
-            android:theme="@style/Theme.FilterShow"
+            android:theme="@style/Theme.Crop"
             android:configChanges="keyboardHidden|orientation|screenSize">
            <intent-filter android:label="@string/crop_label">
                 <action android:name="com.android.camera.action.CROP" />
@@ -336,6 +349,13 @@
             </intent-filter>
         </activity>
         <service android:name="com.android.gallery3d.app.BatchService" />
+
+        <receiver android:name="com.android.camera.DisableCameraReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>
+
         <service android:name="com.android.camera.MediaSaveService" />
     </application>
 </manifest>
diff --git a/res/drawable-hdpi-v19/grid_pressed.9.png b/res/drawable-hdpi-v19/grid_pressed.9.png
new file mode 100644
index 0000000..506c2c4
--- /dev/null
+++ b/res/drawable-hdpi-v19/grid_pressed.9.png
Binary files differ
diff --git a/res/drawable-hdpi-v19/list_pressed_holo_light.9.png b/res/drawable-hdpi-v19/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..2054530
--- /dev/null
+++ b/res/drawable-hdpi-v19/list_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-hdpi/btn_shutter_recording.png b/res/drawable-hdpi/btn_shutter_recording.png
deleted file mode 100644
index 4a2e452..0000000
--- a/res/drawable-hdpi/btn_shutter_recording.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/btn_shutter_video_recording.png b/res/drawable-hdpi/btn_shutter_video_recording.png
deleted file mode 100644
index e9921d0..0000000
--- a/res/drawable-hdpi/btn_shutter_video_recording.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/capture_thumbnail_shadow.9.png b/res/drawable-hdpi/capture_thumbnail_shadow.9.png
deleted file mode 100644
index f7a664e..0000000
--- a/res/drawable-hdpi/capture_thumbnail_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_action_overflow.png b/res/drawable-hdpi/ic_action_overflow.png
new file mode 100644
index 0000000..18bc4c5
--- /dev/null
+++ b/res/drawable-hdpi/ic_action_overflow.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_imagesize.png b/res/drawable-hdpi/ic_imagesize.png
deleted file mode 100644
index 126208b..0000000
--- a/res/drawable-hdpi/ic_imagesize.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_indicator_ev_n1.png b/res/drawable-hdpi/ic_indicator_ev_n1.png
deleted file mode 100644
index 56c5e60..0000000
--- a/res/drawable-hdpi/ic_indicator_ev_n1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_indicator_ev_n2.png b/res/drawable-hdpi/ic_indicator_ev_n2.png
deleted file mode 100644
index 72838de..0000000
--- a/res/drawable-hdpi/ic_indicator_ev_n2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_indicator_ev_n3.png b/res/drawable-hdpi/ic_indicator_ev_n3.png
deleted file mode 100644
index 1200d63..0000000
--- a/res/drawable-hdpi/ic_indicator_ev_n3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_indicator_ev_p1.png b/res/drawable-hdpi/ic_indicator_ev_p1.png
deleted file mode 100644
index 5497c86..0000000
--- a/res/drawable-hdpi/ic_indicator_ev_p1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_indicator_ev_p2.png b/res/drawable-hdpi/ic_indicator_ev_p2.png
deleted file mode 100644
index 25107fe..0000000
--- a/res/drawable-hdpi/ic_indicator_ev_p2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_indicator_ev_p3.png b/res/drawable-hdpi/ic_indicator_ev_p3.png
deleted file mode 100644
index 6d0c1e0..0000000
--- a/res/drawable-hdpi/ic_indicator_ev_p3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_indicator_flash_on.png b/res/drawable-hdpi/ic_indicator_flash_on.png
deleted file mode 100644
index ec3d1d5..0000000
--- a/res/drawable-hdpi/ic_indicator_flash_on.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_indicator_loc_off.png b/res/drawable-hdpi/ic_indicator_loc_off.png
deleted file mode 100644
index d5937e4..0000000
--- a/res/drawable-hdpi/ic_indicator_loc_off.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_indicator_sce_hdr.png b/res/drawable-hdpi/ic_indicator_sce_hdr.png
deleted file mode 100644
index 0b8fdfa..0000000
--- a/res/drawable-hdpi/ic_indicator_sce_hdr.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_indicator_sce_on.png b/res/drawable-hdpi/ic_indicator_sce_on.png
deleted file mode 100644
index ef0f56f..0000000
--- a/res/drawable-hdpi/ic_indicator_sce_on.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_indicator_timer_on.png b/res/drawable-hdpi/ic_indicator_timer_on.png
deleted file mode 100644
index 8ceade6..0000000
--- a/res/drawable-hdpi/ic_indicator_timer_on.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_settings_holo_light.png b/res/drawable-hdpi/ic_settings_holo_light.png
deleted file mode 100644
index 5d315a3..0000000
--- a/res/drawable-hdpi/ic_settings_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_switch_photosphere.png b/res/drawable-hdpi/ic_switch_photosphere.png
deleted file mode 100644
index ea28eae..0000000
--- a/res/drawable-hdpi/ic_switch_photosphere.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_timer.png b/res/drawable-hdpi/ic_timer.png
deleted file mode 100644
index a3cec8d..0000000
--- a/res/drawable-hdpi/ic_timer.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-hdpi/switcher_bg.9.png b/res/drawable-land-hdpi/switcher_bg.9.png
deleted file mode 100644
index dad08d4..0000000
--- a/res/drawable-land-hdpi/switcher_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-mdpi/switcher_bg.9.png b/res/drawable-land-mdpi/switcher_bg.9.png
deleted file mode 100644
index 2073686..0000000
--- a/res/drawable-land-mdpi/switcher_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-xhdpi/switcher_bg.9.png b/res/drawable-land-xhdpi/switcher_bg.9.png
deleted file mode 100644
index a726dc8..0000000
--- a/res/drawable-land-xhdpi/switcher_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi-v19/grid_pressed.9.png b/res/drawable-mdpi-v19/grid_pressed.9.png
new file mode 100644
index 0000000..388123d
--- /dev/null
+++ b/res/drawable-mdpi-v19/grid_pressed.9.png
Binary files differ
diff --git a/res/drawable-mdpi-v19/list_pressed_holo_light.9.png b/res/drawable-mdpi-v19/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..061904c
--- /dev/null
+++ b/res/drawable-mdpi-v19/list_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-mdpi/btn_shutter_recording.png b/res/drawable-mdpi/btn_shutter_recording.png
deleted file mode 100644
index 4906d70..0000000
--- a/res/drawable-mdpi/btn_shutter_recording.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/btn_shutter_video_recording.png b/res/drawable-mdpi/btn_shutter_video_recording.png
deleted file mode 100644
index a170eba..0000000
--- a/res/drawable-mdpi/btn_shutter_video_recording.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/capture_thumbnail_shadow.9.png b/res/drawable-mdpi/capture_thumbnail_shadow.9.png
deleted file mode 100644
index db0473d..0000000
--- a/res/drawable-mdpi/capture_thumbnail_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_action_overflow.png b/res/drawable-mdpi/ic_action_overflow.png
new file mode 100644
index 0000000..b58b7c9
--- /dev/null
+++ b/res/drawable-mdpi/ic_action_overflow.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_imagesize.png b/res/drawable-mdpi/ic_imagesize.png
deleted file mode 100644
index d3f8b62..0000000
--- a/res/drawable-mdpi/ic_imagesize.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_indicator_ev_n1.png b/res/drawable-mdpi/ic_indicator_ev_n1.png
deleted file mode 100644
index 528ecd3..0000000
--- a/res/drawable-mdpi/ic_indicator_ev_n1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_indicator_ev_n2.png b/res/drawable-mdpi/ic_indicator_ev_n2.png
deleted file mode 100644
index db4deb1..0000000
--- a/res/drawable-mdpi/ic_indicator_ev_n2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_indicator_ev_n3.png b/res/drawable-mdpi/ic_indicator_ev_n3.png
deleted file mode 100644
index 6b01a56..0000000
--- a/res/drawable-mdpi/ic_indicator_ev_n3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_indicator_ev_p1.png b/res/drawable-mdpi/ic_indicator_ev_p1.png
deleted file mode 100644
index 9a1f6f3..0000000
--- a/res/drawable-mdpi/ic_indicator_ev_p1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_indicator_ev_p2.png b/res/drawable-mdpi/ic_indicator_ev_p2.png
deleted file mode 100644
index 712ded5..0000000
--- a/res/drawable-mdpi/ic_indicator_ev_p2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_indicator_ev_p3.png b/res/drawable-mdpi/ic_indicator_ev_p3.png
deleted file mode 100644
index d01c2c2..0000000
--- a/res/drawable-mdpi/ic_indicator_ev_p3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_indicator_flash_on.png b/res/drawable-mdpi/ic_indicator_flash_on.png
deleted file mode 100644
index 8427072..0000000
--- a/res/drawable-mdpi/ic_indicator_flash_on.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_indicator_loc_off.png b/res/drawable-mdpi/ic_indicator_loc_off.png
deleted file mode 100644
index 87841e3..0000000
--- a/res/drawable-mdpi/ic_indicator_loc_off.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_indicator_sce_hdr.png b/res/drawable-mdpi/ic_indicator_sce_hdr.png
deleted file mode 100644
index 7907f64..0000000
--- a/res/drawable-mdpi/ic_indicator_sce_hdr.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_indicator_sce_on.png b/res/drawable-mdpi/ic_indicator_sce_on.png
deleted file mode 100644
index 6ea6d77..0000000
--- a/res/drawable-mdpi/ic_indicator_sce_on.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_indicator_timer_on.png b/res/drawable-mdpi/ic_indicator_timer_on.png
deleted file mode 100644
index 03d6c1b..0000000
--- a/res/drawable-mdpi/ic_indicator_timer_on.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_settings_holo_light.png b/res/drawable-mdpi/ic_settings_holo_light.png
deleted file mode 100644
index 5b39398..0000000
--- a/res/drawable-mdpi/ic_settings_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_switch_photosphere.png b/res/drawable-mdpi/ic_switch_photosphere.png
deleted file mode 100644
index 1b8db05..0000000
--- a/res/drawable-mdpi/ic_switch_photosphere.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_timer.png b/res/drawable-mdpi/ic_timer.png
deleted file mode 100644
index b55555f..0000000
--- a/res/drawable-mdpi/ic_timer.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-port-hdpi/switcher_bg.9.png b/res/drawable-port-hdpi/switcher_bg.9.png
deleted file mode 100644
index e6b74a4..0000000
--- a/res/drawable-port-hdpi/switcher_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-port-mdpi/switcher_bg.9.png b/res/drawable-port-mdpi/switcher_bg.9.png
deleted file mode 100644
index 6c87aa3..0000000
--- a/res/drawable-port-mdpi/switcher_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-port-xhdpi/switcher_bg.9.png b/res/drawable-port-xhdpi/switcher_bg.9.png
deleted file mode 100644
index bbe21ba..0000000
--- a/res/drawable-port-xhdpi/switcher_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/btn_shutter_recording.png b/res/drawable-sw600dp-hdpi/btn_shutter_recording.png
deleted file mode 100644
index 80f04ff..0000000
--- a/res/drawable-sw600dp-hdpi/btn_shutter_recording.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/btn_shutter_video_recording.png b/res/drawable-sw600dp-hdpi/btn_shutter_video_recording.png
deleted file mode 100644
index 65d9879..0000000
--- a/res/drawable-sw600dp-hdpi/btn_shutter_video_recording.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-land-hdpi/switcher_bg.9.png b/res/drawable-sw600dp-land-hdpi/switcher_bg.9.png
deleted file mode 100644
index 21375b1..0000000
--- a/res/drawable-sw600dp-land-hdpi/switcher_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-land-mdpi/switcher_bg.9.png b/res/drawable-sw600dp-land-mdpi/switcher_bg.9.png
deleted file mode 100644
index bfd996a..0000000
--- a/res/drawable-sw600dp-land-mdpi/switcher_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-land-xhdpi/switcher_bg.9.png b/res/drawable-sw600dp-land-xhdpi/switcher_bg.9.png
deleted file mode 100644
index 35a71db..0000000
--- a/res/drawable-sw600dp-land-xhdpi/switcher_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/btn_shutter_recording.png b/res/drawable-sw600dp-mdpi/btn_shutter_recording.png
deleted file mode 100644
index 5f56b49..0000000
--- a/res/drawable-sw600dp-mdpi/btn_shutter_recording.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/btn_shutter_video_recording.png b/res/drawable-sw600dp-mdpi/btn_shutter_video_recording.png
deleted file mode 100644
index ecf0276..0000000
--- a/res/drawable-sw600dp-mdpi/btn_shutter_video_recording.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-port-hdpi/switcher_bg.9.png b/res/drawable-sw600dp-port-hdpi/switcher_bg.9.png
deleted file mode 100644
index 250276a..0000000
--- a/res/drawable-sw600dp-port-hdpi/switcher_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-port-mdpi/switcher_bg.9.png b/res/drawable-sw600dp-port-mdpi/switcher_bg.9.png
deleted file mode 100644
index 9c4e293..0000000
--- a/res/drawable-sw600dp-port-mdpi/switcher_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-port-xhdpi/switcher_bg.9.png b/res/drawable-sw600dp-port-xhdpi/switcher_bg.9.png
deleted file mode 100644
index 2d0171e..0000000
--- a/res/drawable-sw600dp-port-xhdpi/switcher_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/btn_shutter_recording.png b/res/drawable-sw600dp-xhdpi/btn_shutter_recording.png
deleted file mode 100644
index 0a0e108..0000000
--- a/res/drawable-sw600dp-xhdpi/btn_shutter_recording.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/btn_shutter_video_recording.png b/res/drawable-sw600dp-xhdpi/btn_shutter_video_recording.png
deleted file mode 100644
index aa3a4bd..0000000
--- a/res/drawable-sw600dp-xhdpi/btn_shutter_video_recording.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-v19/filtershow_button_background.xml b/res/drawable-v19/filtershow_button_background.xml
new file mode 100644
index 0000000..cef867b
--- /dev/null
+++ b/res/drawable-v19/filtershow_button_background.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:drawable="@drawable/list_pressed_holo_light" android:state_pressed="true"/>
+    <item android:drawable="@drawable/filtershow_button_selected_background" android:state_selected="true"/>
+    <item android:drawable="@android:color/transparent" android:state_selected="false"/>
+
+</selector>
\ No newline at end of file
diff --git a/res/drawable-v19/photopage_bottom_button_background.xml b/res/drawable-v19/photopage_bottom_button_background.xml
new file mode 100644
index 0000000..78571da
--- /dev/null
+++ b/res/drawable-v19/photopage_bottom_button_background.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/list_pressed_holo_light" android:state_pressed="true"/>
+    <item android:drawable="@color/button_dark_transparent_background" android:state_selected="false"/>
+</selector>
diff --git a/res/drawable-v19/transparent_button_background.xml b/res/drawable-v19/transparent_button_background.xml
new file mode 100644
index 0000000..26b1b7c
--- /dev/null
+++ b/res/drawable-v19/transparent_button_background.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/list_pressed_holo_light" android:state_pressed="true"/>
+    <item android:drawable="@drawable/list_pressed_holo_light" android:state_pressed="true"/>
+    <item android:drawable="@android:color/transparent" android:state_selected="false"/>
+</selector>
\ No newline at end of file
diff --git a/res/drawable-xhdpi-v19/grid_pressed.9.png b/res/drawable-xhdpi-v19/grid_pressed.9.png
new file mode 100644
index 0000000..e713a7b
--- /dev/null
+++ b/res/drawable-xhdpi-v19/grid_pressed.9.png
Binary files differ
diff --git a/res/drawable-xhdpi-v19/list_pressed_holo_light.9.png b/res/drawable-xhdpi-v19/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..f4af926
--- /dev/null
+++ b/res/drawable-xhdpi-v19/list_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/btn_shutter_recording.png b/res/drawable-xhdpi/btn_shutter_recording.png
deleted file mode 100644
index 1bd1743..0000000
--- a/res/drawable-xhdpi/btn_shutter_recording.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/btn_shutter_video_recording.png b/res/drawable-xhdpi/btn_shutter_video_recording.png
deleted file mode 100644
index ba1fb5c..0000000
--- a/res/drawable-xhdpi/btn_shutter_video_recording.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/capture_thumbnail_shadow.9.png b/res/drawable-xhdpi/capture_thumbnail_shadow.9.png
deleted file mode 100644
index 3d771f7..0000000
--- a/res/drawable-xhdpi/capture_thumbnail_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_action_overflow.png b/res/drawable-xhdpi/ic_action_overflow.png
new file mode 100644
index 0000000..799c35c
--- /dev/null
+++ b/res/drawable-xhdpi/ic_action_overflow.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_imagesize.png b/res/drawable-xhdpi/ic_imagesize.png
deleted file mode 100644
index 54fd008..0000000
--- a/res/drawable-xhdpi/ic_imagesize.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_indicator_ev_n1.png b/res/drawable-xhdpi/ic_indicator_ev_n1.png
deleted file mode 100644
index e99cc66..0000000
--- a/res/drawable-xhdpi/ic_indicator_ev_n1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_indicator_ev_n2.png b/res/drawable-xhdpi/ic_indicator_ev_n2.png
deleted file mode 100644
index aced71d..0000000
--- a/res/drawable-xhdpi/ic_indicator_ev_n2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_indicator_ev_n3.png b/res/drawable-xhdpi/ic_indicator_ev_n3.png
deleted file mode 100644
index 41b9f59..0000000
--- a/res/drawable-xhdpi/ic_indicator_ev_n3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_indicator_ev_p1.png b/res/drawable-xhdpi/ic_indicator_ev_p1.png
deleted file mode 100644
index 2850c95..0000000
--- a/res/drawable-xhdpi/ic_indicator_ev_p1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_indicator_ev_p2.png b/res/drawable-xhdpi/ic_indicator_ev_p2.png
deleted file mode 100644
index c6355b1..0000000
--- a/res/drawable-xhdpi/ic_indicator_ev_p2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_indicator_ev_p3.png b/res/drawable-xhdpi/ic_indicator_ev_p3.png
deleted file mode 100644
index 99b860d..0000000
--- a/res/drawable-xhdpi/ic_indicator_ev_p3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_indicator_flash_on.png b/res/drawable-xhdpi/ic_indicator_flash_on.png
deleted file mode 100644
index 7e05e15..0000000
--- a/res/drawable-xhdpi/ic_indicator_flash_on.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_indicator_loc_off.png b/res/drawable-xhdpi/ic_indicator_loc_off.png
deleted file mode 100644
index 966855c..0000000
--- a/res/drawable-xhdpi/ic_indicator_loc_off.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_indicator_sce_hdr.png b/res/drawable-xhdpi/ic_indicator_sce_hdr.png
deleted file mode 100644
index 318c8fa..0000000
--- a/res/drawable-xhdpi/ic_indicator_sce_hdr.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_indicator_sce_on.png b/res/drawable-xhdpi/ic_indicator_sce_on.png
deleted file mode 100644
index 8b2440c..0000000
--- a/res/drawable-xhdpi/ic_indicator_sce_on.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_indicator_timer_on.png b/res/drawable-xhdpi/ic_indicator_timer_on.png
deleted file mode 100644
index 07cef15..0000000
--- a/res/drawable-xhdpi/ic_indicator_timer_on.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_settings_holo_light.png b/res/drawable-xhdpi/ic_settings_holo_light.png
deleted file mode 100644
index 0da5b5e..0000000
--- a/res/drawable-xhdpi/ic_settings_holo_light.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_switch_photosphere.png b/res/drawable-xhdpi/ic_switch_photosphere.png
deleted file mode 100644
index 8ff60a3..0000000
--- a/res/drawable-xhdpi/ic_switch_photosphere.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_switch_refocus.png b/res/drawable-xhdpi/ic_switch_refocus.png
deleted file mode 100644
index 3175f35..0000000
--- a/res/drawable-xhdpi/ic_switch_refocus.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_timer.png b/res/drawable-xhdpi/ic_timer.png
deleted file mode 100644
index 1764fdd..0000000
--- a/res/drawable-xhdpi/ic_timer.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi-v19/grid_pressed.9.png b/res/drawable-xxhdpi-v19/grid_pressed.9.png
new file mode 100644
index 0000000..ad32c46
--- /dev/null
+++ b/res/drawable-xxhdpi-v19/grid_pressed.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi-v19/list_pressed_holo_light.9.png b/res/drawable-xxhdpi-v19/list_pressed_holo_light.9.png
new file mode 100644
index 0000000..1352a17
--- /dev/null
+++ b/res/drawable-xxhdpi-v19/list_pressed_holo_light.9.png
Binary files differ
diff --git a/res/drawable/filtershow_add.png b/res/drawable/filtershow_add.png
index 074ef4c..d353763 100644
--- a/res/drawable/filtershow_add.png
+++ b/res/drawable/filtershow_add.png
Binary files differ
diff --git a/res/drawable/filtershow_color_picker_circle.xml b/res/drawable/filtershow_color_picker_circle.xml
index 9154252..433df48 100644
--- a/res/drawable/filtershow_color_picker_circle.xml
+++ b/res/drawable/filtershow_color_picker_circle.xml
@@ -16,11 +16,11 @@
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle" >
-    <size android:width="20dp" android:height="20dp"/>
+    <size android:width="16dp" android:height="16dp"/>
     <corners
-        android:radius="10dp" />
+        android:radius="0dp" />
     <solid android:color="@color/red"/>
-    <stroke android:width="2dp"
+    <stroke android:width="0dp"
             android:color="#ff000000"/>
 </shape>
 
diff --git a/res/drawable/filtershow_menu_marker.png b/res/drawable/filtershow_menu_marker.png
index 1537a71..cf720e1 100644
--- a/res/drawable/filtershow_menu_marker.png
+++ b/res/drawable/filtershow_menu_marker.png
Binary files differ
diff --git a/res/drawable/filtershow_menu_marker_rtl.xml b/res/drawable/filtershow_menu_marker_rtl.xml
new file mode 100644
index 0000000..1515004
--- /dev/null
+++ b/res/drawable/filtershow_menu_marker_rtl.xml
@@ -0,0 +1,4 @@
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+        android:src="@drawable/filtershow_menu_marker"
+        android:autoMirrored="true">
+</bitmap>
\ No newline at end of file
diff --git a/res/layout-land/filtershow_activity.xml b/res/layout-land/filtershow_activity.xml
index 72bbdea..f438012 100644
--- a/res/layout-land/filtershow_activity.xml
+++ b/res/layout-land/filtershow_activity.xml
@@ -27,79 +27,79 @@
             android:orientation="horizontal"
             android:animateLayoutChanges="true">
 
-        <LinearLayout
+
+        <FrameLayout
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:orientation="vertical"
-                >
+                android:layout_weight="1">
+
+            <ProgressBar
+                    android:id="@+id/loading"
+                    style="@android:style/Widget.Holo.ProgressBar.Large"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:indeterminate="true"
+                    android:indeterminateOnly="true"
+                    android:background="@null"/>
 
             <LinearLayout
-                    android:layout_weight="1"
                     android:layout_width="wrap_content"
-                    android:layout_height="0dp"
-                    android:orientation="horizontal">
+                    android:layout_height="match_parent"
+                    android:layout_weight="1"
+                    android:orientation="vertical"
+                    >
 
-                <FrameLayout
-                        android:id="@+id/central_panel_container"
-                        android:layout_gravity="center"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
+                <LinearLayout
                         android:layout_weight="1"
-                        android:visibility="gone"/>
+                        android:layout_width="wrap_content"
+                        android:layout_height="0dp"
+                        android:orientation="horizontal">
+
+                    <FrameLayout
+                            android:id="@+id/central_panel_container"
+                            android:layout_gravity="center"
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"
+                            android:visibility="gone"/>
+
+                    <FrameLayout
+                            android:id="@+id/editorContainer"
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"/>
+
+                    <com.android.gallery3d.filtershow.imageshow.ImageShow
+                            android:id="@+id/imageShow"
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"/>
+
+                </LinearLayout>
 
                 <FrameLayout
-                        android:id="@+id/editorContainer"
+                        android:id="@+id/state_panel_container"
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
-                        android:layout_weight="1"/>
-
-                <com.android.gallery3d.filtershow.imageshow.ImageShow
-                        android:id="@+id/imageShow"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_weight="1"/>
+                        android:visibility="visible"/>
 
             </LinearLayout>
 
-            <FrameLayout android:id="@+id/state_panel_container"
-                         android:layout_width="match_parent"
-                         android:layout_height="wrap_content"
-                         android:visibility="visible"/>
-
-        </LinearLayout>
-
+        </FrameLayout>
 
         <LinearLayout
                 android:id="@+id/mainPanel"
                 android:layout_width="350dip"
                 android:layout_height="match_parent"
                 android:orientation="vertical"
-                android:animateLayoutChanges="true" >
-
-            <FrameLayout android:id="@+id/main_panel_container"
-                         android:layout_width="350dip"
-                         android:layout_height="0dip"
-                         android:layout_weight="1" />
+                android:animateLayoutChanges="true">
 
             <FrameLayout
-                    android:layout_gravity="bottom"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:visibility="gone">
-
-
-                <ProgressBar
-                        android:id="@+id/loading"
-                        style="@android:style/Widget.Holo.ProgressBar.Large"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="center"
-                        android:indeterminate="true"
-                        android:indeterminateOnly="true"
-                        android:background="@color/background_screen"/>
-
-            </FrameLayout>
+                    android:id="@+id/main_panel_container"
+                    android:layout_width="350dip"
+                    android:layout_height="0dip"
+                    android:layout_weight="1"/>
 
         </LinearLayout>
 
diff --git a/res/layout-land/filtershow_color_border_ui.xml b/res/layout-land/filtershow_color_border_ui.xml
index f16ea2c..9945d9d 100644
--- a/res/layout-land/filtershow_color_border_ui.xml
+++ b/res/layout-land/filtershow_color_border_ui.xml
@@ -37,13 +37,6 @@
                 android:orientation="vertical"
                 android:visibility="visible">
 
-            <Button
-                    android:id="@+id/clearButton"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="1"
-                    android:text="@string/color_border_clear"/>
-
             <GridLayout
                     android:layout_width="match_parent"
                     android:layout_height="0dp"
@@ -132,25 +125,36 @@
                     android:layout_marginLeft="8dip"
                     android:id="@+id/textView"/>
 
-            <com.android.gallery3d.filtershow.colorpicker.ColorOpacityView
-                    android:id="@+id/colorOpacityView"
+
+            <com.android.gallery3d.filtershow.colorpicker.ColorCompareView
+                    android:id="@+id/btnSelect"
                     android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="1"/>
+                    android:layout_height="32dp"
+                    android:layout_marginLeft="18dp"
+                    android:layout_marginRight="18dp"
+                    android:layout_marginTop="0dp"
+                    android:layout_marginBottom="0dp"
+                    />
 
             <com.android.gallery3d.filtershow.colorpicker.ColorSVRectView
                     android:id="@+id/colorRectView"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="4"
+                    android:layout_width="@dimen/draw_ui_width"
+                    android:layout_height="@dimen/draw_ui_width"
+                    android:layout_gravity="center"
+                    android:layout_marginTop="8dp"
                     android:layout_marginRight="1dp"/>
 
             <com.android.gallery3d.filtershow.colorpicker.ColorHueView
                     android:id="@+id/ColorHueView"
                     android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="1"/>
+                    android:layout_marginTop="0dp"
+                    android:layout_height="32dp" />
 
+            <com.android.gallery3d.filtershow.colorpicker.ColorOpacityView
+                    android:id="@+id/colorOpacityView"
+                    android:layout_width="match_parent"
+                    android:layout_marginTop="24dp"
+                    android:layout_height="32dp" />
         </LinearLayout>
     </FrameLayout>
 
@@ -163,68 +167,79 @@
             android:layout_marginLeft="1dp"
             android:layout_marginRight="1dp">
 
-        <HorizontalScrollView
-                android:id="@+id/scrollList"
-                android:layout_width="0dp"
-                android:layout_weight="5"
-                android:layout_height="match_parent"
-                android:scrollbars="none">
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="horizontal"
+                android:layout_marginBottom="10dp"
+                android:layout_marginTop="10dp"
+                android:layout_marginLeft="1dp"
+                android:layout_marginRight="1dp">
 
             <LinearLayout
                     android:id="@+id/listColors"
+                    android:layout_weight="5"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:orientation="horizontal">
 
                 <Button
                         android:id="@+id/draw_color_button01"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
+                        android:layout_width="0dp"
                         android:layout_weight="1"
+                        android:layout_height="wrap_content"
                         android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp"/>
+                        android:layout_marginRight="4dp"/>
 
                 <Button
                         android:id="@+id/draw_color_button02"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
+                        android:layout_width="0dp"
                         android:layout_weight="1"
+                        android:layout_height="wrap_content"
                         android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp"/>
+                        android:layout_marginRight="4dp"/>
 
                 <Button
                         android:id="@+id/draw_color_button03"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
+                        android:layout_width="0dp"
                         android:layout_weight="1"
+                        android:layout_height="wrap_content"
                         android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp"/>
+                        android:layout_marginRight="4dp"/>
 
                 <Button
                         android:id="@+id/draw_color_button04"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
+                        android:layout_width="0dp"
                         android:layout_weight="1"
+                        android:layout_height="wrap_content"
                         android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp"/>
+                        android:layout_marginRight="4dp"/>
 
                 <Button
                         android:id="@+id/draw_color_button05"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
+                        android:layout_width="0dp"
                         android:layout_weight="1"
+                        android:layout_height="wrap_content"
                         android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp"/>
+                        android:layout_marginRight="4dp"/>
 
             </LinearLayout>
-        </HorizontalScrollView>
+            <FrameLayout
+                    android:background="@color/background_main_toolbar"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center">
+                <Button
+                        android:id="@+id/draw_color_popupbutton"
+                        android:layout_width="48dp"
+                        android:layout_gravity="center"
+                        android:layout_height="wrap_content"
+                        android:background="@drawable/ic_action_overflow"
+                        />
+            </FrameLayout>
+        </LinearLayout>
 
-        <Button
-                android:id="@+id/draw_color_popupbutton"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:background="@android:drawable/ic_menu_more"
-                android:layout_margin="1dp"/>
+
+
     </LinearLayout>
 </LinearLayout>
diff --git a/res/layout-land/filtershow_draw_ui.xml b/res/layout-land/filtershow_draw_ui.xml
index b98a87c..5e0a327 100644
--- a/res/layout-land/filtershow_draw_ui.xml
+++ b/res/layout-land/filtershow_draw_ui.xml
@@ -42,13 +42,15 @@
                     android:layout_width="match_parent"
                     android:layout_height="0dp"
                     android:layout_weight="1"
-                    android:text="@string/draw_clear"/>
+                    android:text="@string/draw_clear"
+                    android:visibility="gone"/>
 
             <GridLayout
                     android:layout_width="match_parent"
                     android:layout_height="0dp"
                     android:layout_weight="1"
                     android:columnCount="2"
+                    android:layout_marginBottom="3dp"
                     android:orientation="horizontal">
 
                 <TextView
@@ -67,6 +69,7 @@
                         android:id="@+id/drawSizeSeekBar"
                         android:layout_width="match_parent"
                         android:layout_column="0"
+                        android:progress="20"
                         android:layout_columnSpan="2"
                         android:layout_gravity="fill_horizontal"
                         style="@style/FilterShowSlider"/>
@@ -93,8 +96,10 @@
         <LinearLayout
                 android:id="@+id/colorPicker"
                 android:layout_width="match_parent"
-                android:layout_height="match_parent"
+                android:layout_height="wrap_content"
                 android:orientation="vertical"
+                android:layout_gravity="bottom"
+                android:layout_marginBottom="8dp"
                 android:visibility="gone">
 
             <TextView
@@ -102,26 +107,38 @@
                     android:layout_height="wrap_content"
                     android:text="@string/draw_color"
                     android:layout_marginLeft="8dip"
-                    android:id="@+id/textView"/>
+                    android:id="@+id/textView"
+                    android:visibility="gone"/>
 
-            <com.android.gallery3d.filtershow.colorpicker.ColorOpacityView
-                    android:id="@+id/colorOpacityView"
+            <com.android.gallery3d.filtershow.colorpicker.ColorCompareView
+                    android:id="@+id/btnSelect"
                     android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="1"/>
+                    android:layout_height="32dp"
+                    android:layout_marginLeft="18dp"
+                    android:layout_marginRight="18dp"
+                    android:layout_marginTop="0dp"
+                    android:layout_marginBottom="0dp"
+                    />
 
             <com.android.gallery3d.filtershow.colorpicker.ColorSVRectView
                     android:id="@+id/colorRectView"
-                    android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="4"
+                    android:layout_width="@dimen/draw_ui_width"
+                    android:layout_height="@dimen/draw_ui_width"
+                    android:layout_gravity="center"
+                    android:layout_marginTop="8dp"
                     android:layout_marginRight="1dp"/>
 
             <com.android.gallery3d.filtershow.colorpicker.ColorHueView
                     android:id="@+id/ColorHueView"
                     android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="1"/>
+                    android:layout_marginTop="0dp"
+                    android:layout_height="32dp" />
+
+            <com.android.gallery3d.filtershow.colorpicker.ColorOpacityView
+                    android:id="@+id/colorOpacityView"
+                    android:layout_width="match_parent"
+                    android:layout_marginTop="24dp"
+                    android:layout_height="32dp" />
 
         </LinearLayout>
     </FrameLayout>
@@ -135,68 +152,70 @@
             android:layout_marginLeft="1dp"
             android:layout_marginRight="1dp">
 
-        <HorizontalScrollView
-                android:id="@+id/scrollList"
-                android:layout_width="0dp"
-                android:layout_weight="5"
-                android:layout_height="match_parent"
-                android:scrollbars="none">
 
             <LinearLayout
                     android:id="@+id/listColors"
+                    android:layout_weight="5"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:orientation="horizontal">
 
                 <Button
                         android:id="@+id/draw_color_button01"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
+                        android:layout_width="0dp"
                         android:layout_weight="1"
+                        android:layout_height="wrap_content"
                         android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp"/>
+                        android:layout_marginRight="4dp"/>
 
                 <Button
                         android:id="@+id/draw_color_button02"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
+                        android:layout_width="0dp"
                         android:layout_weight="1"
+                        android:layout_height="wrap_content"
                         android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp"/>
+                        android:layout_marginRight="4dp"/>
 
                 <Button
                         android:id="@+id/draw_color_button03"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
+                        android:layout_width="0dp"
                         android:layout_weight="1"
+                        android:layout_height="wrap_content"
                         android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp"/>
+                        android:layout_marginRight="4dp"/>
 
                 <Button
                         android:id="@+id/draw_color_button04"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
+                        android:layout_width="0dp"
                         android:layout_weight="1"
+                        android:layout_height="wrap_content"
                         android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp"/>
+                        android:layout_marginRight="4dp"/>
 
                 <Button
                         android:id="@+id/draw_color_button05"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
+                        android:layout_width="0dp"
                         android:layout_weight="1"
+                        android:layout_height="wrap_content"
                         android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp"/>
+                        android:layout_marginRight="4dp"/>
 
             </LinearLayout>
-        </HorizontalScrollView>
 
-        <Button
-                android:id="@+id/draw_color_popupbutton"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:background="@android:drawable/ic_menu_more"
-                android:layout_margin="1dp"/>
+        <FrameLayout
+                android:background="@color/background_main_toolbar"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center">
+            <Button
+                    android:id="@+id/draw_color_popupbutton"
+                    android:layout_width="48dp"
+                    android:layout_gravity="center"
+                    android:layout_height="wrap_content"
+                    android:background="@drawable/ic_action_overflow"
+                    />
+        </FrameLayout>
+        <!--@drawable/ic_action_overflow
+        @android:drawable/ic_menu_more-->
     </LinearLayout>
 </LinearLayout>
diff --git a/res/layout-land/filtershow_editor_panel.xml b/res/layout-land/filtershow_editor_panel.xml
index e497252..f51dc14 100644
--- a/res/layout-land/filtershow_editor_panel.xml
+++ b/res/layout-land/filtershow_editor_panel.xml
@@ -84,10 +84,9 @@
                         android:layout_height="fill_parent"
                         android:layout_gravity="center"
                         android:background="@android:color/transparent"
-                        android:gravity="center"
                         android:text="@string/apply_effect"
                         android:textSize="18dip"
-                        android:drawableRight="@drawable/filtershow_menu_marker"
+                        android:drawableEnd="@drawable/filtershow_menu_marker_rtl"
                         android:textAllCaps="true"/>
 
             </LinearLayout>
diff --git a/res/layout-land/filtershow_grad_ui.xml b/res/layout-land/filtershow_grad_ui.xml
index 63f9418..fd09bee 100644
--- a/res/layout-land/filtershow_grad_ui.xml
+++ b/res/layout-land/filtershow_grad_ui.xml
@@ -102,18 +102,19 @@
                 android:layout_gravity="fill_horizontal"
                 style="@style/FilterShowSlider" />
     </GridLayout>
-    <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:columnCount="2"
-                android:orientation="horizontal"
-                android:layout_marginTop="24dp" >
+    <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal"
+            android:layout_marginTop="24dp">
 
-    <ImageButton
+
+        <ImageButton
             android:id="@+id/gradAddButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_gravity="left|center_vertical"
+            android:layout_gravity="start|center_vertical"
+            android:gravity="start"
             android:scaleType="centerInside"
             android:layout_weight="0"
             android:background="@drawable/filtershow_button_background"
@@ -121,16 +122,24 @@
             android:paddingBottom="8dp"
             android:layout_marginLeft="48dp" />
 
-    <ImageButton
+        <View
+            android:layout_width="wrap_content"
+            android:layout_height="8dp"
+            android:layout_weight="1"/>
+
+        <ImageButton
             android:id="@+id/gradDelButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_gravity="right|center_vertical"
+            android:layout_gravity="end|center_vertical"
+            android:gravity="end"
             android:scaleType="centerInside"
             android:layout_weight="0"
             android:background="@drawable/filtershow_button_background"
             android:src="@drawable/ic_menu_trash_holo_light"
             android:paddingBottom="8dp"
             android:layout_marginRight="48dp" />
-    </GridLayout>
+
+    </LinearLayout>
+
 </LinearLayout>
diff --git a/res/layout/crop_activity.xml b/res/layout/crop_activity.xml
index 9ff223f..0620cf4 100644
--- a/res/layout/crop_activity.xml
+++ b/res/layout/crop_activity.xml
@@ -18,8 +18,7 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:id="@+id/mainView"
-    android:background="@drawable/filtershow_tiled_background">
+    android:id="@+id/mainView">
 
     <LinearLayout
         android:id="@+id/mainPanel"
diff --git a/res/layout/filtershow_activity.xml b/res/layout/filtershow_activity.xml
index aabdb73..79c315b 100644
--- a/res/layout/filtershow_activity.xml
+++ b/res/layout/filtershow_activity.xml
@@ -26,33 +26,51 @@
             android:layout_height="match_parent"
             android:orientation="vertical">
 
-        <LinearLayout
-                android:layout_weight="1"
+        <FrameLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:orientation="horizontal">
+                android:layout_weight="1">
 
-            <FrameLayout
-                    android:id="@+id/central_panel_container"
+            <ProgressBar
+                    android:id="@+id/loading"
+                    style="@android:style/Widget.Holo.ProgressBar.Large"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
                     android:layout_gravity="center"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
+                    android:indeterminate="true"
+                    android:indeterminateOnly="true"
+                    android:background="@null"/>
+
+            <LinearLayout
                     android:layout_weight="1"
-                    android:visibility="gone"/>
-
-            <FrameLayout
-                    android:id="@+id/editorContainer"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_weight="1"/>
+                    android:orientation="horizontal">
 
-            <com.android.gallery3d.filtershow.imageshow.ImageShow
-                    android:id="@+id/imageShow"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1" />
+                <FrameLayout
+                        android:id="@+id/central_panel_container"
+                        android:layout_gravity="center"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1"
+                        android:visibility="gone"/>
 
-        </LinearLayout>
+                <FrameLayout
+                        android:id="@+id/editorContainer"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1"/>
+
+                <com.android.gallery3d.filtershow.imageshow.ImageShow
+                        android:id="@+id/imageShow"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1"/>
+
+
+            </LinearLayout>
+
+        </FrameLayout>
 
         <com.android.gallery3d.filtershow.CenteredLinearLayout
                 xmlns:custom="http://schemas.android.com/apk/res/com.android.gallery3d"
@@ -69,25 +87,6 @@
                          android:layout_height="0dip"
                          android:layout_weight="1" />
 
-            <FrameLayout
-                    android:layout_gravity="bottom"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:visibility="gone">
-
-
-                <ProgressBar
-                        android:id="@+id/loading"
-                        style="@android:style/Widget.Holo.ProgressBar.Large"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:layout_gravity="center"
-                        android:indeterminate="true"
-                        android:indeterminateOnly="true"
-                        android:background="@color/background_screen"/>
-
-            </FrameLayout>
-
         </com.android.gallery3d.filtershow.CenteredLinearLayout>
 
     </LinearLayout>
diff --git a/res/layout/filtershow_color_picker.xml b/res/layout/filtershow_color_picker.xml
index db32491..e56bc0d 100644
--- a/res/layout/filtershow_color_picker.xml
+++ b/res/layout/filtershow_color_picker.xml
@@ -14,38 +14,76 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:id="@+id/RelativeLayout1"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="@color/default_background"
-   >
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:background="@color/background_main_toolbar">
 
-    <com.android.gallery3d.filtershow.colorpicker.ColorOpacityView
-            android:id="@+id/colorOpacityView"
-            android:layout_width="match_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1"/>
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                  android:orientation="vertical"
+                  android:id="@+id/RelativeLayout1"
+                  android:layout_width="match_parent"
+                  android:layout_height="match_parent"
+                  android:background="@color/background_main_toolbar">
 
-    <com.android.gallery3d.filtershow.colorpicker.ColorSVRectView
-        android:id="@+id/colorRectView"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="4"
-        android:layout_marginRight="1dp" />
+        <com.android.gallery3d.filtershow.colorpicker.ColorCompareView
+                android:id="@+id/btnSelect"
+                android:layout_width="match_parent"
+                android:layout_height="32dp"
+                android:layout_marginLeft="20dp"
+                android:layout_marginRight="20dp"
+                android:layout_marginTop="20dp"
+                android:layout_marginBottom="0dp"
+                />
+        <com.android.gallery3d.filtershow.colorpicker.ColorSVRectView
+                android:id="@+id/colorRectView"
+                android:layout_width="match_parent"
+                android:layout_height="256dp"
+                android:layout_marginTop="0dp"
+                android:layout_marginRight="1dp"/>
 
-    <com.android.gallery3d.filtershow.colorpicker.ColorHueView
-        android:id="@+id/ColorHueView"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1" />
+        <com.android.gallery3d.filtershow.colorpicker.ColorHueView
+                android:id="@+id/ColorHueView"
+                android:layout_width="match_parent"
+                android:layout_marginTop="0dp"
+                android:layout_height="32dp" />
 
-    <Button
-        android:id="@+id/btnSelect"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/color_pick_select"  />
+        <com.android.gallery3d.filtershow.colorpicker.ColorOpacityView
+                android:id="@+id/colorOpacityView"
+                android:layout_width="match_parent"
+                android:layout_marginTop="24dp"
+                android:layout_height="32dp" />
+
+        <LinearLayout android:orientation="horizontal"
+                      android:layout_width="match_parent"
+                      android:layout_height="match_parent"
+                      android:layout_marginTop="4dp">
+            <ImageButton
+                    android:id="@+id/cancelColorPick"
+                    android:layout_width="wrap_content"
+                    android:layout_height="fill_parent"
+                    android:layout_gravity="left|center_vertical"
+                    android:background="@android:color/transparent"
+                    android:layout_weight=".1"
+                    android:gravity="center"
+                    android:src="@drawable/ic_menu_cancel_holo_light"
+                    android:textSize="18dip"/>
+
+            <ImageView
+                    android:layout_width="2dp"
+                    android:layout_height="fill_parent"
+                    android:src="@drawable/filtershow_vertical_bar"/>
+            <ImageButton
+                    android:id="@+id/applyColorPick"
+                    android:layout_width="wrap_content"
+                    android:layout_height="fill_parent"
+                    android:layout_gravity="right|center_vertical"
+                    android:layout_weight=".1"
+                    android:background="@android:color/transparent"
+                    android:gravity="center"
+                    android:src="@drawable/ic_menu_done_holo_light"
+                    android:textSize="18dip"/>
+        </LinearLayout>
+    </LinearLayout>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/filtershow_control_action_slider.xml b/res/layout/filtershow_control_action_slider.xml
index a3ef3ed..34eac1e 100644
--- a/res/layout/filtershow_control_action_slider.xml
+++ b/res/layout/filtershow_control_action_slider.xml
@@ -25,7 +25,7 @@
         android:id="@+id/leftActionButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="left|center_vertical"
+        android:layout_gravity="start|center_vertical"
         android:scaleType="centerInside"
         android:layout_weight="0"
         android:background="@drawable/filtershow_button_background"
@@ -44,7 +44,7 @@
         android:id="@+id/rightActionButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="left|center_vertical"
+        android:layout_gravity="end|center_vertical"
         android:scaleType="centerInside"
         android:layout_weight="0"
         android:background="@drawable/filtershow_button_background"
diff --git a/res/layout/filtershow_control_color_chooser.xml b/res/layout/filtershow_control_color_chooser.xml
index 706402c..5bfe460 100644
--- a/res/layout/filtershow_control_color_chooser.xml
+++ b/res/layout/filtershow_control_color_chooser.xml
@@ -20,63 +20,67 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="horizontal" >
-            <HorizontalScrollView
-                android:id="@+id/scrollList"
+    <LinearLayout
+            android:id="@+id/listColors"
+            android:layout_weight="5"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+        <Button
+                android:id="@+id/draw_color_button01"
                 android:layout_width="0dp"
-                android:layout_weight="5"
-                android:layout_height="match_parent"
-                android:scrollbars="none" >
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:background="@drawable/filtershow_color_picker_circle"
+                android:layout_marginRight="4dp"/>
 
-                <LinearLayout
-                    android:id="@+id/listStyles"
-                    android:layout_width="wrap_content"
-                    android:layout_height="match_parent"
-                    android:orientation="horizontal" >
+        <Button
+                android:id="@+id/draw_color_button02"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:background="@drawable/filtershow_color_picker_circle"
+                android:layout_marginRight="4dp"/>
 
-                <Button
-                        android:id="@+id/draw_color_button01"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
-                        android:layout_weight="1"
-                        android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp" />
-                <Button
-                        android:id="@+id/draw_color_button02"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
-                        android:layout_weight="1"
-                        android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp" />
-                <Button
-                        android:id="@+id/draw_color_button03"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
-                        android:layout_weight="1"
-                        android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp" />
-                <Button
-                        android:id="@+id/draw_color_button04"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
-                        android:layout_weight="1"
-                        android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp" />
-                <Button
-                        android:id="@+id/draw_color_button05"
-                        android:layout_width="0dip"
-                        android:layout_height="wrap_content"
-                        android:layout_weight="1"
-                        android:background="@drawable/filtershow_color_picker_circle"
-                        android:layout_margin="2dp" />
+        <Button
+                android:id="@+id/draw_color_button03"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:background="@drawable/filtershow_color_picker_circle"
+                android:layout_marginRight="4dp"/>
 
-                </LinearLayout>
-            </HorizontalScrollView>
+        <Button
+                android:id="@+id/draw_color_button04"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:background="@drawable/filtershow_color_picker_circle"
+                android:layout_marginRight="4dp"/>
+
+        <Button
+                android:id="@+id/draw_color_button05"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="wrap_content"
+                android:background="@drawable/filtershow_color_picker_circle"
+                android:layout_marginRight="4dp"/>
+
+    </LinearLayout>
+
+    <FrameLayout
+            android:background="@color/background_main_toolbar"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center">
     <Button
             android:id="@+id/draw_color_popupbutton"
-            android:layout_width="@dimen/draw_style_icon_dim"
-            android:layout_height="@dimen/draw_style_icon_dim"
-            android:background="@android:drawable/ic_menu_more"
-            android:layout_margin="1dp"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center"
+            android:layout_height="wrap_content"
+            android:background="@drawable/ic_action_overflow"
             />
+    </FrameLayout>
 </LinearLayout>
 
diff --git a/res/layout/filtershow_control_style_chooser.xml b/res/layout/filtershow_control_style_chooser.xml
index b0426e3..9588ea2 100644
--- a/res/layout/filtershow_control_style_chooser.xml
+++ b/res/layout/filtershow_control_style_chooser.xml
@@ -18,7 +18,7 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res/com.example.imagefilterharness"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="32dp"
     android:orientation="horizontal" >
             <HorizontalScrollView
                 android:id="@+id/scrollList"
@@ -32,6 +32,7 @@
                     android:layout_height="match_parent"
                     android:orientation="horizontal" >
                 </LinearLayout>
+
             </HorizontalScrollView>
 </LinearLayout>
 
diff --git a/res/layout/filtershow_editor_panel.xml b/res/layout/filtershow_editor_panel.xml
index 7332657..9953996 100644
--- a/res/layout/filtershow_editor_panel.xml
+++ b/res/layout/filtershow_editor_panel.xml
@@ -87,11 +87,11 @@
                         android:layout_height="fill_parent"
                         android:layout_gravity="center"
                         android:background="@android:color/transparent"
-                        android:gravity="center"
                         android:text="@string/apply_effect"
                         android:textSize="18dip"
-                        android:drawableRight="@drawable/filtershow_menu_marker"
-                        android:textAllCaps="true" />
+                        android:drawableEnd="@drawable/filtershow_menu_marker_rtl"
+                        android:textAllCaps="true"
+                        />
 
             </LinearLayout>
 
diff --git a/res/layout/filtershow_export_dialog.xml b/res/layout/filtershow_export_dialog.xml
index c7ce1e7..4e13f39 100644
--- a/res/layout/filtershow_export_dialog.xml
+++ b/res/layout/filtershow_export_dialog.xml
@@ -40,7 +40,8 @@
 
         <TextView
                 android:text="@string/size"
-                android:layout_gravity="right|center_vertical"/>
+                android:layout_marginLeft="8dp"
+                android:layout_gravity="start|center_vertical"/>
 
         <EditText
                 android:id="@+id/editableWidth"
@@ -60,26 +61,41 @@
 
         <TextView
                 android:id="@+id/estimadedSize"
-                android:layout_gravity="center"/>
+                android:layout_marginRight="8dp"
+                android:layout_gravity="end|center_vertical"/>
 
-        <TextView
+        <LinearLayout
+                android:layout_columnSpan="5"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+
+            <TextView
                 android:text="@string/quality"
-                android:layout_margin="7dp"
-                android:layout_gravity="right|center_vertical" />
+                android:layout_width="wrap_content"
+                android:layout_height="fill_parent"
+                android:layout_margin="8dp" />
 
-        <SeekBar
+            <SeekBar
                 android:id="@+id/qualitySeekBar"
-                android:layout_margin="7dp"
+                android:layout_margin="8dp"
                 android:layout_gravity="center"
                 android:max="100"
                 android:progress="100"
-                android:layout_width="400dp"
-                android:layout_columnSpan="3"/>
+                android:layout_height="wrap_content"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_columnSpan="3"
+                android:minWidth="250dp" />
 
-        <TextView
+            <TextView
                 android:id="@+id/qualityTextView"
-                android:layout_margin="7dp"
-                android:layout_gravity="left|center_vertical"/>
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="8dp"
+                android:layout_gravity="end|center_vertical" />
+
+        </LinearLayout>
+
 
     </GridLayout>
 
diff --git a/res/layout/filtershow_info_panel.xml b/res/layout/filtershow_info_panel.xml
index 39446df..43878b6 100644
--- a/res/layout/filtershow_info_panel.xml
+++ b/res/layout/filtershow_info_panel.xml
@@ -16,51 +16,11 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="match_parent"
+              android:minWidth="340dp"
               android:layout_height="match_parent"
               android:orientation="vertical"
-              android:background="#3a4e56">
-
-    <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            android:background="#222">
-
-        <ImageButton
-                android:id="@+id/cancelInfo"
-                android:layout_width="wrap_content"
-                android:layout_height="fill_parent"
-                android:layout_gravity="left|center_vertical"
-                android:background="@android:color/transparent"
-                android:layout_weight=".1"
-                android:gravity="left"
-                android:src="@drawable/ic_menu_cancel_holo_light"
-                android:textSize="18dip"/>
-
-        <ImageView
-                android:layout_width="2dp"
-                android:layout_height="fill_parent"
-                android:src="@drawable/filtershow_vertical_bar"/>
-
-        <TextView
-                style="?android:textAppearanceLarge"
-                android:textStyle="bold"
-                android:textColor="#fff"
-                android:layout_weight="1"
-                android:layout_gravity="left|center_vertical"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:padding="8dip"
-                android:text="@string/filtershow_show_info_panel"/>
-
-    </LinearLayout>
-
-    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                  android:layout_width="match_parent"
-                  android:layout_height="match_parent"
-                  android:orientation="vertical"
-                  android:background="#3a4e56"
-                  android:padding="16dp">
+              android:background="@color/background_main_toolbar"
+              android:padding="16dp">
 
     <ScrollView
             android:layout_width="match_parent"
@@ -74,87 +34,78 @@
             <LinearLayout
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:orientation="horizontal">
+                    android:orientation="horizontal"
+                    >
 
-                <LinearLayout
+                <TextView
+                        android:id="@+id/imageName"
+                        style="?android:textAppearanceSmall"
+                        android:textStyle="bold"
+                        android:textColor="#80ffffff"
+                        android:layout_gravity="start"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1"
+                        android:gravity="start"/>
+
+                <TextView
+                        android:id="@+id/imageSize"
+                        style="?android:textAppearanceSmall"
+                        android:textAllCaps="true"
+                        android:textColor="#80ffffff"
+                        android:textStyle="bold"
+                        android:layout_gravity="end"
                         android:layout_width="0dp"
                         android:layout_height="wrap_content"
                         android:layout_weight="1"
-                        android:orientation="vertical">
+                        android:gravity="end"/>
 
-                    <TextView
-                            style="?android:textAppearanceLarge"
-                            android:textStyle="bold"
-                            android:textColor="#fff"
-                            android:layout_width="match_parent"
-                            android:layout_height="wrap_content"
-                            android:text="@string/filtershow_show_info_panel_name"/>
+            </LinearLayout>
 
-                    <TextView
-                            android:id="@+id/imageName"
-                            style="?android:textAppearanceSmall"
-                            android:textStyle="bold"
-                            android:textColor="#80ffffff"
-                            android:layout_width="match_parent"
-                            android:layout_height="wrap_content"/>
-
-                    <TextView
-                            style="?android:textAppearanceLarge"
-                            android:textStyle="bold"
-                            android:textColor="#fff"
-                            android:layout_width="match_parent"
-                            android:layout_height="wrap_content"
-                            android:text="@string/filtershow_show_info_panel_size"/>
-
-                    <TextView
-                            android:id="@+id/imageSize"
-                            style="?android:textAppearanceSmall"
-                            android:textAllCaps="true"
-                            android:textColor="#80ffffff"
-                            android:textStyle="bold"
-                            android:lineSpacingMultiplier="1.2"
-                            android:layout_width="match_parent"
-                            android:layout_height="wrap_content"/>
-
-                </LinearLayout>
+            <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    >
 
                 <ImageView
                         android:id="@+id/imageThumbnail"
-                        android:layout_width="200dp"
-                        android:layout_height="200dp"
-                        android:scaleType="centerInside"
+                        android:layout_weight="0.3"
+                        android:layout_width="0dp"
+                        android:layout_height="196dp"
+                        android:scaleType="centerCrop"
+                        android:layout_marginRight="16dp"
                         android:background="@null"
-                        android:layout_weight="1"
+
                         />
+
+                <com.android.gallery3d.filtershow.info.HistogramView
+                        android:id="@+id/histogramView"
+                        android:layout_weight="0.7"
+                        android:layout_width="0dp"
+                        android:layout_height="196dp"
+                        android:layout_margin="0dp"/>
             </LinearLayout>
 
             <TextView
                     style="?android:textAppearanceLarge"
+                    android:id="@+id/exifLabel"
                     android:textStyle="bold"
                     android:textColor="#fff"
-                    android:layout_width="match_parent"
+                    android:layout_marginTop="16dp"
+                    android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="@string/filtershow_show_info_panel_histogram"/>
-
-            <com.android.gallery3d.filtershow.info.HistogramView
-                    android:id="@+id/histogramView"
-                    android:layout_width="match_parent"
-                    android:layout_height="250dp"
-                    android:layout_margin="16dp"/>
-
-            <TextView
-                    style="?android:textAppearanceLarge"
-                    android:textStyle="bold"
-                    android:textColor="#fff"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
+                    android:layout_gravity="start"
                     android:text="@string/filtershow_show_info_panel_exif"/>
 
             <TextView
                     android:id="@+id/exifData"
                     android:layout_width="match_parent"
-                    android:layout_height="wrap_content"/>
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="16dp"
+                    android:layout_gravity="start"
+                    />
         </LinearLayout>
     </ScrollView>
-    </LinearLayout>
+
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/filtershow_splashscreen.xml b/res/layout/filtershow_splashscreen.xml
new file mode 100644
index 0000000..06958ca
--- /dev/null
+++ b/res/layout/filtershow_splashscreen.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent"
+              android:orientation="vertical"
+              android:gravity="center_horizontal|center_vertical"
+              android:padding="10dip">
+
+<ProgressBar android:id="@+id/progress_bar"
+             android:layout_alignParentRight="true"
+             android:layout_width="wrap_content"
+             android:layout_height="wrap_content"
+             android:gravity="center_vertical"
+             android:visibility="visible"
+             android:paddingBottom="144dp" />
+
+</LinearLayout>
diff --git a/res/values-xlarge/filtershow_values.xml b/res/values-xlarge/filtershow_values.xml
index c6fe399..6d0906a 100644
--- a/res/values-xlarge/filtershow_values.xml
+++ b/res/values-xlarge/filtershow_values.xml
@@ -29,4 +29,7 @@
 
     <!-- Category Panel Text Size -->
     <dimen name="category_panel_margin">4dip</dimen>
+
+    <!-- draw check UI size  -->
+    <dimen name="draw_ui_width">350dip</dimen>
 </resources>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 692a87f..ef742d2 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -141,6 +141,7 @@
     <dimen name="thumbnail_size">96dip</dimen>
     <dimen name="thumbnail_margin">3dip</dimen>
     <dimen name="action_item_height">175dip</dimen>
+    <dimen name="touch_circle_size">8dp</dimen>
 
     <!-- configuration for album set page -->
     <dimen name="album_set_item_image_height">120dp</dimen>
diff --git a/res/values/filtershow_strings.xml b/res/values/filtershow_strings.xml
index 75e23b0..98f32eb 100644
--- a/res/values/filtershow_strings.xml
+++ b/res/values/filtershow_strings.xml
@@ -20,7 +20,7 @@
 
     <!--  String shown when we cannot load the image when starting the activity [CHAR LIMIT=NONE] -->
     <string name="cannot_load_image">Cannot load the image!</string>
-    <!--  String shown when we cannot load the original to edit [CHAR LIMIT=NONE] -->
+    <!--  String shown when cannot load the original image. We will edit selected image [CHAR LIMIT=NONE] -->
     <string name="cannot_edit_original">Cannot edit original</string>
     <!--  String displayed when showing the original image [CHAR LIMIT=NONE] -->
     <string name="original_picture_text">@string/original</string>
@@ -32,12 +32,12 @@
 
     <!--  Text for to display on a download failure [CHAR LIMIT=NONE] -->
     <string name="download_failure">Could not download photo. Network unavailable.</string>
-    <!--  Text for original image [CHAR LIMIT=20] -->
+    <!--  Text to label an image as "original" [CHAR LIMIT=20] -->
     <string name="original">Original</string>
-    <!--  Text for original image [CHAR LIMIT=20] -->
-    <string name="saved">Saved</string>
     <!--  Text for filters that apply a border to a picture [CHAR LIMIT=20] -->
     <string name="borders" msgid="4461692156695893616">Borders</string>
+    <!-- Text for the custom border filter [CHAR LIMIT=20] -->
+    <string name="custom_border">Custom</string>
 
     <!--  actionbar menu -->
 
@@ -56,7 +56,7 @@
 
     <!-- Export Dialog-->
 
-    <!--  Text for the dialog title to export a flattened photo[CHAR LIMIT=30] -->
+    <!--  Text for the dialog title to export a flattened photo[CHAR LIMIT=25] -->
     <string name="export_flattened">Export Flattened Image</string>
     <!--  Text for selecting export image quality [CHAR LIMIT=100] -->
     <string name="select_output_settings">The exported image will be a copy, without the history.</string>
@@ -164,7 +164,7 @@
     <string name="vignette_saturation">Saturation</string>
     <!--  Label for the image vignette contrast fparameter ui [CHAR LIMIT=15] -->
     <string name="vignette_contrast">Contrast</string>
-    <!--  Label for the image vignette Falloff rate parameter ui [CHAR LIMIT=15] -->
+    <!--  Label for the rate of change in brightness as it goes to the edge [CHAR LIMIT=15] -->
     <string name="vignette_falloff">Falloff</string>
 
     <!--  Label for the image effect that removes redeye. [CHAR LIMIT=10] -->
@@ -190,7 +190,7 @@
     <string name="kmeans">Posterize</string>
     <!--  Label for the image downsampling effect (makes image smaller) [CHAR LIMIT=15] -->
     <string name="downsample">Downsample</string>
-    <!--  Label for the image graduated filter effect  [CHAR LIMIT=15] -->
+    <!--  Label for the "neutral density graduated filter" filter effect  [CHAR LIMIT=15] -->
     <string name="grad">Graduated</string>
     <!--  Label for the Brightness effect  [CHAR LIMIT=20] -->
     <string name="editor_grad_brightness">Brightness</string>
@@ -214,7 +214,7 @@
     <string name="editor_chan_sat_magenta">Magenta</string>
     <!--  Label for the image graduated filter effect  [CHAR LIMIT=20] -->
     <string name="editor_grad_style">Style</string>
-    <!--  Label for the image new grad layer  [CHAR LIMIT=20] -->
+    <!-- new virtual graduated neutral density filter  [CHAR LIMIT=20] -->
     <string name="editor_grad_new">new</string>
 
 
@@ -252,7 +252,7 @@
 
 
     <!--  Label for the with of the border[CHAR LIMIT=14] -->
-    <string name="color_border_size">thickness</string>
+    <string name="color_border_size">Thickness</string>
     <!--  Label for the border corner size (or rounding) [CHAR LIMIT=14] -->
     <string name="color_border_corner_size">Corner Size</string>
     <!--  Label for selecting the border color [CHAR LIMIT=14] -->
diff --git a/res/values/filtershow_values.xml b/res/values/filtershow_values.xml
index 0cf723f..a788afe 100644
--- a/res/values/filtershow_values.xml
+++ b/res/values/filtershow_values.xml
@@ -51,7 +51,18 @@
     <!-- Grad filter minimum touch distance -->
     <dimen name="draw_rect_round">10dip</dimen>
 
-    <!-- Grad filter minimum touch distance -->
-    <dimen name="draw_style_icon_dim">64dip</dimen>
+    <!-- draw color icon size -->
+    <dimen name="draw_style_icon_dim">42dip</dimen>
 
+    <!-- draw color icon size -->
+    <dimen name="draw_color_icon_dim">42dip</dimen>
+
+    <!-- draw check UI size  -->
+    <dimen name="draw_color_check_dim">5dip</dimen>
+
+    <!-- draw check UI size  -->
+    <dimen name="draw_ui_width">192dip</dimen>
+
+    <!-- Glow effect size -->
+    <dimen name="edge_glow_size">50dip</dimen>
 </resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0b2c79e..1ef8c13 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -345,7 +345,7 @@
     <string name="ffx_original">Original</string>
     <!-- String for brown-colored old-fashion looking filter (filtershow_fx_0000_vintage) [CHAR LIMIT=10] -->
     <string name="ffx_vintage">Vintage</string>
-    <!-- String for filter that brightens colors (filtershow_fx_0001_instant) [CHAR LIMIT=10] -->
+    <!-- String for filter that brightens colors like instant cameras(filtershow_fx_0001_instant) [CHAR LIMIT=10] -->
     <string name="ffx_instant">Instant</string>
     <!-- String for filter that washes out colors (filtershow_fx_0002_bleach) [CHAR LIMIT=10] -->
     <string name="ffx_bleach">Bleach</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 58c87ce..33dcbec 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -31,6 +31,14 @@
     <style name="Theme.FilterShow" parent="Theme.Gallery">
         <item name="android:windowBackground">@null</item>
     </style>
+    <style name="Theme.Crop" parent="Theme.GalleryBase">
+        <item name="android:displayOptions"></item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:actionBarStyle">@style/Holo.ActionBar</item>
+        <item name="android:colorBackground">@null</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        <item name="android:windowBackground">@drawable/filtershow_tiled_background</item>
+    </style>
     <style name="Holo.ActionBar" parent="android:Widget.Holo.ActionBar">
         <item name="android:displayOptions">useLogo|showHome</item>
         <item name="android:background">@drawable/actionbar_translucent</item>
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index c8dadbd..4ed6309 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -15,19 +15,22 @@
  */
 package com.android.camera;
 
-import com.android.gallery3d.util.CameraHelper;
+import com.android.gallery3d.util.IntentHelper;
 
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 
-/** Trampoline activity that launches the new Camera activity defined in CameraHelper. */
+/** Trampoline activity that launches the new Camera activity defined in IntentHelper. */
 public class CameraActivity extends Activity {
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        Intent intent = CameraHelper.CAMERA_LAUNCHER_INTENT;
-        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
+        Intent intent = IntentHelper.getCameraIntent(CameraActivity.this);
+        // Since this is being launched from a homescreen shorcut,
+        // it's already in a new task. Start Camera activity and
+        // reset the task to its initial state if needed.
+        intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
         startActivity(intent);
         finish();
     }
diff --git a/src/com/android/camera/DisableCameraReceiver.java b/src/com/android/camera/DisableCameraReceiver.java
new file mode 100644
index 0000000..d51d6b9
--- /dev/null
+++ b/src/com/android/camera/DisableCameraReceiver.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.hardware.Camera.CameraInfo;
+import android.util.Log;
+
+// We want to disable camera-related activities if there is no camera. This
+// receiver runs when BOOT_COMPLETED intent is received. After running once
+// this receiver will be disabled, so it will not run again.
+public class DisableCameraReceiver extends BroadcastReceiver {
+    private static final String TAG = "G:DisableCameraReceiver";
+    private static final boolean CHECK_BACK_CAMERA_ONLY = true;
+    private static final String ACTIVITIES[] = {
+        "com.android.camera.CameraLauncher",
+    };
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        // Disable camera-related activities if there is no camera.
+        boolean needCameraActivity = CHECK_BACK_CAMERA_ONLY
+            ? hasBackCamera()
+            : hasCamera();
+
+        if (!needCameraActivity) {
+            Log.i(TAG, "disable all camera activities");
+            for (int i = 0; i < ACTIVITIES.length; i++) {
+                disableComponent(context, ACTIVITIES[i]);
+            }
+        }
+
+        // Disable this receiver so it won't run again.
+        disableComponent(context, "com.android.camera.DisableCameraReceiver");
+    }
+
+    private boolean hasCamera() {
+        int n = android.hardware.Camera.getNumberOfCameras();
+        Log.i(TAG, "number of camera: " + n);
+        return (n > 0);
+    }
+
+    private boolean hasBackCamera() {
+        int n = android.hardware.Camera.getNumberOfCameras();
+        CameraInfo info = new CameraInfo();
+        for (int i = 0; i < n; i++) {
+            android.hardware.Camera.getCameraInfo(i, info);
+            if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
+                Log.i(TAG, "back camera found: " + i);
+                return true;
+            }
+        }
+        Log.i(TAG, "no back camera");
+        return false;
+    }
+
+    private void disableComponent(Context context, String klass) {
+        ComponentName name = new ComponentName(context, klass);
+        PackageManager pm = context.getPackageManager();
+
+        // We need the DONT_KILL_APP flag, otherwise we will be killed
+        // immediately because we are in the same app.
+        pm.setComponentEnabledSetting(name,
+            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+            PackageManager.DONT_KILL_APP);
+    }
+}
diff --git a/src/com/android/gallery3d/app/AbstractGalleryActivity.java b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
index 923c5b2..9af1fb8 100644
--- a/src/com/android/gallery3d/app/AbstractGalleryActivity.java
+++ b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
@@ -32,6 +32,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.support.v4.print.PrintHelper;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.Window;
@@ -45,12 +46,12 @@
 import com.android.gallery3d.ui.GLRoot;
 import com.android.gallery3d.ui.GLRootView;
 import com.android.gallery3d.util.PanoramaViewHelper;
-import com.android.gallery3d.util.PrintJob;
 import com.android.gallery3d.util.ThreadPool;
 import com.android.photos.data.GalleryBitmapPool;
 
+import java.io.FileNotFoundException;
+
 public class AbstractGalleryActivity extends Activity implements GalleryContext {
-    @SuppressWarnings("unused")
     private static final String TAG = "AbstractGalleryActivity";
     private GLRootView mGLRootView;
     private StateManager mStateManager;
@@ -357,6 +358,11 @@
         } else {
             path = uri.getLastPathSegment();
         }
-        PrintJob.printBitmapAtUri(this, path, uri);
+        PrintHelper printer = new PrintHelper(this);
+        try {
+            printer.printBitmap(path, uri);
+        } catch (FileNotFoundException fnfe) {
+            Log.e(TAG, "Error printing an image", fnfe);
+        }
     }
 }
diff --git a/src/com/android/gallery3d/app/AlbumPage.java b/src/com/android/gallery3d/app/AlbumPage.java
index ad757da..44f2404 100644
--- a/src/com/android/gallery3d/app/AlbumPage.java
+++ b/src/com/android/gallery3d/app/AlbumPage.java
@@ -316,7 +316,7 @@
     private void onGetContent(final MediaItem item) {
         DataManager dm = mActivity.getDataManager();
         Activity activity = mActivity;
-        if (mData.getString(Gallery.EXTRA_CROP) != null) {
+        if (mData.getString(GalleryActivity.EXTRA_CROP) != null) {
             Uri uri = dm.getContentUri(item.getPath());
             Intent intent = new Intent(CropActivity.CROP_ACTION, uri)
                     .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
@@ -367,7 +367,7 @@
         mUserDistance = GalleryUtils.meterToPixel(USER_DISTANCE_METER);
         initializeViews();
         initializeData(data);
-        mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false);
+        mGetContent = data.getBoolean(GalleryActivity.KEY_GET_CONTENT, false);
         mShowClusterMenu = data.getBoolean(KEY_SHOW_CLUSTER_MENU, false);
         mDetailsSource = new MyDetailsSource();
         Context context = mActivity.getAndroidContext();
@@ -538,34 +538,13 @@
         mSlotView.invalidate();
     }
 
-    private boolean canDoSlideShow() {
-        if (mMediaSet == null) {
-            return false;
-        }
-
-        final int[] count = new int[]{0};
-        mMediaSet.enumerateMediaItems(new MediaSet.ItemConsumer() {
-            @Override
-            public void consume(int index, MediaItem item) {
-                if (item.getMediaType() == MediaObject.MEDIA_TYPE_IMAGE) {
-                    count[0]++;
-                }
-            }
-        });
-
-        if (count[0] < 2) { // you must have 2 pictures to go into slide show
-            return false;
-        }
-        return true;
-    }
-
     @Override
     protected boolean onCreateActionBar(Menu menu) {
         GalleryActionBar actionBar = mActivity.getGalleryActionBar();
         MenuInflater inflator = getSupportMenuInflater();
         if (mGetContent) {
             inflator.inflate(R.menu.pickup, menu);
-            int typeBits = mData.getInt(Gallery.KEY_TYPE_BITS,
+            int typeBits = mData.getInt(GalleryActivity.KEY_TYPE_BITS,
                     DataManager.INCLUDE_IMAGE);
             actionBar.setTitle(GalleryUtils.getSelectionModePrompt(typeBits));
         } else {
@@ -574,7 +553,6 @@
 
             FilterUtils.setupMenuItems(actionBar, mMediaSetPath, true);
 
-            menu.findItem(R.id.action_slideshow).setVisible(canDoSlideShow());
             menu.findItem(R.id.action_group_by).setVisible(mShowClusterMenu);
             menu.findItem(R.id.action_camera).setVisible(
                     MediaSetUtils.isCameraSource(mMediaSetPath)
diff --git a/src/com/android/gallery3d/app/AlbumPicker.java b/src/com/android/gallery3d/app/AlbumPicker.java
index 65eb772..8c2d09a 100644
--- a/src/com/android/gallery3d/app/AlbumPicker.java
+++ b/src/com/android/gallery3d/app/AlbumPicker.java
@@ -32,7 +32,7 @@
         Bundle extras = intent.getExtras();
         Bundle data = extras == null ? new Bundle() : new Bundle(extras);
 
-        data.putBoolean(Gallery.KEY_GET_ALBUM, true);
+        data.putBoolean(GalleryActivity.KEY_GET_ALBUM, true);
         data.putString(AlbumSetPage.KEY_MEDIA_PATH,
                 getDataManager().getTopSetPath(DataManager.INCLUDE_IMAGE));
         getStateManager().startState(AlbumSetPage.class, data);
diff --git a/src/com/android/gallery3d/app/AlbumSetPage.java b/src/com/android/gallery3d/app/AlbumSetPage.java
index dd9d8ec..d56b5b8 100644
--- a/src/com/android/gallery3d/app/AlbumSetPage.java
+++ b/src/com/android/gallery3d/app/AlbumSetPage.java
@@ -322,8 +322,8 @@
         initializeViews();
         initializeData(data);
         Context context = mActivity.getAndroidContext();
-        mGetContent = data.getBoolean(Gallery.KEY_GET_CONTENT, false);
-        mGetAlbum = data.getBoolean(Gallery.KEY_GET_ALBUM, false);
+        mGetContent = data.getBoolean(GalleryActivity.KEY_GET_CONTENT, false);
+        mGetAlbum = data.getBoolean(GalleryActivity.KEY_GET_ALBUM, false);
         mTitle = data.getString(AlbumSetPage.KEY_SET_TITLE);
         mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE);
         mEyePosition = new EyePosition(context, this);
@@ -534,7 +534,7 @@
         if (mGetContent) {
             inflater.inflate(R.menu.pickup, menu);
             int typeBits = mData.getInt(
-                    Gallery.KEY_TYPE_BITS, DataManager.INCLUDE_IMAGE);
+                    GalleryActivity.KEY_TYPE_BITS, DataManager.INCLUDE_IMAGE);
             mActionBar.setTitle(GalleryUtils.getSelectionModePrompt(typeBits));
         } else  if (mGetAlbum) {
             inflater.inflate(R.menu.pickup, menu);
diff --git a/src/com/android/gallery3d/app/DialogPicker.java b/src/com/android/gallery3d/app/DialogPicker.java
index 7ca86e5..1de3324 100644
--- a/src/com/android/gallery3d/app/DialogPicker.java
+++ b/src/com/android/gallery3d/app/DialogPicker.java
@@ -33,7 +33,7 @@
         Bundle extras = intent.getExtras();
         Bundle data = extras == null ? new Bundle() : new Bundle(extras);
 
-        data.putBoolean(Gallery.KEY_GET_CONTENT, true);
+        data.putBoolean(GalleryActivity.KEY_GET_CONTENT, true);
         data.putString(AlbumSetPage.KEY_MEDIA_PATH,
                 getDataManager().getTopSetPath(typeBits));
         getStateManager().startState(AlbumSetPage.class, data);
diff --git a/src/com/android/gallery3d/app/Gallery.java b/src/com/android/gallery3d/app/Gallery.java
index baef56b..a1b6d01 100644
--- a/src/com/android/gallery3d/app/Gallery.java
+++ b/src/com/android/gallery3d/app/Gallery.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -13,262 +13,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package com.android.gallery3d.app;
 
-import android.app.Dialog;
-import android.content.ContentResolver;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
+import com.android.gallery3d.util.IntentHelper;
+
+import android.app.Activity;
 import android.content.Intent;
-import android.net.Uri;
 import android.os.Bundle;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.Toast;
 
-import com.android.gallery3d.R;
-import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.DataManager;
-import com.android.gallery3d.data.MediaItem;
-import com.android.gallery3d.data.MediaSet;
-import com.android.gallery3d.data.Path;
-import com.android.gallery3d.picasasource.PicasaSource;
-import com.android.gallery3d.util.GalleryUtils;
-
-public final class Gallery extends AbstractGalleryActivity implements OnCancelListener {
-    public static final String EXTRA_SLIDESHOW = "slideshow";
-    public static final String EXTRA_DREAM = "dream";
-    public static final String EXTRA_CROP = "crop";
-
-    public static final String ACTION_REVIEW = "com.android.camera.action.REVIEW";
-    public static final String KEY_GET_CONTENT = "get-content";
-    public static final String KEY_GET_ALBUM = "get-album";
-    public static final String KEY_TYPE_BITS = "type-bits";
-    public static final String KEY_MEDIA_TYPES = "mediaTypes";
-    public static final String KEY_DISMISS_KEYGUARD = "dismiss-keyguard";
-
-    private static final String TAG = "Gallery";
-    private Dialog mVersionCheckDialog;
-
+/** Trampoline activity that launches the Gallery activity defined in IntentHelper. */
+public class Gallery extends Activity {
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
+    public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        requestWindowFeature(Window.FEATURE_ACTION_BAR);
-        requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
-
-        if (getIntent().getBooleanExtra(KEY_DISMISS_KEYGUARD, false)) {
-            getWindow().addFlags(
-                    WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
-        }
-
-        setContentView(R.layout.main);
-
-        if (savedInstanceState != null) {
-            getStateManager().restoreFromState(savedInstanceState);
-        } else {
-            initializeByIntent();
-        }
-    }
-
-    private void initializeByIntent() {
-        Intent intent = getIntent();
-        String action = intent.getAction();
-
-        if (Intent.ACTION_GET_CONTENT.equalsIgnoreCase(action)) {
-            startGetContent(intent);
-        } else if (Intent.ACTION_PICK.equalsIgnoreCase(action)) {
-            // We do NOT really support the PICK intent. Handle it as
-            // the GET_CONTENT. However, we need to translate the type
-            // in the intent here.
-            Log.w(TAG, "action PICK is not supported");
-            String type = Utils.ensureNotNull(intent.getType());
-            if (type.startsWith("vnd.android.cursor.dir/")) {
-                if (type.endsWith("/image")) intent.setType("image/*");
-                if (type.endsWith("/video")) intent.setType("video/*");
-            }
-            startGetContent(intent);
-        } else if (Intent.ACTION_VIEW.equalsIgnoreCase(action)
-                || ACTION_REVIEW.equalsIgnoreCase(action)){
-            startViewAction(intent);
-        } else {
-            startDefaultPage();
-        }
-    }
-
-    public void startDefaultPage() {
-        PicasaSource.showSignInReminder(this);
-        Bundle data = new Bundle();
-        data.putString(AlbumSetPage.KEY_MEDIA_PATH,
-                getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));
-        getStateManager().startState(AlbumSetPage.class, data);
-        mVersionCheckDialog = PicasaSource.getVersionCheckDialog(this);
-        if (mVersionCheckDialog != null) {
-            mVersionCheckDialog.setOnCancelListener(this);
-        }
-    }
-
-    private void startGetContent(Intent intent) {
-        Bundle data = intent.getExtras() != null
-                ? new Bundle(intent.getExtras())
-                : new Bundle();
-        data.putBoolean(KEY_GET_CONTENT, true);
-        int typeBits = GalleryUtils.determineTypeBits(this, intent);
-        data.putInt(KEY_TYPE_BITS, typeBits);
-        data.putString(AlbumSetPage.KEY_MEDIA_PATH,
-                getDataManager().getTopSetPath(typeBits));
-        getStateManager().startState(AlbumSetPage.class, data);
-    }
-
-    private String getContentType(Intent intent) {
-        String type = intent.getType();
-        if (type != null) {
-            return GalleryUtils.MIME_TYPE_PANORAMA360.equals(type)
-                ? MediaItem.MIME_TYPE_JPEG : type;
-        }
-
-        Uri uri = intent.getData();
-        try {
-            return getContentResolver().getType(uri);
-        } catch (Throwable t) {
-            Log.w(TAG, "get type fail", t);
-            return null;
-        }
-    }
-
-    private void startViewAction(Intent intent) {
-        Boolean slideshow = intent.getBooleanExtra(EXTRA_SLIDESHOW, false);
-        if (slideshow) {
-            getActionBar().hide();
-            DataManager manager = getDataManager();
-            Path path = manager.findPathByUri(intent.getData(), intent.getType());
-            if (path == null || manager.getMediaObject(path)
-                    instanceof MediaItem) {
-                path = Path.fromString(
-                        manager.getTopSetPath(DataManager.INCLUDE_IMAGE));
-            }
-            Bundle data = new Bundle();
-            data.putString(SlideshowPage.KEY_SET_PATH, path.toString());
-            data.putBoolean(SlideshowPage.KEY_RANDOM_ORDER, true);
-            data.putBoolean(SlideshowPage.KEY_REPEAT, true);
-            if (intent.getBooleanExtra(EXTRA_DREAM, false)) {
-                data.putBoolean(SlideshowPage.KEY_DREAM, true);
-            }
-            getStateManager().startState(SlideshowPage.class, data);
-        } else {
-            Bundle data = new Bundle();
-            DataManager dm = getDataManager();
-            Uri uri = intent.getData();
-            String contentType = getContentType(intent);
-            if (contentType == null) {
-                Toast.makeText(this,
-                        R.string.no_such_item, Toast.LENGTH_LONG).show();
-                finish();
-                return;
-            }
-            if (uri == null) {
-                int typeBits = GalleryUtils.determineTypeBits(this, intent);
-                data.putInt(KEY_TYPE_BITS, typeBits);
-                data.putString(AlbumSetPage.KEY_MEDIA_PATH,
-                        getDataManager().getTopSetPath(typeBits));
-                getStateManager().startState(AlbumSetPage.class, data);
-            } else if (contentType.startsWith(
-                    ContentResolver.CURSOR_DIR_BASE_TYPE)) {
-                int mediaType = intent.getIntExtra(KEY_MEDIA_TYPES, 0);
-                if (mediaType != 0) {
-                    uri = uri.buildUpon().appendQueryParameter(
-                            KEY_MEDIA_TYPES, String.valueOf(mediaType))
-                            .build();
-                }
-                Path setPath = dm.findPathByUri(uri, null);
-                MediaSet mediaSet = null;
-                if (setPath != null) {
-                    mediaSet = (MediaSet) dm.getMediaObject(setPath);
-                }
-                if (mediaSet != null) {
-                    if (mediaSet.isLeafAlbum()) {
-                        data.putString(AlbumPage.KEY_MEDIA_PATH, setPath.toString());
-                        data.putString(AlbumPage.KEY_PARENT_MEDIA_PATH,
-                                dm.getTopSetPath(DataManager.INCLUDE_ALL));
-                        getStateManager().startState(AlbumPage.class, data);
-                    } else {
-                        data.putString(AlbumSetPage.KEY_MEDIA_PATH, setPath.toString());
-                        getStateManager().startState(AlbumSetPage.class, data);
-                    }
-                } else {
-                    startDefaultPage();
-                }
-            } else {
-                Path itemPath = dm.findPathByUri(uri, contentType);
-                Path albumPath = dm.getDefaultSetOf(itemPath);
-
-                data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, itemPath.toString());
-
-                // TODO: Make the parameter "SingleItemOnly" public so other
-                //       activities can reference it.
-                boolean singleItemOnly = (albumPath == null)
-                        || intent.getBooleanExtra("SingleItemOnly", false);
-                if (!singleItemOnly) {
-                    data.putString(PhotoPage.KEY_MEDIA_SET_PATH, albumPath.toString());
-                    // when FLAG_ACTIVITY_NEW_TASK is set, (e.g. when intent is fired
-                    // from notification), back button should behave the same as up button
-                    // rather than taking users back to the home screen
-                    if (intent.getBooleanExtra(PhotoPage.KEY_TREAT_BACK_AS_UP, false)
-                            || ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0)) {
-                        data.putBoolean(PhotoPage.KEY_TREAT_BACK_AS_UP, true);
-                    }
-                }
-
-                getStateManager().startState(SinglePhotoPage.class, data);
-            }
-        }
-    }
-
-    @Override
-    protected void onResume() {
-        Utils.assertTrue(getStateManager().getStateCount() > 0);
-        super.onResume();
-        if (mVersionCheckDialog != null) {
-            mVersionCheckDialog.show();
-        }
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        if (mVersionCheckDialog != null) {
-            mVersionCheckDialog.dismiss();
-        }
-    }
-
-    @Override
-    public void onCancel(DialogInterface dialog) {
-        if (dialog == mVersionCheckDialog) {
-            mVersionCheckDialog = null;
-        }
-    }
-
-    @Override
-    public boolean onGenericMotionEvent(MotionEvent event) {
-        final boolean isTouchPad = (event.getSource()
-                & InputDevice.SOURCE_CLASS_POSITION) != 0;
-        if (isTouchPad) {
-            float maxX = event.getDevice().getMotionRange(MotionEvent.AXIS_X).getMax();
-            float maxY = event.getDevice().getMotionRange(MotionEvent.AXIS_Y).getMax();
-            View decor = getWindow().getDecorView();
-            float scaleX = decor.getWidth() / maxX;
-            float scaleY = decor.getHeight() / maxY;
-            float x = event.getX() * scaleX;
-            //x = decor.getWidth() - x; // invert x
-            float y = event.getY() * scaleY;
-            //y = decor.getHeight() - y; // invert y
-            MotionEvent touchEvent = MotionEvent.obtain(event.getDownTime(),
-                    event.getEventTime(), event.getAction(), x, y, event.getMetaState());
-            return dispatchTouchEvent(touchEvent);
-        }
-        return super.onGenericMotionEvent(event);
+        Intent intent = IntentHelper.getGalleryIntent(Gallery.this);
+        // Since this is being launched from a homescreen shortcut,
+        // it's already in a new task. Start Gallery activity and
+        // reset the task to its initial state if needed.
+        intent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        startActivity(intent);
+        finish();
     }
 }
diff --git a/src/com/android/gallery3d/app/GalleryActivity.java b/src/com/android/gallery3d/app/GalleryActivity.java
new file mode 100644
index 0000000..bb2a6b8
--- /dev/null
+++ b/src/com/android/gallery3d/app/GalleryActivity.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.app;
+
+import android.app.Dialog;
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.DataManager;
+import com.android.gallery3d.data.MediaItem;
+import com.android.gallery3d.data.MediaSet;
+import com.android.gallery3d.data.Path;
+import com.android.gallery3d.picasasource.PicasaSource;
+import com.android.gallery3d.util.GalleryUtils;
+
+public final class GalleryActivity extends AbstractGalleryActivity implements OnCancelListener {
+    public static final String EXTRA_SLIDESHOW = "slideshow";
+    public static final String EXTRA_DREAM = "dream";
+    public static final String EXTRA_CROP = "crop";
+
+    public static final String ACTION_REVIEW = "com.android.camera.action.REVIEW";
+    public static final String KEY_GET_CONTENT = "get-content";
+    public static final String KEY_GET_ALBUM = "get-album";
+    public static final String KEY_TYPE_BITS = "type-bits";
+    public static final String KEY_MEDIA_TYPES = "mediaTypes";
+    public static final String KEY_DISMISS_KEYGUARD = "dismiss-keyguard";
+
+    private static final String TAG = "GalleryActivity";
+    private Dialog mVersionCheckDialog;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        requestWindowFeature(Window.FEATURE_ACTION_BAR);
+        requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+
+        if (getIntent().getBooleanExtra(KEY_DISMISS_KEYGUARD, false)) {
+            getWindow().addFlags(
+                    WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+        }
+
+        setContentView(R.layout.main);
+
+        if (savedInstanceState != null) {
+            getStateManager().restoreFromState(savedInstanceState);
+        } else {
+            initializeByIntent();
+        }
+    }
+
+    private void initializeByIntent() {
+        Intent intent = getIntent();
+        String action = intent.getAction();
+
+        if (Intent.ACTION_GET_CONTENT.equalsIgnoreCase(action)) {
+            startGetContent(intent);
+        } else if (Intent.ACTION_PICK.equalsIgnoreCase(action)) {
+            // We do NOT really support the PICK intent. Handle it as
+            // the GET_CONTENT. However, we need to translate the type
+            // in the intent here.
+            Log.w(TAG, "action PICK is not supported");
+            String type = Utils.ensureNotNull(intent.getType());
+            if (type.startsWith("vnd.android.cursor.dir/")) {
+                if (type.endsWith("/image")) intent.setType("image/*");
+                if (type.endsWith("/video")) intent.setType("video/*");
+            }
+            startGetContent(intent);
+        } else if (Intent.ACTION_VIEW.equalsIgnoreCase(action)
+                || ACTION_REVIEW.equalsIgnoreCase(action)){
+            startViewAction(intent);
+        } else {
+            startDefaultPage();
+        }
+    }
+
+    public void startDefaultPage() {
+        PicasaSource.showSignInReminder(this);
+        Bundle data = new Bundle();
+        data.putString(AlbumSetPage.KEY_MEDIA_PATH,
+                getDataManager().getTopSetPath(DataManager.INCLUDE_ALL));
+        getStateManager().startState(AlbumSetPage.class, data);
+        mVersionCheckDialog = PicasaSource.getVersionCheckDialog(this);
+        if (mVersionCheckDialog != null) {
+            mVersionCheckDialog.setOnCancelListener(this);
+        }
+    }
+
+    private void startGetContent(Intent intent) {
+        Bundle data = intent.getExtras() != null
+                ? new Bundle(intent.getExtras())
+                : new Bundle();
+        data.putBoolean(KEY_GET_CONTENT, true);
+        int typeBits = GalleryUtils.determineTypeBits(this, intent);
+        data.putInt(KEY_TYPE_BITS, typeBits);
+        data.putString(AlbumSetPage.KEY_MEDIA_PATH,
+                getDataManager().getTopSetPath(typeBits));
+        getStateManager().startState(AlbumSetPage.class, data);
+    }
+
+    private String getContentType(Intent intent) {
+        String type = intent.getType();
+        if (type != null) {
+            return GalleryUtils.MIME_TYPE_PANORAMA360.equals(type)
+                ? MediaItem.MIME_TYPE_JPEG : type;
+        }
+
+        Uri uri = intent.getData();
+        try {
+            return getContentResolver().getType(uri);
+        } catch (Throwable t) {
+            Log.w(TAG, "get type fail", t);
+            return null;
+        }
+    }
+
+    private void startViewAction(Intent intent) {
+        Boolean slideshow = intent.getBooleanExtra(EXTRA_SLIDESHOW, false);
+        if (slideshow) {
+            getActionBar().hide();
+            DataManager manager = getDataManager();
+            Path path = manager.findPathByUri(intent.getData(), intent.getType());
+            if (path == null || manager.getMediaObject(path)
+                    instanceof MediaItem) {
+                path = Path.fromString(
+                        manager.getTopSetPath(DataManager.INCLUDE_IMAGE));
+            }
+            Bundle data = new Bundle();
+            data.putString(SlideshowPage.KEY_SET_PATH, path.toString());
+            data.putBoolean(SlideshowPage.KEY_RANDOM_ORDER, true);
+            data.putBoolean(SlideshowPage.KEY_REPEAT, true);
+            if (intent.getBooleanExtra(EXTRA_DREAM, false)) {
+                data.putBoolean(SlideshowPage.KEY_DREAM, true);
+            }
+            getStateManager().startState(SlideshowPage.class, data);
+        } else {
+            Bundle data = new Bundle();
+            DataManager dm = getDataManager();
+            Uri uri = intent.getData();
+            String contentType = getContentType(intent);
+            if (contentType == null) {
+                Toast.makeText(this,
+                        R.string.no_such_item, Toast.LENGTH_LONG).show();
+                finish();
+                return;
+            }
+            if (uri == null) {
+                int typeBits = GalleryUtils.determineTypeBits(this, intent);
+                data.putInt(KEY_TYPE_BITS, typeBits);
+                data.putString(AlbumSetPage.KEY_MEDIA_PATH,
+                        getDataManager().getTopSetPath(typeBits));
+                getStateManager().startState(AlbumSetPage.class, data);
+            } else if (contentType.startsWith(
+                    ContentResolver.CURSOR_DIR_BASE_TYPE)) {
+                int mediaType = intent.getIntExtra(KEY_MEDIA_TYPES, 0);
+                if (mediaType != 0) {
+                    uri = uri.buildUpon().appendQueryParameter(
+                            KEY_MEDIA_TYPES, String.valueOf(mediaType))
+                            .build();
+                }
+                Path setPath = dm.findPathByUri(uri, null);
+                MediaSet mediaSet = null;
+                if (setPath != null) {
+                    mediaSet = (MediaSet) dm.getMediaObject(setPath);
+                }
+                if (mediaSet != null) {
+                    if (mediaSet.isLeafAlbum()) {
+                        data.putString(AlbumPage.KEY_MEDIA_PATH, setPath.toString());
+                        data.putString(AlbumPage.KEY_PARENT_MEDIA_PATH,
+                                dm.getTopSetPath(DataManager.INCLUDE_ALL));
+                        getStateManager().startState(AlbumPage.class, data);
+                    } else {
+                        data.putString(AlbumSetPage.KEY_MEDIA_PATH, setPath.toString());
+                        getStateManager().startState(AlbumSetPage.class, data);
+                    }
+                } else {
+                    startDefaultPage();
+                }
+            } else {
+                Path itemPath = dm.findPathByUri(uri, contentType);
+                Path albumPath = dm.getDefaultSetOf(itemPath);
+
+                data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, itemPath.toString());
+                data.putBoolean(PhotoPage.KEY_READONLY, true);
+
+                // TODO: Make the parameter "SingleItemOnly" public so other
+                //       activities can reference it.
+                boolean singleItemOnly = (albumPath == null)
+                        || intent.getBooleanExtra("SingleItemOnly", false);
+                if (!singleItemOnly) {
+                    data.putString(PhotoPage.KEY_MEDIA_SET_PATH, albumPath.toString());
+                    // when FLAG_ACTIVITY_NEW_TASK is set, (e.g. when intent is fired
+                    // from notification), back button should behave the same as up button
+                    // rather than taking users back to the home screen
+                    if (intent.getBooleanExtra(PhotoPage.KEY_TREAT_BACK_AS_UP, false)
+                            || ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0)) {
+                        data.putBoolean(PhotoPage.KEY_TREAT_BACK_AS_UP, true);
+                    }
+                }
+
+                getStateManager().startState(SinglePhotoPage.class, data);
+            }
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        Utils.assertTrue(getStateManager().getStateCount() > 0);
+        super.onResume();
+        if (mVersionCheckDialog != null) {
+            mVersionCheckDialog.show();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mVersionCheckDialog != null) {
+            mVersionCheckDialog.dismiss();
+        }
+    }
+
+    @Override
+    public void onCancel(DialogInterface dialog) {
+        if (dialog == mVersionCheckDialog) {
+            mVersionCheckDialog = null;
+        }
+    }
+
+    @Override
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        final boolean isTouchPad = (event.getSource()
+                & InputDevice.SOURCE_CLASS_POSITION) != 0;
+        if (isTouchPad) {
+            float maxX = event.getDevice().getMotionRange(MotionEvent.AXIS_X).getMax();
+            float maxY = event.getDevice().getMotionRange(MotionEvent.AXIS_Y).getMax();
+            View decor = getWindow().getDecorView();
+            float scaleX = decor.getWidth() / maxX;
+            float scaleY = decor.getHeight() / maxY;
+            float x = event.getX() * scaleX;
+            //x = decor.getWidth() - x; // invert x
+            float y = event.getY() * scaleY;
+            //y = decor.getHeight() - y; // invert y
+            MotionEvent touchEvent = MotionEvent.obtain(event.getDownTime(),
+                    event.getEventTime(), event.getAction(), x, y, event.getMetaState());
+            return dispatchTouchEvent(touchEvent);
+        }
+        return super.onGenericMotionEvent(event);
+    }
+}
diff --git a/src/com/android/gallery3d/app/MovieActivity.java b/src/com/android/gallery3d/app/MovieActivity.java
index 40edbbe..1547f6f 100644
--- a/src/com/android/gallery3d/app/MovieActivity.java
+++ b/src/com/android/gallery3d/app/MovieActivity.java
@@ -198,7 +198,7 @@
             if (mTreatUpAsBack) {
                 finish();
             } else {
-                startActivity(new Intent(this, Gallery.class));
+                startActivity(new Intent(this, GalleryActivity.class));
                 finish();
             }
             return true;
diff --git a/src/com/android/gallery3d/app/MoviePlayer.java b/src/com/android/gallery3d/app/MoviePlayer.java
index ce91834..f6bd367 100644
--- a/src/com/android/gallery3d/app/MoviePlayer.java
+++ b/src/com/android/gallery3d/app/MoviePlayer.java
@@ -27,6 +27,8 @@
 import android.content.IntentFilter;
 import android.media.AudioManager;
 import android.media.MediaPlayer;
+import android.media.audiofx.AudioEffect;
+import android.media.audiofx.Virtualizer;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -66,6 +68,7 @@
     private static final String CMDNAME = "command";
     private static final String CMDPAUSE = "pause";
 
+    private static final String VIRTUALIZE_EXTRA = "virtualize";
     private static final long BLACK_TIMEOUT = 500;
 
     // If we resume the acitivty with in RESUMEABLE_TIMEOUT, we will keep playing.
@@ -92,6 +95,8 @@
     // If the time bar is visible.
     private boolean mShowing;
 
+    private Virtualizer mVirtualizer;
+
     private final Runnable mPlayingChecker = new Runnable() {
         @Override
         public void run() {
@@ -127,6 +132,18 @@
         mVideoView.setOnErrorListener(this);
         mVideoView.setOnCompletionListener(this);
         mVideoView.setVideoURI(mUri);
+
+        Intent ai = movieActivity.getIntent();
+        boolean virtualize = ai.getBooleanExtra(VIRTUALIZE_EXTRA, false);
+        if (virtualize) {
+            int session = mVideoView.getAudioSessionId();
+            if (session != 0) {
+                mVirtualizer = new Virtualizer(0, session);
+                mVirtualizer.setEnabled(true);
+            } else {
+                Log.w(TAG, "no audio session to virtualize");
+            }
+        }
         mVideoView.setOnTouchListener(new View.OnTouchListener() {
             @Override
             public boolean onTouch(View v, MotionEvent event) {
@@ -280,6 +297,10 @@
     }
 
     public void onDestroy() {
+        if (mVirtualizer != null) {
+            mVirtualizer.release();
+            mVirtualizer = null;
+        }
         mVideoView.stopPlayback();
         mAudioBecomingNoisyReceiver.unregister();
     }
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
index 1dacb87..3359888 100644
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -35,6 +35,7 @@
 import android.os.SystemClock;
 import android.view.Menu;
 import android.view.MenuItem;
+import android.view.View;
 import android.widget.RelativeLayout;
 import android.widget.ShareActionProvider;
 import android.widget.Toast;
@@ -63,6 +64,7 @@
 import com.android.gallery3d.ui.DetailsHelper;
 import com.android.gallery3d.ui.DetailsHelper.CloseListener;
 import com.android.gallery3d.ui.DetailsHelper.DetailsSource;
+import com.android.gallery3d.ui.GLRootView;
 import com.android.gallery3d.ui.GLView;
 import com.android.gallery3d.ui.MenuExecutor;
 import com.android.gallery3d.ui.PhotoView;
@@ -110,6 +112,7 @@
     public static final String KEY_RETURN_INDEX_HINT = "return-index-hint";
     public static final String KEY_SHOW_WHEN_LOCKED = "show_when_locked";
     public static final String KEY_IN_CAMERA_ROLL = "in_camera_roll";
+    public static final String KEY_READONLY = "read-only";
 
     public static final String KEY_ALBUMPAGE_TRANSITION = "albumpage-transition";
     public static final int MSG_ALBUMPAGE_NONE = 0;
@@ -149,6 +152,7 @@
     private boolean mShowSpinner;
     private String mSetPathString;
     // This is the original mSetPathString before adding the camera preview item.
+    private boolean mReadOnlyView = false;
     private String mOriginalSetPathString;
     private AppBridge mAppBridge;
     private SnailItem mScreenNailItem;
@@ -180,6 +184,8 @@
     private final MyMenuVisibilityListener mMenuVisibilityListener =
             new MyMenuVisibilityListener();
 
+    private int mLastSystemUiVis = 0;
+
     private final PanoramaSupportCallback mUpdatePanoramaMenuItemsCallback = new PanoramaSupportCallback() {
         @Override
         public void panoramaInfoAvailable(MediaObject mediaObject, boolean isPanorama,
@@ -375,6 +381,7 @@
         };
 
         mSetPathString = data.getString(KEY_MEDIA_SET_PATH);
+        mReadOnlyView = data.getBoolean(KEY_READONLY);
         mOriginalSetPathString = mSetPathString;
         setupNfcBeamPush();
         String itemPathString = data.getString(KEY_MEDIA_ITEM_PATH);
@@ -542,6 +549,19 @@
                 mBottomControls = new PhotoPageBottomControls(this, mActivity, galleryRoot);
             }
         }
+
+        ((GLRootView) mActivity.getGLRoot()).setOnSystemUiVisibilityChangeListener(
+                new View.OnSystemUiVisibilityChangeListener() {
+                @Override
+                    public void onSystemUiVisibilityChange(int visibility) {
+                        int diff = mLastSystemUiVis ^ visibility;
+                        mLastSystemUiVis = visibility;
+                        if ((diff & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0
+                                && (visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
+                            showBars();
+                        }
+                    }
+                });
     }
 
     @Override
@@ -565,7 +585,7 @@
         }
         switch(control) {
             case R.id.photopage_bottom_control_edit:
-                return mHaveImageEditor && mShowBars
+                return mHaveImageEditor && mShowBars && !mReadOnlyView
                         && !mPhotoView.getFilmMode()
                         && (mCurrentPhoto.getSupportedOperations() & MediaItem.SUPPORT_EDIT) != 0
                         && mCurrentPhoto.getMediaType() == MediaObject.MEDIA_TYPE_IMAGE;
@@ -752,6 +772,9 @@
         if (mCurrentPhoto == null) return;
 
         int supportedOperations = mCurrentPhoto.getSupportedOperations();
+        if (mReadOnlyView) {
+            supportedOperations ^= MediaObject.SUPPORT_EDIT;
+        }
         if (mSecureAlbum != null) {
             supportedOperations &= MediaObject.SUPPORT_DELETE;
         } else {
@@ -760,32 +783,16 @@
                 supportedOperations &= ~MediaObject.SUPPORT_EDIT;
             }
         }
-
         MenuExecutor.updateMenuOperation(menu, supportedOperations);
     }
 
     private boolean canDoSlideShow() {
-
         if (mMediaSet == null || mCurrentPhoto == null) {
             return false;
         }
         if (mCurrentPhoto.getMediaType() != MediaObject.MEDIA_TYPE_IMAGE) {
             return false;
         }
-        final int[] count = new int[]{0};
-
-        mMediaSet.enumerateMediaItems(new MediaSet.ItemConsumer() {
-            @Override
-            public void consume(int index, MediaItem item) {
-                if (item.getMediaType() == MediaObject.MEDIA_TYPE_IMAGE) {
-                    count[0]++;
-                }
-            }
-        });
-
-        if (count[0] < 2) { // you must have 3 pictures to go into slide show
-            return false;
-        }
         return true;
     }
 
@@ -1153,8 +1160,8 @@
         } else if (goBack) {
             onBackPressed();
         } else if (unlock) {
-            Intent intent = new Intent(mActivity, Gallery.class);
-            intent.putExtra(Gallery.KEY_DISMISS_KEYGUARD, true);
+            Intent intent = new Intent(mActivity, GalleryActivity.class);
+            intent.putExtra(GalleryActivity.KEY_DISMISS_KEYGUARD, true);
             mActivity.startActivity(intent);
         } else if (launchCamera) {
             launchCamera();
diff --git a/src/com/android/gallery3d/app/Wallpaper.java b/src/com/android/gallery3d/app/Wallpaper.java
index b0a26c2..4887e7d 100644
--- a/src/com/android/gallery3d/app/Wallpaper.java
+++ b/src/com/android/gallery3d/app/Wallpaper.java
@@ -18,6 +18,8 @@
 
 import android.annotation.TargetApi;
 import android.app.Activity;
+import android.app.WallpaperManager;
+import android.content.ActivityNotFoundException;
 import android.content.Intent;
 import android.graphics.Point;
 import android.net.Uri;
@@ -95,24 +97,38 @@
                 // fall-through
             }
             case STATE_PHOTO_PICKED: {
+                Intent cropAndSetWallpaperIntent;
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+                    WallpaperManager wpm = WallpaperManager.getInstance(getApplicationContext());
+                    try {
+                        cropAndSetWallpaperIntent = wpm.getCropAndSetWallpaperIntent(mPickedItem);
+                        startActivity(cropAndSetWallpaperIntent);
+                        finish();
+                        return;
+                    } catch (ActivityNotFoundException anfe) {
+                        // ignored; fallthru to existing crop activity
+                    }
+                }
+
                 int width = getWallpaperDesiredMinimumWidth();
                 int height = getWallpaperDesiredMinimumHeight();
                 Point size = getDefaultDisplaySize(new Point());
                 float spotlightX = (float) size.x / width;
                 float spotlightY = (float) size.y / height;
-                Intent request = new Intent(CropActivity.CROP_ACTION)
-                        .setDataAndType(mPickedItem, IMAGE_TYPE)
-                        .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
-                        .putExtra(CropExtras.KEY_OUTPUT_X, width)
-                        .putExtra(CropExtras.KEY_OUTPUT_Y, height)
-                        .putExtra(CropExtras.KEY_ASPECT_X, width)
-                        .putExtra(CropExtras.KEY_ASPECT_Y, height)
-                        .putExtra(CropExtras.KEY_SPOTLIGHT_X, spotlightX)
-                        .putExtra(CropExtras.KEY_SPOTLIGHT_Y, spotlightY)
-                        .putExtra(CropExtras.KEY_SCALE, true)
-                        .putExtra(CropExtras.KEY_SCALE_UP_IF_NEEDED, true)
-                        .putExtra(CropExtras.KEY_SET_AS_WALLPAPER, true);
-                startActivity(request);
+                cropAndSetWallpaperIntent = new Intent(CropActivity.CROP_ACTION)
+                    .setClass(this, CropActivity.class)
+                    .setDataAndType(mPickedItem, IMAGE_TYPE)
+                    .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
+                    .putExtra(CropExtras.KEY_OUTPUT_X, width)
+                    .putExtra(CropExtras.KEY_OUTPUT_Y, height)
+                    .putExtra(CropExtras.KEY_ASPECT_X, width)
+                    .putExtra(CropExtras.KEY_ASPECT_Y, height)
+                    .putExtra(CropExtras.KEY_SPOTLIGHT_X, spotlightX)
+                    .putExtra(CropExtras.KEY_SPOTLIGHT_Y, spotlightY)
+                    .putExtra(CropExtras.KEY_SCALE, true)
+                    .putExtra(CropExtras.KEY_SCALE_UP_IF_NEEDED, true)
+                    .putExtra(CropExtras.KEY_SET_AS_WALLPAPER, true);
+                startActivity(cropAndSetWallpaperIntent);
                 finish();
             }
         }
diff --git a/src/com/android/gallery3d/data/LocalImage.java b/src/com/android/gallery3d/data/LocalImage.java
index 94cb059..2b01c1e 100644
--- a/src/com/android/gallery3d/data/LocalImage.java
+++ b/src/com/android/gallery3d/data/LocalImage.java
@@ -237,9 +237,9 @@
     @Override
     public int getSupportedOperations() {
         int operation = SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_CROP
-                | SUPPORT_SETAS | SUPPORT_EDIT | SUPPORT_INFO;
+                | SUPPORT_SETAS | SUPPORT_PRINT | SUPPORT_INFO;
         if (BitmapUtils.isSupportedByRegionDecoder(mimeType)) {
-            operation |= SUPPORT_FULL_IMAGE;
+            operation |= SUPPORT_FULL_IMAGE | SUPPORT_EDIT;
         }
 
         if (BitmapUtils.isRotationSupported(mimeType)) {
diff --git a/src/com/android/gallery3d/data/LocalSource.java b/src/com/android/gallery3d/data/LocalSource.java
index a2e3d14..810aef4 100644
--- a/src/com/android/gallery3d/data/LocalSource.java
+++ b/src/com/android/gallery3d/data/LocalSource.java
@@ -22,7 +22,7 @@
 import android.net.Uri;
 import android.provider.MediaStore;
 
-import com.android.gallery3d.app.Gallery;
+import com.android.gallery3d.app.GalleryActivity;
 import com.android.gallery3d.app.GalleryApp;
 import com.android.gallery3d.data.MediaSet.ItemConsumer;
 
@@ -130,7 +130,7 @@
 
     private Path getAlbumPath(Uri uri, int defaultType) {
         int mediaType = getMediaType(
-                uri.getQueryParameter(Gallery.KEY_MEDIA_TYPES),
+                uri.getQueryParameter(GalleryActivity.KEY_MEDIA_TYPES),
                 defaultType);
         String bucketId = uri.getQueryParameter(KEY_BUCKET_ID);
         int id = 0;
diff --git a/src/com/android/gallery3d/data/MediaObject.java b/src/com/android/gallery3d/data/MediaObject.java
index 270d4cf..530ee30 100644
--- a/src/com/android/gallery3d/data/MediaObject.java
+++ b/src/com/android/gallery3d/data/MediaObject.java
@@ -41,6 +41,7 @@
     public static final int SUPPORT_ACTION = 1 << 14;
     public static final int SUPPORT_CAMERA_SHORTCUT = 1 << 15;
     public static final int SUPPORT_MUTE = 1 << 16;
+    public static final int SUPPORT_PRINT = 1 << 17;
     public static final int SUPPORT_ALL = 0xffffffff;
 
     // These are the bits returned from getMediaType():
diff --git a/src/com/android/gallery3d/data/UriImage.java b/src/com/android/gallery3d/data/UriImage.java
index e8875b5..b3fe1de 100644
--- a/src/com/android/gallery3d/data/UriImage.java
+++ b/src/com/android/gallery3d/data/UriImage.java
@@ -211,10 +211,10 @@
 
     @Override
     public int getSupportedOperations() {
-        int supported = SUPPORT_EDIT | SUPPORT_SETAS;
+        int supported = SUPPORT_PRINT | SUPPORT_SETAS;
         if (isSharable()) supported |= SUPPORT_SHARE;
         if (BitmapUtils.isSupportedByRegionDecoder(mContentType)) {
-            supported |= SUPPORT_FULL_IMAGE;
+            supported |= SUPPORT_EDIT | SUPPORT_FULL_IMAGE;
         }
         return supported;
     }
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
index f46a0b7..3807e1d 100644
--- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java
+++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
@@ -29,7 +29,9 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
@@ -44,6 +46,7 @@
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentTransaction;
+import android.support.v4.print.PrintHelper;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
@@ -57,8 +60,10 @@
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.FrameLayout;
+import android.widget.PopupMenu;
 import android.widget.ShareActionProvider;
 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
+import android.widget.Spinner;
 import android.widget.Toast;
 
 import com.android.gallery3d.R;
@@ -87,9 +92,11 @@
 import com.android.gallery3d.filtershow.editors.EditorStraighten;
 import com.android.gallery3d.filtershow.editors.EditorTinyPlanet;
 import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
+import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation;
 import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation;
 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
 import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation;
+import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation;
 import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
 import com.android.gallery3d.filtershow.filters.FiltersManager;
 import com.android.gallery3d.filtershow.filters.ImageFilter;
@@ -112,8 +119,6 @@
 import com.android.gallery3d.filtershow.ui.ExportDialog;
 import com.android.gallery3d.filtershow.ui.FramedTextButton;
 import com.android.gallery3d.util.GalleryUtils;
-import com.android.gallery3d.util.PrintJob;
-import com.android.gallery3d.util.UsageStatistics;
 import com.android.photos.data.GalleryBitmapPool;
 
 import java.io.File;
@@ -124,7 +129,8 @@
 import java.util.Vector;
 
 public class FilterShowActivity extends FragmentActivity implements OnItemClickListener,
-        OnShareTargetSelectedListener {
+        OnShareTargetSelectedListener, DialogInterface.OnShowListener,
+        DialogInterface.OnDismissListener, PopupMenu.OnDismissListener{
 
     private String mAction = "";
     MasterImage mMasterImage = null;
@@ -133,6 +139,7 @@
 
     public static final String TINY_PLANET_ACTION = "com.android.camera.action.TINY_PLANET";
     public static final String LAUNCH_FULLSCREEN = "launch-fullscreen";
+    public static final boolean RESET_TO_LOADED = false;
     private ImageShow mImageShow = null;
 
     private View mSaveButton = null;
@@ -146,7 +153,6 @@
     private boolean mShowingTinyPlanet = false;
     private boolean mShowingImageStatePanel = false;
     private boolean mShowingVersionsPanel = false;
-    private boolean mShowingInformationPanel = false;
 
     private final Vector<ImageShow> mImageViews = new Vector<ImageShow>();
 
@@ -164,6 +170,7 @@
 
     private Uri mSelectedImageUri = null;
 
+    private ArrayList<Action> mActions = new ArrayList<Action>();
     private UserPresetsManager mUserPresetsManager = null;
     private UserPresetsAdapter mUserPresetsAdapter = null;
     private CategoryAdapter mCategoryLooksAdapter = null;
@@ -184,6 +191,10 @@
 
     private ProcessingService mBoundService;
     private boolean mIsBound = false;
+    private Menu mMenu;
+    private DialogInterface mCurrentDialog = null;
+    private PopupMenu mCurrentMenu = null;
+    private boolean mLoadingVisible = true;
 
     public ProcessingService getProcessingService() {
         return mBoundService;
@@ -240,17 +251,21 @@
         }
     }
 
-    private void setupPipeline() {
-        doBindService();
+    public void updateUIAfterServiceStarted() {
+        MasterImage.setMaster(mMasterImage);
         ImageFilter.setActivityForMemoryToasts(this);
         mUserPresetsManager = new UserPresetsManager(this);
         mUserPresetsAdapter = new UserPresetsAdapter(this);
-    }
 
-    public void updateUIAfterServiceStarted() {
+        setupMasterImage();
+        setupMenu();
+        setDefaultValues();
+        fillEditors();
+        getWindow().setBackgroundDrawable(new ColorDrawable(0));
+        loadXML();
+
         fillCategories();
         loadMainPanel();
-        setDefaultPreset();
         extractXMPData();
         processIntent();
     }
@@ -263,19 +278,11 @@
         if (onlyUsePortrait) {
             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
         }
-        MasterImage.setMaster(mMasterImage);
 
         clearGalleryBitmapPool();
-        setupPipeline();
-
-        setupMasterImage();
-        setDefaultValues();
-        fillEditors();
-
-        loadXML();
-        UsageStatistics.onContentViewChanged(UsageStatistics.COMPONENT_EDITOR, "Main");
-        UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
-                UsageStatistics.CATEGORY_LIFECYCLE, UsageStatistics.LIFECYCLE_START);
+        doBindService();
+        getWindow().setBackgroundDrawable(new ColorDrawable(Color.GRAY));
+        setContentView(R.layout.filtershow_splashscreen);
     }
 
     public boolean isShowingImageStatePanel() {
@@ -330,35 +337,12 @@
         }
     }
 
-    public void hideInformationPanel() {
-        FrameLayout infoLayout = (FrameLayout) findViewById(R.id.central_panel_container);
-        infoLayout.setVisibility(View.GONE);
-        Fragment fragment = getSupportFragmentManager().findFragmentByTag(InfoPanel.FRAGMENT_TAG);
-        if (fragment != null) {
-            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-            transaction.remove(fragment);
-            transaction.commit();
-        }
-        mShowingInformationPanel = false;
-    }
-
     public void toggleInformationPanel() {
-        mShowingInformationPanel = !mShowingInformationPanel;
-        if (!mShowingInformationPanel) {
-            hideInformationPanel();
-            showDefaultImageView();
-            return;
-        }
         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
         transaction.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
-        FrameLayout infoLayout = (FrameLayout) findViewById(R.id.central_panel_container);
-        infoLayout.setVisibility(View.VISIBLE);
-        mEditorPlaceHolder.hide();
-        mImageShow.setVisibility(View.GONE);
 
         InfoPanel panel = new InfoPanel();
-        transaction.replace(R.id.central_panel_container, panel, InfoPanel.FRAGMENT_TAG);
-        transaction.commit();
+        panel.show(transaction, InfoPanel.FRAGMENT_TAG);
     }
 
     private void loadXML() {
@@ -384,7 +368,7 @@
         setupEditors();
 
         mEditorPlaceHolder.hide();
-        mImageShow.bindAsImageLoadListener();
+        mImageShow.attach();
 
         setupStatePanel();
     }
@@ -403,10 +387,27 @@
     }
 
     private void fillVersions() {
+        if (mCategoryVersionsAdapter != null) {
+            mCategoryVersionsAdapter.clear();
+        }
         mCategoryVersionsAdapter = new CategoryAdapter(this);
         mCategoryVersionsAdapter.setShowAddButton(true);
     }
 
+    public void registerAction(Action action) {
+        if (mActions.contains(action)) {
+            return;
+        }
+        mActions.add(action);
+    }
+
+    private void loadActions() {
+        for (int i = 0; i < mActions.size(); i++) {
+            Action action = mActions.get(i);
+            action.setImageFrame(new Rect(0, 0, 96, 96), 0);
+        }
+    }
+
     public void updateVersions() {
         mCategoryVersionsAdapter.clear();
         FilterUserPresetRepresentation originalRep = new FilterUserPresetRepresentation(
@@ -455,6 +456,9 @@
     private void fillEffects() {
         FiltersManager filtersManager = FiltersManager.getManager();
         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getEffects();
+        if (mCategoryFiltersAdapter != null) {
+            mCategoryFiltersAdapter.clear();
+        }
         mCategoryFiltersAdapter = new CategoryAdapter(this);
         for (FilterRepresentation representation : filtersRepresentations) {
             if (representation.getTextId() != 0) {
@@ -467,9 +471,22 @@
     private void fillTools() {
         FiltersManager filtersManager = FiltersManager.getManager();
         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getTools();
+        if (mCategoryGeometryAdapter != null) {
+            mCategoryGeometryAdapter.clear();
+        }
         mCategoryGeometryAdapter = new CategoryAdapter(this);
+        boolean found = false;
         for (FilterRepresentation representation : filtersRepresentations) {
             mCategoryGeometryAdapter.add(new Action(this, representation));
+            if (representation instanceof FilterDrawRepresentation) {
+                found = true;
+            }
+        }
+        if (!found) {
+            FilterRepresentation representation = new FilterDrawRepresentation();
+            Action action = new Action(this, representation);
+            action.setIsDoubleAction(true);
+            mCategoryGeometryAdapter.add(action);
         }
     }
 
@@ -525,13 +542,14 @@
         int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
         Spline.setCurveHandle(curveHandle, curveHandleSize);
         Spline.setCurveWidth((int) getPixelsFromDip(3));
+
+        mOriginalImageUri = null;
     }
 
     private void startLoadBitmap(Uri uri) {
-        final View loading = findViewById(R.id.loading);
         final View imageShow = findViewById(R.id.imageShow);
         imageShow.setVisibility(View.INVISIBLE);
-        loading.setVisibility(View.VISIBLE);
+        startLoadingIndicator();
         mShowingTinyPlanet = false;
         mLoadBitmapTask = new LoadBitmapTask();
         mLoadBitmapTask.execute(uri);
@@ -549,6 +567,9 @@
             }
         }
 
+        if (mCategoryBordersAdapter != null) {
+            mCategoryBordersAdapter.clear();
+        }
         mCategoryBordersAdapter = new CategoryAdapter(this);
         for (FilterRepresentation representation : borders) {
             if (representation.getTextId() != 0) {
@@ -646,6 +667,18 @@
             FilterMirrorRepresentation r = (FilterMirrorRepresentation) representation;
             r.cycle();
         }
+        if (representation.isBooleanFilter()) {
+            ImagePreset preset = MasterImage.getImage().getPreset();
+            if (preset.getRepresentation(representation) != null) {
+                // remove
+                ImagePreset copy = new ImagePreset(preset);
+                copy.removeFilter(representation);
+                FilterRepresentation filterRepresentation = representation.copy();
+                MasterImage.getImage().setPreset(copy, filterRepresentation, true);
+                MasterImage.getImage().setCurrentFilterRepresentation(null);
+                return;
+            }
+        }
         useFilterRepresentation(representation);
 
         // show representation
@@ -654,7 +687,6 @@
         }
         mCurrentEditor = mEditorPlaceHolder.showEditor(representation.getEditorId());
         loadEditorPanel(representation, mCurrentEditor);
-        hideInformationPanel();
     }
 
     public Editor getEditor(int editorID) {
@@ -670,6 +702,9 @@
     }
 
     public void updateCategories() {
+        if (mMasterImage == null) {
+            return;
+        }
         ImagePreset preset = mMasterImage.getPreset();
         mCategoryLooksAdapter.reflectImagePreset(preset);
         mCategoryBordersAdapter.reflectImagePreset(preset);
@@ -679,6 +714,30 @@
         return findViewById(id);
     }
 
+    public void onShowMenu(PopupMenu menu) {
+        mCurrentMenu = menu;
+        menu.setOnDismissListener(this);
+    }
+
+    @Override
+    public void onDismiss(PopupMenu popupMenu){
+        if (mCurrentMenu == null) {
+            return;
+        }
+        mCurrentMenu.setOnDismissListener(null);
+        mCurrentMenu = null;
+    }
+
+    @Override
+    public void onShow(DialogInterface dialog) {
+        mCurrentDialog = dialog;
+    }
+
+    @Override
+    public void onDismiss(DialogInterface dialogInterface) {
+        mCurrentDialog = null;
+    }
+
     private class LoadHighresBitmapTask extends AsyncTask<Void, Void, Boolean> {
         @Override
         protected Boolean doInBackground(Void... params) {
@@ -713,6 +772,22 @@
         }
     }
 
+    public boolean isLoadingVisible() {
+        return mLoadingVisible;
+    }
+
+    public void startLoadingIndicator() {
+        final View loading = findViewById(R.id.loading);
+        mLoadingVisible = true;
+        loading.setVisibility(View.VISIBLE);
+    }
+
+    public void stopLoadingIndicator() {
+        final View loading = findViewById(R.id.loading);
+        loading.setVisibility(View.GONE);
+        mLoadingVisible = false;
+    }
+
     private class LoadBitmapTask extends AsyncTask<Uri, Boolean, Boolean> {
         int mBitmapSize;
 
@@ -748,7 +823,8 @@
             }
 
             if (!result) {
-                if (!mOriginalImageUri.equals(mSelectedImageUri)) {
+                if (mOriginalImageUri != null
+                        && !mOriginalImageUri.equals(mSelectedImageUri)) {
                     mOriginalImageUri = mSelectedImageUri;
                     mOriginalPreset = null;
                     Toast.makeText(FilterShowActivity.this,
@@ -764,14 +840,12 @@
                 Log.v(LOGTAG,"RenderScript context destroyed during load");
                 return;
             }
-            final View loading = findViewById(R.id.loading);
-            loading.setVisibility(View.GONE);
             final View imageShow = findViewById(R.id.imageShow);
             imageShow.setVisibility(View.VISIBLE);
 
+
             Bitmap largeBitmap = MasterImage.getImage().getOriginalBitmapLarge();
             mBoundService.setOriginalBitmap(largeBitmap);
-            MasterImage.getImage().resetGeometryImages(true);
 
             float previewScale = (float) largeBitmap.getWidth()
                     / (float) MasterImage.getImage().getOriginalBounds().width();
@@ -785,13 +859,20 @@
             mCategoryFiltersAdapter.imageLoaded();
             mLoadBitmapTask = null;
 
+            MasterImage.getImage().warnListeners();
+            loadActions();
+
             if (mOriginalPreset != null) {
                 MasterImage.getImage().setLoadedPreset(mOriginalPreset);
                 MasterImage.getImage().setPreset(mOriginalPreset,
                         mOriginalPreset.getLastRepresentation(), true);
                 mOriginalPreset = null;
+            } else {
+                setDefaultPreset();
             }
 
+            MasterImage.getImage().resetGeometryImages(true);
+
             if (mAction == TINY_PLANET_ACTION) {
                 showRepresentation(mCategoryFiltersAdapter.getTinyPlanet());
             }
@@ -916,14 +997,25 @@
                 .getActionProvider();
         mShareActionProvider.setShareIntent(getDefaultShareIntent());
         mShareActionProvider.setOnShareTargetSelectedListener(this);
-
-        MenuItem undoItem = menu.findItem(R.id.undoButton);
-        MenuItem redoItem = menu.findItem(R.id.redoButton);
-        MenuItem resetItem = menu.findItem(R.id.resetHistoryButton);
-        mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
+        mMenu = menu;
+        setupMenu();
         return true;
     }
 
+    private void setupMenu(){
+        if (mMenu == null || mMasterImage == null) {
+            return;
+        }
+        MenuItem undoItem = mMenu.findItem(R.id.undoButton);
+        MenuItem redoItem = mMenu.findItem(R.id.redoButton);
+        MenuItem resetItem = mMenu.findItem(R.id.resetHistoryButton);
+        MenuItem printItem = mMenu.findItem(R.id.printButton);
+        if (!PrintHelper.systemSupportsPrint()) {
+            printItem.setVisible(false);
+        }
+        mMasterImage.getHistory().setMenuItems(undoItem, redoItem, resetItem);
+    }
+
     @Override
     public void onPause() {
         super.onPause();
@@ -949,8 +1041,6 @@
                 mMasterImage.onHistoryItemClick(position);
                 backToMain();
                 invalidateViews();
-                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
-                        UsageStatistics.CATEGORY_BUTTON_PRESS, "Undo");
                 return true;
             }
             case R.id.redoButton: {
@@ -958,21 +1048,14 @@
                 int position = adapter.redo();
                 mMasterImage.onHistoryItemClick(position);
                 invalidateViews();
-                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
-                        UsageStatistics.CATEGORY_BUTTON_PRESS, "Redo");
                 return true;
             }
             case R.id.resetHistoryButton: {
                 resetHistory();
-                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
-                        UsageStatistics.CATEGORY_BUTTON_PRESS, "ResetHistory");
                 return true;
             }
             case R.id.showImageStateButton: {
                 toggleImageStatePanel();
-                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
-                        UsageStatistics.CATEGORY_BUTTON_PRESS,
-                        mShowingImageStatePanel ? "ShowPanel" : "HidePanel");
                 return true;
             }
             case R.id.exportFlattenButton: {
@@ -1001,7 +1084,8 @@
 
     public void print() {
         Bitmap bitmap = MasterImage.getImage().getHighresImage();
-        PrintJob.printBitmap(this, "ImagePrint", bitmap);
+        PrintHelper printer = new PrintHelper(this);
+        printer.printBitmap("ImagePrint", bitmap);
     }
 
     public void addNewPreset() {
@@ -1087,6 +1171,9 @@
         FiltersManager filtersManager = FiltersManager.getManager();
         ArrayList<FilterRepresentation> filtersRepresentations = filtersManager.getLooks();
 
+        if (mCategoryLooksAdapter != null) {
+            mCategoryLooksAdapter.clear();
+        }
         mCategoryLooksAdapter = new CategoryAdapter(this);
         int verticalItemHeight = (int) getResources().getDimension(R.dimen.action_item_height);
         mCategoryLooksAdapter.setItemHeight(verticalItemHeight);
@@ -1161,17 +1248,28 @@
     public void onConfigurationChanged(Configuration newConfig)
     {
         super.onConfigurationChanged(newConfig);
+
         setDefaultValues();
+        if (mMasterImage == null) {
+            return;
+        }
         loadXML();
         fillCategories();
         loadMainPanel();
 
+        if (mCurrentMenu != null) {
+            mCurrentMenu.dismiss();
+            mCurrentMenu = null;
+        }
+        if (mCurrentDialog != null) {
+            mCurrentDialog.dismiss();
+            mCurrentDialog = null;
+        }
         // mLoadBitmapTask==null implies you have looked at the intent
         if (!mShowingTinyPlanet && (mLoadBitmapTask == null)) {
             mCategoryFiltersAdapter.removeTinyPlanet();
         }
-        final View loading = findViewById(R.id.loading);
-        loading.setVisibility(View.GONE);
+        stopLoadingIndicator();
     }
 
     public void setupMasterImage() {
@@ -1195,14 +1293,22 @@
         HistoryManager adapter = mMasterImage.getHistory();
         adapter.reset();
         HistoryItem historyItem = adapter.getItem(0);
-        ImagePreset original = new ImagePreset(historyItem.getImagePreset());
-        mMasterImage.setPreset(original, historyItem.getFilterRepresentation(), true);
+        ImagePreset original = null;
+        if (RESET_TO_LOADED) {
+            original = new ImagePreset(historyItem.getImagePreset());
+        } else {
+            original = new ImagePreset();
+        }
+        FilterRepresentation rep = null;
+        if (historyItem != null) {
+            rep = historyItem.getFilterRepresentation();
+        }
+        mMasterImage.setPreset(original, rep, true);
         invalidateViews();
         backToMain();
     }
 
     public void showDefaultImageView() {
-        hideInformationPanel();
         mEditorPlaceHolder.hide();
         mImageShow.setVisibility(View.VISIBLE);
         MasterImage.getImage().setCurrentFilter(null);
@@ -1374,11 +1480,23 @@
         return super.dispatchTouchEvent(ev);
     }
 
+    public Point mHintTouchPoint = new Point();
+
+    public Point hintTouchPoint(View view) {
+        int location[] = new int[2];
+        view.getLocationOnScreen(location);
+        int x = mHintTouchPoint.x - location[0];
+        int y = mHintTouchPoint.y - location[1];
+        return new Point(x, y);
+    }
+
     public void startTouchAnimation(View target, float x, float y) {
         final CategorySelected hint =
                 (CategorySelected) findViewById(R.id.categorySelectedIndicator);
         int location[] = new int[2];
         target.getLocationOnScreen(location);
+        mHintTouchPoint.x = (int) (location[0] + x);
+        mHintTouchPoint.y = (int) (location[1] + y);
         int locationHint[] = new int[2];
         ((View)hint.getParent()).getLocationOnScreen(locationHint);
         int dx = (int) (x - (hint.getWidth())/2);
diff --git a/src/com/android/gallery3d/filtershow/cache/BitmapCache.java b/src/com/android/gallery3d/filtershow/cache/BitmapCache.java
index 339ddf0..cd63d30 100644
--- a/src/com/android/gallery3d/filtershow/cache/BitmapCache.java
+++ b/src/com/android/gallery3d/filtershow/cache/BitmapCache.java
@@ -20,6 +20,7 @@
 import android.graphics.Canvas;
 import android.util.Log;
 import com.android.gallery3d.filtershow.pipeline.Buffer;
+import com.android.gallery3d.filtershow.pipeline.CacheProcessing;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -31,6 +32,92 @@
             mBitmapCache = new HashMap<Long, ArrayList<WeakReference<Bitmap>>>();
     private final int mMaxItemsPerKey = 4;
 
+    private static final boolean DEBUG = false;
+    private CacheProcessing mCacheProcessing;
+
+    public final static int PREVIEW_CACHE = 1;
+    public final static int NEW_LOOK = 2;
+    public final static int ICON = 3;
+    public final static int FILTERS = 4;
+    public final static int GEOMETRY = 5;
+    public final static int HIGHRES = 6;
+    public final static int UTIL_GEOMETRY = 7;
+    public final static int RENDERING_REQUEST = 8;
+    public final static int REGION = 9;
+    public final static int TINY_PLANET = 10;
+    public final static int PREVIEW_CACHE_NO_FILTERS = 11;
+    public final static int PREVIEW_CACHE_NO_ROOT = 12;
+    public static final int PREVIEW_CACHE_NO_APPLY = 13;
+    public final static int TRACKING_COUNT = 14;
+    private int[] mTracking = new int[TRACKING_COUNT];
+
+    class BitmapTracking {
+        Bitmap bitmap;
+        int type;
+    }
+
+    private ArrayList<BitmapTracking> mBitmapTracking = new ArrayList<BitmapTracking>();
+
+    private void track(Bitmap bitmap, int type) {
+        for (int i = 0; i < mBitmapTracking.size(); i++) {
+            BitmapTracking tracking = mBitmapTracking.get(i);
+            if (tracking.bitmap == bitmap) {
+                Log.e(LOGTAG, "giving a bitmap already given!!!");
+            }
+        }
+        BitmapTracking tracking = new BitmapTracking();
+        tracking.bitmap = bitmap;
+        tracking.type = type;
+        mBitmapTracking.add(tracking);
+        mTracking[tracking.type] ++;
+    }
+
+    private void untrack(Bitmap bitmap) {
+        for (int i = 0; i < mBitmapTracking.size(); i++) {
+            BitmapTracking tracking = mBitmapTracking.get(i);
+            if (tracking.bitmap == bitmap) {
+                mTracking[tracking.type] --;
+                mBitmapTracking.remove(i);
+                return;
+            }
+        }
+    }
+
+    public String getTrackingName(int i) {
+        switch (i) {
+            case PREVIEW_CACHE: return "PREVIEW_CACHE";
+            case PREVIEW_CACHE_NO_FILTERS: return "PREVIEW_CACHE_NO_FILTERS";
+            case PREVIEW_CACHE_NO_ROOT: return "PREVIEW_CACHE_NO_ROOT";
+            case PREVIEW_CACHE_NO_APPLY: return "PREVIEW_CACHE_NO_APPLY";
+            case NEW_LOOK: return "NEW_LOOK";
+            case ICON: return "ICON";
+            case FILTERS: return "FILTERS";
+            case GEOMETRY: return "GEOMETRY";
+            case HIGHRES: return "HIGHRES";
+            case UTIL_GEOMETRY: return "UTIL_GEOMETRY";
+            case RENDERING_REQUEST: return "RENDERING_REQUEST";
+            case REGION: return "REGION";
+            case TINY_PLANET: return "TINY_PLANET";
+        }
+        return "UNKNOWN";
+    }
+
+    public void showBitmapCounts() {
+        if (!DEBUG) {
+            return;
+        }
+        Log.v(LOGTAG, "\n--- showBitmap --- ");
+        for (int i = 0; i < TRACKING_COUNT; i++) {
+            if (mTracking[i] != 0) {
+                Log.v(LOGTAG, getTrackingName(i) + " => " + mTracking[i]);
+            }
+        }
+    }
+
+    public void setCacheProcessing(CacheProcessing cache) {
+        mCacheProcessing = cache;
+    }
+
     public void cache(Buffer buffer) {
         if (buffer == null) {
             return;
@@ -39,9 +126,20 @@
         cache(bitmap);
     }
 
-    public synchronized void cache(Bitmap bitmap) {
+    public synchronized boolean cache(Bitmap bitmap) {
         if (bitmap == null) {
-            return;
+            return true;
+        }
+        if (mCacheProcessing != null && mCacheProcessing.contains(bitmap)) {
+            Log.e(LOGTAG, "Trying to cache a bitmap still used in the pipeline");
+            return false;
+        }
+        if (DEBUG) {
+            untrack(bitmap);
+        }
+        if (!bitmap.isMutable()) {
+            Log.e(LOGTAG, "Trying to cache a non mutable bitmap");
+            return true;
         }
         Long key = calcKey(bitmap.getWidth(), bitmap.getHeight());
         ArrayList<WeakReference<Bitmap>> list = mBitmapCache.get(key);
@@ -66,14 +164,15 @@
             for (i = 0; i < list.size(); i++) {
                 WeakReference<Bitmap> ref = list.get(i);
                 if (ref.get() == bitmap) {
-                    return; // bitmap already in the cache
+                    return true; // bitmap already in the cache
                 }
             }
             list.add(new WeakReference<Bitmap>(bitmap));
         }
+        return true;
     }
 
-    public synchronized Bitmap getBitmap(int w, int h) {
+    public synchronized Bitmap getBitmap(int w, int h, int type) {
         Long key = calcKey(w, h);
         WeakReference<Bitmap> ref = null;
         ArrayList<WeakReference<Bitmap>> list = mBitmapCache.get(key);
@@ -92,12 +191,20 @@
                 || bitmap.getHeight() != h) {
             bitmap = Bitmap.createBitmap(
                     w, h, Bitmap.Config.ARGB_8888);
+            showBitmapCounts();
+        }
+
+        if (DEBUG) {
+            track(bitmap, type);
+            if (mCacheProcessing != null && mCacheProcessing.contains(bitmap)) {
+                Log.e(LOGTAG, "Trying to give a bitmap used in the pipeline");
+            }
         }
         return bitmap;
     }
 
-    public Bitmap getBitmapCopy(Bitmap source) {
-        Bitmap bitmap = getBitmap(source.getWidth(), source.getHeight());
+    public synchronized Bitmap getBitmapCopy(Bitmap source, int type) {
+        Bitmap bitmap = getBitmap(source.getWidth(), source.getHeight(), type);
         Canvas canvas = new Canvas(bitmap);
         canvas.drawBitmap(source, 0, 0, null);
         return bitmap;
diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
index 30cd3f3..69edd60 100644
--- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
+++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java
@@ -65,7 +65,7 @@
     public static final int ORI_TRANSVERSE = ExifInterface.Orientation.LEFT_BOTTOM;
 
     private static final int BITMAP_LOAD_BACKOUT_ATTEMPTS = 5;
-
+    private static final float OVERDRAW_ZOOM = 1.2f;
     private ImageLoader() {}
 
     /**
@@ -124,6 +124,8 @@
             // Do nothing
         } catch (IllegalArgumentException e) {
             // Do nothing
+        } catch (IllegalStateException e) {
+            // Do nothing
         } finally {
             Utils.closeSilently(cursor);
         }
@@ -233,7 +235,7 @@
      * if it is a subset of the bitmap stored at uri.  Otherwise returns
      * null.
      */
-    public static Bitmap loadRegionBitmap(Context context, FilterEnvironment environment,
+    public static Bitmap loadRegionBitmap(Context context, BitmapCache cache,
                                           Uri uri, BitmapFactory.Options options,
                                           Rect bounds) {
         InputStream is = null;
@@ -252,21 +254,15 @@
             // return null if bounds are not entirely within the bitmap
             if (!r.contains(imageBounds)) {
                 imageBounds.intersect(r);
+                bounds.left = imageBounds.left;
+                bounds.top = imageBounds.top;
             }
-            Bitmap reuse = environment.getBitmap(imageBounds.width(), imageBounds.height());
+            Bitmap reuse = cache.getBitmap(imageBounds.width(),
+                    imageBounds.height(), BitmapCache.REGION);
             options.inBitmap = reuse;
             Bitmap bitmap = decoder.decodeRegion(imageBounds, options);
             if (bitmap != reuse) {
-                environment.cache(reuse); // not reused, put back in cache
-            }
-            if (imageBounds.width() != bounds.width() || imageBounds.height() != bounds.height()) {
-                Bitmap temp = environment.getBitmap(bounds.width(), bounds.height());
-                Canvas canvas = new Canvas(temp);
-                canvas.drawARGB(0, 0, 0, 0);
-                float dx = imageBounds.left - bounds.left;
-                float dy = imageBounds.top - bounds.top;
-                canvas.drawBitmap(bitmap, dx, dy, null);
-                return temp;
+                cache.cache(reuse); // not reused, put back in cache
             }
             return bitmap;
         } catch (FileNotFoundException e) {
@@ -288,6 +284,7 @@
      */
     public static Rect loadBitmapBounds(Context context, Uri uri) {
         BitmapFactory.Options o = new BitmapFactory.Options();
+        o.inJustDecodeBounds = true;
         loadBitmap(context, uri, o);
         return new Rect(0, 0, o.outWidth, o.outHeight);
     }
@@ -400,24 +397,24 @@
     }
 
     public static Bitmap getScaleOneImageForPreset(Context context,
-                                                   FilterEnvironment environment,
+                                                   BitmapCache cache,
                                                    Uri uri, Rect bounds,
                                                    Rect destination) {
         BitmapFactory.Options options = new BitmapFactory.Options();
         options.inMutable = true;
         if (destination != null) {
-            if (bounds.width() > destination.width()) {
+            int thresholdWidth = (int) (destination.width() * OVERDRAW_ZOOM);
+            if (bounds.width() > thresholdWidth) {
                 int sampleSize = 1;
                 int w = bounds.width();
-                while (w > destination.width()) {
+                while (w > thresholdWidth) {
                     sampleSize *= 2;
                     w /= sampleSize;
                 }
                 options.inSampleSize = sampleSize;
             }
         }
-        Bitmap bmp = loadRegionBitmap(context, environment, uri, options, bounds);
-        return bmp;
+        return loadRegionBitmap(context, cache, uri, options, bounds);
     }
 
     /**
diff --git a/src/com/android/gallery3d/filtershow/category/Action.java b/src/com/android/gallery3d/filtershow/category/Action.java
index 7bdc8ec..b3a4148 100644
--- a/src/com/android/gallery3d/filtershow/category/Action.java
+++ b/src/com/android/gallery3d/filtershow/category/Action.java
@@ -24,8 +24,13 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.util.Log;
 import android.widget.ArrayAdapter;
 import android.widget.ListAdapter;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.filters.FilterDrawRepresentation;
 import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation;
 import com.android.gallery3d.filtershow.pipeline.RenderingRequest;
 import com.android.gallery3d.filtershow.pipeline.RenderingRequestCaller;
@@ -48,29 +53,42 @@
     private int mType = CROP_VIEW;
     private Bitmap mPortraitImage;
     private Bitmap mOverlayBitmap;
-    private Context mContext;
+    private FilterShowActivity mContext;
     private boolean mCanBeRemoved = false;
+    private int mTextSize = 32;
+    private boolean mIsDoubleAction = false;
 
-    public Action(Context context, FilterRepresentation representation, int type,
+    public Action(FilterShowActivity context, FilterRepresentation representation, int type,
                   boolean canBeRemoved) {
         this(context, representation, type);
         mCanBeRemoved = canBeRemoved;
+        mTextSize = context.getResources().getDimensionPixelSize(
+                R.dimen.category_panel_text_size);
     }
 
-    public Action(Context context, FilterRepresentation representation, int type) {
+    public Action(FilterShowActivity context, FilterRepresentation representation, int type) {
         this(context, type);
         setRepresentation(representation);
     }
 
-    public Action(Context context, int type) {
+    public Action(FilterShowActivity context, int type) {
         mContext = context;
         setType(type);
+        mContext.registerAction(this);
     }
 
-    public Action(Context context, FilterRepresentation representation) {
+    public Action(FilterShowActivity context, FilterRepresentation representation) {
         this(context, representation, CROP_VIEW);
     }
 
+    public boolean isDoubleAction() {
+        return mIsDoubleAction;
+    }
+
+    public void setIsDoubleAction(boolean value) {
+        mIsDoubleAction = value;
+    }
+
     public boolean canBeRemoved() {
         return mCanBeRemoved;
     }
@@ -100,19 +118,19 @@
         if (mImageFrame != null && mImageFrame.equals(imageFrame)) {
             return;
         }
-        Bitmap bitmap = MasterImage.getImage().getLargeThumbnailBitmap();
+        if (getType() == Action.ADD_ACTION) {
+            return;
+        }
+        Bitmap temp = MasterImage.getImage().getTemporaryThumbnailBitmap();
+        if (temp != null) {
+            mImage = temp;
+        }
+        Bitmap bitmap = MasterImage.getImage().getThumbnailBitmap();
         if (bitmap != null) {
             mImageFrame = imageFrame;
             int w = mImageFrame.width();
             int h = mImageFrame.height();
-            if (orientation == CategoryView.VERTICAL
-                && mType == CROP_VIEW) {
-                w /= 2;
-            }
-            Bitmap bitmapCrop = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
-            drawCenteredImage(bitmap, bitmapCrop, true);
-
-            postNewIconRenderRequest(bitmapCrop);
+            postNewIconRenderRequest(w, h);
         }
     }
 
@@ -132,40 +150,37 @@
         mType = type;
     }
 
-    private void postNewIconRenderRequest(Bitmap bitmap) {
-        if (bitmap != null && mRepresentation != null) {
+    private void postNewIconRenderRequest(int w, int h) {
+        if (mRepresentation != null) {
             ImagePreset preset = new ImagePreset();
             preset.addFilter(mRepresentation);
-            RenderingRequest.post(mContext, bitmap,
-                    preset, RenderingRequest.ICON_RENDERING, this);
+            RenderingRequest.postIconRequest(mContext, w, h, preset, this);
         }
     }
 
     private void drawCenteredImage(Bitmap source, Bitmap destination, boolean scale) {
-        RectF image = new RectF(0, 0, source.getWidth(), source.getHeight());
-        int border = 0;
-        if (!scale) {
-            border = destination.getWidth() - destination.getHeight();
-            if (border < 0) {
-                border = 0;
-            }
-        }
-        RectF frame = new RectF(border, 0,
-                destination.getWidth() - border,
-                destination.getHeight());
+        int minSide = Math.min(destination.getWidth(), destination.getHeight());
         Matrix m = new Matrix();
-        m.setRectToRect(frame, image, Matrix.ScaleToFit.CENTER);
-        image.set(frame);
-        m.mapRect(image);
-        m.setRectToRect(image, frame, Matrix.ScaleToFit.FILL);
+        float scaleFactor = minSide / (float) Math.min(source.getWidth(), source.getHeight());
+
+        float dx = (destination.getWidth() - source.getWidth() * scaleFactor) / 2.0f;
+        float dy = (destination.getHeight() - source.getHeight() * scaleFactor) / 2.0f;
+        if (mImageFrame.height() > mImageFrame.width()) {
+            // if portrait
+            dy -= mTextSize;
+        }
+        m.setScale(scaleFactor, scaleFactor);
+        m.postTranslate(dx, dy);
         Canvas canvas = new Canvas(destination);
         canvas.drawBitmap(source, m, new Paint(Paint.FILTER_BITMAP_FLAG));
     }
 
     @Override
     public void available(RenderingRequest request) {
+        clearBitmap();
         mImage = request.getBitmap();
         if (mImage == null) {
+            mImageFrame = null;
             return;
         }
         if (mRepresentation.getOverlayId() != 0 && mOverlayBitmap == null) {
@@ -204,4 +219,12 @@
     public void setOverlayBitmap(Bitmap overlayBitmap) {
         mOverlayBitmap = overlayBitmap;
     }
+
+    public void clearBitmap() {
+        if (mImage != null
+                && mImage != MasterImage.getImage().getTemporaryThumbnailBitmap()) {
+            MasterImage.getImage().getBitmapCache().cache(mImage);
+        }
+        mImage = null;
+    }
 }
diff --git a/src/com/android/gallery3d/filtershow/category/CategoryAdapter.java b/src/com/android/gallery3d/filtershow/category/CategoryAdapter.java
index 51ae07f..09f02dd 100644
--- a/src/com/android/gallery3d/filtershow/category/CategoryAdapter.java
+++ b/src/com/android/gallery3d/filtershow/category/CategoryAdapter.java
@@ -49,6 +49,15 @@
         this(context, 0);
     }
 
+    @Override
+    public void clear() {
+        for (int i = 0; i < getCount(); i++) {
+            Action action = getItem(i);
+            action.clearBitmap();
+        }
+        super.clear();
+    }
+
     public void setItemHeight(int height) {
         mItemHeight = height;
     }
diff --git a/src/com/android/gallery3d/filtershow/category/CategoryPanel.java b/src/com/android/gallery3d/filtershow/category/CategoryPanel.java
index d1b7d18..fb51bf5 100644
--- a/src/com/android/gallery3d/filtershow/category/CategoryPanel.java
+++ b/src/com/android/gallery3d/filtershow/category/CategoryPanel.java
@@ -54,29 +54,39 @@
         switch (adapter) {
             case MainPanel.LOOKS: {
                 mAdapter = activity.getCategoryLooksAdapter();
-                mAdapter.initializeSelection(MainPanel.LOOKS);
+                if (mAdapter != null) {
+                    mAdapter.initializeSelection(MainPanel.LOOKS);
+                }
                 activity.updateCategories();
                 break;
             }
             case MainPanel.BORDERS: {
                 mAdapter = activity.getCategoryBordersAdapter();
-                mAdapter.initializeSelection(MainPanel.BORDERS);
+                if (mAdapter != null) {
+                    mAdapter.initializeSelection(MainPanel.BORDERS);
+                }
                 activity.updateCategories();
                 break;
             }
             case MainPanel.GEOMETRY: {
                 mAdapter = activity.getCategoryGeometryAdapter();
-                mAdapter.initializeSelection(MainPanel.GEOMETRY);
+                if (mAdapter != null) {
+                    mAdapter.initializeSelection(MainPanel.GEOMETRY);
+                }
                 break;
             }
             case MainPanel.FILTERS: {
                 mAdapter = activity.getCategoryFiltersAdapter();
-                mAdapter.initializeSelection(MainPanel.FILTERS);
+                if (mAdapter != null) {
+                    mAdapter.initializeSelection(MainPanel.FILTERS);
+                }
                 break;
             }
             case MainPanel.VERSIONS: {
                 mAdapter = activity.getCategoryVersionsAdapter();
-                mAdapter.initializeSelection(MainPanel.VERSIONS);
+                if (mAdapter != null) {
+                    mAdapter.initializeSelection(MainPanel.VERSIONS);
+                }
                 break;
             }
         }
@@ -104,10 +114,12 @@
         View panelView = main.findViewById(R.id.listItems);
         if (panelView instanceof CategoryTrack) {
             CategoryTrack panel = (CategoryTrack) panelView;
-            mAdapter.setOrientation(CategoryView.HORIZONTAL);
-            panel.setAdapter(mAdapter);
-            mAdapter.setContainer(panel);
-        } else {
+            if (mAdapter != null) {
+                mAdapter.setOrientation(CategoryView.HORIZONTAL);
+                panel.setAdapter(mAdapter);
+                mAdapter.setContainer(panel);
+            }
+        } else if (mAdapter != null) {
             ListView panel = (ListView) main.findViewById(R.id.listItems);
             panel.setAdapter(mAdapter);
             mAdapter.setContainer(panel);
diff --git a/src/com/android/gallery3d/filtershow/category/CategorySelected.java b/src/com/android/gallery3d/filtershow/category/CategorySelected.java
index 1a6135b..42058c0 100644
--- a/src/com/android/gallery3d/filtershow/category/CategorySelected.java
+++ b/src/com/android/gallery3d/filtershow/category/CategorySelected.java
@@ -7,21 +7,25 @@
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.gallery3d.R;
+
 public class CategorySelected extends View {
-    Paint mPaint = new Paint();
+    private Paint mPaint = new Paint();
+    private int mMargin = 20;
 
     public CategorySelected(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mMargin = getResources().getDimensionPixelSize(R.dimen.touch_circle_size);
     }
 
     public void onDraw(Canvas canvas) {
         mPaint.reset();
-        int margin = 20;
-        mPaint.setStrokeWidth(margin);
+        mPaint.setStrokeWidth(mMargin);
         mPaint.setAntiAlias(true);
         mPaint.setStyle(Paint.Style.STROKE);
-        mPaint.setColor(Color.GRAY);
-        canvas.drawCircle(getWidth()/2, getHeight()/2, getWidth()/2 - margin, mPaint);
+        mPaint.setColor(Color.argb(128, 128, 128, 128));
+        canvas.drawCircle(getWidth()/2, getHeight()/2,
+                getWidth()/2 - mMargin, mPaint);
     }
 
 }
diff --git a/src/com/android/gallery3d/filtershow/category/CategoryView.java b/src/com/android/gallery3d/filtershow/category/CategoryView.java
index c613c21..1570517 100644
--- a/src/com/android/gallery3d/filtershow/category/CategoryView.java
+++ b/src/com/android/gallery3d/filtershow/category/CategoryView.java
@@ -49,6 +49,8 @@
     private int mSelectionColor = Color.WHITE;
     private int mSpacerColor = Color.WHITE;
     private boolean mCanBeRemoved = false;
+    private long mDoubleActionLast = 0;
+    private long mDoubleTapDelay = 250;
 
     public CategoryView(Context context) {
         super(context);
@@ -109,9 +111,11 @@
                 drawSpacer(canvas);
                 return;
             }
-            if (mAction.getImage() == null) {
-                mAction.setImageFrame(new Rect(0, 0, getWidth(), getHeight()), getOrientation());
-            } else {
+            if (mAction.isDoubleAction()) {
+                return;
+            }
+            mAction.setImageFrame(new Rect(0, 0, getWidth(), getHeight()), getOrientation());
+            if (mAction.getImage() != null) {
                 setBitmap(mAction.getImage());
             }
         }
@@ -146,7 +150,15 @@
         if (mAction.getType() == Action.ADD_ACTION) {
             activity.addNewPreset();
         } else if (mAction.getType() != Action.SPACER) {
-            activity.showRepresentation(mAction.getRepresentation());
+            if (mAction.isDoubleAction()) {
+                long current = System.currentTimeMillis() - mDoubleActionLast;
+                if (current < mDoubleTapDelay) {
+                    activity.showRepresentation(mAction.getRepresentation());
+                }
+                mDoubleActionLast = System.currentTimeMillis();
+            } else {
+                activity.showRepresentation(mAction.getRepresentation());
+            }
             mAdapter.setSelected(this);
         }
     }
diff --git a/src/com/android/gallery3d/filtershow/category/IconView.java b/src/com/android/gallery3d/filtershow/category/IconView.java
index 0443823..cba2d79 100644
--- a/src/com/android/gallery3d/filtershow/category/IconView.java
+++ b/src/com/android/gallery3d/filtershow/category/IconView.java
@@ -193,6 +193,7 @@
     public void onDraw(Canvas canvas) {
         mPaint.reset();
         mPaint.setAntiAlias(true);
+        mPaint.setFilterBitmap(true);
         canvas.drawColor(mBackgroundColor);
         computeBitmapBounds();
         computeTextPosition(getText());
diff --git a/src/com/android/gallery3d/filtershow/colorpicker/ColorCompareView.java b/src/com/android/gallery3d/filtershow/colorpicker/ColorCompareView.java
new file mode 100644
index 0000000..1599671
--- /dev/null
+++ b/src/com/android/gallery3d/filtershow/colorpicker/ColorCompareView.java
@@ -0,0 +1,177 @@
+/*
+ * 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.gallery3d.filtershow.colorpicker;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.gallery3d.R;
+
+import java.util.ArrayList;
+
+public class ColorCompareView extends View implements ColorListener {
+
+    private float mRadius;
+    private float mWidth;
+    private Paint mBarPaint1;
+    private Paint mOrigBarPaint1;
+    private Paint mCheckPaint;
+
+    private float mHeight;
+
+    private int mBgcolor = 0;
+
+    private float mBorder;
+
+    private float[] mHSVO = new float[4];
+    private float[] mOrigHSVO = new float[4];
+    private Path mRegion;
+    private Path mOrigRegion;
+
+    public final static float BORDER_SIZE = 0;
+    private int mCheckDim = 8;
+
+    public ColorCompareView(Context ctx, AttributeSet attrs) {
+        super(ctx, attrs);
+        DisplayMetrics metrics = ctx.getResources().getDisplayMetrics();
+        float mDpToPix = metrics.density;
+        mBorder = BORDER_SIZE * mDpToPix;
+        mBarPaint1 = new Paint();
+        mOrigBarPaint1 = new Paint();
+        Resources res = ctx.getResources();
+        mCheckDim = res.getDimensionPixelSize(R.dimen.draw_color_check_dim);
+        mBarPaint1.setStyle(Paint.Style.FILL);
+        mOrigBarPaint1.setStyle(Paint.Style.FILL);
+
+        makeCheckPaint();
+    }
+
+    private void makeCheckPaint() {
+        int imgdim = mCheckDim * 2;
+        int[] colors = new int[imgdim * imgdim];
+        for (int i = 0; i < colors.length; i++) {
+            int y = i / (imgdim * mCheckDim);
+            int x = (i / mCheckDim) % 2;
+            colors[i] = (x == y) ? 0xFFAAAAAA : 0xFF444444;
+        }
+        Bitmap bitmap = Bitmap.createBitmap(colors, imgdim, imgdim, Bitmap.Config.ARGB_8888);
+        BitmapShader bs = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
+        mCheckPaint = new Paint();
+        mCheckPaint.setShader(bs);
+    }
+
+    public boolean onDown(MotionEvent e) {
+        return true;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (event.getAction() != MotionEvent.ACTION_UP) {
+            return true;
+        }
+        float x = event.getX();
+        float y = event.getY();
+        if (x> mWidth-2*mHeight) {
+            resetToOriginal();
+        }
+        return true;
+    }
+
+    public void resetToOriginal(){
+        System.arraycopy(mOrigHSVO, 0, mHSVO, 0, mOrigHSVO.length);
+        updatePaint();
+        notifyColorListeners(mHSVO);
+        invalidate();
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        mWidth = w;
+        mHeight = h;
+        updatePaint();
+    }
+
+    private void updatePaint() {
+        int color = Color.HSVToColor((int) (mHSVO[3] * 255), mHSVO);
+        mBarPaint1.setColor(color);
+        int origColor = Color.HSVToColor((int) (mOrigHSVO[3] * 255), mOrigHSVO);
+        mOrigBarPaint1.setColor(origColor);
+        mOrigRegion = new Path();
+        mOrigRegion.moveTo(mWidth, 0);
+        mOrigRegion.lineTo(mWidth, mHeight);
+        mOrigRegion.lineTo(mWidth - mHeight * 2, mHeight);
+        mOrigRegion.lineTo(mWidth - mHeight, 0);
+
+        mRegion = new Path();
+        mRegion.moveTo(0, 0);
+        mRegion.lineTo(mWidth - mHeight, 0);
+        mRegion.lineTo(mWidth - mHeight * 2, mHeight);
+        mRegion.lineTo(0, mHeight);
+
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        canvas.drawColor(mBgcolor);
+        canvas.drawRect(mBorder, 0, mWidth, mHeight, mCheckPaint);
+        canvas.drawPath(mRegion, mBarPaint1);
+        canvas.drawPath(mOrigRegion, mOrigBarPaint1);
+    }
+
+    public void setOrigColor(float[] hsv) {
+        System.arraycopy(hsv, 0, mOrigHSVO, 0, mOrigHSVO.length);
+        int color2 = Color.HSVToColor((int) (mOrigHSVO[3] * 255), mOrigHSVO);
+        mOrigBarPaint1.setColor(color2);
+        updatePaint();
+    }
+
+    @Override
+    public void setColor(float[] hsv) {
+        System.arraycopy(hsv, 0, mHSVO, 0, mHSVO.length);
+        updatePaint();
+        invalidate();
+    }
+
+    ArrayList<ColorListener> mColorListeners = new ArrayList<ColorListener>();
+
+    public void notifyColorListeners(float[] hsvo) {
+        for (ColorListener l : mColorListeners) {
+            l.setColor(hsvo);
+        }
+    }
+
+    public void addColorListener(ColorListener l) {
+        mColorListeners.add(l);
+    }
+
+    public void removeColorListener(ColorListener l) {
+        mColorListeners.remove(l);
+    }
+}
+
diff --git a/src/com/android/gallery3d/filtershow/colorpicker/ColorGridDialog.java b/src/com/android/gallery3d/filtershow/colorpicker/ColorGridDialog.java
index dd4df7d..0285994 100644
--- a/src/com/android/gallery3d/filtershow/colorpicker/ColorGridDialog.java
+++ b/src/com/android/gallery3d/filtershow/colorpicker/ColorGridDialog.java
@@ -92,6 +92,9 @@
                 c |= alpha << 24;
                 mCallback.setColor(c);
             }
+            @Override
+            public void addColorListener(ColorListener l) {
+            }
         };
         ColorPickerDialog cpd = new ColorPickerDialog(this.getContext(), cl);
         cpd.show();
diff --git a/src/com/android/gallery3d/filtershow/colorpicker/ColorHueView.java b/src/com/android/gallery3d/filtershow/colorpicker/ColorHueView.java
index 713ab11..498f5a4 100644
--- a/src/com/android/gallery3d/filtershow/colorpicker/ColorHueView.java
+++ b/src/com/android/gallery3d/filtershow/colorpicker/ColorHueView.java
@@ -81,8 +81,8 @@
         mLinePaint2.setColor(mSliderColor);
         mLinePaint2.setStrokeWidth(4);
 
-        mBitmap = Bitmap.createBitmap(256, 7, Bitmap.Config.ARGB_8888);
-        mTmpBuff = new int[256 * 7];
+        mBitmap = Bitmap.createBitmap(256, 2, Bitmap.Config.ARGB_8888);
+        mTmpBuff = new int[mBitmap.getWidth() * mBitmap.getHeight()];
         mPaint.setAntiAlias(true);
         mPaint.setFilterBitmap(true);
         fillBitmap();
@@ -96,20 +96,13 @@
         for (int x = 0; x < w; x++) {
             float hue = 360 * (x) / (float) w;
 
-
-            int color = Color.HSVToColor((int)(mHSVO[3]*255),mHSVO);
-            mTmpBuff[x + w * 2] = color;
-            mTmpBuff[x + w * 3] = color;
-            mTmpBuff[x + w * 4] = color;
-
             mTmpHSV[0] = hue;
             mTmpHSV[1] = 1;
             mTmpHSV[2] = 1;
-            color = Color.HSVToColor(mTmpHSV);
+            int color = Color.HSVToColor(mTmpHSV);
             mTmpBuff[x] = color;
             mTmpBuff[x + w] = color;
-            mTmpBuff[x + w * 5] = color;
-            mTmpBuff[x + w * 6] = color;
+
         }
 
         mBitmap.setPixels(mTmpBuff, 0, w, 0, 0, w, h);
@@ -192,10 +185,12 @@
     }
 
     private void makeCheckPaint(){
-        int[] colors = new int[16 * 16];
+        int block = 16;
+        int checkdim = block*2;
+        int[] colors = new int[checkdim * checkdim];
         for (int i = 0; i < colors.length; i++) {
-            int y = i / (16 * 8);
-            int x = (i / 8) % 2;
+            int y = i / (checkdim * block);
+            int x = (i / block) % 2;
             colors[i] = (x == y) ? 0xFFAAAAAA : 0xFF444444;
         }
         Bitmap bitmap = Bitmap.createBitmap(colors, 16, 16, Bitmap.Config.ARGB_8888);
diff --git a/src/com/android/gallery3d/filtershow/colorpicker/ColorListener.java b/src/com/android/gallery3d/filtershow/colorpicker/ColorListener.java
index 5127dad..22dd424 100644
--- a/src/com/android/gallery3d/filtershow/colorpicker/ColorListener.java
+++ b/src/com/android/gallery3d/filtershow/colorpicker/ColorListener.java
@@ -18,4 +18,5 @@
 
 public interface ColorListener {
     void setColor(float[] hsvo);
+    public void addColorListener(ColorListener l);
 }
diff --git a/src/com/android/gallery3d/filtershow/colorpicker/ColorOpacityView.java b/src/com/android/gallery3d/filtershow/colorpicker/ColorOpacityView.java
index de4b2f9..ec15b5b 100644
--- a/src/com/android/gallery3d/filtershow/colorpicker/ColorOpacityView.java
+++ b/src/com/android/gallery3d/filtershow/colorpicker/ColorOpacityView.java
@@ -17,6 +17,7 @@
 package com.android.gallery3d.filtershow.colorpicker;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
@@ -56,7 +57,7 @@
     private float mDotY = mBorder;
     private final static float DOT_SIZE = ColorHueView.DOT_SIZE;
     public final static float BORDER_SIZE = 20;;
-
+    private  int mCheckDim = 8;
     public ColorOpacityView(Context ctx, AttributeSet attrs) {
         super(ctx, attrs);
         DisplayMetrics metrics = ctx.getResources().getDisplayMetrics();
@@ -68,8 +69,10 @@
         mDotPaint = new Paint();
 
         mDotPaint.setStyle(Paint.Style.FILL);
-        mDotPaint.setColor(ctx.getResources().getColor(R.color.slider_dot_color));
-        mSliderColor = ctx.getResources().getColor(R.color.slider_line_color);
+        Resources res = ctx.getResources();
+        mCheckDim = res.getDimensionPixelSize(R.dimen.draw_color_check_dim);
+        mDotPaint.setColor(res.getColor(R.color.slider_dot_color));
+        mSliderColor = res.getColor(R.color.slider_line_color);
 
         mBarPaint1.setStyle(Paint.Style.FILL);
 
@@ -83,13 +86,14 @@
     }
 
     private void makeCheckPaint(){
-        int[] colors = new int[16 * 16];
+        int imgdim = mCheckDim*2;
+        int[] colors = new int[imgdim * imgdim];
         for (int i = 0; i < colors.length; i++) {
-            int y = i / (16 * 8);
-            int x = (i / 8) % 2;
+            int y = i / (imgdim * mCheckDim);
+            int x = (i / mCheckDim) % 2;
             colors[i] = (x == y) ? 0xFFAAAAAA : 0xFF444444;
         }
-        Bitmap bitmap = Bitmap.createBitmap(colors, 16, 16, Bitmap.Config.ARGB_8888);
+        Bitmap bitmap = Bitmap.createBitmap(colors, imgdim, imgdim, Bitmap.Config.ARGB_8888);
         BitmapShader bs = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
         mCheckPaint = new Paint();
         mCheckPaint.setShader(bs);
diff --git a/src/com/android/gallery3d/filtershow/colorpicker/ColorPickerDialog.java b/src/com/android/gallery3d/filtershow/colorpicker/ColorPickerDialog.java
index 9527095..ff36f5a 100644
--- a/src/com/android/gallery3d/filtershow/colorpicker/ColorPickerDialog.java
+++ b/src/com/android/gallery3d/filtershow/colorpicker/ColorPickerDialog.java
@@ -18,22 +18,29 @@
 
 import android.app.Dialog;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.drawable.GradientDrawable;
 import android.util.DisplayMetrics;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
 import android.view.WindowManager;
 import android.widget.Button;
+import android.widget.ImageButton;
 import android.widget.ToggleButton;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.photos.views.GalleryThumbnailView;
 
 public class ColorPickerDialog extends Dialog   {
     ToggleButton mSelectedButton;
-    GradientDrawable mSelectRect;
     ColorHueView mColorHueView;
     ColorSVRectView mColorSVRectView;
     ColorOpacityView mColorOpacityView;
+    ColorCompareView mColorCompareView;
+
     float[] mHSVO = new float[4]; // hue=0..360, sat & val opacity = 0...1
 
     public ColorPickerDialog(Context context, final ColorListener cl) {
@@ -44,52 +51,61 @@
         int height = metrics.heightPixels*8/10;
         int width = metrics.widthPixels*8/10;
         getWindow().setLayout(width, height);
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
         setContentView(R.layout.filtershow_color_picker);
         mColorHueView = (ColorHueView) findViewById(R.id.ColorHueView);
         mColorSVRectView = (ColorSVRectView) findViewById(R.id.colorRectView);
         mColorOpacityView = (ColorOpacityView) findViewById(R.id.colorOpacityView);
+        mColorCompareView = (ColorCompareView) findViewById(R.id.btnSelect);
+
         float[] hsvo = new float[] {
                 123, .9f, 1, 1 };
 
-        mSelectRect = (GradientDrawable) getContext()
-                .getResources().getDrawable(R.drawable.filtershow_color_picker_roundrect);
-        Button selButton = (Button) findViewById(R.id.btnSelect);
-        selButton.setCompoundDrawablesWithIntrinsicBounds(null, null, mSelectRect, null);
-        Button sel = (Button) findViewById(R.id.btnSelect);
+        ImageButton apply = (ImageButton) findViewById(R.id.applyColorPick);
+        ImageButton cancel = (ImageButton) findViewById(R.id.cancelColorPick);
 
-        sel.setOnClickListener(new View.OnClickListener() {
+        apply.setOnClickListener(new View.OnClickListener() {
             @Override
-            public void onClick(View v) {
+            public void onClick(View view) {
+                cl.setColor(mHSVO);
                 ColorPickerDialog.this.dismiss();
-                if (cl != null) {
-                    cl.setColor(mHSVO);
-                }
             }
         });
+        cancel.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                ColorPickerDialog.this.dismiss();
+            }
+        });
+        ColorListener [] c = {mColorCompareView,mColorSVRectView,mColorOpacityView,mColorHueView};
+        for (int i = 0; i < c.length; i++) {
+            c[i].setColor(hsvo);
+            for (int j = 0; j < c.length; j++) {
+                if (i==j) {
+                     continue;
+                }
+               c[i].addColorListener(c[j]);
+            }
+        }
 
-        mColorSVRectView.setColor(hsvo);
-        mColorOpacityView.setColor(hsvo);
-        mColorHueView.setColor(hsvo);
-        mColorHueView.addColorListener(mColorSVRectView);
-        mColorSVRectView.addColorListener(mColorHueView);
-        mColorHueView.addColorListener(mColorOpacityView);
-        mColorSVRectView.addColorListener(mColorOpacityView);
-        mColorOpacityView.addColorListener(mColorSVRectView);
-        mColorOpacityView.addColorListener(mColorHueView);
         ColorListener colorListener = new ColorListener(){
-
             @Override
             public void setColor(float[] hsvo) {
                 System.arraycopy(hsvo, 0, mHSVO, 0, mHSVO.length);
                 int color = Color.HSVToColor(hsvo);
-                mSelectRect.setColor(color);
                 setButtonColor(mSelectedButton, hsvo);
             }
-        };
-        mColorOpacityView.addColorListener(colorListener);
-        mColorHueView.addColorListener(colorListener);
-        mColorSVRectView.addColorListener(colorListener);
 
+            @Override
+            public void addColorListener(ColorListener l) {
+            }
+        };
+
+        for (int i = 0; i < c.length; i++) {
+            c[i].addColorListener(colorListener);
+        }
+        setOnShowListener((FilterShowActivity) context);
+        setOnDismissListener((FilterShowActivity) context);
     }
 
     void toggleClick(ToggleButton v, int[] buttons, boolean isChecked) {
@@ -116,11 +132,15 @@
         csv.setColor(hsv);
     }
 
+    public void setOrigColor(float[] hsvo) {
+        mColorCompareView.setOrigColor(hsvo);
+    }
 
     public void setColor(float[] hsvo) {
         mColorOpacityView.setColor(hsvo);
         mColorHueView.setColor(hsvo);
         mColorSVRectView.setColor(hsvo);
+        mColorCompareView.setColor(hsvo);
     }
 
     private void setButtonColor(ToggleButton button, float[] hsv) {
diff --git a/src/com/android/gallery3d/filtershow/colorpicker/ColorSVRectView.java b/src/com/android/gallery3d/filtershow/colorpicker/ColorSVRectView.java
index b529b43..fb8b4fc 100644
--- a/src/com/android/gallery3d/filtershow/colorpicker/ColorSVRectView.java
+++ b/src/com/android/gallery3d/filtershow/colorpicker/ColorSVRectView.java
@@ -86,6 +86,11 @@
         fillBitmap();
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+    }
+
     void fillBitmap() {
         int w = mBitmap.getWidth();
         int h = mBitmap.getHeight();
diff --git a/src/com/android/gallery3d/filtershow/controller/ColorChooser.java b/src/com/android/gallery3d/filtershow/controller/ColorChooser.java
index 7fcffd7..f9f29bc 100644
--- a/src/com/android/gallery3d/filtershow/controller/ColorChooser.java
+++ b/src/com/android/gallery3d/filtershow/controller/ColorChooser.java
@@ -164,10 +164,14 @@
             public void setColor(float[] hsvo) {
                 changeSelectedColor(hsvo);
             }
+            @Override
+            public void addColorListener(ColorListener l) {
+            }
         };
         ColorPickerDialog cpd = new ColorPickerDialog(mContext, cl);
         float[] c = (float[]) mButton[mSelectedButton].getTag();
         cpd.setColor(Arrays.copyOf(c, 4));
+        cpd.setOrigColor(Arrays.copyOf(c, 4));
         cpd.show();
     }
 }
diff --git a/src/com/android/gallery3d/filtershow/controller/SliderBrightness.java b/src/com/android/gallery3d/filtershow/controller/SliderBrightness.java
index 6dc8688..2929d68 100644
--- a/src/com/android/gallery3d/filtershow/controller/SliderBrightness.java
+++ b/src/com/android/gallery3d/filtershow/controller/SliderBrightness.java
@@ -52,6 +52,9 @@
                 mParameter.setValue((int)(255* hsvo[3]));
                 mEditor.commitLocalRepresentation();
             }
+            @Override
+            public void addColorListener(ColorListener l) {
+            }
         });
     }
 
diff --git a/src/com/android/gallery3d/filtershow/controller/SliderHue.java b/src/com/android/gallery3d/filtershow/controller/SliderHue.java
index a550afd..1820ce2 100644
--- a/src/com/android/gallery3d/filtershow/controller/SliderHue.java
+++ b/src/com/android/gallery3d/filtershow/controller/SliderHue.java
@@ -63,6 +63,9 @@
                 mParameter.setValue((int)(360* hsvo[3]));
                 mEditor.commitLocalRepresentation();
             }
+            @Override
+            public void addColorListener(ColorListener l) {
+            }
         });
     }
 
diff --git a/src/com/android/gallery3d/filtershow/controller/SliderOpacity.java b/src/com/android/gallery3d/filtershow/controller/SliderOpacity.java
index cd21e9b..2a34aad 100644
--- a/src/com/android/gallery3d/filtershow/controller/SliderOpacity.java
+++ b/src/com/android/gallery3d/filtershow/controller/SliderOpacity.java
@@ -53,6 +53,9 @@
                 mParameter.setValue((int) (255 * hsvo[3]));
                 mEditor.commitLocalRepresentation();
             }
+            @Override
+            public void addColorListener(ColorListener l) {
+            }
         });
     }
 
diff --git a/src/com/android/gallery3d/filtershow/controller/SliderSaturation.java b/src/com/android/gallery3d/filtershow/controller/SliderSaturation.java
index 1f2b3fb..6f3ae6e 100644
--- a/src/com/android/gallery3d/filtershow/controller/SliderSaturation.java
+++ b/src/com/android/gallery3d/filtershow/controller/SliderSaturation.java
@@ -54,6 +54,9 @@
                 mParameter.setValue((int) (255 * hsvo[3]));
                 mEditor.commitLocalRepresentation();
             }
+            @Override
+            public void addColorListener(ColorListener l) {
+            }
         });
     }
 
diff --git a/src/com/android/gallery3d/filtershow/crop/CropDrawingUtils.java b/src/com/android/gallery3d/filtershow/crop/CropDrawingUtils.java
index b0d324c..df0f14f 100644
--- a/src/com/android/gallery3d/filtershow/crop/CropDrawingUtils.java
+++ b/src/com/android/gallery3d/filtershow/crop/CropDrawingUtils.java
@@ -54,6 +54,24 @@
         canvas.drawRect(bounds, p);
     }
 
+    public static void drawShade(Canvas canvas, RectF bounds) {
+        int w = canvas.getWidth();
+        int h = canvas.getHeight();
+        Paint p = new Paint();
+        p.setStyle(Paint.Style.FILL);
+        p.setColor(Color.BLACK & 0x88000000);
+
+        RectF r = new RectF();
+        r.set(0,0,w,bounds.top);
+        canvas.drawRect(r, p);
+        r.set(0,bounds.top,bounds.left,h);
+        canvas.drawRect(r, p);
+        r.set(bounds.left,bounds.bottom,w,h);
+        canvas.drawRect(r, p);
+        r.set(bounds.right,bounds.top,w,bounds.bottom);
+        canvas.drawRect(r, p);
+    }
+
     public static void drawIndicator(Canvas canvas, Drawable indicator, int indicatorSize,
             float centerX, float centerY) {
         int left = (int) centerX - indicatorSize / 2;
diff --git a/src/com/android/gallery3d/filtershow/data/UserPresetsManager.java b/src/com/android/gallery3d/filtershow/data/UserPresetsManager.java
index f36509d..6232a2d 100644
--- a/src/com/android/gallery3d/filtershow/data/UserPresetsManager.java
+++ b/src/com/android/gallery3d/filtershow/data/UserPresetsManager.java
@@ -89,7 +89,7 @@
     public void save(ImagePreset preset, String name) {
         Message msg = mProcessingHandler.obtainMessage(SAVE);
         SaveOperation op = new SaveOperation();
-        op.json = preset.getJsonString(mActivity.getString(R.string.saved));
+        op.json = preset.getJsonString(ImagePreset.JASON_SAVED);
         op.name = name;
         msg.obj = op;
         mProcessingHandler.sendMessage(msg);
diff --git a/src/com/android/gallery3d/filtershow/editors/Editor.java b/src/com/android/gallery3d/filtershow/editors/Editor.java
index 5f8e8f6..e3eec39 100644
--- a/src/com/android/gallery3d/filtershow/editors/Editor.java
+++ b/src/com/android/gallery3d/filtershow/editors/Editor.java
@@ -285,7 +285,7 @@
 
     protected void setMenuIcon(boolean on) {
         mEditTitle.setCompoundDrawablesRelativeWithIntrinsicBounds(
-                0, 0, on ? R.drawable.filtershow_menu_marker : 0, 0);
+                0, 0, on ? R.drawable.filtershow_menu_marker_rtl : 0, 0);
     }
 
     protected void createMenu(int[] strId, View button) {
diff --git a/src/com/android/gallery3d/filtershow/editors/EditorChanSat.java b/src/com/android/gallery3d/filtershow/editors/EditorChanSat.java
index e806a5a..abf0a69 100644
--- a/src/com/android/gallery3d/filtershow/editors/EditorChanSat.java
+++ b/src/com/android/gallery3d/filtershow/editors/EditorChanSat.java
@@ -30,6 +30,7 @@
 
 import android.widget.TextView;
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.controller.BasicParameterStyle;
 import com.android.gallery3d.filtershow.controller.BitmapCaller;
 import com.android.gallery3d.filtershow.controller.FilterView;
@@ -115,6 +116,7 @@
                 @Override
                 public void onClick(View arg0) {
                     popupMenu.show();
+                    ((FilterShowActivity)mContext).onShowMenu(popupMenu);
                 }
             });
             mButton.setListener(this);
diff --git a/src/com/android/gallery3d/filtershow/editors/EditorColorBorder.java b/src/com/android/gallery3d/filtershow/editors/EditorColorBorder.java
index 8cdad7e..98659df 100644
--- a/src/com/android/gallery3d/filtershow/editors/EditorColorBorder.java
+++ b/src/com/android/gallery3d/filtershow/editors/EditorColorBorder.java
@@ -33,6 +33,7 @@
 import android.widget.SeekBar;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.controller.BitmapCaller;
 import com.android.gallery3d.filtershow.controller.ColorChooser;
 import com.android.gallery3d.filtershow.controller.FilterView;
@@ -45,7 +46,6 @@
     private static final String LOGTAG = "EditorColorBorder";
     public static final int ID = R.id.editorColorBorder;
 
-
     int[] mBasColors = {
             FilterColorBorderRepresentation.DEFAULT_MENU_COLOR1,
             FilterColorBorderRepresentation.DEFAULT_MENU_COLOR2,
@@ -138,6 +138,7 @@
             }
         });
         popupMenu.show();
+        ((FilterShowActivity)mContext).onShowMenu(popupMenu);
     }
 
     protected void selectMenuItem(MenuItem item) {
@@ -167,7 +168,9 @@
             ColorChooser c = (ColorChooser) mControl;
             mBasColors = c.getColorSet();
         }
-        control(rep.getCurrentParam(), mEditControl);
+        if (mEditControl != null) {
+            control(rep.getCurrentParam(), mEditControl);
+        }
         if (mControl instanceof ColorChooser) {
             ColorChooser c = (ColorChooser) mControl;
             c.setColorSet(mBasColors);
diff --git a/src/com/android/gallery3d/filtershow/editors/EditorColorBorderTabletUI.java b/src/com/android/gallery3d/filtershow/editors/EditorColorBorderTabletUI.java
index 518c56a..9f1a11b 100644
--- a/src/com/android/gallery3d/filtershow/editors/EditorColorBorderTabletUI.java
+++ b/src/com/android/gallery3d/filtershow/editors/EditorColorBorderTabletUI.java
@@ -29,6 +29,7 @@
 import android.widget.TextView;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.colorpicker.ColorCompareView;
 import com.android.gallery3d.filtershow.colorpicker.ColorHueView;
 import com.android.gallery3d.filtershow.colorpicker.ColorListener;
 import com.android.gallery3d.filtershow.colorpicker.ColorOpacityView;
@@ -48,6 +49,7 @@
     private ColorHueView mHueView;
     private ColorSVRectView mSatValView;
     private ColorOpacityView mOpacityView;
+    private ColorCompareView mColorCompareView;
 
     private int[] mBasColors;
     private int mSelected;
@@ -98,23 +100,13 @@
         mCBCornerSizeSeekBar = (SeekBar) lp.findViewById(R.id.colorBorderCornerSizeSeekBar);
         mCBCornerSizeValue = (TextView) lp.findViewById(R.id.colorBorderCornerValue);
         mCBSizeSeekBar = (SeekBar) lp.findViewById(R.id.colorBorderSizeSeekBar);
+
         mCBSizeValue = (TextView) lp.findViewById(R.id.colorBorderSizeValue);
-        setupClearButton(lp);
         setupCBSizeSeekBar(lp);
         setupCBCornerSizeSeekBar(lp);
         setupColor(lp, res);
     }
 
-    private void setupClearButton(LinearLayout lp) {
-        Button clearButton = (Button) lp.findViewById(R.id.clearButton);
-        clearButton.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                mEditorDraw.clearFrame();
-            }
-        });
-    }
-
     private void setupCBSizeSeekBar(LinearLayout lp) {
         mCBSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
             @Override
@@ -200,18 +192,32 @@
                     mHueView.setColor(hsvo);
                     mSatValView.setColor(hsvo);
                     mOpacityView.setColor(hsvo);
+                    mColorCompareView.setOrigColor(hsvo);
+
                 }
             });
         }
         mHueView = (ColorHueView) lp.findViewById(R.id.ColorHueView);
         mSatValView = (ColorSVRectView) lp.findViewById(R.id.colorRectView);
         mOpacityView = (ColorOpacityView) lp.findViewById(R.id.colorOpacityView);
-        mHueView.addColorListener(mSatValView);
-        mSatValView.addColorListener(mHueView);
-        mHueView.addColorListener(mOpacityView);
-        mSatValView.addColorListener(mOpacityView);
-        mOpacityView.addColorListener(mSatValView);
-        mOpacityView.addColorListener(mHueView);
+        mColorCompareView = (ColorCompareView) lp.findViewById(R.id.btnSelect);
+
+        float[] hsvo = new float[4];
+        Color.colorToHSV(mBasColors[0], hsvo);
+        hsvo[3] = (0xFF & (mBasColors[0] >> 24)) / (float) 255;
+        mColorCompareView.setOrigColor(hsvo);
+
+        ColorListener[] colorViews = {mHueView, mSatValView, mOpacityView, mColorCompareView};
+        for (int i = 0; i < colorViews.length; i++) {
+            colorViews[i].setColor(hsvo);
+            for (int j = 0; j < colorViews.length; j++) {
+                if (i == j) {
+                    continue;
+                }
+                colorViews[i].addColorListener(colorViews[j]);
+            }
+        }
+
         ColorListener colorListener = new ColorListener() {
             @Override
             public void setColor(float[] hsvo) {
@@ -228,10 +234,14 @@
                 pram.setValue(color);
                 mEditorDraw.commitLocalRepresentation();
             }
+            @Override
+            public void addColorListener(ColorListener l) {
+            }
         };
-        mHueView.addColorListener(colorListener);
-        mSatValView.addColorListener(colorListener);
-        mOpacityView.addColorListener(colorListener);
+
+        for (int i = 0; i < colorViews.length; i++) {
+            colorViews[i].addColorListener(colorListener);
+        }
     }
 
     private void resetBorders() {
diff --git a/src/com/android/gallery3d/filtershow/editors/EditorCrop.java b/src/com/android/gallery3d/filtershow/editors/EditorCrop.java
index 511d4ff..6b19d3c 100644
--- a/src/com/android/gallery3d/filtershow/editors/EditorCrop.java
+++ b/src/com/android/gallery3d/filtershow/editors/EditorCrop.java
@@ -28,6 +28,7 @@
 import android.widget.PopupMenu;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
 import com.android.gallery3d.filtershow.imageshow.ImageCrop;
@@ -140,6 +141,13 @@
             }
         });
         popupMenu.show();
+        ((FilterShowActivity)mContext).onShowMenu(popupMenu);
+    }
+
+    @Override
+    public void setUtilityPanelUI(View actionButton, View editControl) {
+        super.setUtilityPanelUI(actionButton,editControl);
+        setMenuIcon(true);
     }
 
     @Override
diff --git a/src/com/android/gallery3d/filtershow/editors/EditorDraw.java b/src/com/android/gallery3d/filtershow/editors/EditorDraw.java
index abd81c5..58bde12 100644
--- a/src/com/android/gallery3d/filtershow/editors/EditorDraw.java
+++ b/src/com/android/gallery3d/filtershow/editors/EditorDraw.java
@@ -24,6 +24,7 @@
 import android.graphics.Color;
 import android.graphics.drawable.GradientDrawable;
 import android.view.LayoutInflater;
+import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -37,6 +38,7 @@
 import android.widget.SeekBar;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.colorpicker.ColorHueView;
 import com.android.gallery3d.filtershow.colorpicker.ColorListener;
 import com.android.gallery3d.filtershow.colorpicker.ColorOpacityView;
@@ -76,6 +78,7 @@
     private EditorDrawTabletUI mTabletUI;
     private String mParameterString;
     private int mSelectedColorButton;
+    private String mDrawString = null;
 
     public EditorDraw() {
         super(ID);
@@ -84,9 +87,16 @@
     @Override
     public String calculateUserMessage(Context context, String effectName, Object parameterValue) {
         FilterDrawRepresentation rep = getDrawRep();
+        if (mDrawString != null) {
+            mImageDraw.displayDrawLook();
+            return mDrawString;
+        }
         if (rep == null) {
             return "";
         }
+        if (!ParametricEditor.useCompact(mContext)) {
+
+        }
         if (mParameterString == null) {
             mParameterString = "";
         }
@@ -122,7 +132,7 @@
 
             drawRep.getParam(FilterDrawRepresentation.PARAM_STYLE).setFilterView(this);
             drawRep.setPramMode(FilterDrawRepresentation.PARAM_COLOR);
-            mParameterString = mContext.getString(R.string.draw_hue);
+            mParameterString = mContext.getString(R.string.draw_color);
             control(drawRep.getCurrentParam(), mEditControl);
         }
     }
@@ -130,18 +140,16 @@
     @Override
     public void openUtilityPanel(final LinearLayout accessoryViewList) {
         Button view = (Button) accessoryViewList.findViewById(R.id.applyEffect);
-        if (useCompact(mContext)) {
-            view.setText(mContext.getString(R.string.draw_hue));
-            view.setOnClickListener(new OnClickListener() {
 
-                @Override
-                public void onClick(View arg0) {
-                    showPopupMenu(accessoryViewList);
-                }
-            });
-        } else {
-            view.setText(mContext.getString(R.string.imageDraw));
-        }
+        view.setText(mContext.getString(R.string.draw_color));
+        view.setOnClickListener(new OnClickListener() {
+
+            @Override
+            public void onClick(View arg0) {
+                showPopupMenu(accessoryViewList);
+            }
+        });
+
     }
 
     @Override
@@ -157,16 +165,35 @@
         }
         final PopupMenu popupMenu = new PopupMenu(mImageShow.getActivity(), button);
         popupMenu.getMenuInflater().inflate(R.menu.filtershow_menu_draw, popupMenu.getMenu());
-        popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
-
-            @Override
-            public boolean onMenuItemClick(MenuItem item) {
-                selectMenuItem(item);
-                return true;
+        if (!ParametricEditor.useCompact(mContext)) {
+            Menu menu = popupMenu.getMenu();
+            int count = menu.size();
+            for (int i = 0; i < count; i++) {
+                MenuItem item = menu.getItem(i);
+                if (item.getItemId() != R.id.draw_menu_clear) {
+                    item.setVisible(false);
+                }
             }
-        });
-        popupMenu.show();
+            popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
 
+                @Override
+                public boolean onMenuItemClick(MenuItem item) {
+                    clearDrawing();
+                    return true;
+                }
+            });
+        } else {
+            popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+
+                @Override
+                public boolean onMenuItemClick(MenuItem item) {
+                    selectMenuItem(item);
+                    return true;
+                }
+            });
+        }
+        popupMenu.show();
+        ((FilterShowActivity)mContext).onShowMenu(popupMenu);
     }
 
     protected void selectMenuItem(MenuItem item) {
@@ -192,6 +219,7 @@
         }
         if (item.getItemId() != R.id.draw_menu_clear) {
             mParameterString = item.getTitle().toString();
+            updateText();
         }
         if (mControl instanceof ColorChooser) {
             ColorChooser c = (ColorChooser) mControl;
@@ -218,6 +246,7 @@
             super.setUtilityPanelUI(actionButton, editControl);
             return;
         }
+
         mSeekBar = (SeekBar) editControl.findViewById(R.id.primarySeekBar);
         if (mSeekBar != null) {
             mSeekBar.setVisibility(View.GONE);
@@ -228,7 +257,8 @@
                 R.layout.filtershow_draw_ui, (ViewGroup) editControl, true);
 
         mTabletUI = new EditorDrawTabletUI(this, mContext, lp);
-        setMenuIcon(false);
+        mDrawString = mContext.getResources().getString(R.string.imageDraw).toUpperCase();
+        setMenuIcon(true);
 
     }
 
diff --git a/src/com/android/gallery3d/filtershow/editors/EditorDrawTabletUI.java b/src/com/android/gallery3d/filtershow/editors/EditorDrawTabletUI.java
index 01fd9ed..83d89c9 100644
--- a/src/com/android/gallery3d/filtershow/editors/EditorDrawTabletUI.java
+++ b/src/com/android/gallery3d/filtershow/editors/EditorDrawTabletUI.java
@@ -32,6 +32,7 @@
 import android.widget.TextView;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.colorpicker.ColorCompareView;
 import com.android.gallery3d.filtershow.colorpicker.ColorHueView;
 import com.android.gallery3d.filtershow.colorpicker.ColorListener;
 import com.android.gallery3d.filtershow.colorpicker.ColorOpacityView;
@@ -55,6 +56,8 @@
     private ColorHueView mHueView;
     private ColorSVRectView mSatValView;
     private ColorOpacityView mOpacityView;
+    private ColorCompareView mColorCompareView;
+    private TextView mDrawSizeValue;
 
     private int[] mBasColors;
     private int mSelected;
@@ -72,8 +75,9 @@
         mRep = rep;
         BasicParameterInt size;
         size = (BasicParameterInt) mRep.getParam(FilterDrawRepresentation.PARAM_SIZE);
-        mdrawSizeSeekBar.setProgress(size.getDefaultValue());
         mdrawSizeSeekBar.setMax(size.getMaximum() - size.getMinimum());
+        mdrawSizeSeekBar.setProgress(size.getValue());
+
         ParameterColor color;
         color = (ParameterColor) mRep.getParam(FilterDrawRepresentation.PARAM_COLOR);
         color.setValue(mBasColors[mSelectedColorButton]);
@@ -91,7 +95,7 @@
         LinearLayout buttonContainer = (LinearLayout) lp.findViewById(R.id.listStyles);
 
         mdrawSizeSeekBar = (SeekBar) lp.findViewById(R.id.drawSizeSeekBar);
-        TextView drawSizeValue = (TextView) lp.findViewById(R.id.drawSizeValue);
+        mDrawSizeValue = (TextView) lp.findViewById(R.id.drawSizeValue);
 
         Button clearButton = (Button) lp.findViewById(R.id.clearButton);
         clearButton.setOnClickListener(new View.OnClickListener() {
@@ -117,6 +121,8 @@
                 size = (BasicParameterInt) mRep.getParam(FilterDrawRepresentation.PARAM_SIZE);
                 size.setValue(progress + size.getMinimum());
                 mEditorDraw.commitLocalRepresentation();
+                int val  = progress + size.getMinimum();
+                mDrawSizeValue.setText(((val>0)?"+":"")+val);
             }
         });
 
@@ -183,7 +189,7 @@
                 public void onClick(View arg0) {
 
                     mSelectedColorButton = buttonNo;
-                    float[] hsvo = Arrays.copyOf((float[]) mColorButton[buttonNo].getTag(),4);
+                    float[] hsvo = Arrays.copyOf((float[]) mColorButton[buttonNo].getTag(), 4);
                     resetBorders();
                     if (mRep == null) {
                         return;
@@ -195,18 +201,33 @@
                     mHueView.setColor(hsvo);
                     mSatValView.setColor(hsvo);
                     mOpacityView.setColor(hsvo);
+                    mColorCompareView.setColor(hsvo);
+                    mColorCompareView.setOrigColor(hsvo);
                 }
             });
         }
+
         mHueView = (ColorHueView) lp.findViewById(R.id.ColorHueView);
         mSatValView = (ColorSVRectView) lp.findViewById(R.id.colorRectView);
         mOpacityView = (ColorOpacityView) lp.findViewById(R.id.colorOpacityView);
-        mHueView.addColorListener(mSatValView);
-        mSatValView.addColorListener(mHueView);
-        mHueView.addColorListener(mOpacityView);
-        mSatValView.addColorListener(mOpacityView);
-        mOpacityView.addColorListener(mSatValView);
-        mOpacityView.addColorListener(mHueView);
+        mColorCompareView = (ColorCompareView) lp.findViewById(R.id.btnSelect);
+
+        float[] hsvo = new float[4];
+        Color.colorToHSV(mBasColors[0], hsvo);
+        hsvo[3] = (0xFF & (mBasColors[0] >> 24)) / (float) 255;
+
+        mColorCompareView.setOrigColor(hsvo);
+        ColorListener[] colorViews = {mHueView, mSatValView, mOpacityView, mColorCompareView};
+        for (int i = 0; i < colorViews.length; i++) {
+            colorViews[i].setColor(hsvo);
+
+            for (int j = 0; j < colorViews.length; j++) {
+                if (i == j) {
+                    continue;
+                }
+                colorViews[i].addColorListener(colorViews[j]);
+            }
+        }
         ColorListener colorListener = new ColorListener() {
 
             @Override
@@ -214,7 +235,7 @@
                 int color = Color.HSVToColor((int) (hsvo[3] * 255), hsvo);
                 Button b = mColorButton[mSelectedColorButton];
                 float[] f = (float[]) b.getTag();
-                System.arraycopy(hsvo,0,f,0,4);
+                System.arraycopy(hsvo, 0, f, 0, 4);
                 mBasColors[mSelectedColorButton] = color;
                 GradientDrawable sd = ((GradientDrawable) b.getBackground());
                 sd.setColor(color);
@@ -224,10 +245,16 @@
                 pram.setValue(color);
                 mEditorDraw.commitLocalRepresentation();
             }
+
+            @Override
+            public void addColorListener(ColorListener l) {
+            }
         };
-        mHueView.addColorListener(colorListener);
-        mSatValView.addColorListener(colorListener);
-        mOpacityView.addColorListener(colorListener);
+
+        for (int i = 0; i < colorViews.length; i++) {
+            colorViews[i].addColorListener(colorListener);
+        }
+
     }
 
     public void resetStyle() {
diff --git a/src/com/android/gallery3d/filtershow/editors/EditorGrad.java b/src/com/android/gallery3d/filtershow/editors/EditorGrad.java
index 4be435f..0591216 100644
--- a/src/com/android/gallery3d/filtershow/editors/EditorGrad.java
+++ b/src/com/android/gallery3d/filtershow/editors/EditorGrad.java
@@ -31,6 +31,7 @@
 import android.widget.ToggleButton;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.controller.Control;
 import com.android.gallery3d.filtershow.controller.FilterView;
 import com.android.gallery3d.filtershow.controller.Parameter;
@@ -191,13 +192,13 @@
         public ParamAdapter(int seekId, int textId, LinearLayout layout, int mode) {
             mSlider = (SeekBar) layout.findViewById(seekId);
             mTextView = (TextView) layout.findViewById(textId);
-            mSlider.setOnSeekBarChangeListener(this);
             mSlider.setMax(mMax - mMin);
             mMode = mode;
             FilterGradRepresentation rep = getGradRepresentation();
             if (rep != null){
                 updateValues(rep);
             }
+            mSlider.setOnSeekBarChangeListener(this);
         }
 
         public void updateValues(FilterGradRepresentation rep) {
@@ -254,6 +255,7 @@
             setUpPopupMenu(button);
         }
         mPopupMenu.show();
+        ((FilterShowActivity)mContext).onShowMenu(mPopupMenu);
     }
 
     private void setUpPopupMenu(Button button) {
@@ -428,5 +430,4 @@
     public void copyFrom(Parameter src) {
 
     }
-
 }
diff --git a/src/com/android/gallery3d/filtershow/editors/EditorVignette.java b/src/com/android/gallery3d/filtershow/editors/EditorVignette.java
index 3a94dce..630a1a9 100644
--- a/src/com/android/gallery3d/filtershow/editors/EditorVignette.java
+++ b/src/com/android/gallery3d/filtershow/editors/EditorVignette.java
@@ -29,6 +29,7 @@
 import android.widget.TextView;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.controller.BasicParameterInt;
 import com.android.gallery3d.filtershow.controller.Parameter;
 import com.android.gallery3d.filtershow.filters.FilterVignetteRepresentation;
@@ -173,6 +174,7 @@
                 @Override
                 public void onClick(View arg0) {
                     popupMenu.show();
+                    ((FilterShowActivity)mContext).onShowMenu(popupMenu);
                 }
             });
             mButton.setListener(this);
diff --git a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
index 7a5ec56..64b4974 100644
--- a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
+++ b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Color;
 import android.util.Log;
 
 import com.android.gallery3d.R;
@@ -35,6 +36,7 @@
     protected ArrayList<FilterRepresentation> mBorders = new ArrayList<FilterRepresentation>();
     protected ArrayList<FilterRepresentation> mTools = new ArrayList<FilterRepresentation>();
     protected ArrayList<FilterRepresentation> mEffects = new ArrayList<FilterRepresentation>();
+    private static int mImageBorderSize = 4; // in percent
 
     protected void init() {
         mFilters = new HashMap<Class, ImageFilter>();
@@ -158,6 +160,73 @@
 
     public void addBorders(Context context) {
 
+        // Do not localize
+        String[] serializationNames = {
+                "FRAME_4X5",
+                "FRAME_BRUSH",
+                "FRAME_GRUNGE",
+                "FRAME_SUMI_E",
+                "FRAME_TAPE",
+                "FRAME_BLACK",
+                "FRAME_BLACK_ROUNDED",
+                "FRAME_WHITE",
+                "FRAME_WHITE_ROUNDED",
+                "FRAME_CREAM",
+                "FRAME_CREAM_ROUNDED"
+        };
+
+        // The "no border" implementation
+        int i = 0;
+        FilterRepresentation rep = new FilterImageBorderRepresentation(0);
+        mBorders.add(rep);
+
+        // Regular borders
+        ArrayList <FilterRepresentation> borderList = new ArrayList<FilterRepresentation>();
+
+
+        rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_4x5);
+        borderList.add(rep);
+
+        rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_brush);
+        borderList.add(rep);
+
+        rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_grunge);
+        borderList.add(rep);
+
+        rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_sumi_e);
+        borderList.add(rep);
+
+        rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_tape);
+        borderList.add(rep);
+
+        rep = new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize, 0);
+        borderList.add(rep);
+
+        rep = new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize,
+                mImageBorderSize);
+        borderList.add(rep);
+
+        rep = new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize, 0);
+        borderList.add(rep);
+
+        rep = new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize,
+                mImageBorderSize);
+        borderList.add(rep);
+
+        int creamColor = Color.argb(255, 237, 237, 227);
+        rep = new FilterColorBorderRepresentation(creamColor, mImageBorderSize, 0);
+        borderList.add(rep);
+
+        rep = new FilterColorBorderRepresentation(creamColor, mImageBorderSize,
+                mImageBorderSize);
+        borderList.add(rep);
+
+        for (FilterRepresentation filter : borderList) {
+            filter.setSerializationName(serializationNames[i++]);
+            addRepresentation(filter);
+            mBorders.add(filter);
+        }
+
     }
 
     public void addLooks(Context context) {
@@ -274,6 +343,17 @@
         mTools.add(getRepresentation(ImageFilterDraw.class));
     }
 
+    public void removeRepresentation(ArrayList<FilterRepresentation> list,
+                                          FilterRepresentation representation) {
+        for (int i = 0; i < list.size(); i++) {
+            FilterRepresentation r = list.get(i);
+            if (r.getFilterClass() == representation.getFilterClass()) {
+                list.remove(i);
+                break;
+            }
+        }
+    }
+
     public void setFilterResources(Resources resources) {
         ImageFilterBorder filterBorder = (ImageFilterBorder) getFilter(ImageFilterBorder.class);
         filterBorder.setResources(resources);
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterColorBorderRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterColorBorderRepresentation.java
index 4f625d7..5162927 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterColorBorderRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterColorBorderRepresentation.java
@@ -126,7 +126,10 @@
 
     @Override
     public int getTextId() {
-        return R.string.borders;
+        if (super.getTextId() == 0) {
+            return R.string.borders;
+        }
+        return super.getTextId();
     }
 
     public int getColor() {
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterCropRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterCropRepresentation.java
index ec4b573..ba697d8 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterCropRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterCropRepresentation.java
@@ -35,7 +35,7 @@
     RectF mCrop = getNil();
 
     public FilterCropRepresentation(RectF crop) {
-        super(FilterCropRepresentation.class.getSimpleName());
+        super(SERIALIZATION_NAME);
         setSerializationName(SERIALIZATION_NAME);
         setShowParameterValue(true);
         setFilterClass(FilterCropRepresentation.class);
@@ -48,6 +48,7 @@
 
     public FilterCropRepresentation(FilterCropRepresentation m) {
         this(m.mCrop);
+        setName(m.getName());
     }
 
     public FilterCropRepresentation() {
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java
index f324573..48d3d90 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java
@@ -45,7 +45,7 @@
     public static final int PARAM_SIZE = 0;
     public static final int PARAM_STYLE = 1;
     public static final int PARAM_COLOR = 2;
-    private BasicParameterInt mParamSize = new BasicParameterInt(PARAM_SIZE, 20, 2, 300);
+    private BasicParameterInt mParamSize = new BasicParameterInt(PARAM_SIZE, 30, 2, 300);
     private BasicParameterStyle mParamStyle = new BasicParameterStyle(PARAM_STYLE, 5);
     public static int DEFAULT_MENU_COLOR1 = Color.RED & 0x80FFFFFF;
     public static int DEFAULT_MENU_COLOR2 = Color.GREEN & 0x80FFFFFF;
@@ -144,7 +144,7 @@
         switch (mParamMode) {
             case PARAM_COLOR:
                 val = ((ParameterColor) mAllParam[mParamMode]).getValue();
-                return ((val > 0) ? " +" : " ") + colorHexString(val);
+                return "";
             case PARAM_SIZE:
                 val = ((BasicParameterInt) mAllParam[mParamMode]).getValue();
                 return ((val > 0) ? " +" : " ") + val;
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterGradRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterGradRepresentation.java
index 354fa59..a3a7e95 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterGradRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterGradRepresentation.java
@@ -114,7 +114,7 @@
         p.yPos1 = 100;
         p.xPos2 = -1;
         p.yPos2 = 100;
-        p.brightness = 40;
+        p.brightness = -50;
         p.contrast = 0;
         p.saturation = 0;
         mBands.add(0, p);
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterMirrorRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterMirrorRepresentation.java
index 84caa9e..c281443 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterMirrorRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterMirrorRepresentation.java
@@ -66,7 +66,7 @@
     }
 
     public FilterMirrorRepresentation(Mirror mirror) {
-        super(FilterMirrorRepresentation.class.getSimpleName());
+        super(SERIALIZATION_NAME);
         setSerializationName(SERIALIZATION_NAME);
         setShowParameterValue(false);
         setFilterClass(FilterMirrorRepresentation.class);
@@ -79,6 +79,7 @@
 
     public FilterMirrorRepresentation(FilterMirrorRepresentation m) {
         this(m.getMirror());
+        setName(m.getName());
     }
 
     public FilterMirrorRepresentation() {
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java
index cc7ec88..0fb157d 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java
@@ -38,6 +38,7 @@
     private int mOverlayId = 0;
     private boolean mOverlayOnly = false;
     private boolean mShowParameterValue = true;
+    private boolean mIsBooleanFilter = false;
     private String mSerializationName;
     public static final byte TYPE_BORDER = 1;
     public static final byte TYPE_FX = 2;
@@ -69,7 +70,7 @@
         representation.setOverlayOnly(getOverlayOnly());
         representation.setShowParameterValue(showParameterValue());
         representation.mSerializationName = mSerializationName;
-
+        representation.setIsBooleanFilter(isBooleanFilter());
     }
 
     public boolean equals(FilterRepresentation representation) {
@@ -87,12 +88,21 @@
                 && representation.mButtonId == mButtonId
                 && representation.mOverlayId == mOverlayId
                 && representation.mOverlayOnly == mOverlayOnly
-                && representation.mShowParameterValue == mShowParameterValue) {
+                && representation.mShowParameterValue == mShowParameterValue
+                && representation.mIsBooleanFilter == mIsBooleanFilter) {
             return true;
         }
         return false;
     }
 
+    public boolean isBooleanFilter() {
+        return mIsBooleanFilter;
+    }
+
+    public void setIsBooleanFilter(boolean value) {
+        mIsBooleanFilter = value;
+    }
+
     @Override
     public String toString() {
         return mName;
@@ -261,7 +271,8 @@
     }
 
     public boolean canMergeWith(FilterRepresentation representation) {
-        if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
+        if (getFilterType() == FilterRepresentation.TYPE_GEOMETRY
+            && representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
             return true;
         }
         return false;
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterRotateRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterRotateRepresentation.java
index d7e0b58..4299dd3 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterRotateRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterRotateRepresentation.java
@@ -62,7 +62,7 @@
     }
 
     public FilterRotateRepresentation(Rotation rotation) {
-        super(FilterRotateRepresentation.class.getSimpleName());
+        super(SERIALIZATION_NAME);
         setSerializationName(SERIALIZATION_NAME);
         setShowParameterValue(false);
         setFilterClass(FilterRotateRepresentation.class);
@@ -75,6 +75,7 @@
 
     public FilterRotateRepresentation(FilterRotateRepresentation r) {
         this(r.getRotation());
+        setName(r.getName());
     }
 
     public FilterRotateRepresentation() {
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterStraightenRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterStraightenRepresentation.java
index 4769b36..1ba80e8 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterStraightenRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterStraightenRepresentation.java
@@ -35,7 +35,7 @@
     float mStraighten;
 
     public FilterStraightenRepresentation(float straighten) {
-        super(FilterStraightenRepresentation.class.getSimpleName());
+        super(SERIALIZATION_NAME);
         setSerializationName(SERIALIZATION_NAME);
         setShowParameterValue(true);
         setFilterClass(FilterStraightenRepresentation.class);
@@ -48,6 +48,7 @@
 
     public FilterStraightenRepresentation(FilterStraightenRepresentation s) {
         this(s.getStraighten());
+        setName(s.getName());
     }
 
     public FilterStraightenRepresentation() {
diff --git a/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java b/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java
index 2e362f8..d316ade 100644
--- a/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java
+++ b/src/com/android/gallery3d/filtershow/filters/FilterVignetteRepresentation.java
@@ -43,7 +43,7 @@
     private static int MAX = 100;
     private static int MAXFALLOF = 200;
 
-    private BasicParameterInt mParamVignette = new BasicParameterInt(MODE_VIGNETTE, 0, MIN, MAX);
+    private BasicParameterInt mParamVignette = new BasicParameterInt(MODE_VIGNETTE, 50, MIN, MAX);
     private BasicParameterInt mParamExposure = new BasicParameterInt(MODE_EXPOSURE, 0, MIN, MAX);
     private BasicParameterInt mParamSaturation = new BasicParameterInt(MODE_SATURATION, 0, MIN, MAX);
     private BasicParameterInt mParamContrast = new BasicParameterInt(MODE_CONTRAST, 0, MIN, MAX);
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java
index 9849759..9a1a840 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java
@@ -19,6 +19,7 @@
         representation.setShowParameterValue(false);
         representation.setEditorId(ImageOnlyEditor.ID);
         representation.setSupportsPartialRendering(true);
+        representation.setIsBooleanFilter(true);
         return representation;
     }
 
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java
index 77250bd..6cc4970 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterTinyPlanet.java
@@ -23,6 +23,7 @@
 import com.adobe.xmp.XMPException;
 import com.adobe.xmp.XMPMeta;
 import com.android.gallery3d.app.Log;
+import com.android.gallery3d.filtershow.cache.BitmapCache;
 import com.android.gallery3d.filtershow.cache.ImageLoader;
 import com.android.gallery3d.filtershow.imageshow.MasterImage;
 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
@@ -92,7 +93,8 @@
         }
         while (mBitmapOut == null) {
             try {
-                mBitmapOut = getEnvironment().getBitmap(outputSize, outputSize);
+                mBitmapOut = getEnvironment().getBitmap(outputSize,
+                        outputSize, BitmapCache.TINY_PLANET);
             } catch (java.lang.OutOfMemoryError e) {
                 System.gc();
                 outputSize /= 2;
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java
index 0ab5016..49ac595 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterVignette.java
@@ -19,8 +19,10 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Rect;
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.imageshow.MasterImage;
 import com.android.gallery3d.filtershow.pipeline.FilterEnvironment;
 import android.support.v8.renderscript.Allocation;
 import android.support.v8.renderscript.Element;
@@ -91,16 +93,23 @@
         float r = calcRadius(cx, cy, w, h);
         float rx = r;
         float ry = r;
+
+        float[]c = new float[2];
         if (mParameters.isCenterSet()) {
-
-            cx = mParameters.getCenterX() * w;
-            cy = mParameters.getCenterY() * h;
-
-            rx = mParameters.getRadiusX() * w;
-            ry = mParameters.getRadiusY() * h;
+            Matrix m = getOriginalToScreenMatrix(w, h);
+            Rect bounds = MasterImage.getImage().getOriginalBounds();
+            c[0] = bounds.right * mParameters.getCenterX();
+            c[1] = bounds.bottom * mParameters.getCenterY();
+            m.mapPoints(c);
+            cx = c[0];
+            cy = c[1];
+            c[0] = bounds.right * mParameters.getRadiusX();
+            c[1] = bounds.bottom * mParameters.getRadiusY();
+            m.mapVectors(c);
+            rx = c[0];
+            ry = c[1];
         }
 
-
         mScript.set_inputWidth(w);
         mScript.set_inputHeight(h);
         int v = mParameters.getValue(MODE_VIGNETTE);
@@ -115,7 +124,6 @@
         mScript.set_strength(mParameters.getValue(MODE_FALLOFF)/10.f);
         mScript.invoke_setupVignetteParams();
         mScript.forEach_vignette(getInPixelsAllocation(), getOutPixelsAllocation());
-
     }
 
     @Override
diff --git a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
index 6bb88ec..7aa19a4 100644
--- a/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
+++ b/src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
@@ -38,6 +38,7 @@
         representation.setShowParameterValue(false);
         representation.setEditorId(ImageOnlyEditor.ID);
         representation.setSupportsPartialRendering(true);
+        representation.setIsBooleanFilter(true);
         return representation;
     }
 
diff --git a/src/com/android/gallery3d/filtershow/history/HistoryManager.java b/src/com/android/gallery3d/filtershow/history/HistoryManager.java
index 755e2ea..569b299 100644
--- a/src/com/android/gallery3d/filtershow/history/HistoryManager.java
+++ b/src/com/android/gallery3d/filtershow/history/HistoryManager.java
@@ -42,6 +42,9 @@
     }
 
     public HistoryItem getItem(int position) {
+        if (position > mHistoryItems.size() - 1) {
+            return null;
+        }
         return mHistoryItems.elementAt(position);
     }
 
@@ -58,7 +61,7 @@
     }
 
     public boolean canReset() {
-        if (getCount() <= 1) {
+        if (getCount() <= 0) {
             return false;
         }
         return true;
@@ -108,9 +111,7 @@
         if (getCount() == 0) {
             return;
         }
-        HistoryItem first = getItem(getCount() - 1);
         clear();
-        addHistoryItem(first);
         updateMenuItems();
     }
 
diff --git a/src/com/android/gallery3d/filtershow/imageshow/EclipseControl.java b/src/com/android/gallery3d/filtershow/imageshow/EclipseControl.java
index 4c0c24d..b94e52e 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/EclipseControl.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/EclipseControl.java
@@ -108,36 +108,26 @@
         return (!mImageBounds.contains((int) x1, (int) y1));
     }
 
-    public void actionDown2(float x, float y, int iw, int ih, Oval oval) {
+    public void actionDown(float x, float y, Oval oval)  {
         float[] point = new float[]{
                 x, y};
         mScrToImg.mapPoints(point);
         mDownX = point[0];
         mDownY = point[1];
-        mDownCenterX = oval.getCenterX() * iw;
-        mDownCenterY = oval.getCenterY() * ih;
-        mDownRadiusX = oval.getRadiusX() * iw;
-        mDownRadiusY = oval.getRadiusY() * ih;
+        mDownCenterX = oval.getCenterX();
+        mDownCenterY = oval.getCenterY();
+        mDownRadiusX = oval.getRadiusX();
+        mDownRadiusY = oval.getRadiusY();
     }
 
-    public void actionDown(float x, float y, Oval oval) {
-        actionDown2(x, y, 1, 1, oval);
-    }
 
     public void actionMove(int handle, float x, float y, Oval oval) {
-        actionMove2(handle, x, y, 1, 1, oval);
-    }
-
-    public void actionMove2(int handle, float x, float y, int w, int h, Oval oval) {
         float[] point = new float[]{
                 x, y};
         mScrToImg.mapPoints(point);
         x = point[0];
         y = point[1];
-        if (w == 0) {
-            w = 1;
-            h = 1;
-        }
+
         // Test if the matrix is swapping x and y
         point[0] = 0;
         point[1] = 1;
@@ -152,7 +142,7 @@
                 if (centerIsOutside(x - ctrdx, y - ctrdy)) {
                     break;
                 }
-                oval.setCenter((x - ctrdx) / w, (y - ctrdy) / h);
+                oval.setCenter((x - ctrdx), (y - ctrdy));
                 // setRepresentation(mVignetteRep);
                 break;
             case HAN_NORTH:
@@ -160,10 +150,10 @@
             case HAN_SOUTH:
                 if (swapxy) {
                     float raddx = mDownRadiusY - Math.abs(mDownX - mDownCenterY);
-                    oval.setRadiusY(Math.abs(x - oval.getCenterY() * h + sign * raddx)/h);
+                    oval.setRadiusY(Math.abs(x - oval.getCenterY() + sign * raddx));
                 } else {
                     float raddy = mDownRadiusY - Math.abs(mDownY - mDownCenterY);
-                    oval.setRadiusY(Math.abs(y - oval.getCenterY() * h + sign * raddy)/h);
+                    oval.setRadiusY(Math.abs(y - oval.getCenterY() + sign * raddy));
                 }
                 break;
             case HAN_EAST:
@@ -171,10 +161,10 @@
             case HAN_WEST:
                 if (swapxy) {
                     float raddy = mDownRadiusX - Math.abs(mDownY - mDownCenterX);
-                    oval.setRadiusX(Math.abs(y - oval.getCenterX() * w + sign * raddy)/w);
+                    oval.setRadiusX(Math.abs(y - oval.getCenterX() + sign * raddy));
                 } else {
                     float raddx = mDownRadiusX - Math.abs(mDownX - mDownCenterX);
-                    oval.setRadiusX(Math.abs(x - oval.getCenterX() * w - sign * raddx)/w);
+                    oval.setRadiusX(Math.abs(x - oval.getCenterX() -  sign * raddx));
                 }
                 break;
             case HAN_SE:
@@ -186,13 +176,13 @@
                 float ctr_dx = mDownX - mDownCenterX;
                 float ctr_dy = mDownY - mDownCenterY;
                 float downRad = Math.abs(ctr_dx) + Math.abs(ctr_dy) - dr;
-                float rx = oval.getRadiusX() * w;
-                float ry = oval.getRadiusY() * h;
+                float rx = oval.getRadiusX();
+                float ry = oval.getRadiusY();
                 float r = (Math.abs(rx) + Math.abs(ry)) * sin45;
-                float dx = x - oval.getCenterX() * w;
-                float dy = y - oval.getCenterY() * h;
+                float dx = x - oval.getCenterX();
+                float dy = y - oval.getCenterY();
                 float nr = Math.abs(Math.abs(dx) + Math.abs(dy) - downRad);
-                oval.setRadius((rx * nr / r) / w, (ry * nr / r) / h);
+                oval.setRadius((rx * nr / r), (ry * nr / r));
 
                 break;
         }
diff --git a/src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java b/src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java
index e3cd56d..dada7dc 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/GeometryMathUtils.java
@@ -22,6 +22,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.util.Log;
 
 import com.android.gallery3d.filtershow.cache.BitmapCache;
 import com.android.gallery3d.filtershow.cache.ImageLoader;
@@ -38,6 +39,9 @@
 import java.util.Iterator;
 
 public final class GeometryMathUtils {
+    private static final String TAG = "GeometryMathUtils";
+    public static final float SHOW_SCALE = .9f;
+
     private GeometryMathUtils() {};
 
     // Holder class for Geometry data.
@@ -313,7 +317,8 @@
         Matrix m = getCropSelectionToScreenMatrix(null, holder, width, height, frame.width(),
                 frame.height());
         BitmapCache bitmapCache = MasterImage.getImage().getBitmapCache();
-        Bitmap temp = bitmapCache.getBitmap(frame.width(), frame.height());
+        Bitmap temp = bitmapCache.getBitmap(frame.width(),
+                frame.height(), BitmapCache.UTIL_GEOMETRY);
         Canvas canvas = new Canvas(temp);
         Paint paint = new Paint();
         paint.setAntiAlias(true);
@@ -434,7 +439,15 @@
 
     public static Matrix getFullGeometryToScreenMatrix(GeometryHolder holder, int bitmapWidth,
             int bitmapHeight, int viewWidth, int viewHeight) {
-        float scale = GeometryMathUtils.scale(bitmapWidth, bitmapHeight, viewWidth, viewHeight);
+        int bh = bitmapHeight;
+        int bw = bitmapWidth;
+        if (GeometryMathUtils.needsDimensionSwap(holder.rotation)) {
+            bh = bitmapWidth;
+            bw = bitmapHeight;
+        }
+        float scale = GeometryMathUtils.scale(bw, bh, viewWidth, viewHeight);
+        scale *= SHOW_SCALE;
+        float s = Math.min(viewWidth / (float) bitmapWidth, viewHeight / (float) bitmapHeight);
         Matrix m = getFullGeometryMatrix(holder, bitmapWidth, bitmapHeight);
         m.postScale(scale, scale);
         m.postTranslate(viewWidth / 2f, viewHeight / 2f);
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
index e027d01..4b03991 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageCrop.java
@@ -282,6 +282,22 @@
             // Scale min side and tolerance by display matrix scale factor
             mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize));
             mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance));
+            // drive Crop engine to clamp to crop bounds
+            int[] sides = {CropObject.MOVE_TOP,
+                    CropObject.MOVE_BOTTOM,
+                    CropObject.MOVE_LEFT,
+                    CropObject.MOVE_RIGHT};
+            int delta = Math.min(canvas.getWidth(), canvas.getHeight()) / 4;
+            int[] dy = {delta, -delta, 0, 0};
+            int[] dx = {0, 0, delta, -delta};
+
+            for (int i = 0; i < sides.length; i++) {
+                mCropObj.selectEdge(sides[i]);
+
+                mCropObj.moveCurrentSelection(dx[i], dy[i]);
+                mCropObj.moveCurrentSelection(-dx[i], -dy[i]);
+            }
+            mCropObj.selectEdge(CropObject.MOVE_NONE);
         }
         // Draw actual bitmap
         mPaint.reset();
@@ -297,6 +313,7 @@
         if (mDisplayCropMatrix.mapRect(mScreenCropBounds)) {
             // Draw crop rect and markers
             CropDrawingUtils.drawCropRect(canvas, mScreenCropBounds);
+            CropDrawingUtils.drawShade(canvas, mScreenCropBounds);
             CropDrawingUtils.drawRuleOfThird(canvas, mScreenCropBounds);
             CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize,
                     mScreenCropBounds, mCropObj.isFixedAspect(),
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageCurves.java b/src/com/android/gallery3d/filtershow/imageshow/ImageCurves.java
index 82c4b2f..0d20322 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageCurves.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageCurves.java
@@ -34,6 +34,7 @@
 import android.widget.PopupMenu;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.editors.Editor;
 import com.android.gallery3d.filtershow.editors.EditorCurves;
 import com.android.gallery3d.filtershow.filters.FilterCurvesRepresentation;
@@ -116,6 +117,7 @@
         });
         Editor.hackFixStrings(popupMenu.getMenu());
         popupMenu.show();
+        ((FilterShowActivity)getContext()).onShowMenu(popupMenu);
     }
 
     @Override
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java b/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java
index 0fdac1c..795a247 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java
@@ -244,20 +244,11 @@
         byte type = mTmpStrokData.mType;
         float radius = mTmpStrokData.mRadius;
         mFRep.fillStrokeParameters(mTmpStrokData);
-        if (type != mTmpStrokData.mType || radius != mTmpStrokData.mRadius) {
-            mBitmap = getBrush(mEditorDraw.getBrushIcon(mTmpStrokData.mType));
-            float size = mRotateToScreen.mapRadius(mTmpStrokData.mRadius) * 2;
-            mBitmap = createScaledBitmap(mBitmap, (int) size, (int) size, true);
-        }
 
-        if (color == mTmpStrokData.mColor
-                && type == mTmpStrokData.mType
-                && radius == mTmpStrokData.mRadius) {
-            return;
+        if (radius != mTmpStrokData.mRadius) {
+            mTimeout = DISPLAY_TIME + System.currentTimeMillis();
+            scheduleWakeup(DISPLAY_TIME);
         }
-
-        mTimeout = DISPLAY_TIME + System.currentTimeMillis();
-        scheduleWakeup(DISPLAY_TIME);
     }
 
     public void drawLook(Canvas canvas) {
@@ -269,28 +260,22 @@
         int centerx = cw / 2;
         int centery = ch / 2;
 
-        mFRep.fillStrokeParameters(mTmpStrokData);
+//        mFRep.fillStrokeParameters(mTmpStrokData);
         mIconPaint.setAntiAlias(true);
-        mIconPaint.setColor(mTmpStrokData.mColor);
-
-        mIconPaint.setColorFilter(new PorterDuffColorFilter(mTmpStrokData.mColor,
-                PorterDuff.Mode.MULTIPLY));
+        mIconPaint.setStyle(Paint.Style.STROKE);
         float rad = mRotateToScreen.mapRadius(mTmpStrokData.mRadius);
+
         RectF rec = new RectF();
-        rec.set(centerx - mDisplayBorder - rad,
-                centery - mDisplayBorder - rad,
-                centerx + mDisplayBorder + rad,
-                centery + mDisplayBorder + rad);
-        mShadow.setBounds((int) (mBorderShadowSize + rec.left),
-                (int) (mBorderShadowSize + rec.top),
-                (int) (mBorderShadowSize + rec.right),
-                (int) (mBorderShadowSize + rec.bottom));
-        mShadow.draw(canvas);
-        canvas.drawRoundRect(rec, mDisplayRound, mDisplayRound, mCheckerdPaint);
-        canvas.drawRoundRect(rec, mDisplayRound, mDisplayRound, mBorderPaint);
-        canvas.drawBitmap(mBitmap,
-                centerx - mBitmap.getWidth() / 2,
-                centery - mBitmap.getHeight() / 2, mIconPaint);
+        rec.set(centerx - rad,
+                centery - rad,
+                centerx + rad,
+                centery + rad);
+        mIconPaint.setColor(Color.BLACK);
+        mIconPaint.setStrokeWidth(5);
+        canvas.drawArc(rec, 0, 360, true, mIconPaint);
+        mIconPaint.setColor(Color.WHITE);
+        mIconPaint.setStrokeWidth(3);
+        canvas.drawArc(rec, 0, 360, true, mIconPaint);
     }
 
     @Override
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
index 77eaf4d..7089e60 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
@@ -16,6 +16,8 @@
 
 package com.android.gallery3d.filtershow.imageshow;
 
+import android.animation.Animator;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -27,9 +29,12 @@
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.Shader;
 import android.graphics.drawable.NinePatchDrawable;
+import android.support.v4.widget.EdgeEffectCompat;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.GestureDetector;
 import android.view.GestureDetector.OnDoubleTapListener;
 import android.view.GestureDetector.OnGestureListener;
@@ -48,7 +53,6 @@
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.Collection;
 
 public class ImageShow extends View implements OnGestureListener,
         ScaleGestureDetector.OnScaleGestureListener,
@@ -91,6 +95,21 @@
     Point mOriginalTranslation = new Point();
     float mOriginalScale;
     float mStartFocusX, mStartFocusY;
+
+    private EdgeEffectCompat mEdgeEffect = null;
+    private static final int EDGE_LEFT = 1;
+    private static final int EDGE_TOP = 2;
+    private static final int EDGE_RIGHT = 3;
+    private static final int EDGE_BOTTOM = 4;
+    private int mCurrentEdgeEffect = 0;
+    private int mEdgeSize = 100;
+
+    private static final int mAnimationSnapDelay = 200;
+    private static final int mAnimationZoomDelay = 400;
+    private ValueAnimator mAnimatorScale = null;
+    private ValueAnimator mAnimatorTranslateX = null;
+    private ValueAnimator mAnimatorTranslateY = null;
+
     private enum InteractionMode {
         NONE,
         SCALE,
@@ -163,11 +182,14 @@
             Bitmap mask = BitmapFactory.decodeResource(res, R.drawable.spot_mask);
             sMask = convertToAlphaMask(mask);
         }
+        mEdgeEffect = new EdgeEffectCompat(context);
+        mEdgeSize = res.getDimensionPixelSize(R.dimen.edge_glow_size);
     }
 
     public void attach() {
         MasterImage.getImage().addObserver(this);
         bindAsImageLoadListener();
+        MasterImage.getImage().resetGeometryImages(false);
     }
 
     public void detach() {
@@ -231,55 +253,84 @@
 
     @Override
     public void onDraw(Canvas canvas) {
+        mPaint.reset();
+        mPaint.setAntiAlias(true);
+        mPaint.setFilterBitmap(true);
         MasterImage.getImage().setImageShowSize(
                 getWidth() - 2*mShadowMargin,
                 getHeight() - 2*mShadowMargin);
 
-        float cx = canvas.getWidth()/2.0f;
-        float cy = canvas.getHeight()/2.0f;
-        float scaleFactor = MasterImage.getImage().getScaleFactor();
-        Point translation = MasterImage.getImage().getTranslation();
+        MasterImage img = MasterImage.getImage();
+        // Hide the loading indicator as needed
+        if (mActivity.isLoadingVisible() && getFilteredImage() != null) {
+            if ((img.getLoadedPreset() == null)
+                    || (img.getLoadedPreset() != null
+                    && img.getLoadedPreset().equals(img.getCurrentPreset()))) {
+                mActivity.stopLoadingIndicator();
+            } else if (img.getLoadedPreset() != null) {
+                return;
+            }
+            mActivity.stopLoadingIndicator();
+        }
 
         canvas.save();
 
         mShadowDrawn = false;
 
-        canvas.save();
-        // TODO: center scale on gesture
-        canvas.scale(scaleFactor, scaleFactor, cx, cy);
-        canvas.translate(translation.x, translation.y);
         Bitmap highresPreview = MasterImage.getImage().getHighresImage();
+        Bitmap fullHighres = MasterImage.getImage().getPartialImage();
 
         boolean isDoingNewLookAnimation = MasterImage.getImage().onGoingNewLookAnimation();
 
-        if (!isDoingNewLookAnimation && highresPreview != null) {
-            drawImage(canvas, highresPreview, true);
+        if (highresPreview == null || isDoingNewLookAnimation) {
+            drawImageAndAnimate(canvas, getFilteredImage());
         } else {
-            drawImage(canvas, getFilteredImage(), true);
+            drawImageAndAnimate(canvas, highresPreview);
         }
+
+        drawHighresImage(canvas, fullHighres);
+        drawCompareImage(canvas, getGeometryOnlyImage());
+
         canvas.restore();
 
-        Bitmap partialPreview = MasterImage.getImage().getPartialImage();
-        if (!isDoingNewLookAnimation && partialPreview != null) {
+        if (!mEdgeEffect.isFinished()) {
             canvas.save();
-            Rect originalBounds = MasterImage.getImage().getOriginalBounds();
-            Collection<FilterRepresentation> geo = MasterImage.getImage().getPreset()
-                    .getGeometryFilters();
-
-            Matrix compensation = GeometryMathUtils.getPartialToScreenMatrix(geo,
-                    originalBounds, getWidth(), getHeight(),
-                    partialPreview.getWidth(), partialPreview.getHeight());
-            canvas.drawBitmap(partialPreview, compensation, null);
+            float dx = (getHeight() - getWidth()) / 2f;
+            if (getWidth() > getHeight()) {
+                dx = - (getWidth() - getHeight()) / 2f;
+            }
+            if (mCurrentEdgeEffect == EDGE_BOTTOM) {
+                canvas.rotate(180, getWidth()/2, getHeight()/2);
+            } else if (mCurrentEdgeEffect == EDGE_RIGHT) {
+                canvas.rotate(90, getWidth()/2, getHeight()/2);
+                canvas.translate(0, dx);
+            } else if (mCurrentEdgeEffect == EDGE_LEFT) {
+                canvas.rotate(270, getWidth()/2, getHeight()/2);
+                canvas.translate(0, dx);
+            }
+            if (mCurrentEdgeEffect != 0) {
+                mEdgeEffect.draw(canvas);
+            }
             canvas.restore();
+            invalidate();
+        } else {
+            mCurrentEdgeEffect = 0;
         }
+    }
 
-        canvas.save();
-        canvas.scale(scaleFactor, scaleFactor, cx, cy);
-        canvas.translate(translation.x, translation.y);
-        drawPartialImage(canvas, getGeometryOnlyImage());
-        canvas.restore();
-
-        canvas.restore();
+    private void drawHighresImage(Canvas canvas, Bitmap fullHighres) {
+        Matrix originalToScreen = MasterImage.getImage().originalImageToScreen();
+        if (fullHighres != null && originalToScreen != null) {
+            Matrix screenToOriginal = new Matrix();
+            originalToScreen.invert(screenToOriginal);
+            Rect rBounds = new Rect();
+            rBounds.set(MasterImage.getImage().getPartialBounds());
+            if (fullHighres != null) {
+                originalToScreen.preTranslate(rBounds.left, rBounds.top);
+                canvas.clipRect(mImageBounds);
+                canvas.drawBitmap(fullHighres, originalToScreen, mPaint);
+            }
+        }
     }
 
     public void resetImageCaches(ImageShow caller) {
@@ -298,52 +349,82 @@
         return MasterImage.getImage().getFilteredImage();
     }
 
-    public void drawImage(Canvas canvas, Bitmap image, boolean updateBounds) {
+    public void drawImageAndAnimate(Canvas canvas,
+                                    Bitmap image) {
         if (image == null) {
             return;
         }
-
-        Rect d = computeImageBounds(image.getWidth(), image.getHeight());
-
-        if (updateBounds) {
-            mImageBounds = d;
+        MasterImage master = MasterImage.getImage();
+        Matrix m = master.computeImageToScreen(image, 0, false);
+        if (m == null) {
+            return;
         }
 
-        float centerX = mShadowMargin + (getWidth() - 2 * mShadowMargin) / 2;
-        float centerY = mShadowMargin + (getHeight() - 2 * mShadowMargin) / 2;
-
-        MasterImage master = MasterImage.getImage();
         canvas.save();
-        if (master.onGoingNewLookAnimation()
-                || mDidStartAnimation) {
+
+        RectF d = new RectF(0, 0, image.getWidth(), image.getHeight());
+        m.mapRect(d);
+        d.roundOut(mImageBounds);
+
+        boolean showAnimatedImage = master.onGoingNewLookAnimation();
+        if (!showAnimatedImage && mDidStartAnimation) {
+            // animation ended, but do we have the correct image to show?
+            if (master.getPreset().equals(master.getCurrentPreset())) {
+                // we do, let's stop showing the animated image
+                mDidStartAnimation = false;
+                MasterImage.getImage().resetAnimBitmap();
+            } else {
+                showAnimatedImage = true;
+            }
+        } else if (showAnimatedImage) {
             mDidStartAnimation = true;
+        }
+
+        if (showAnimatedImage) {
+            canvas.save();
+
+            // Animation uses the image before the change
+            Bitmap previousImage = master.getPreviousImage();
+            Matrix mp = master.computeImageToScreen(previousImage, 0, false);
+            RectF dp = new RectF(0, 0, previousImage.getWidth(), previousImage.getHeight());
+            mp.mapRect(dp);
+            Rect previousBounds = new Rect();
+            dp.roundOut(previousBounds);
+            float centerX = dp.centerX();
+            float centerY = dp.centerY();
+            boolean needsToDrawImage = true;
+
             if (master.getCurrentLookAnimation()
-                    == MasterImage.CIRCLE_ANIMATION
-                    && MasterImage.getImage().getPreviousImage() != null) {
+                    == MasterImage.CIRCLE_ANIMATION) {
                 float maskScale = MasterImage.getImage().getMaskScale();
-                if (maskScale > 0.0f) {
+                if (maskScale >= 0.0f) {
                     float maskW = sMask.getWidth() / 2.0f;
                     float maskH = sMask.getHeight() / 2.0f;
-                    float x = centerX - maskW * maskScale;
-                    float y = centerY - maskH * maskScale;
+                    Point point = mActivity.hintTouchPoint(this);
+                    float maxMaskScale = 2 * Math.max(getWidth(), getHeight())
+                            / Math.min(maskW, maskH);
+                    maskScale = maskScale * maxMaskScale;
+                    float x = point.x - maskW * maskScale;
+                    float y = point.y - maskH * maskScale;
 
                     // Prepare the shader
                     mShaderMatrix.reset();
                     mShaderMatrix.setScale(1.0f / maskScale, 1.0f / maskScale);
-                    mShaderMatrix.preTranslate(-x + d.left, -y + d.top);
-                    float scaleImageX = d.width() / (float) image.getWidth();
-                    float scaleImageY = d.height() / (float) image.getHeight();
+                    mShaderMatrix.preTranslate(-x + mImageBounds.left, -y + mImageBounds.top);
+                    float scaleImageX = mImageBounds.width() / (float) image.getWidth();
+                    float scaleImageY = mImageBounds.height() / (float) image.getHeight();
                     mShaderMatrix.preScale(scaleImageX, scaleImageY);
                     mMaskPaint.reset();
                     mMaskPaint.setShader(createShader(image));
                     mMaskPaint.getShader().setLocalMatrix(mShaderMatrix);
 
-                    drawImage(canvas, MasterImage.getImage().getPreviousImage());
+                    drawShadow(canvas, mImageBounds); // as needed
+                    canvas.drawBitmap(previousImage, m, mPaint);
+                    canvas.clipRect(mImageBounds);
                     canvas.translate(x, y);
                     canvas.scale(maskScale, maskScale);
                     canvas.drawBitmap(sMask, 0, 0, mMaskPaint);
-                } else {
-                    drawImage(canvas, image);
+                    needsToDrawImage = false;
                 }
             } else if (master.getCurrentLookAnimation()
                     == MasterImage.ROTATE_ANIMATION) {
@@ -356,7 +437,6 @@
                         + (finalScale * master.getAnimFraction());
                 canvas.rotate(master.getAnimRotationValue(), centerX, centerY);
                 canvas.scale(finalScale, finalScale, centerX, centerY);
-                drawImage(canvas, master.getPreviousImage());
             } else if (master.getCurrentLookAnimation()
                     == MasterImage.MIRROR_ANIMATION) {
                 if (master.getCurrentFilterRepresentation()
@@ -392,33 +472,22 @@
                         }
                     }
                 }
-                drawImage(canvas, master.getPreviousImage());
             }
+
+            if (needsToDrawImage) {
+                drawShadow(canvas, previousBounds); // as needed
+                canvas.drawBitmap(previousImage, mp, mPaint);
+            }
+
+            canvas.restore();
         } else {
-            drawImage(canvas, image);
+            drawShadow(canvas, mImageBounds); // as needed
+            canvas.drawBitmap(image, m, mPaint);
         }
 
-        if (!master.onGoingNewLookAnimation()
-                && mDidStartAnimation
-                && !master.getPreviousPreset().equals(master.getCurrentPreset())) {
-            mDidStartAnimation = false;
-            MasterImage.getImage().resetAnimBitmap();
-        }
         canvas.restore();
     }
 
-    private void drawImage(Canvas canvas, Bitmap image) {
-        Rect d = computeImageBounds(image.getWidth(), image.getHeight());
-        float scaleImageX = d.width() / (float) image.getWidth();
-        float scaleImageY = d.height() / (float) image.getHeight();
-        Matrix imageMatrix = new Matrix();
-        imageMatrix.postScale(scaleImageX, scaleImageY);
-        imageMatrix.postTranslate(d.left, d.top);
-        drawShadow(canvas, d);
-        canvas.clipRect(d);
-        canvas.drawBitmap(image, imageMatrix, mPaint);
-    }
-
     private Rect computeImageBounds(int imageWidth, int imageHeight) {
         float scale = GeometryMathUtils.scale(imageWidth, imageHeight,
                 getWidth(), getHeight());
@@ -443,8 +512,9 @@
         }
     }
 
-    public void drawPartialImage(Canvas canvas, Bitmap image) {
-        boolean showsOriginal = MasterImage.getImage().showsOriginal();
+    public void drawCompareImage(Canvas canvas, Bitmap image) {
+        MasterImage master = MasterImage.getImage();
+        boolean showsOriginal = master.showsOriginal();
         if (!showsOriginal && !mTouchShowOriginal)
             return;
         canvas.save();
@@ -472,8 +542,20 @@
 
             Rect d = new Rect(mImageBounds.left, mImageBounds.top,
                     mImageBounds.left + px, mImageBounds.top + py);
+            if (mShowOriginalDirection == UNVEIL_HORIZONTAL) {
+                if (mTouchDown.x - mTouch.x > 0) {
+                    d.set(mImageBounds.left + px, mImageBounds.top,
+                            mImageBounds.right, mImageBounds.top + py);
+                }
+            } else {
+                if (mTouchDown.y - mTouch.y > 0) {
+                    d.set(mImageBounds.left, mImageBounds.top + py,
+                            mImageBounds.left + px, mImageBounds.bottom);
+                }
+            }
             canvas.clipRect(d);
-            drawImage(canvas, image, false);
+            Matrix m = master.computeImageToScreen(image, 0, false);
+            canvas.drawBitmap(image, m, mPaint);
             Paint paint = new Paint();
             paint.setColor(Color.BLACK);
             paint.setStrokeWidth(3);
@@ -566,7 +648,6 @@
                 Point translation = MasterImage.getImage().getTranslation();
                 translation.x = (int) (originalTranslation.x + translateX);
                 translation.y = (int) (originalTranslation.y + translateY);
-                constrainTranslation(translation, scaleFactor);
                 MasterImage.getImage().setTranslation(translation);
                 mTouchShowOriginal = false;
             } else if (enableComparison() && !mOriginalDisabled
@@ -577,7 +658,9 @@
             }
         }
 
-        if (action == MotionEvent.ACTION_UP) {
+        if (action == MotionEvent.ACTION_UP
+                || action == MotionEvent.ACTION_CANCEL
+                || action == MotionEvent.ACTION_OUTSIDE) {
             mInteractionMode = InteractionMode.NONE;
             mTouchShowOriginal = false;
             mTouchDown.x = 0;
@@ -589,10 +672,67 @@
                 MasterImage.getImage().resetTranslation();
             }
         }
+
+        float scaleFactor = MasterImage.getImage().getScaleFactor();
+        Point translation = MasterImage.getImage().getTranslation();
+        constrainTranslation(translation, scaleFactor);
+        MasterImage.getImage().setTranslation(translation);
+
         invalidate();
         return true;
     }
 
+    private void startAnimTranslation(int fromX, int toX,
+                                      int fromY, int toY, int delay) {
+        if (fromX == toX && fromY == toY) {
+            return;
+        }
+        if (mAnimatorTranslateX != null) {
+            mAnimatorTranslateX.cancel();
+        }
+        if (mAnimatorTranslateY != null) {
+            mAnimatorTranslateY.cancel();
+        }
+        mAnimatorTranslateX = ValueAnimator.ofInt(fromX, toX);
+        mAnimatorTranslateY = ValueAnimator.ofInt(fromY, toY);
+        mAnimatorTranslateX.setDuration(delay);
+        mAnimatorTranslateY.setDuration(delay);
+        mAnimatorTranslateX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                Point translation = MasterImage.getImage().getTranslation();
+                translation.x = (Integer) animation.getAnimatedValue();
+                MasterImage.getImage().setTranslation(translation);
+                invalidate();
+            }
+        });
+        mAnimatorTranslateY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                Point translation = MasterImage.getImage().getTranslation();
+                translation.y = (Integer) animation.getAnimatedValue();
+                MasterImage.getImage().setTranslation(translation);
+                invalidate();
+            }
+        });
+        mAnimatorTranslateX.start();
+        mAnimatorTranslateY.start();
+    }
+
+    private void applyTranslationConstraints() {
+        float scaleFactor = MasterImage.getImage().getScaleFactor();
+        Point translation = MasterImage.getImage().getTranslation();
+        int x = translation.x;
+        int y = translation.y;
+        constrainTranslation(translation, scaleFactor);
+
+        if (x != translation.x || y != translation.y) {
+            startAnimTranslation(x, translation.x,
+                                 y, translation.y,
+                                 mAnimationSnapDelay);
+        }
+    }
+
     protected boolean enableComparison() {
         return true;
     }
@@ -601,34 +741,130 @@
     public boolean onDoubleTap(MotionEvent arg0) {
         mZoomIn = !mZoomIn;
         float scale = 1.0f;
+        final float x = arg0.getX();
+        final float y = arg0.getY();
         if (mZoomIn) {
             scale = MasterImage.getImage().getMaxScaleFactor();
         }
         if (scale != MasterImage.getImage().getScaleFactor()) {
-            MasterImage.getImage().setScaleFactor(scale);
-            float translateX = (getWidth() / 2 - arg0.getX());
-            float translateY = (getHeight() / 2 - arg0.getY());
+            if (mAnimatorScale != null) {
+                mAnimatorScale.cancel();
+            }
+            mAnimatorScale = ValueAnimator.ofFloat(
+                    MasterImage.getImage().getScaleFactor(),
+                    scale
+            );
+            float translateX = (getWidth() / 2 - x);
+            float translateY = (getHeight() / 2 - y);
             Point translation = MasterImage.getImage().getTranslation();
-            translation.x = (int) (mOriginalTranslation.x + translateX);
-            translation.y = (int) (mOriginalTranslation.y + translateY);
+            int startTranslateX = translation.x;
+            int startTranslateY = translation.y;
+            if (scale != 1.0f) {
+                translation.x = (int) (mOriginalTranslation.x + translateX);
+                translation.y = (int) (mOriginalTranslation.y + translateY);
+            } else {
+                translation.x = 0;
+                translation.y = 0;
+            }
             constrainTranslation(translation, scale);
-            MasterImage.getImage().setTranslation(translation);
-            invalidate();
+
+            startAnimTranslation(startTranslateX, translation.x,
+                                 startTranslateY, translation.y,
+                                 mAnimationZoomDelay);
+            mAnimatorScale.setDuration(mAnimationZoomDelay);
+            mAnimatorScale.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    MasterImage.getImage().setScaleFactor((Float) animation.getAnimatedValue());
+                    invalidate();
+                }
+            });
+            mAnimatorScale.addListener(new Animator.AnimatorListener() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    applyTranslationConstraints();
+                    MasterImage.getImage().needsUpdatePartialPreview();
+                    invalidate();
+                }
+
+                @Override
+                public void onAnimationCancel(Animator animation) {
+
+                }
+
+                @Override
+                public void onAnimationRepeat(Animator animation) {
+
+                }
+            });
+            mAnimatorScale.start();
         }
         return true;
     }
 
     private void constrainTranslation(Point translation, float scale) {
-        float maxTranslationX = getWidth() / scale;
-        float maxTranslationY = getHeight() / scale;
-        if (Math.abs(translation.x) > maxTranslationX) {
-            translation.x = (int) (Math.signum(translation.x) *
-                    maxTranslationX);
-            if (Math.abs(translation.y) > maxTranslationY) {
-                translation.y = (int) (Math.signum(translation.y) *
-                        maxTranslationY);
-            }
+        int currentEdgeEffect = 0;
+        if (scale <= 1) {
+            mCurrentEdgeEffect = 0;
+            mEdgeEffect.finish();
+            return;
+        }
 
+        Matrix originalToScreen = MasterImage.getImage().originalImageToScreen();
+        Rect originalBounds = MasterImage.getImage().getOriginalBounds();
+        RectF screenPos = new RectF(originalBounds);
+        originalToScreen.mapRect(screenPos);
+
+        boolean rightConstraint = screenPos.right < getWidth() - mShadowMargin;
+        boolean leftConstraint = screenPos.left > mShadowMargin;
+        boolean topConstraint = screenPos.top > mShadowMargin;
+        boolean bottomConstraint = screenPos.bottom < getHeight() - mShadowMargin;
+
+        if (screenPos.width() > getWidth()) {
+            if (rightConstraint && !leftConstraint) {
+                float tx = screenPos.right - translation.x * scale;
+                translation.x = (int) ((getWidth() - mShadowMargin - tx) / scale);
+                currentEdgeEffect = EDGE_RIGHT;
+            } else if (leftConstraint && !rightConstraint) {
+                float tx = screenPos.left - translation.x * scale;
+                translation.x = (int) ((mShadowMargin - tx) / scale);
+                currentEdgeEffect = EDGE_LEFT;
+            }
+        } else {
+            float tx = screenPos.right - translation.x * scale;
+            float dx = (getWidth() - 2 * mShadowMargin - screenPos.width()) / 2f;
+            translation.x = (int) ((getWidth() - mShadowMargin - tx - dx) / scale);
+        }
+
+        if (screenPos.height() > getHeight()) {
+            if (bottomConstraint && !topConstraint) {
+                float ty = screenPos.bottom - translation.y * scale;
+                translation.y = (int) ((getHeight() - mShadowMargin - ty) / scale);
+                currentEdgeEffect = EDGE_BOTTOM;
+            } else if (topConstraint && !bottomConstraint) {
+                float ty = screenPos.top - translation.y * scale;
+                translation.y = (int) ((mShadowMargin - ty) / scale);
+                currentEdgeEffect = EDGE_TOP;
+            }
+        } else {
+            float ty = screenPos.bottom - translation.y * scale;
+            float dy = (getHeight()- 2 * mShadowMargin - screenPos.height()) / 2f;
+            translation.y = (int) ((getHeight() - mShadowMargin - ty - dy) / scale);
+        }
+
+        if (mCurrentEdgeEffect != currentEdgeEffect) {
+            if (mCurrentEdgeEffect == 0 || currentEdgeEffect != 0) {
+                mCurrentEdgeEffect = currentEdgeEffect;
+                mEdgeEffect.finish();
+            }
+            mEdgeEffect.setSize(getWidth(), mEdgeSize);
+        }
+        if (currentEdgeEffect != 0) {
+            mEdgeEffect.onPull(mEdgeSize);
         }
     }
 
@@ -692,8 +928,8 @@
         if (scaleFactor > MasterImage.getImage().getMaxScaleFactor()) {
             scaleFactor = MasterImage.getImage().getMaxScaleFactor();
         }
-        if (scaleFactor < 0.5) {
-            scaleFactor = 0.5f;
+        if (scaleFactor < 1.0f) {
+            scaleFactor = 1.0f;
         }
         MasterImage.getImage().setScaleFactor(scaleFactor);
         scaleFactor = img.getScaleFactor();
@@ -704,9 +940,7 @@
         Point translation = MasterImage.getImage().getTranslation();
         translation.x = (int) (mOriginalTranslation.x + translateX);
         translation.y = (int) (mOriginalTranslation.y + translateY);
-        constrainTranslation(translation, scaleFactor);
         MasterImage.getImage().setTranslation(translation);
-
         invalidate();
         return true;
     }
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
index a85f58d..4742f2e 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageStraighten.java
@@ -16,6 +16,7 @@
 
 package com.android.gallery3d.filtershow.imageshow;
 
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -28,6 +29,7 @@
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 
+import com.android.gallery3d.filtershow.crop.CropDrawingUtils;
 import com.android.gallery3d.filtershow.editors.EditorStraighten;
 import com.android.gallery3d.filtershow.filters.FilterCropRepresentation;
 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
@@ -43,6 +45,7 @@
     private float mBaseAngle = 0;
     private float mAngle = 0;
     private float mInitialAngle = 0;
+    private static final int NBLINES = 16;
     private boolean mFirstDrawSinceUp = false;
     private EditorStraighten mEditorStraighten;
     private FilterStraightenRepresentation mLocalRep = new FilterStraightenRepresentation();
@@ -54,6 +57,11 @@
         NONE, MOVE
     }
     private MODES mState = MODES.NONE;
+    private ValueAnimator mAnimator = null;
+    private int mDefaultGridAlpha = 60;
+    private float mGridAlpha = 1f;
+    private int mOnStartAnimDelay = 1000;
+    private int mAnimDelay = 500;
     private static final float MAX_STRAIGHTEN_ANGLE
         = FilterStraightenRepresentation.MAX_STRAIGHTEN_ANGLE;
     private static final float MIN_STRAIGHTEN_ANGLE
@@ -73,6 +81,27 @@
         super(context, attrs);
     }
 
+    @Override
+    public void attach() {
+        super.attach();
+        mGridAlpha = 1f;
+        hidesGrid(mOnStartAnimDelay);
+    }
+
+    private void hidesGrid(int delay) {
+        mAnimator = ValueAnimator.ofFloat(1, 0);
+        mAnimator.setStartDelay(delay);
+        mAnimator.setDuration(mAnimDelay);
+        mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                mGridAlpha = ((Float) animation.getAnimatedValue());
+                invalidate();
+            }
+        });
+        mAnimator.start();
+    }
+
     public void setFilterStraightenRepresentation(FilterStraightenRepresentation rep) {
         mLocalRep = (rep == null) ? new FilterStraightenRepresentation() : rep;
         mInitialAngle = mBaseAngle = mAngle = mLocalRep.getStraighten();
@@ -110,6 +139,7 @@
                     mCurrentY = y;
                     computeValue();
                     mFirstDrawSinceUp = true;
+                    hidesGrid(0);
                 }
                 break;
             case (MotionEvent.ACTION_MOVE):
@@ -175,12 +205,25 @@
 
     private void updateCurrentCrop(Matrix m, GeometryHolder h, RectF tmp, int imageWidth,
             int imageHeight, int viewWidth, int viewHeight) {
+        tmp.set(0, 0, imageHeight, imageWidth);
+        m.mapRect(tmp);
+        float top = tmp.top;
+        float bottom = tmp.bottom;
+        float left = tmp.left;
+        float right = tmp.right;
+        m.mapRect(tmp);
+        int iw,ih;
         if (GeometryMathUtils.needsDimensionSwap(h.rotation)) {
             tmp.set(0, 0, imageHeight, imageWidth);
+            iw = imageHeight;
+            ih = imageWidth;
         } else {
             tmp.set(0, 0, imageWidth, imageHeight);
+            iw = imageWidth;
+            ih = imageHeight;
         }
-        float scale = GeometryMathUtils.scale(imageWidth, imageHeight, viewWidth, viewHeight);
+        float scale = GeometryMathUtils.scale(iw, ih, viewWidth, viewHeight);
+        scale *= GeometryMathUtils.SHOW_SCALE;
         GeometryMathUtils.scaleRect(tmp, scale);
         getUntranslatedStraightenCropBounds(tmp, mAngle);
         tmp.offset(viewWidth / 2f - tmp.centerX(), viewHeight / 2f - tmp.centerY());
@@ -229,19 +272,23 @@
             mLocalRep.setStraighten(mAngle);
             mFirstDrawSinceUp = false;
         }
-
+        CropDrawingUtils.drawShade(canvas, mDrawRect);
         // Draw the grid
-        if (mState == MODES.MOVE) {
+        if (mState == MODES.MOVE || mGridAlpha > 0) {
             canvas.save();
             canvas.clipRect(mDrawRect);
-            int n = 16;
-            float step = viewWidth / n;
+
+            float step = Math.max(viewWidth, viewHeight) / NBLINES;
             float p = 0;
-            for (int i = 1; i < n; i++) {
+            for (int i = 1; i < NBLINES; i++) {
                 p = i * step;
-                mPaint.setAlpha(60);
+                int alpha = (int) (mDefaultGridAlpha * mGridAlpha);
+                if (alpha == 0 && mState == MODES.MOVE) {
+                    alpha = mDefaultGridAlpha;
+                }
+                mPaint.setAlpha(alpha);
                 canvas.drawLine(p, 0, p, viewHeight, mPaint);
-                canvas.drawLine(0, p, viewHeight, p, mPaint);
+                canvas.drawLine(0, p, viewWidth, p, mPaint);
             }
             canvas.restore();
         }
@@ -250,6 +297,8 @@
         mPaint.setStyle(Style.STROKE);
         mPaint.setStrokeWidth(3);
         mDrawPath.reset();
+
+
         mDrawPath.addRect(mDrawRect, Path.Direction.CW);
         canvas.drawPath(mDrawPath, mPaint);
     }
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java b/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java
index 389fe3f..9571287 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java
@@ -20,7 +20,6 @@
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.MotionEvent;
 
 import com.android.gallery3d.filtershow.editors.EditorVignette;
@@ -31,7 +30,7 @@
 
     private FilterVignetteRepresentation mVignetteRep;
     private EditorVignette mEditorVignette;
-
+    private OvalSpaceAdapter mScreenOval = new OvalSpaceAdapter();
     private int mActiveHandle = -1;
 
     EclipseControl mElipse;
@@ -46,6 +45,97 @@
         mElipse = new EclipseControl(context);
     }
 
+    static class OvalSpaceAdapter implements Oval {
+        private Oval mOval;
+        Matrix mToScr;
+        Matrix mToImage;
+        int mImgWidth;
+        int mImgHeight;
+        float[] mTmp = new float[2];
+        float mTmpRadiusX;
+        float mTmpRadiusY;
+
+        public void setImageOval(Oval oval) {
+            mOval = oval;
+        }
+
+        public void setTransform(Matrix toScr, Matrix toImage, int imgWidth, int imgHeight) {
+            mToScr = toScr;
+            mToImage = toImage;
+            mImgWidth = imgWidth;
+            mImgHeight = imgHeight;
+            mTmpRadiusX = getRadiusX();
+            mTmpRadiusY = getRadiusY();
+        }
+
+        @Override
+        public void setCenter(float x, float y) {
+            mTmp[0] = x;
+            mTmp[1] = y;
+            mToImage.mapPoints(mTmp);
+            mOval.setCenter(mTmp[0] / mImgWidth, mTmp[1] / mImgHeight);
+        }
+
+        @Override
+        public void setRadius(float w, float h) {
+            mTmp[0] = mTmpRadiusX = w;
+            mTmp[1] = mTmpRadiusY = h;
+            mToImage.mapVectors(mTmp);
+            mOval.setRadius(mTmp[0] / mImgWidth, mTmp[1] / mImgHeight);
+        }
+
+        @Override
+        public float getCenterX() {
+            mTmp[0] = mOval.getCenterX() * mImgWidth;
+            mTmp[1] = mOval.getCenterY() * mImgHeight;
+            mToScr.mapPoints(mTmp);
+
+            return mTmp[0];
+        }
+
+        @Override
+        public float getCenterY() {
+            mTmp[0] = mOval.getCenterX() * mImgWidth;
+            mTmp[1] = mOval.getCenterY() * mImgHeight;
+            mToScr.mapPoints(mTmp);
+            return mTmp[1];
+        }
+
+        @Override
+        public float getRadiusX() {
+            mTmp[0] = mOval.getRadiusX() * mImgWidth;
+            mTmp[1] = mOval.getRadiusY() * mImgHeight;
+            mToScr.mapVectors(mTmp);
+            return Math.abs(mTmp[0]);
+        }
+
+        @Override
+        public float getRadiusY() {
+            mTmp[0] = mOval.getRadiusX() * mImgWidth;
+            mTmp[1] = mOval.getRadiusY() * mImgHeight;
+            mToScr.mapVectors(mTmp);
+            return Math.abs(mTmp[1]);
+        }
+
+        @Override
+        public void setRadiusY(float y) {
+            mTmp[0] = mTmpRadiusX;
+            mTmp[1] = mTmpRadiusY = y;
+            mToImage.mapVectors(mTmp);
+            mOval.setRadiusX(mTmp[0] / mImgWidth);
+            mOval.setRadiusY(mTmp[1] / mImgHeight);
+        }
+
+        @Override
+        public void setRadiusX(float x) {
+            mTmp[0] = mTmpRadiusX = x;
+            mTmp[1] = mTmpRadiusY;
+            mToImage.mapVectors(mTmp);
+            mOval.setRadiusX(mTmp[0] / mImgWidth);
+            mOval.setRadiusY(mTmp[1] / mImgHeight);
+        }
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         int w = MasterImage.getImage().getOriginalBounds().width();
@@ -73,17 +163,18 @@
         float x = event.getX();
         float y = event.getY();
 
-        mElipse.setScrImageInfo(getScreenToImageMatrix(true),
+        mElipse.setScrImageInfo(new Matrix(),
                 MasterImage.getImage().getOriginalBounds());
 
         boolean didComputeEllipses = false;
         switch (mask) {
             case (MotionEvent.ACTION_DOWN):
-                mElipse.actionDown2(x, y, w, h, mVignetteRep);
+                mElipse.actionDown(x, y, mScreenOval);
                 break;
             case (MotionEvent.ACTION_UP):
             case (MotionEvent.ACTION_MOVE):
-                mElipse.actionMove2(mActiveHandle, x, y, w, h, mVignetteRep);
+
+                mElipse.actionMove(mActiveHandle, x, y, mScreenOval);
                 setRepresentation(mVignetteRep);
                 didComputeEllipses = true;
                 break;
@@ -97,6 +188,7 @@
 
     public void setRepresentation(FilterVignetteRepresentation vignetteRep) {
         mVignetteRep = vignetteRep;
+        mScreenOval.setImageOval(mVignetteRep);
         computeEllipses();
     }
 
@@ -109,32 +201,11 @@
         Matrix toImg = getScreenToImageMatrix(false);
         Matrix toScr = new Matrix();
         toImg.invert(toScr);
+        mScreenOval.setTransform(toScr, toImg, (int) w, (int) h);
 
-        float[] c = new float[] {
-                mVignetteRep.getCenterX() * w, mVignetteRep.getCenterY() * h};
-        if (Float.isNaN(c[0])) {
-            float cx = w / 2;
-            float cy = h / 2;
-            float rx = Math.min(cx, cy) * .8f;
-            float ry = rx;
-            mVignetteRep.setCenter(cx / w, cy / h);
-            mVignetteRep.setRadius(rx / w, ry / h);
+        mElipse.setCenter(mScreenOval.getCenterX(), mScreenOval.getCenterY());
+        mElipse.setRadius(mScreenOval.getRadiusX(), mScreenOval.getRadiusY());
 
-            c[0] = cx;
-            c[1] = cy;
-            toScr.mapPoints(c);
-            if (getWidth() != 0) {
-                mElipse.setCenter(c[0], c[1]);
-                mElipse.setRadius(c[0] * 0.8f, c[1] * 0.8f);
-            }
-        } else {
-
-            toScr.mapPoints(c);
-
-            mElipse.setCenter(c[0], c[1]);
-            mElipse.setRadius(toScr.mapRadius(mVignetteRep.getRadiusX() * w),
-                    toScr.mapRadius(mVignetteRep.getRadiusY() * h));
-        }
         mEditorVignette.commitLocalRepresentation();
     }
 
@@ -144,7 +215,7 @@
 
     @Override
     public void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w,  h, oldw, oldh);
+        super.onSizeChanged(w, h, oldw, oldh);
         computeEllipses();
     }
 
@@ -159,12 +230,9 @@
         Matrix toImg = getScreenToImageMatrix(false);
         Matrix toScr = new Matrix();
         toImg.invert(toScr);
-        float[] c = new float[] {
-                mVignetteRep.getCenterX() * w, mVignetteRep.getCenterY() * h };
-        toScr.mapPoints(c);
-        mElipse.setCenter(c[0], c[1]);
-        mElipse.setRadius(toScr.mapRadius(mVignetteRep.getRadiusX() * w),
-                toScr.mapRadius(mVignetteRep.getRadiusY() * h));
+        mScreenOval.setTransform(toScr, toImg, (int) w, (int) h);
+        mElipse.setCenter(mScreenOval.getCenterX(), mScreenOval.getCenterY());
+        mElipse.setRadius(mScreenOval.getRadiusX(), mScreenOval.getRadiusY());
 
         mElipse.draw(canvas);
     }
diff --git a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java
index c562e4a..3513ded 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/MasterImage.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -70,6 +71,7 @@
     private Bitmap mOriginalBitmapSmall = null;
     private Bitmap mOriginalBitmapLarge = null;
     private Bitmap mOriginalBitmapHighres = null;
+    private Bitmap mTemporaryThumbnail = null;
     private int mOrientation;
     private Rect mOriginalBounds;
     private final Vector<ImageShow> mLoadListeners = new Vector<ImageShow>();
@@ -81,7 +83,8 @@
     private Bitmap mPartialBitmap = null;
     private Bitmap mHighresBitmap = null;
     private Bitmap mPreviousImage = null;
-    private ImagePreset mPreviousPreset = null;
+    private int mShadowMargin = 15; // not scaled, fixed in the asset
+    private Rect mPartialBounds = new Rect();
 
     private ValueAnimator mAnimator = null;
     private float mMaskScale = 1;
@@ -347,6 +350,10 @@
         return mPartialBitmap;
     }
 
+    public Rect getPartialBounds() {
+        return mPartialBounds;
+    }
+
     public Bitmap getHighresImage() {
         if (mHighresBitmap == null) {
             return getFilteredImage();
@@ -358,10 +365,6 @@
         return mPreviousImage;
     }
 
-    public ImagePreset getPreviousPreset() {
-        return mPreviousPreset;
-    }
-
     public ImagePreset getCurrentPreset() {
         return getPreviewBuffer().getConsumer().getPreset();
     }
@@ -406,7 +409,6 @@
     }
 
     public void onNewLook(FilterRepresentation newRepresentation) {
-        getBitmapCache().cache(mPreviousImage);
         if (getFilteredImage() == null) {
             return;
         }
@@ -416,13 +418,13 @@
                 mCurrentAnimRotationStartValue += 90;
             }
         } else {
-            mPreviousImage = getBitmapCache().getBitmapCopy(getFilteredImage());
+            resetAnimBitmap();
+            mPreviousImage = mBitmapCache.getBitmapCopy(getFilteredImage(), BitmapCache.NEW_LOOK);
         }
-        mPreviousPreset = getPreviewBuffer().getConsumer().getPreset();
         if (newRepresentation instanceof FilterUserPresetRepresentation) {
             mCurrentLookAnimation = CIRCLE_ANIMATION;
-            mAnimator = ValueAnimator.ofFloat(0, 20);
-            mAnimator.setDuration(500);
+            mAnimator = ValueAnimator.ofFloat(0, 1);
+            mAnimator.setDuration(650);
         }
         if (newRepresentation instanceof FilterRotateRepresentation) {
             mCurrentLookAnimation = ROTATE_ANIMATION;
@@ -457,6 +459,7 @@
                 mOnGoingNewLookAnimation = false;
                 mCurrentAnimRotationStartValue = 0;
                 mAnimator = null;
+                notifyObservers();
             }
 
             @Override
@@ -480,6 +483,9 @@
     }
 
     public void resetGeometryImages(boolean force) {
+        if (mPreset == null) {
+            return;
+        }
         ImagePreset newPresetGeometryOnly = new ImagePreset(mPreset);
         newPresetGeometryOnly.setDoApplyFilters(false);
         newPresetGeometryOnly.setDoApplyGeometry(true);
@@ -514,7 +520,6 @@
 
     public void invalidateFiltersOnly() {
         mFiltersOnlyPreset = null;
-        resetGeometryImages(false);
         invalidatePreview();
     }
 
@@ -535,6 +540,10 @@
     }
 
     public void invalidatePreview() {
+        if (mPreset == null) {
+            return;
+        }
+
         mPreviewPreset.enqueuePreset(mPreset);
         mPreviewBuffer.invalidate();
         invalidatePartialPreview();
@@ -547,11 +556,66 @@
     public void setImageShowSize(int w, int h) {
         if (mImageShowSize.x != w || mImageShowSize.y != h) {
             mImageShowSize.set(w, h);
+            float maxWidth = mOriginalBounds.width() / (float) w;
+            float maxHeight = mOriginalBounds.height() / (float) h;
+            mMaxScaleFactor = Math.max(3.f, Math.max(maxWidth, maxHeight));
             needsUpdatePartialPreview();
             needsUpdateHighResPreview();
         }
     }
 
+    public Matrix originalImageToScreen() {
+        return computeImageToScreen(null, 0, true);
+    }
+
+    public Matrix computeImageToScreen(Bitmap bitmapToDraw,
+                                       float rotate,
+                                       boolean applyGeometry) {
+        if (getOriginalBounds() == null
+                || mImageShowSize.x == 0
+                || mImageShowSize.y == 0) {
+            return null;
+        }
+
+        Matrix m = null;
+        float scale = 1f;
+        float translateX = 0;
+        float translateY = 0;
+
+        if (applyGeometry) {
+            GeometryMathUtils.GeometryHolder holder = GeometryMathUtils.unpackGeometry(
+                    mPreset.getGeometryFilters());
+            m = GeometryMathUtils.getCropSelectionToScreenMatrix(null, holder,
+                    getOriginalBounds().width(), getOriginalBounds().height(),
+                    mImageShowSize.x, mImageShowSize.y);
+        } else if (bitmapToDraw != null) {
+            m = new Matrix();
+            RectF size = new RectF(0, 0,
+                    bitmapToDraw.getWidth(),
+                    bitmapToDraw.getHeight());
+            scale = mImageShowSize.x / size.width();
+            if (size.width() < size.height()) {
+                scale = mImageShowSize.y / size.height();
+            }
+            translateX = (mImageShowSize.x - (size.width() * scale)) / 2.0f;
+            translateY = (mImageShowSize.y - (size.height() * scale)) / 2.0f;
+        } else {
+            return null;
+        }
+
+        Point translation = getTranslation();
+        m.postScale(scale, scale);
+        m.postRotate(rotate, mImageShowSize.x / 2.0f, mImageShowSize.y / 2.0f);
+        m.postTranslate(translateX, translateY);
+        m.postTranslate(mShadowMargin, mShadowMargin);
+        m.postScale(getScaleFactor(), getScaleFactor(),
+                mImageShowSize.x / 2.0f,
+                mImageShowSize.y / 2.0f);
+        m.postTranslate(translation.x * getScaleFactor(),
+                        translation.y * getScaleFactor());
+        return m;
+    }
+
     private Matrix getImageToScreenMatrix(boolean reflectRotation) {
         if (getOriginalBounds() == null || mImageShowSize.x == 0 || mImageShowSize.y == 0) {
             return new Matrix();
@@ -600,14 +664,21 @@
             invalidatePartialPreview();
             return;
         }
-        Matrix m = getScreenToImageMatrix(true);
-        RectF r = new RectF(0, 0, mImageShowSize.x, mImageShowSize.y);
-        RectF dest = new RectF();
-        m.mapRect(dest, r);
-        Rect bounds = new Rect();
-        dest.roundOut(bounds);
+        Matrix originalToScreen = MasterImage.getImage().originalImageToScreen();
+        if (originalToScreen == null) {
+            return;
+        }
+        Matrix screenToOriginal = new Matrix();
+        originalToScreen.invert(screenToOriginal);
+        RectF bounds = new RectF(0, 0,
+                mImageShowSize.x + 2 * mShadowMargin,
+                mImageShowSize.y + 2 * mShadowMargin);
+        screenToOriginal.mapRect(bounds);
+        Rect rBounds = new Rect();
+        bounds.roundOut(rBounds);
+
         mActivity.getProcessingService().postFullresRenderingRequest(mPreset,
-                getScaleFactor(), bounds,
+                getScaleFactor(), rBounds,
                 new Rect(0, 0, mImageShowSize.x, mImageShowSize.y), this);
         invalidatePartialPreview();
     }
@@ -634,6 +705,7 @@
                 && request.getScaleFactor() == getScaleFactor()) {
             mBitmapCache.cache(mPartialBitmap);
             mPartialBitmap = request.getBitmap();
+            mPartialBounds.set(request.getBounds());
             notifyObservers();
             needsCheckModification = true;
         }
@@ -700,6 +772,16 @@
         needsUpdatePartialPreview();
     }
 
+    public Bitmap getTemporaryThumbnailBitmap() {
+        if (mTemporaryThumbnail == null
+                && getOriginalBitmapSmall() != null) {
+            mTemporaryThumbnail = getOriginalBitmapSmall().copy(Bitmap.Config.ARGB_8888, true);
+            Canvas canvas = new Canvas(mTemporaryThumbnail);
+            canvas.drawARGB(200, 80, 80, 80);
+        }
+        return mTemporaryThumbnail;
+    }
+
     public Bitmap getThumbnailBitmap() {
         return getOriginalBitmapSmall();
     }
diff --git a/src/com/android/gallery3d/filtershow/info/HistogramView.java b/src/com/android/gallery3d/filtershow/info/HistogramView.java
index ad56fe4..99cf235 100644
--- a/src/com/android/gallery3d/filtershow/info/HistogramView.java
+++ b/src/com/android/gallery3d/filtershow/info/HistogramView.java
@@ -27,15 +27,15 @@
 import android.os.AsyncTask;
 import android.util.AttributeSet;
 import android.view.View;
-import com.android.gallery3d.filtershow.imageshow.Spline;
 
 public class HistogramView extends View {
 
     private Bitmap mBitmap;
-    int[] redHistogram = new int[256];
-    int[] greenHistogram = new int[256];
-    int[] blueHistogram = new int[256];
-    Path gHistoPath = new Path();
+    private Paint mPaint = new Paint();
+    private int[] redHistogram = new int[256];
+    private int[] greenHistogram = new int[256];
+    private int[] blueHistogram = new int[256];
+    private Path mHistoPath = new Path();
 
     class ComputeHistogramTask extends AsyncTask<Bitmap, Void, int[]> {
         @Override
@@ -85,28 +85,29 @@
                 max = histogram[i];
             }
         }
-        float w = getWidth() - Spline.curveHandleSize();
-        float h = getHeight() - Spline.curveHandleSize() / 2.0f;
-        float dx = Spline.curveHandleSize() / 2.0f;
+        float w = getWidth(); // - Spline.curveHandleSize();
+        float h = getHeight(); // - Spline.curveHandleSize() / 2.0f;
+        float dx = 0; // Spline.curveHandleSize() / 2.0f;
         float wl = w / histogram.length;
         float wh = h / max;
-        Paint paint = new Paint();
-        paint.setARGB(100, 255, 255, 255);
-        paint.setStrokeWidth((int) Math.ceil(wl));
+
+        mPaint.reset();
+        mPaint.setAntiAlias(true);
+        mPaint.setARGB(100, 255, 255, 255);
+        mPaint.setStrokeWidth((int) Math.ceil(wl));
 
         // Draw grid
-        paint.setStyle(Paint.Style.STROKE);
-        canvas.drawRect(dx, 0, dx + w, h, paint);
-        canvas.drawLine(dx + w / 3, 0, dx + w / 3, h, paint);
-        canvas.drawLine(dx + 2 * w / 3, 0, dx + 2 * w / 3, h, paint);
-        paint.setStyle(Paint.Style.FILL_AND_STROKE);
+        mPaint.setStyle(Paint.Style.STROKE);
+        canvas.drawRect(dx, 0, dx + w, h, mPaint);
+        canvas.drawLine(dx + w / 3, 0, dx + w / 3, h, mPaint);
+        canvas.drawLine(dx + 2 * w / 3, 0, dx + 2 * w / 3, h, mPaint);
 
-        Paint paint2 = new Paint();
-        paint2.setColor(color);
-        paint2.setStrokeWidth(6);
-        paint2.setXfermode(new PorterDuffXfermode(mode));
-        gHistoPath.reset();
-        gHistoPath.moveTo(dx, h);
+        mPaint.setStyle(Paint.Style.FILL);
+        mPaint.setColor(color);
+        mPaint.setStrokeWidth(6);
+        mPaint.setXfermode(new PorterDuffXfermode(mode));
+        mHistoPath.reset();
+        mHistoPath.moveTo(dx, h);
         boolean firstPointEncountered = false;
         float prev = 0;
         float last = 0;
@@ -116,22 +117,22 @@
             if (l != 0) {
                 float v = h - (l + prev) / 2.0f;
                 if (!firstPointEncountered) {
-                    gHistoPath.lineTo(x, h);
+                    mHistoPath.lineTo(x, h);
                     firstPointEncountered = true;
                 }
-                gHistoPath.lineTo(x, v);
+                mHistoPath.lineTo(x, v);
                 prev = l;
                 last = x;
             }
         }
-        gHistoPath.lineTo(last, h);
-        gHistoPath.lineTo(w, h);
-        gHistoPath.close();
-        canvas.drawPath(gHistoPath, paint2);
-        paint2.setStrokeWidth(2);
-        paint2.setStyle(Paint.Style.STROKE);
-        paint2.setARGB(255, 200, 200, 200);
-        canvas.drawPath(gHistoPath, paint2);
+        mHistoPath.lineTo(last, h);
+        mHistoPath.lineTo(w, h);
+        mHistoPath.close();
+        canvas.drawPath(mHistoPath, mPaint);
+        mPaint.setStrokeWidth(2);
+        mPaint.setStyle(Paint.Style.STROKE);
+        mPaint.setARGB(255, 200, 200, 200);
+        canvas.drawPath(mHistoPath, mPaint);
     }
 
     public void onDraw(Canvas canvas) {
diff --git a/src/com/android/gallery3d/filtershow/info/InfoPanel.java b/src/com/android/gallery3d/filtershow/info/InfoPanel.java
index 39102f1..3a66578 100644
--- a/src/com/android/gallery3d/filtershow/info/InfoPanel.java
+++ b/src/com/android/gallery3d/filtershow/info/InfoPanel.java
@@ -20,11 +20,12 @@
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.v4.app.Fragment;
+import android.support.v4.app.DialogFragment;
 import android.text.Html;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.Window;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -32,13 +33,12 @@
 import com.android.gallery3d.R;
 import com.android.gallery3d.exif.ExifInterface;
 import com.android.gallery3d.exif.ExifTag;
-import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.cache.ImageLoader;
 import com.android.gallery3d.filtershow.imageshow.MasterImage;
 
 import java.util.List;
 
-public class InfoPanel extends Fragment {
+public class InfoPanel extends DialogFragment {
     public static final String FRAGMENT_TAG = "InfoPanel";
     private static final String LOGTAG = FRAGMENT_TAG;
     private LinearLayout mMainView;
@@ -46,7 +46,6 @@
     private TextView mImageName;
     private TextView mImageSize;
     private TextView mExifData;
-    private ImageButton mHideButton;
 
     private String createStringFromIfFound(ExifTag exifTag, int tag, int str) {
         String exifString = "";
@@ -63,6 +62,9 @@
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
+        if (getDialog() != null) {
+            getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+        }
 
         mMainView = (LinearLayout) inflater.inflate(
                 R.layout.filtershow_info_panel, null, false);
@@ -74,19 +76,11 @@
         mImageName = (TextView) mMainView.findViewById(R.id.imageName);
         mImageSize = (TextView) mMainView.findViewById(R.id.imageSize);
         mExifData = (TextView) mMainView.findViewById(R.id.exifData);
-        mHideButton =(ImageButton) mMainView.findViewById(R.id.cancelInfo);
+        TextView exifLabel = (TextView) mMainView.findViewById(R.id.exifLabel);
 
         HistogramView histogramView = (HistogramView) mMainView.findViewById(R.id.histogramView);
-
         histogramView.setBitmap(bitmap);
 
-        mHideButton.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                FilterShowActivity activity = (FilterShowActivity)getActivity();
-                activity.toggleInformationPanel();
-            }
-        });
         Uri uri = MasterImage.getImage().getUri();
         String path = ImageLoader.getLocalPathFromUri(getActivity(), uri);
         Uri localUri = null;
@@ -102,6 +96,7 @@
 
         List<ExifTag> exif = MasterImage.getImage().getEXIF();
         String exifString = "";
+        boolean hasExifData = false;
         if (exif != null) {
             for (ExifTag tag : exif) {
                 exifString += createStringFromIfFound(tag,
@@ -131,9 +126,15 @@
                 exifString += createStringFromIfFound(tag,
                         ExifInterface.TAG_COPYRIGHT,
                         R.string.filtershow_exif_copyright);
+                hasExifData = true;
             }
         }
-        mExifData.setText(Html.fromHtml(exifString));
+        if (hasExifData) {
+            exifLabel.setVisibility(View.VISIBLE);
+            mExifData.setText(Html.fromHtml(exifString));
+        } else {
+            exifLabel.setVisibility(View.GONE);
+        }
         return mMainView;
     }
 }
diff --git a/src/com/android/gallery3d/filtershow/pipeline/Buffer.java b/src/com/android/gallery3d/filtershow/pipeline/Buffer.java
index 7b51a75..c378eb9 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/Buffer.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/Buffer.java
@@ -17,6 +17,7 @@
 package com.android.gallery3d.filtershow.pipeline;
 
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.support.v8.renderscript.Allocation;
 import android.support.v8.renderscript.RenderScript;
 import android.util.Log;
@@ -34,8 +35,7 @@
         RenderScript rs = CachingPipeline.getRenderScriptContext();
         if (bitmap != null) {
             BitmapCache cache = MasterImage.getImage().getBitmapCache();
-            cache.cache(mBitmap);
-            mBitmap = cache.getBitmapCopy(bitmap);
+            mBitmap = cache.getBitmapCopy(bitmap, BitmapCache.PREVIEW_CACHE);
         }
         if (mUseAllocation) {
             // TODO: recreate the allocation when the RS context changes
@@ -45,7 +45,23 @@
         }
     }
 
-    public Bitmap getBitmap() {
+    public boolean isSameSize(Bitmap bitmap) {
+        if (mBitmap == null || bitmap == null) {
+            return false;
+        }
+        if (mBitmap.getWidth() == bitmap.getWidth()
+                && mBitmap.getHeight() == bitmap.getHeight()) {
+            return true;
+        }
+        return false;
+    }
+
+    public synchronized void useBitmap(Bitmap bitmap) {
+        Canvas canvas = new Canvas(mBitmap);
+        canvas.drawBitmap(bitmap, 0, 0, null);
+    }
+
+    public synchronized Bitmap getBitmap() {
         return mBitmap;
     }
 
@@ -73,8 +89,9 @@
 
     public void remove() {
         BitmapCache cache = MasterImage.getImage().getBitmapCache();
-        cache.cache(mBitmap);
-        mBitmap = null;
+        if (cache.cache(mBitmap)) {
+            mBitmap = null;
+        }
     }
 }
 
diff --git a/src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java b/src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java
index 2a83219..c2912ed 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/CacheProcessing.java
@@ -18,6 +18,8 @@
 
 import android.graphics.Bitmap;
 import android.util.Log;
+
+import com.android.gallery3d.filtershow.cache.BitmapCache;
 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
 import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils;
 
@@ -27,6 +29,7 @@
 public class CacheProcessing {
     private static final String LOGTAG = "CacheProcessing";
     private static final boolean DEBUG = false;
+    private static final boolean NO_CACHING = false;
     private Vector<CacheStep> mSteps = new Vector<CacheStep>();
 
     static class CacheStep {
@@ -90,6 +93,7 @@
 
         public Bitmap apply(FilterEnvironment environment, Bitmap cacheBitmap) {
             boolean onlyGeometry = true;
+            Bitmap source = cacheBitmap;
             for (FilterRepresentation representation : representations) {
                 if (representation.getFilterType() != FilterRepresentation.TYPE_GEOMETRY) {
                     onlyGeometry = false;
@@ -101,12 +105,25 @@
                 for (FilterRepresentation representation : representations) {
                     geometry.add(representation);
                 }
+                if (DEBUG) {
+                    Log.v(LOGTAG, "Apply geometry to bitmap " + cacheBitmap);
+                }
                 cacheBitmap = GeometryMathUtils.applyGeometryRepresentations(geometry, cacheBitmap);
             } else {
                 for (FilterRepresentation representation : representations) {
+                    if (DEBUG) {
+                        Log.v(LOGTAG, "Apply " + representation.getSerializationName()
+                                + " to bitmap " + cacheBitmap);
+                    }
                     cacheBitmap = environment.applyRepresentation(representation, cacheBitmap);
                 }
             }
+            if (cacheBitmap != source) {
+                environment.cache(source);
+            }
+            if (DEBUG) {
+                Log.v(LOGTAG, "Apply returns bitmap " + cacheBitmap);
+            }
             return cacheBitmap;
         }
     }
@@ -116,9 +133,11 @@
                           FilterEnvironment environment) {
 
         if (filters.size() == 0) {
-            return environment.getBitmapCopy(originalBitmap);
+            return environment.getBitmapCopy(originalBitmap, BitmapCache.PREVIEW_CACHE_NO_FILTERS);
         }
 
+        environment.getBimapCache().setCacheProcessing(this);
+
         if (DEBUG) {
             displayFilters(filters);
         }
@@ -145,9 +164,9 @@
             if (similar) {
                 similarUpToIndex = i;
             } else {
-                environment.cache(cacheStep.cache);
                 mSteps.remove(i);
                 mSteps.insertElementAt(newStep, i);
+                environment.cache(cacheStep.cache);
             }
         }
         if (DEBUG) {
@@ -170,10 +189,26 @@
                     + mSteps.size() + " cacheBitmap: " + cacheBitmap);
         }
 
+        if (NO_CACHING) {
+            cacheBitmap = environment.getBitmapCopy(originalBitmap,
+                    BitmapCache.PREVIEW_CACHE_NO_ROOT);
+            for (int i = 0; i < mSteps.size(); i++) {
+                CacheStep step = mSteps.elementAt(i);
+                Bitmap prev = cacheBitmap;
+                cacheBitmap = step.apply(environment, cacheBitmap);
+                if (prev != cacheBitmap) {
+                    environment.cache(prev);
+                }
+            }
+            return cacheBitmap;
+        }
+
         Bitmap originalCopy = null;
+        int lastPositionCached = -1;
         for (int i = findBaseImageIndex; i < mSteps.size(); i++) {
             if (i == -1 || cacheBitmap == null) {
-                cacheBitmap = environment.getBitmapCopy(originalBitmap);
+                cacheBitmap = environment.getBitmapCopy(originalBitmap,
+                        BitmapCache.PREVIEW_CACHE_NO_ROOT);
                 originalCopy = cacheBitmap;
                 if (DEBUG) {
                     Log.v(LOGTAG, "i: " + i + " cacheBitmap: " + cacheBitmap + " w: "
@@ -191,9 +226,10 @@
                     Log.v(LOGTAG, "i: " + i + " get new copy for cacheBitmap "
                             + cacheBitmap + " apply...");
                 }
-                cacheBitmap = environment.getBitmapCopy(cacheBitmap);
+                cacheBitmap = environment.getBitmapCopy(cacheBitmap, BitmapCache.PREVIEW_CACHE);
                 cacheBitmap = step.apply(environment, cacheBitmap);
                 step.cache = cacheBitmap;
+                lastPositionCached = i;
             }
         }
         environment.cache(originalCopy);
@@ -206,17 +242,34 @@
         // Let's see if we can cleanup the cache for unused bitmaps
         for (int i = 0; i < similarUpToIndex; i++) {
             CacheStep currentStep = mSteps.elementAt(i);
-            environment.cache(currentStep.cache);
+            Bitmap bitmap = currentStep.cache;
             currentStep.cache = null;
+            environment.cache(bitmap);
         }
 
         if (DEBUG) {
             Log.v(LOGTAG, "cleanup done...");
             displayNbBitmapsInCache();
         }
+        if (lastPositionCached != -1) {
+            // The last element will never be reused, remove it from the cache.
+            mSteps.elementAt(lastPositionCached).cache = null;
+        }
+        if (contains(cacheBitmap)) {
+            return environment.getBitmapCopy(cacheBitmap, BitmapCache.PREVIEW_CACHE_NO_APPLY);
+        }
         return cacheBitmap;
     }
 
+    public boolean contains(Bitmap bitmap) {
+        for (int i = 0; i < mSteps.size(); i++) {
+            if (mSteps.elementAt(i).cache == bitmap) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void displayFilters(Vector<FilterRepresentation> filters) {
         Log.v(LOGTAG, "------>>> Filters received");
         for (int i = 0; i < filters.size(); i++) {
diff --git a/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java b/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java
index 823da64..8ae9a7c 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/CachingPipeline.java
@@ -19,10 +19,16 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.support.v8.renderscript.Allocation;
 import android.support.v8.renderscript.RenderScript;
 import android.util.Log;
 
+import com.android.gallery3d.filtershow.cache.BitmapCache;
 import com.android.gallery3d.filtershow.cache.ImageLoader;
 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
 import com.android.gallery3d.filtershow.filters.FiltersManager;
@@ -177,6 +183,9 @@
     }
 
     private synchronized boolean updateOriginalAllocation(ImagePreset preset) {
+        if (preset == null) {
+            return false;
+        }
         Bitmap originalBitmap = mOriginalBitmap;
 
         if (originalBitmap == null) {
@@ -214,7 +223,7 @@
             if (bitmap == null) {
                 return;
             }
-            bitmap = mEnvironment.getBitmapCopy(bitmap);
+            bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.HIGHRES);
             bitmap = preset.applyGeometry(bitmap, mEnvironment);
 
             mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
@@ -239,7 +248,7 @@
             if (bitmap == null) {
                 return;
             }
-            bitmap = mEnvironment.getBitmapCopy(bitmap);
+            bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.GEOMETRY);
             bitmap = preset.applyGeometry(bitmap, mEnvironment);
             if (!mEnvironment.needsStop()) {
                 request.setBitmap(bitmap);
@@ -261,7 +270,7 @@
             if (bitmap == null) {
                 return;
             }
-            bitmap = mEnvironment.getBitmapCopy(bitmap);
+            bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.FILTERS);
             bitmap = preset.apply(bitmap, mEnvironment);
             if (!mEnvironment.needsStop()) {
                 request.setBitmap(bitmap);
@@ -278,7 +287,8 @@
             if (getRenderScriptContext() == null) {
                 return;
             }
-            if (((request.getType() != RenderingRequest.PARTIAL_RENDERING)
+            if ((request.getType() != RenderingRequest.PARTIAL_RENDERING
+                  && request.getType() != RenderingRequest.ICON_RENDERING
                     && request.getBitmap() == null)
                     || request.getImagePreset() == null) {
                 return;
@@ -296,7 +306,7 @@
             if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
                 MasterImage master = MasterImage.getImage();
                 bitmap = ImageLoader.getScaleOneImageForPreset(master.getActivity(),
-                        mEnvironment,
+                        mEnvironment.getBimapCache(),
                         master.getUri(), request.getBounds(),
                         request.getDestination());
                 if (bitmap == null) {
@@ -311,7 +321,7 @@
                 updateOriginalAllocation(preset);
             }
 
-            if (DEBUG) {
+            if (DEBUG && bitmap != null) {
                 Log.v(LOGTAG, "after update, req bitmap (" + bitmap.getWidth() + "x" + bitmap.getHeight()
                         + " ? resizeOriginal (" + mResizedOriginalBitmap.getWidth() + "x"
                         + mResizedOriginalBitmap.getHeight());
@@ -336,6 +346,29 @@
                     mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
                 }
 
+                if (request.getType() == RenderingRequest.ICON_RENDERING) {
+                    Rect iconBounds = request.getIconBounds();
+                    Bitmap source = MasterImage.getImage().getThumbnailBitmap();
+                    if (iconBounds.width() > source.getWidth() * 2) {
+                        source = MasterImage.getImage().getLargeThumbnailBitmap();
+                    }
+                    if (iconBounds != null) {
+                        bitmap = mEnvironment.getBitmap(iconBounds.width(),
+                                iconBounds.height(), BitmapCache.ICON);
+                        Canvas canvas = new Canvas(bitmap);
+                        Matrix m = new Matrix();
+                        float minSize = Math.min(source.getWidth(), source.getHeight());
+                        float maxSize = Math.max(iconBounds.width(), iconBounds.height());
+                        float scale = maxSize / minSize;
+                        m.setScale(scale, scale);
+                        float dx = (iconBounds.width() - (source.getWidth() * scale))/2.0f;
+                        float dy = (iconBounds.height() - (source.getHeight() * scale))/2.0f;
+                        m.postTranslate(dx, dy);
+                        canvas.drawBitmap(source, m, new Paint(Paint.FILTER_BITMAP_FLAG));
+                    } else {
+                        bitmap = mEnvironment.getBitmapCopy(source, BitmapCache.ICON);
+                    }
+                }
                 Bitmap bmp = preset.apply(bitmap, mEnvironment);
                 if (!mEnvironment.needsStop()) {
                     request.setBitmap(bmp);
@@ -391,57 +424,6 @@
         mEnvironment.cache(result);
     }
 
-    public synchronized void computeOld(SharedBuffer buffer, ImagePreset preset, int type) {
-        synchronized (CachingPipeline.class) {
-            if (getRenderScriptContext() == null) {
-                return;
-            }
-            if (DEBUG) {
-                Log.v(LOGTAG, "compute preset " + preset);
-                preset.showFilters();
-            }
-
-            String thread = Thread.currentThread().getName();
-            long time = System.currentTimeMillis();
-            setupEnvironment(preset, false);
-            mFiltersManager.freeFilterResources(preset);
-
-            Bitmap resizedOriginalBitmap = mResizedOriginalBitmap;
-            if (updateOriginalAllocation(preset) || buffer.getProducer() == null) {
-                resizedOriginalBitmap = mResizedOriginalBitmap;
-                buffer.setProducer(resizedOriginalBitmap);
-                mEnvironment.cache(buffer.getProducer());
-            }
-
-            Bitmap bitmap = buffer.getProducer().getBitmap();
-            long time2 = System.currentTimeMillis();
-
-            if (bitmap == null || (bitmap.getWidth() != resizedOriginalBitmap.getWidth())
-                    || (bitmap.getHeight() != resizedOriginalBitmap.getHeight())) {
-                mEnvironment.cache(buffer.getProducer());
-                buffer.setProducer(resizedOriginalBitmap);
-                bitmap = buffer.getProducer().getBitmap();
-            }
-            mOriginalAllocation.copyTo(bitmap);
-
-            Bitmap tmpbitmap = preset.apply(bitmap, mEnvironment);
-            if (tmpbitmap != bitmap) {
-                mEnvironment.cache(buffer.getProducer());
-                buffer.setProducer(tmpbitmap);
-            }
-
-            mFiltersManager.freeFilterResources(preset);
-
-            time = System.currentTimeMillis() - time;
-            time2 = System.currentTimeMillis() - time2;
-            if (DEBUG) {
-                Log.v(LOGTAG, "Applying type " + type + " filters to bitmap "
-                        + bitmap + " (" + bitmap.getWidth() + " x " + bitmap.getHeight()
-                        + ") took " + time + " ms, " + time2 + " ms for the filter, on thread " + thread);
-            }
-        }
-    }
-
     public boolean needsRepaint() {
         SharedBuffer buffer = MasterImage.getImage().getPreviewBuffer();
         return buffer.checkRepaintNeeded();
diff --git a/src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java b/src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java
index 72c02f3..ebf83b7 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/FilterEnvironment.java
@@ -67,12 +67,12 @@
         mBitmapCache.cache(bitmap);
     }
 
-    public Bitmap getBitmap(int w, int h) {
-        return mBitmapCache.getBitmap(w, h);
+    public Bitmap getBitmap(int w, int h, int type) {
+        return mBitmapCache.getBitmap(w, h, type);
     }
 
-    public Bitmap getBitmapCopy(Bitmap source) {
-        return mBitmapCache.getBitmapCopy(source);
+    public Bitmap getBitmapCopy(Bitmap source, int type) {
+        return mBitmapCache.getBitmapCopy(source, type);
     }
 
     public void setImagePreset(ImagePreset imagePreset) {
@@ -168,4 +168,7 @@
         generalParameters.put(id, value);
     }
 
+    public BitmapCache getBimapCache() {
+        return mBitmapCache;
+    }
 }
diff --git a/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java b/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java
index 3f71547..4322ed7 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/ImagePreset.java
@@ -41,7 +41,6 @@
 import com.android.gallery3d.filtershow.imageshow.MasterImage;
 import com.android.gallery3d.filtershow.state.State;
 import com.android.gallery3d.filtershow.state.StateAdapter;
-import com.android.gallery3d.util.UsageStatistics;
 
 import java.io.IOException;
 import java.io.StringReader;
@@ -53,6 +52,7 @@
 public class ImagePreset {
 
     private static final String LOGTAG = "ImagePreset";
+    public static final String JASON_SAVED = "Saved";
 
     private Vector<FilterRepresentation> mFilters = new Vector<FilterRepresentation>();
 
@@ -181,6 +181,16 @@
         return false;
     }
 
+    public boolean contains(byte type) {
+        for (FilterRepresentation representation : mFilters) {
+            if (representation.getFilterType() == type
+                    && !representation.isNil()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public boolean isPanoramaSafe() {
         for (FilterRepresentation representation : mFilters) {
             if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY
@@ -258,8 +268,20 @@
         for (int i = 0; i < preset.mFilters.size(); i++) {
             FilterRepresentation a = preset.mFilters.elementAt(i);
             FilterRepresentation b = mFilters.elementAt(i);
-
-            if (!a.equals(b)) {
+            boolean isGeometry = false;
+            if (a instanceof FilterRotateRepresentation
+                    || a instanceof FilterMirrorRepresentation
+                    || a instanceof FilterCropRepresentation
+                    || a instanceof FilterStraightenRepresentation) {
+                isGeometry = true;
+            }
+            boolean evaluate = true;
+            if (!isGeometry && mDoApplyGeometry && !mDoApplyFilters) {
+                evaluate = false;
+            } else if (isGeometry && !mDoApplyGeometry && mDoApplyFilters) {
+                evaluate = false;
+            }
+            if (evaluate && !a.equals(b)) {
                 return false;
             }
         }
@@ -325,12 +347,18 @@
     public void addFilter(FilterRepresentation representation) {
         if (representation instanceof FilterUserPresetRepresentation) {
             ImagePreset preset = ((FilterUserPresetRepresentation) representation).getImagePreset();
-            // user preset replace everything but geometry
-            mFilters.clear();
-            for (int i = 0; i < preset.nbFilters(); i++) {
-                addFilter(preset.getFilterRepresentation(i));
+            if (preset.nbFilters() == 1
+                && preset.contains(FilterRepresentation.TYPE_FX)) {
+                FilterRepresentation rep = preset.getFilterRepresentationForType(
+                        FilterRepresentation.TYPE_FX);
+                addFilter(rep);
+            } else {
+                // user preset replaces everything
+                mFilters.clear();
+                for (int i = 0; i < preset.nbFilters(); i++) {
+                    addFilter(preset.getFilterRepresentation(i));
+                }
             }
-            mFilters.add(representation);
         } else if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
             // Add geometry filter, removing duplicates and do-nothing operations.
             for (int i = 0; i < mFilters.size(); i++) {
@@ -354,56 +382,38 @@
                 mFilters.add(representation);
             }
         } else if (representation.getFilterType() == FilterRepresentation.TYPE_FX) {
-            boolean found = false;
+            boolean replaced = false;
             for (int i = 0; i < mFilters.size(); i++) {
                 FilterRepresentation current = mFilters.elementAt(i);
-                int type = current.getFilterType();
-                if (found) {
-                    if (type != FilterRepresentation.TYPE_VIGNETTE) {
-                        mFilters.remove(i);
-                        continue;
+                if (current.getFilterType() == FilterRepresentation.TYPE_FX) {
+                    mFilters.remove(i);
+                    replaced = true;
+                    if (!isNoneFxFilter(representation)) {
+                        mFilters.add(i, representation);
                     }
-                }
-                if (type == FilterRepresentation.TYPE_FX) {
-                    if (current instanceof FilterUserPresetRepresentation) {
-                        ImagePreset preset = ((FilterUserPresetRepresentation) current)
-                                .getImagePreset();
-                        // If we had an existing user preset, let's remove all the presets that
-                        // were added by it
-                        for (int j = 0; j < preset.nbFilters(); j++) {
-                            FilterRepresentation rep = preset.getFilterRepresentation(j);
-                            int pos = getPositionForRepresentation(rep);
-                            if (pos != -1) {
-                                mFilters.remove(pos);
-                            }
-                        }
-                        int pos = getPositionForRepresentation(current);
-                        if (pos != -1) {
-                            mFilters.remove(pos);
-                        } else {
-                            pos = 0;
-                        }
-                        if (!isNoneFxFilter(representation)) {
-                            mFilters.add(pos, representation);
-                        }
-
-                    } else {
-                        mFilters.remove(i);
-                        if (!isNoneFxFilter(representation)) {
-                            mFilters.add(i, representation);
-                        }
-                    }
-                    found = true;
+                    break;
                 }
             }
-            if (!found) {
-                if (!isNoneFxFilter(representation)) {
-                    mFilters.add(representation);
-                }
+            if (!replaced) {
+                mFilters.add(0, representation);
             }
         } else {
             mFilters.add(representation);
         }
+        // Enforces Filter type ordering for borders
+        FilterRepresentation border = null;
+        for (int i = 0; i < mFilters.size();) {
+            FilterRepresentation rep = mFilters.elementAt(i);
+            if (rep.getFilterType() == FilterRepresentation.TYPE_BORDER) {
+                border = rep;
+                mFilters.remove(i);
+                continue;
+            }
+            i++;
+        }
+        if (border != null) {
+            mFilters.add(border);
+        }
     }
 
     private boolean isNoneBorderFilter(FilterRepresentation representation) {
@@ -461,7 +471,12 @@
         // Apply any transform -- 90 rotate, flip, straighten, crop
         // Returns a new bitmap.
         if (mDoApplyGeometry) {
-            bitmap = GeometryMathUtils.applyGeometryRepresentations(getGeometryFilters(), bitmap);
+            Bitmap bmp = GeometryMathUtils.applyGeometryRepresentations(
+                    getGeometryFilters(), bitmap);
+            if (bmp != bitmap) {
+                environment.cache(bitmap);
+            }
+            return bmp;
         }
         return bitmap;
     }
@@ -473,8 +488,6 @@
         if (border != null && mDoApplyGeometry) {
             bitmap = environment.applyRepresentation(border, bitmap);
             if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
-                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
-                        "SaveBorder", border.getSerializationName(), 1);
             }
         }
         return bitmap;
@@ -492,10 +505,6 @@
             if (to == -1) {
                 to = mFilters.size();
             }
-            if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
-                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
-                        "SaveFilters", "Total", to - from + 1);
-            }
             for (int i = from; i < to; i++) {
                 FilterRepresentation representation = mFilters.elementAt(i);
                 if (representation.getFilterType() == FilterRepresentation.TYPE_GEOMETRY) {
@@ -513,10 +522,6 @@
                 if (tmp != bitmap) {
                     environment.cache(tmp);
                 }
-                if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) {
-                    UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
-                            "SaveFilter", representation.getSerializationName(), 1);
-                }
                 if (environment.needsStop()) {
                     return bitmap;
                 }
@@ -676,6 +681,7 @@
             }
             reader.close();
         } catch (Exception e) {
+            Log.e(LOGTAG, "\""+filterString+"\"");
             Log.e(LOGTAG, "parsing the filter parameters:", e);
             return false;
         }
diff --git a/src/com/android/gallery3d/filtershow/pipeline/ImageSavingTask.java b/src/com/android/gallery3d/filtershow/pipeline/ImageSavingTask.java
index 5c416bc..801aee4 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/ImageSavingTask.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/ImageSavingTask.java
@@ -38,6 +38,7 @@
         int quality;
         float sizeFactor;
         Bitmap previewImage;
+        boolean exit;
     }
 
     static class UpdateBitmap implements Update {
@@ -49,8 +50,14 @@
         int current;
     }
 
+    static class UpdatePreviewSaved implements Update {
+        Uri uri;
+        boolean exit;
+    }
+
     static class URIResult implements Result {
         Uri uri;
+        boolean exit;
     }
 
     public ImageSavingTask(ProcessingService service) {
@@ -60,7 +67,7 @@
     public void saveImage(Uri sourceUri, Uri selectedUri,
                           File destinationFile, ImagePreset preset,
                           Bitmap previewImage, boolean flatten,
-                          int quality, float sizeFactor) {
+                          int quality, float sizeFactor, boolean exit) {
         SaveRequest request = new SaveRequest();
         request.sourceUri = sourceUri;
         request.selectedUri = selectedUri;
@@ -70,6 +77,7 @@
         request.quality = quality;
         request.sizeFactor = sizeFactor;
         request.previewImage = previewImage;
+        request.exit = exit;
         postRequest(request);
     }
 
@@ -81,15 +89,24 @@
         Bitmap previewImage = request.previewImage;
         ImagePreset preset = request.preset;
         boolean flatten = request.flatten;
+        final boolean exit = request.exit;
         // We create a small bitmap showing the result that we can
         // give to the notification
         UpdateBitmap updateBitmap = new UpdateBitmap();
-        updateBitmap.bitmap = createNotificationBitmap(sourceUri, preset);
+        updateBitmap.bitmap = createNotificationBitmap(previewImage, sourceUri, preset);
         postUpdate(updateBitmap);
         SaveImage saveImage = new SaveImage(mProcessingService, sourceUri,
                 selectedUri, destinationFile, previewImage,
                 new SaveImage.Callback() {
                     @Override
+                    public void onPreviewSaved(Uri uri){
+                        UpdatePreviewSaved previewSaved = new UpdatePreviewSaved();
+                        previewSaved.uri = uri;
+                        previewSaved.exit = exit;
+                        postUpdate(previewSaved);
+                    }
+
+                    @Override
                     public void onProgress(int max, int current) {
                         UpdateProgress updateProgress = new UpdateProgress();
                         updateProgress.max = max;
@@ -97,21 +114,27 @@
                         postUpdate(updateProgress);
                     }
                 });
-        Uri uri = saveImage.processAndSaveImage(preset, !flatten,
-                request.quality, request.sizeFactor);
+        Uri uri = saveImage.processAndSaveImage(preset, flatten,
+                request.quality, request.sizeFactor, request.exit);
         URIResult result = new URIResult();
         result.uri = uri;
+        result.exit = request.exit;
         return result;
     }
 
     @Override
     public void onResult(Result message) {
         URIResult result = (URIResult) message;
-        mProcessingService.completeSaveImage(result.uri);
+        mProcessingService.completeSaveImage(result.uri, result.exit);
     }
 
     @Override
     public void onUpdate(Update message) {
+        if (message instanceof UpdatePreviewSaved){
+            Uri uri = ((UpdatePreviewSaved) message).uri;
+            boolean exit = ((UpdatePreviewSaved) message).exit;
+            mProcessingService.completePreviewSaveImage(uri, exit);
+        }
         if (message instanceof UpdateBitmap) {
             Bitmap bitmap = ((UpdateBitmap) message).bitmap;
             mProcessingService.updateNotificationWithBitmap(bitmap);
@@ -122,9 +145,13 @@
         }
     }
 
-    private Bitmap createNotificationBitmap(Uri sourceUri, ImagePreset preset) {
+    private Bitmap createNotificationBitmap(Bitmap preview, Uri sourceUri, ImagePreset preset) {
         int notificationBitmapSize = Resources.getSystem().getDimensionPixelSize(
                 android.R.dimen.notification_large_icon_width);
+        if (preview != null) {
+            return Bitmap.createScaledBitmap(preview,
+                    notificationBitmapSize, notificationBitmapSize, true);
+        }
         Bitmap bitmap = ImageLoader.loadConstrainedBitmap(sourceUri, getContext(),
                 notificationBitmapSize, null, true);
         CachingPipeline pipeline = new CachingPipeline(FiltersManager.getManager(), "Thumb");
diff --git a/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java b/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java
index 7d3767a..b5b636e 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/ProcessingService.java
@@ -52,6 +52,7 @@
     private static final String SAVING = "saving";
     private static final String FLATTEN = "flatten";
     private static final String SIZE_FACTOR = "sizeFactor";
+    private static final String EXIT = "exit";
 
     private ProcessingTaskController mProcessingTaskController;
     private ImageSavingTask mImageSavingTask;
@@ -140,7 +141,8 @@
     }
 
     public static Intent getSaveIntent(Context context, ImagePreset preset, File destination,
-            Uri selectedImageUri, Uri sourceImageUri, boolean doFlatten, int quality, float sizeFactor) {
+            Uri selectedImageUri, Uri sourceImageUri, boolean doFlatten, int quality,
+            float sizeFactor, boolean needsExit) {
         Intent processIntent = new Intent(context, ProcessingService.class);
         processIntent.putExtra(ProcessingService.SOURCE_URI,
                 sourceImageUri.toString());
@@ -152,8 +154,9 @@
             processIntent.putExtra(ProcessingService.DESTINATION_FILE, destination.toString());
         }
         processIntent.putExtra(ProcessingService.PRESET,
-                preset.getJsonString(context.getString(R.string.saved)));
+                preset.getJsonString(ImagePreset.JASON_SAVED));
         processIntent.putExtra(ProcessingService.SAVING, true);
+        processIntent.putExtra(ProcessingService.EXIT, needsExit);
         if (doFlatten) {
             processIntent.putExtra(ProcessingService.FLATTEN, true);
         }
@@ -196,6 +199,7 @@
             int quality = intent.getIntExtra(QUALITY, 100);
             float sizeFactor = intent.getFloatExtra(SIZE_FACTOR, 1);
             boolean flatten = intent.getBooleanExtra(FLATTEN, false);
+            boolean exit = intent.getBooleanExtra(EXIT, false);
             Uri sourceUri = Uri.parse(source);
             Uri selectedUri = null;
             if (selected != null) {
@@ -211,7 +215,7 @@
             mSaving = true;
             handleSaveRequest(sourceUri, selectedUri, destinationFile, preset,
                     MasterImage.getImage().getHighresImage(),
-                    flatten, quality, sizeFactor);
+                    flatten, quality, sizeFactor, exit);
         }
         return START_REDELIVER_INTENT;
     }
@@ -230,10 +234,9 @@
 
     public void handleSaveRequest(Uri sourceUri, Uri selectedUri,
             File destinationFile, ImagePreset preset, Bitmap previewImage,
-            boolean flatten, int quality, float sizeFactor) {
+            boolean flatten, int quality, float sizeFactor, boolean exit) {
         mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
-
-        mNotificationId++;
+        mNotifyMgr.cancelAll();
 
         mBuilder =
                 new Notification.Builder(this)
@@ -248,7 +251,7 @@
         // Process the image
 
         mImageSavingTask.saveImage(sourceUri, selectedUri, destinationFile,
-                preset, previewImage, flatten, quality, sizeFactor);
+                preset, previewImage, flatten, quality, sizeFactor, exit);
     }
 
     public void updateNotificationWithBitmap(Bitmap bitmap) {
@@ -261,13 +264,25 @@
         mNotifyMgr.notify(mNotificationId, mBuilder.build());
     }
 
-    public void completeSaveImage(Uri result) {
+    public void completePreviewSaveImage(Uri result, boolean exit) {
+        if (exit && !mNeedsAlive && !mFiltershowActivity.isSimpleEditAction()) {
+            mFiltershowActivity.completeSaveImage(result);
+        }
+    }
+
+    public void completeSaveImage(Uri result, boolean exit) {
         if (SHOW_IMAGE) {
             // TODO: we should update the existing image in Gallery instead
             Intent viewImage = new Intent(Intent.ACTION_VIEW, result);
             viewImage.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             startActivity(viewImage);
         }
+        mNotifyMgr.cancel(mNotificationId);
+        if (!exit) {
+            stopForeground(true);
+            stopSelf();
+            return;
+        }
         stopForeground(true);
         stopSelf();
         if (mNeedsAlive) {
diff --git a/src/com/android/gallery3d/filtershow/pipeline/RenderingRequest.java b/src/com/android/gallery3d/filtershow/pipeline/RenderingRequest.java
index 463b38d..4cb9e5a 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/RenderingRequest.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/RenderingRequest.java
@@ -21,6 +21,7 @@
 import android.graphics.Rect;
 import com.android.gallery3d.app.Log;
 import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.cache.BitmapCache;
 import com.android.gallery3d.filtershow.filters.FiltersManager;
 import com.android.gallery3d.filtershow.imageshow.MasterImage;
 
@@ -34,6 +35,7 @@
     private float mScaleFactor = 1.0f;
     private Rect mBounds = null;
     private Rect mDestination = null;
+    private Rect mIconBounds = null;
     private int mType = FULL_RENDERING;
     public static final int FULL_RENDERING = 0;
     public static final int FILTERS_RENDERING = 1;
@@ -70,7 +72,7 @@
         } else if (type != PARTIAL_RENDERING && type != HIGHRES_RENDERING
                 && type != GEOMETRY_RENDERING && type != FILTERS_RENDERING) {
             bitmap = MasterImage.getImage().getBitmapCache().getBitmap(
-                    source.getWidth(), source.getHeight());
+                    source.getWidth(), source.getHeight(), BitmapCache.RENDERING_REQUEST);
         }
 
         request.setBitmap(bitmap);
@@ -90,6 +92,25 @@
         request.post(context);
     }
 
+    public static void postIconRequest(Context context, int w, int h,
+                                       ImagePreset preset,
+                                       RenderingRequestCaller caller) {
+        if (preset == null || caller == null) {
+            Log.v(LOGTAG, "something null, preset: "
+                    + preset + " or caller: " + caller);
+            return;
+        }
+        RenderingRequest request = new RenderingRequest();
+        ImagePreset passedPreset = new ImagePreset(preset);
+        request.setOriginalImagePreset(preset);
+        request.setScaleFactor(MasterImage.getImage().getScaleFactor());
+        request.setImagePreset(passedPreset);
+        request.setType(RenderingRequest.ICON_RENDERING);
+        request.setCaller(caller);
+        request.setIconBounds(new Rect(0, 0, w, h));
+        request.post(context);
+    }
+
     public void post(Context context) {
         if (context instanceof FilterShowActivity) {
             FilterShowActivity activity = (FilterShowActivity) context;
@@ -166,6 +187,14 @@
         mDestination = destination;
     }
 
+    public void setIconBounds(Rect bounds) {
+        mIconBounds = bounds;
+    }
+
+    public Rect getIconBounds() {
+        return mIconBounds;
+    }
+
     public ImagePreset getOriginalImagePreset() {
         return mOriginalImagePreset;
     }
diff --git a/src/com/android/gallery3d/filtershow/pipeline/SharedBuffer.java b/src/com/android/gallery3d/filtershow/pipeline/SharedBuffer.java
index 871e4cd..f243aa6 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/SharedBuffer.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/SharedBuffer.java
@@ -29,15 +29,16 @@
     private volatile boolean mNeedsSwap = false;
     private volatile boolean mNeedsRepaint = true;
 
-    public void setProducer(Bitmap producer) {
-        synchronized (this) {
-            if (mProducer != null) {
-                mProducer.remove();
-            }
+    public synchronized void setProducer(Bitmap producer) {
+        if (mProducer != null
+                && !mProducer.isSameSize(producer)) {
+            mProducer.remove();
+            mProducer = null;
         }
-        Buffer buffer = new Buffer(producer);
-        synchronized (this) {
-            mProducer = buffer;
+        if (mProducer == null) {
+            mProducer = new Buffer(producer);
+        } else {
+            mProducer.useBitmap(producer);
         }
     }
 
diff --git a/src/com/android/gallery3d/filtershow/pipeline/SharedPreset.java b/src/com/android/gallery3d/filtershow/pipeline/SharedPreset.java
index 3f850fe..3343612 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/SharedPreset.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/SharedPreset.java
@@ -21,6 +21,7 @@
     private volatile ImagePreset mProducerPreset = null;
     private volatile ImagePreset mConsumerPreset = null;
     private volatile ImagePreset mIntermediatePreset = null;
+    private volatile boolean mHasNewContent = false;
 
     public synchronized void enqueuePreset(ImagePreset preset) {
         if (mProducerPreset == null || (!mProducerPreset.same(preset))) {
@@ -31,12 +32,17 @@
         ImagePreset temp = mIntermediatePreset;
         mIntermediatePreset = mProducerPreset;
         mProducerPreset = temp;
+        mHasNewContent = true;
     }
 
     public synchronized ImagePreset dequeuePreset() {
+        if (!mHasNewContent) {
+            return mConsumerPreset;
+        }
         ImagePreset temp = mConsumerPreset;
         mConsumerPreset = mIntermediatePreset;
         mIntermediatePreset = temp;
+        mHasNewContent = false;
         return mConsumerPreset;
     }
 }
diff --git a/src/com/android/gallery3d/filtershow/pipeline/UpdatePreviewTask.java b/src/com/android/gallery3d/filtershow/pipeline/UpdatePreviewTask.java
index 406cc9b..61ee8eb 100644
--- a/src/com/android/gallery3d/filtershow/pipeline/UpdatePreviewTask.java
+++ b/src/com/android/gallery3d/filtershow/pipeline/UpdatePreviewTask.java
@@ -17,10 +17,12 @@
 package com.android.gallery3d.filtershow.pipeline;
 
 import android.graphics.Bitmap;
+
 import com.android.gallery3d.filtershow.filters.FiltersManager;
 import com.android.gallery3d.filtershow.imageshow.MasterImage;
 
 public class UpdatePreviewTask extends ProcessingTask {
+    private static final String LOGTAG = "UpdatePreviewTask";
     private CachingPipeline mPreviewPipeline = null;
     private boolean mHasUnhandledPreviewRequest = false;
     private boolean mPipelineIsOn = false;
diff --git a/src/com/android/gallery3d/filtershow/state/StatePanel.java b/src/com/android/gallery3d/filtershow/state/StatePanel.java
index ff405cd..95c2df9 100644
--- a/src/com/android/gallery3d/filtershow/state/StatePanel.java
+++ b/src/com/android/gallery3d/filtershow/state/StatePanel.java
@@ -27,6 +27,7 @@
 import com.android.gallery3d.R;
 import com.android.gallery3d.filtershow.category.MainPanel;
 import com.android.gallery3d.filtershow.imageshow.MasterImage;
+import com.android.gallery3d.util.FilterShowHelper;
 
 public class StatePanel extends Fragment {
     private static final String LOGTAG = "StatePanel";
@@ -49,9 +50,18 @@
         track = (StatePanelTrack) panel;
         track.setAdapter(MasterImage.getImage().getState());
         mToggleVersionsPanel = (ImageButton) mMainView.findViewById(R.id.toggleVersionsPanel);
-        if (mMainPanel != null) {
-            mMainPanel.setToggleVersionsPanelButton(mToggleVersionsPanel);
-        } else if (mToggleVersionsPanel != null) {
+        if (FilterShowHelper.shouldUseVersions()) {
+            if (mToggleVersionsPanel.getVisibility() == View.GONE
+                    || mToggleVersionsPanel.getVisibility() == View.INVISIBLE) {
+                mToggleVersionsPanel.setVisibility(View.VISIBLE);
+                mToggleVersionsPanel.setImageBitmap(null);
+            }
+            if (mMainPanel != null) {
+                mMainPanel.setToggleVersionsPanelButton(mToggleVersionsPanel);
+            } else if (mToggleVersionsPanel != null) {
+                mToggleVersionsPanel.setVisibility(View.GONE);
+            }
+        } else {
             mToggleVersionsPanel.setVisibility(View.GONE);
         }
         return mMainView;
diff --git a/src/com/android/gallery3d/filtershow/state/StateView.java b/src/com/android/gallery3d/filtershow/state/StateView.java
index 73d5784..a43cd47 100644
--- a/src/com/android/gallery3d/filtershow/state/StateView.java
+++ b/src/com/android/gallery3d/filtershow/state/StateView.java
@@ -19,17 +19,17 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.*;
-import android.util.AttributeSet;
-import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewParent;
 import android.widget.LinearLayout;
 import com.android.gallery3d.R;
 import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.category.SwipableView;
+import com.android.gallery3d.filtershow.filters.FilterRepresentation;
 import com.android.gallery3d.filtershow.imageshow.MasterImage;
+import com.android.gallery3d.filtershow.pipeline.ImagePreset;
 
-public class StateView extends View {
+public class StateView extends View implements SwipableView {
 
     private static final String LOGTAG = "StateView";
     private Path mPath = new Path();
@@ -51,6 +51,10 @@
     private static int sMargin = 16;
     private static int sArrowHeight = 16;
     private static int sArrowWidth = 8;
+    private float mStartTouchX = 0;
+    private float mStartTouchY = 0;
+    private float mDeleteSlope = 20;
+
     private int mOrientation = LinearLayout.VERTICAL;
     private int mDirection = DOWN;
     private boolean mDuplicateButton;
@@ -104,24 +108,6 @@
         invalidate();
     }
 
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            ViewParent parent = getParent();
-            if (parent instanceof PanelTrack) {
-                ((PanelTrack) getParent()).onTouch(event, this);
-            }
-            if (mType == BEGIN) {
-                MasterImage.getImage().setShowsOriginal(true);
-            }
-        }
-        if (event.getActionMasked() == MotionEvent.ACTION_UP
-                || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
-            MasterImage.getImage().setShowsOriginal(false);
-        }
-        return true;
-    }
-
     public void drawText(Canvas canvas) {
         if (mText == null) {
             return;
@@ -180,22 +166,42 @@
     }
 
     private void drawHorizontalPath(float w, float h, float r, float d) {
-        mPath.moveTo(0, 0);
-        if (mType == END) {
-            mPath.lineTo(w, 0);
+        if (this.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+            mPath.moveTo(w, 0);
+            if (mType == END) {
+                mPath.lineTo(0, 0);
+                mPath.lineTo(0, h);
+            } else {
+                mPath.lineTo(d, 0);
+                mPath.lineTo(d, r);
+                mPath.lineTo(0, r + d);
+                mPath.lineTo(d, r + d + r);
+                mPath.lineTo(d, h);
+            }
             mPath.lineTo(w, h);
+            if (mType != BEGIN) {
+                mPath.lineTo(w, r + d + r);
+                mPath.lineTo(w - d, r + d);
+                mPath.lineTo(w, r);
+            }
         } else {
-            mPath.lineTo(w - d, 0);
-            mPath.lineTo(w - d, r);
-            mPath.lineTo(w, r + d);
-            mPath.lineTo(w - d, r + d + r);
-            mPath.lineTo(w - d, h);
-        }
-        mPath.lineTo(0, h);
-        if (mType != BEGIN) {
-            mPath.lineTo(0, r + d + r);
-            mPath.lineTo(d, r + d);
-            mPath.lineTo(0, r);
+            mPath.moveTo(0, 0);
+            if (mType == END) {
+                mPath.lineTo(w, 0);
+                mPath.lineTo(w, h);
+            } else {
+                mPath.lineTo(w - d, 0);
+                mPath.lineTo(w - d, r);
+                mPath.lineTo(w, r + d);
+                mPath.lineTo(w - d, r + d + r);
+                mPath.lineTo(w - d, h);
+            }
+            mPath.lineTo(0, h);
+            if (mType != BEGIN) {
+                mPath.lineTo(0, r + d + r);
+                mPath.lineTo(d, r + d);
+                mPath.lineTo(0, r);
+            }
         }
         mPath.close();
     }
@@ -288,4 +294,54 @@
     public boolean isDraggable() {
         return mState.isDraggable();
     }
+
+    @Override
+    public void delete() {
+        FilterShowActivity activity = (FilterShowActivity) getContext();
+        FilterRepresentation representation = getState().getFilterRepresentation();
+        activity.removeFilterRepresentation(representation);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        boolean ret = super.onTouchEvent(event);
+        FilterShowActivity activity = (FilterShowActivity) getContext();
+
+        if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+            activity.startTouchAnimation(this, event.getX(), event.getY());
+        }
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            mStartTouchY = event.getY();
+            mStartTouchX = event.getX();
+            if (mType == BEGIN) {
+                MasterImage.getImage().setShowsOriginal(true);
+            }
+        }
+        if (event.getActionMasked() == MotionEvent.ACTION_UP
+            || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+            setTranslationX(0);
+            setTranslationY(0);
+            MasterImage.getImage().setShowsOriginal(false);
+            if (mType != BEGIN && event.getActionMasked() == MotionEvent.ACTION_UP) {
+                setSelected(true);
+                FilterRepresentation representation = getState().getFilterRepresentation();
+                MasterImage image = MasterImage.getImage();
+                ImagePreset preset = image != null ? image.getCurrentPreset() : null;
+                if (getTranslationY() == 0
+                        && image != null && preset != null
+                        && representation != image.getCurrentFilterRepresentation()
+                        && preset.getRepresentation(representation) != null) {
+                    activity.showRepresentation(representation);
+                    setSelected(false);
+                }
+            }
+        }
+        if (mType != BEGIN && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
+            float delta = event.getY() - mStartTouchY;
+            if (Math.abs(delta) > mDeleteSlope) {
+                activity.setHandlesSwipeForView(this, mStartTouchX, mStartTouchY);
+            }
+        }
+        return true;
+    }
 }
diff --git a/src/com/android/gallery3d/filtershow/tools/SaveImage.java b/src/com/android/gallery3d/filtershow/tools/SaveImage.java
index 51dddfd..354081e 100644
--- a/src/com/android/gallery3d/filtershow/tools/SaveImage.java
+++ b/src/com/android/gallery3d/filtershow/tools/SaveImage.java
@@ -35,12 +35,12 @@
 import com.android.gallery3d.exif.ExifInterface;
 import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.cache.ImageLoader;
+import com.android.gallery3d.filtershow.filters.FilterRepresentation;
 import com.android.gallery3d.filtershow.filters.FiltersManager;
 import com.android.gallery3d.filtershow.imageshow.MasterImage;
 import com.android.gallery3d.filtershow.pipeline.CachingPipeline;
 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
 import com.android.gallery3d.filtershow.pipeline.ProcessingService;
-import com.android.gallery3d.util.UsageStatistics;
 import com.android.gallery3d.util.XmpUtilHelper;
 
 import java.io.File;
@@ -63,6 +63,7 @@
      * Callback for updates
      */
     public interface Callback {
+        void onPreviewSaved(Uri uri);
         void onProgress(int max, int current);
     }
 
@@ -314,10 +315,23 @@
         }
     }
 
-    public Uri processAndSaveImage(ImagePreset preset, boolean doAuxBackup,
-                                   int quality, float sizeFactor) {
+    private void updateExifData(ExifInterface exif, long time) {
+        // Set tags
+        exif.addDateTimeStampTag(ExifInterface.TAG_DATE_TIME, time,
+                TimeZone.getDefault());
+        exif.setTag(exif.buildTag(ExifInterface.TAG_ORIENTATION,
+                ExifInterface.Orientation.TOP_LEFT));
+        // Remove old thumbnail
+        exif.removeCompressedThumbnail();
+    }
 
-        Uri uri = resetToOriginalImageIfNeeded(preset, doAuxBackup);
+    public Uri processAndSaveImage(ImagePreset preset, boolean flatten,
+                                   int quality, float sizeFactor, boolean exit) {
+
+        Uri uri = null;
+        if (exit) {
+            uri = resetToOriginalImageIfNeeded(preset, !flatten);
+        }
         if (uri != null) {
             return null;
         }
@@ -332,33 +346,45 @@
         // newSourceUri is then pointing to the new location.
         // If no file is moved, newSourceUri will be the same as mSourceUri.
         Uri newSourceUri = mSourceUri;
-        if (doAuxBackup) {
+        if (!flatten) {
             newSourceUri = moveSrcToAuxIfNeeded(mSourceUri, mDestinationFile);
         }
 
         Uri savedUri = mSelectedImageUri;
         if (mPreviewImage != null) {
-            Object xmp = getPanoramaXMPData(newSourceUri, preset);
-            ExifInterface exif = getExifData(newSourceUri);
-            // Set tags
-            long time = System.currentTimeMillis();
-            exif.addDateTimeStampTag(ExifInterface.TAG_DATE_TIME, time,
-                    TimeZone.getDefault());
-            exif.setTag(exif.buildTag(ExifInterface.TAG_ORIENTATION,
-                    ExifInterface.Orientation.TOP_LEFT));
-            // Remove old thumbnail
-            exif.removeCompressedThumbnail();
-            // If we succeed in writing the bitmap as a jpeg, return a uri.
-            if (putExifData(mDestinationFile, exif, mPreviewImage, quality)) {
-                putPanoramaXMPData(mDestinationFile, xmp);
-                // mDestinationFile will save the newSourceUri info in the XMP.
-                XmpPresets.writeFilterXMP(mContext, newSourceUri,
-                        mDestinationFile, preset);
+            if (flatten) {
+                Object xmp = getPanoramaXMPData(newSourceUri, preset);
+                ExifInterface exif = getExifData(newSourceUri);
+                long time = System.currentTimeMillis();
+                updateExifData(exif, time);
+                if (putExifData(mDestinationFile, exif, mPreviewImage, quality)) {
+                    putPanoramaXMPData(mDestinationFile, xmp);
+                    ContentValues values = getContentValues(mContext, mSelectedImageUri, mDestinationFile, time);
+                    Object result = mContext.getContentResolver().insert(
+                            Images.Media.EXTERNAL_CONTENT_URI, values);
 
-                // After this call, mSelectedImageUri will be actually
-                // pointing at the new file mDestinationFile.
-                savedUri = SaveImage.linkNewFileToUri(mContext, mSelectedImageUri,
-                        mDestinationFile, time, doAuxBackup);
+                }
+            } else {
+                Object xmp = getPanoramaXMPData(newSourceUri, preset);
+                ExifInterface exif = getExifData(newSourceUri);
+                long time = System.currentTimeMillis();
+                updateExifData(exif, time);
+                // If we succeed in writing the bitmap as a jpeg, return a uri.
+                if (putExifData(mDestinationFile, exif, mPreviewImage, quality)) {
+                    putPanoramaXMPData(mDestinationFile, xmp);
+                    // mDestinationFile will save the newSourceUri info in the XMP.
+                    if (!flatten) {
+                        XmpPresets.writeFilterXMP(mContext, newSourceUri,
+                                mDestinationFile, preset);
+                    }
+                    // After this call, mSelectedImageUri will be actually
+                    // pointing at the new file mDestinationFile.
+                    savedUri = SaveImage.linkNewFileToUri(mContext, mSelectedImageUri,
+                            mDestinationFile, time, !flatten);
+                }
+            }
+            if (mCallback != null) {
+                mCallback.onPreviewSaved(savedUri);
             }
         }
 
@@ -387,33 +413,31 @@
 
                 Object xmp = getPanoramaXMPData(newSourceUri, preset);
                 ExifInterface exif = getExifData(newSourceUri);
-
-                updateProgress();
-                // Set tags
                 long time = System.currentTimeMillis();
-                exif.addDateTimeStampTag(ExifInterface.TAG_DATE_TIME, time,
-                        TimeZone.getDefault());
-                exif.setTag(exif.buildTag(ExifInterface.TAG_ORIENTATION,
-                        ExifInterface.Orientation.TOP_LEFT));
-                // Remove old thumbnail
-                exif.removeCompressedThumbnail();
+                updateProgress();
 
+                updateExifData(exif, time);
                 updateProgress();
 
                 // If we succeed in writing the bitmap as a jpeg, return a uri.
                 if (putExifData(mDestinationFile, exif, bitmap, quality)) {
                     putPanoramaXMPData(mDestinationFile, xmp);
                     // mDestinationFile will save the newSourceUri info in the XMP.
-                    XmpPresets.writeFilterXMP(mContext, newSourceUri,
-                            mDestinationFile, preset);
+                    if (!flatten) {
+                        XmpPresets.writeFilterXMP(mContext, newSourceUri,
+                                mDestinationFile, preset);
+                        uri = updateFile(mContext, savedUri, mDestinationFile, time);
 
-                    uri = updateFile(mContext, savedUri, mDestinationFile, time);
+                    } else {
+
+                        ContentValues values = getContentValues(mContext, mSelectedImageUri, mDestinationFile, time);
+                        Object result = mContext.getContentResolver().insert(
+                                Images.Media.EXTERNAL_CONTENT_URI, values);
+                    }
                 }
                 updateProgress();
 
                 noBitmap = false;
-                UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR,
-                        "SaveComplete", null);
             } catch (OutOfMemoryError e) {
                 // Try 5 times before failing for good.
                 if (++num_tries >= 5) {
@@ -508,9 +532,12 @@
             File destination) {
         Uri selectedImageUri = filterShowActivity.getSelectedImageUri();
         Uri sourceImageUri = MasterImage.getImage().getUri();
-
+        boolean flatten = false;
+        if (preset.contains(FilterRepresentation.TYPE_TINYPLANET)){
+            flatten = true;
+        }
         Intent processIntent = ProcessingService.getSaveIntent(filterShowActivity, preset,
-                destination, selectedImageUri, sourceImageUri, false, 90, 1f);
+                destination, selectedImageUri, sourceImageUri, flatten, 90, 1f, true);
 
         filterShowActivity.startService(processIntent);
 
@@ -520,9 +547,6 @@
             Toast.makeText(filterShowActivity,
                     toastMessage,
                     Toast.LENGTH_SHORT).show();
-
-            // terminate for now
-            filterShowActivity.completeSaveImage(selectedImageUri);
         }
     }
 
diff --git a/src/com/android/gallery3d/filtershow/tools/XmpPresets.java b/src/com/android/gallery3d/filtershow/tools/XmpPresets.java
index 3995eeb..7095e74 100644
--- a/src/com/android/gallery3d/filtershow/tools/XmpPresets.java
+++ b/src/com/android/gallery3d/filtershow/tools/XmpPresets.java
@@ -39,6 +39,7 @@
     public static final String XMP_GOOGLE_FILTER_PREFIX = "AFltr";
     public static final String XMP_SRC_FILE_URI = "SourceFileUri";
     public static final String XMP_FILTERSTACK = "filterstack";
+
     private static final String LOGTAG = "XmpPresets";
 
     public static class XMresults {
@@ -76,7 +77,7 @@
             xmpMeta.setProperty(XMP_GOOGLE_FILTER_NAMESPACE,
                     XMP_SRC_FILE_URI, srcUri.toString());
             xmpMeta.setProperty(XMP_GOOGLE_FILTER_NAMESPACE,
-                    XMP_FILTERSTACK, preset.getJsonString(context.getString(R.string.saved)));
+                    XMP_FILTERSTACK, preset.getJsonString(ImagePreset.JASON_SAVED));
         } catch (XMPException e) {
             Log.v(LOGTAG, "Write XMP meta to file failed:" + dstFile.getAbsolutePath());
             return;
@@ -116,7 +117,7 @@
                 Uri srcUri = Uri.parse(strSrcUri);
                 ret.originalimage = srcUri;
 
-                ret.preset = new ImagePreset(mMasterImage.getPreset());
+                ret.preset = new ImagePreset();
                 ret.presetString = filterString;
                 boolean ok = ret.preset.readJsonFromString(filterString);
                 if (!ok) {
diff --git a/src/com/android/gallery3d/filtershow/ui/ExportDialog.java b/src/com/android/gallery3d/filtershow/ui/ExportDialog.java
index b0046e1..f6a84ce 100644
--- a/src/com/android/gallery3d/filtershow/ui/ExportDialog.java
+++ b/src/com/android/gallery3d/filtershow/ui/ExportDialog.java
@@ -157,11 +157,11 @@
             case R.id.done:
                 FilterShowActivity activity = (FilterShowActivity) getActivity();
                 Uri sourceUri = MasterImage.getImage().getUri();
-                File dest = SaveImage.getNewFile(activity, sourceUri);
+                File dest = SaveImage.getNewFile(activity,  activity.getSelectedImageUri());
                 float scaleFactor = mExportWidth / (float) mOriginalBounds.width();
                 Intent processIntent = ProcessingService.getSaveIntent(activity, MasterImage
                         .getImage().getPreset(), dest, activity.getSelectedImageUri(), sourceUri,
-                        true, mSeekBar.getProgress(), scaleFactor);
+                        true, mSeekBar.getProgress(), scaleFactor, false);
                 activity.startService(processIntent);
                 dismiss();
                 break;
@@ -170,6 +170,9 @@
 
     public void updateCompressionFactor() {
         Bitmap bitmap = MasterImage.getImage().getFilteredImage();
+        if (bitmap == null) {
+            return;
+        }
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         bitmap.compress(Bitmap.CompressFormat.JPEG, mQuality, out);
         mCompressedSize = out.size();
diff --git a/src/com/android/gallery3d/gadget/WidgetClickHandler.java b/src/com/android/gallery3d/gadget/WidgetClickHandler.java
index 37ee1a6..e66a2a6 100644
--- a/src/com/android/gallery3d/gadget/WidgetClickHandler.java
+++ b/src/com/android/gallery3d/gadget/WidgetClickHandler.java
@@ -27,7 +27,7 @@
 import android.widget.Toast;
 
 import com.android.gallery3d.R;
-import com.android.gallery3d.app.Gallery;
+import com.android.gallery3d.app.GalleryActivity;
 import com.android.gallery3d.app.PhotoPage;
 import com.android.gallery3d.common.ApiHelper;
 
@@ -63,7 +63,7 @@
         } else {
             Toast.makeText(this,
                     R.string.no_such_item, Toast.LENGTH_LONG).show();
-            intent = new Intent(this, Gallery.class);
+            intent = new Intent(this, GalleryActivity.class);
         }
         if (tediousBack) {
             intent.setFlags(
diff --git a/src/com/android/gallery3d/glrenderer/TiledTexture.java b/src/com/android/gallery3d/glrenderer/TiledTexture.java
index 6ca1de0..78cb2f2 100644
--- a/src/com/android/gallery3d/glrenderer/TiledTexture.java
+++ b/src/com/android/gallery3d/glrenderer/TiledTexture.java
@@ -129,18 +129,25 @@
 
         @Override
         protected Bitmap onGetBitmap() {
-            int x = BORDER_SIZE - offsetX;
-            int y = BORDER_SIZE - offsetY;
-            int r = bitmap.getWidth() + x;
-            int b = bitmap.getHeight() + y;
-            sCanvas.drawBitmap(bitmap, x, y, sBitmapPaint);
+            // make a local copy of the reference to the bitmap,
+            // since it might be null'd in a different thread. b/8694871
+            Bitmap localBitmapRef = bitmap;
             bitmap = null;
 
-            // draw borders if need
-            if (x > 0) sCanvas.drawLine(x - 1, 0, x - 1, TILE_SIZE, sPaint);
-            if (y > 0) sCanvas.drawLine(0, y - 1, TILE_SIZE, y - 1, sPaint);
-            if (r < CONTENT_SIZE) sCanvas.drawLine(r, 0, r, TILE_SIZE, sPaint);
-            if (b < CONTENT_SIZE) sCanvas.drawLine(0, b, TILE_SIZE, b, sPaint);
+            if (localBitmapRef != null) {
+                int x = BORDER_SIZE - offsetX;
+                int y = BORDER_SIZE - offsetY;
+                int r = localBitmapRef.getWidth() + x;
+                int b = localBitmapRef.getHeight() + y;
+                sCanvas.drawBitmap(localBitmapRef, x, y, sBitmapPaint);
+                localBitmapRef = null;
+
+                // draw borders if need
+                if (x > 0) sCanvas.drawLine(x - 1, 0, x - 1, TILE_SIZE, sPaint);
+                if (y > 0) sCanvas.drawLine(0, y - 1, TILE_SIZE, y - 1, sPaint);
+                if (r < CONTENT_SIZE) sCanvas.drawLine(r, 0, r, TILE_SIZE, sPaint);
+                if (b < CONTENT_SIZE) sCanvas.drawLine(0, b, TILE_SIZE, b, sPaint);
+            }
 
             return sUploadBitmap;
         }
diff --git a/src/com/android/gallery3d/ingest/MtpDeviceIndex.java b/src/com/android/gallery3d/ingest/MtpDeviceIndex.java
index d30f94a..fed851e 100644
--- a/src/com/android/gallery3d/ingest/MtpDeviceIndex.java
+++ b/src/com/android/gallery3d/ingest/MtpDeviceIndex.java
@@ -267,6 +267,9 @@
                 break;
             }
         }
+        if (mBuckets.length == 0 || mUnifiedLookupIndex.length == 0) {
+            return -1;
+        }
         int mappedPos = mBuckets[bucketNumber].unifiedStartIndex
                 + position - mBuckets[bucketNumber].itemsStartIndex;
         if (order == SortOrder.Descending) {
@@ -283,6 +286,9 @@
             return bucket.itemsStartIndex + position - 1 - bucket.unifiedStartIndex;
         } else {
             int zeroIndex = mUnifiedLookupIndex.length - 1 - position;
+            if (mBuckets.length == 0 || mUnifiedLookupIndex.length == 0) {
+                return -1;
+            }
             DateBucket bucket = mBuckets[mUnifiedLookupIndex[zeroIndex]];
             if (bucket.unifiedEndIndex == zeroIndex) zeroIndex--;
             return mMtpObjects.length - 1 - bucket.itemsStartIndex
diff --git a/src/com/android/gallery3d/ui/MenuExecutor.java b/src/com/android/gallery3d/ui/MenuExecutor.java
index 8f4854e..1ace718 100644
--- a/src/com/android/gallery3d/ui/MenuExecutor.java
+++ b/src/com/android/gallery3d/ui/MenuExecutor.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Message;
+import android.support.v4.print.PrintHelper;
 import android.view.Menu;
 import android.view.MenuItem;
 
@@ -45,7 +46,6 @@
 import java.util.ArrayList;
 
 public class MenuExecutor {
-    @SuppressWarnings("unused")
     private static final String TAG = "MenuExecutor";
 
     private static final int MSG_TASK_COMPLETE = 1;
@@ -177,6 +177,8 @@
         boolean supportCache = (supported & MediaObject.SUPPORT_CACHE) != 0;
         boolean supportEdit = (supported & MediaObject.SUPPORT_EDIT) != 0;
         boolean supportInfo = (supported & MediaObject.SUPPORT_INFO) != 0;
+        boolean supportPrint = (supported & MediaObject.SUPPORT_PRINT) != 0;
+        supportPrint &= PrintHelper.systemSupportsPrint();
 
         setMenuItemVisible(menu, R.id.action_delete, supportDelete);
         setMenuItemVisible(menu, R.id.action_rotate_ccw, supportRotate);
@@ -192,6 +194,7 @@
         setMenuItemVisible(menu, R.id.action_edit, supportEdit);
         // setMenuItemVisible(menu, R.id.action_simple_edit, supportEdit);
         setMenuItemVisible(menu, R.id.action_details, supportInfo);
+        setMenuItemVisible(menu, R.id.print, supportPrint);
     }
 
     public static void updateMenuForPanorama(Menu menu, boolean shareAsPanorama360,
diff --git a/src/com/android/gallery3d/util/GalleryUtils.java b/src/com/android/gallery3d/util/GalleryUtils.java
index 9245e2c..9a78fcd 100644
--- a/src/com/android/gallery3d/util/GalleryUtils.java
+++ b/src/com/android/gallery3d/util/GalleryUtils.java
@@ -37,7 +37,7 @@
 import android.view.WindowManager;
 
 import com.android.gallery3d.R;
-import com.android.gallery3d.app.Gallery;
+import com.android.gallery3d.app.GalleryActivity;
 import com.android.gallery3d.app.PackagesMonitor;
 import com.android.gallery3d.common.ApiHelper;
 import com.android.gallery3d.data.DataManager;
@@ -239,8 +239,8 @@
         int state = pm.getComponentEnabledSetting(name);
         sCameraAvailableInitialized = true;
         sCameraAvailable =
-            (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
-             || (state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+           (state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+           || (state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
         return sCameraAvailable;
     }
 
@@ -252,7 +252,7 @@
     }
 
     public static void startGalleryActivity(Context context) {
-        Intent intent = new Intent(context, Gallery.class)
+        Intent intent = new Intent(context, GalleryActivity.class)
                 .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
                 | Intent.FLAG_ACTIVITY_NEW_TASK);
         context.startActivity(intent);
diff --git a/src/com/android/gallery3d/util/PrintJob.java b/src/com/android/gallery3d/util/PrintJob.java
deleted file mode 100644
index 83cb96a..0000000
--- a/src/com/android/gallery3d/util/PrintJob.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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.gallery3d.util;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Matrix;
-import android.graphics.RectF;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.print.PageRange;
-import android.print.PrintAttributes;
-import android.print.PrintDocumentAdapter;
-import android.print.PrintDocumentInfo;
-import android.print.PrintManager;
-import android.print.pdf.PdfDocument.Page;
-import android.print.pdf.PrintedPdfDocument;
-
-import com.android.gallery3d.filtershow.cache.ImageLoader;
-
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-public class PrintJob {
-    private final static int MAX_PRINT_SIZE = 2048;
-
-    public static void printBitmap(final Context context, final String jobName,
-            final Bitmap bitmap) {
-        if (bitmap == null) {
-            return;
-        }
-        PrintManager printManager = (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
-        printManager.print(jobName,
-                new PrintDocumentAdapter() {
-                    private PrintAttributes mAttributes;
-
-                    @Override
-                    public void onLayout(PrintAttributes oldPrintAttributes,
-                                         PrintAttributes newPrintAttributes,
-                                         CancellationSignal cancellationSignal,
-                                         LayoutResultCallback layoutResultCallback,
-                                         Bundle bundle) {
-
-                        mAttributes = newPrintAttributes;
-
-                        PrintDocumentInfo info = new PrintDocumentInfo
-                                .Builder(jobName, newPrintAttributes)
-                                .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
-                                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
-                                .setPageCount(1)
-                                .create();
-
-                        layoutResultCallback.onLayoutFinished(info, false);
-                    }
-
-                    @Override
-                    public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
-                                        CancellationSignal cancellationSignal,
-                                        WriteResultCallback writeResultCallback) {
-                        try {
-                            PrintedPdfDocument pdfDocument = PrintedPdfDocument.open(context,
-                                    mAttributes);
-                            Page page = pdfDocument.startPage(1);
-
-                            RectF content = new RectF(page.getInfo().getContentSize());
-                            Matrix matrix = new Matrix();
-
-                            // Handle orientation.
-                            if (mAttributes.getOrientation()
-                                    == PrintAttributes.ORIENTATION_LANDSCAPE) {
-                                // Compute and apply scale based on fitting mode.
-                                final float scale;
-                                switch (mAttributes.getFittingMode()) {
-                                    case PrintAttributes.FITTING_MODE_SCALE_TO_FILL: {
-                                        scale = Math.max(content.width() / bitmap.getHeight(),
-                                                content.height() / bitmap.getWidth());
-                                    } break;
-
-                                    case PrintAttributes.FITTING_MODE_SCALE_TO_FIT: {
-                                        scale = Math.min(content.width() / bitmap.getHeight(),
-                                                content.height() / bitmap.getWidth());
-                                    } break;
-
-                                    default: {
-                                        scale = 1.0f;
-                                    }
-                                }
-                                matrix.postScale(scale, scale);
-
-                                // Apply the rotation.
-                                matrix.postRotate(90);
-                                matrix.postTranslate(bitmap.getHeight() * scale, 0);
-
-                                // Center the content.
-                                final float translateX = (content.width()
-                                        - bitmap.getHeight() * scale) / 2;
-                                final float translateY = (content.height()
-                                        - bitmap.getWidth() * scale) / 2;
-                                matrix.postTranslate(translateX, translateY);
-                            } else {
-                                // Compute and apply scale based on fitting mode.
-                                float scale = 1.0f;
-                                switch (mAttributes.getFittingMode()) {
-                                    case PrintAttributes.FITTING_MODE_SCALE_TO_FILL: {
-                                        scale = Math.max(content.width() / bitmap.getWidth(),
-                                                content.height() / bitmap.getHeight());
-                                    } break;
-
-                                    case PrintAttributes.FITTING_MODE_SCALE_TO_FIT: {
-                                        scale = Math.min(content.width() / bitmap.getWidth(),
-                                                content.height() / bitmap.getHeight());
-                                    } break;
-                                }
-                                matrix.postScale(scale, scale);
-
-                                // Center the content.
-                                final float translateX = (content.width()
-                                        - bitmap.getWidth() * scale) / 2;
-                                final float translateY = (content.height()
-                                        - bitmap.getHeight() * scale) / 2;
-                                matrix.postTranslate(translateX, translateY);
-                            }
-
-                            // Draw the bitmap.
-                            page.getCanvas().drawBitmap(bitmap, matrix, null);
-
-                            // Write the document.
-                            pdfDocument.finishPage(page);
-                            pdfDocument.writeTo(new FileOutputStream(
-                                    fileDescriptor.getFileDescriptor()));
-                            pdfDocument.close();
-
-                            // Done.
-                            writeResultCallback.onWriteFinished(
-                                    new PageRange[] { PageRange.ALL_PAGES });
-                        } finally {
-                            if (fileDescriptor != null) {
-                                try {
-                                    fileDescriptor.close();
-                                } catch (IOException ioe) {
-                                    /* ignore */
-                                }
-                            }
-                            writeResultCallback.onWriteFailed(null);
-                        }
-                    }
-                }, new PrintAttributes.Builder().create());
-    }
-
-    public static void printBitmapAtUri(Context context, String imagePrint, Uri uri) {
-        // TODO: load full size images. For now, it's better to constrain ourselves.
-        Bitmap bitmap = ImageLoader.loadConstrainedBitmap(uri, context, MAX_PRINT_SIZE, null, false);
-        printBitmap(context, imagePrint, bitmap);
-    }
-}
diff --git a/src/com/android/photos/AlbumFragment.java b/src/com/android/photos/AlbumFragment.java
index 406fd2a..886ca68 100644
--- a/src/com/android/photos/AlbumFragment.java
+++ b/src/com/android/photos/AlbumFragment.java
@@ -31,7 +31,7 @@
 import android.widget.TextView;
 
 import com.android.gallery3d.R;
-import com.android.gallery3d.app.Gallery;
+import com.android.gallery3d.app.GalleryActivity;
 import com.android.photos.adapters.PhotoThumbnailAdapter;
 import com.android.photos.data.PhotoSetLoader;
 import com.android.photos.shims.LoaderCompatShim;
@@ -108,7 +108,7 @@
         Cursor item = (Cursor) getItemAtPosition(position);
         Uri uri = mLoaderCompatShim.uriForItem(item);
         Intent intent = new Intent(Intent.ACTION_VIEW, uri);
-        intent.setClass(getActivity(), Gallery.class);
+        intent.setClass(getActivity(), GalleryActivity.class);
         startActivity(intent);
     }
 
diff --git a/src/com/android/photos/PhotoSetFragment.java b/src/com/android/photos/PhotoSetFragment.java
index 961fd0b..7ce876f 100644
--- a/src/com/android/photos/PhotoSetFragment.java
+++ b/src/com/android/photos/PhotoSetFragment.java
@@ -28,7 +28,7 @@
 import android.view.ViewGroup;
 import android.widget.GridView;
 
-import com.android.gallery3d.app.Gallery;
+import com.android.gallery3d.app.GalleryActivity;
 import com.android.photos.adapters.PhotoThumbnailAdapter;
 import com.android.photos.data.PhotoSetLoader;
 import com.android.photos.shims.LoaderCompatShim;
@@ -74,7 +74,7 @@
         Cursor item = (Cursor) getItemAtPosition(position);
         Uri uri = mLoaderCompatShim.uriForItem(item);
         Intent intent = new Intent(Intent.ACTION_VIEW, uri);
-        intent.setClass(getActivity(), Gallery.class);
+        intent.setClass(getActivity(), GalleryActivity.class);
         startActivity(intent);
     }
 
diff --git a/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java b/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java
index 66372c2..cde615e 100644
--- a/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java
+++ b/src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java
@@ -30,7 +30,7 @@
     private static FiltersManager sInstance = null;
     private static FiltersManager sPreviewInstance = null;
     private static FiltersManager sHighresInstance = null;
-    private static int mImageBorderSize = 4; // in percent
+
     public FiltersManager() {
         init();
     }
@@ -49,77 +49,6 @@
         return sInstance;
     }
 
-    @Override
-    public void addBorders(Context context) {
-
-        // Do not localize
-        String[] serializationNames = {
-                "FRAME_4X5",
-                "FRAME_BRUSH",
-                "FRAME_GRUNGE",
-                "FRAME_SUMI_E",
-                "FRAME_TAPE",
-                "FRAME_BLACK",
-                "FRAME_BLACK_ROUNDED",
-                "FRAME_WHITE",
-                "FRAME_WHITE_ROUNDED",
-                "FRAME_CREAM",
-                "FRAME_CREAM_ROUNDED"
-        };
-
-        // The "no border" implementation
-        int i = 0;
-        FilterRepresentation rep = new FilterImageBorderRepresentation(0);
-        mBorders.add(rep);
-
-        // Regular borders
-        ArrayList <FilterRepresentation> borderList = new ArrayList<FilterRepresentation>();
-
-
-        rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_4x5);
-        borderList.add(rep);
-
-        rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_brush);
-        borderList.add(rep);
-
-        rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_grunge);
-        borderList.add(rep);
-
-        rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_sumi_e);
-        borderList.add(rep);
-
-        rep = new FilterImageBorderRepresentation(R.drawable.filtershow_border_tape);
-        borderList.add(rep);
-
-        rep = new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize, 0);
-        borderList.add(rep);
-
-        rep = new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize,
-                mImageBorderSize);
-        borderList.add(rep);
-
-        rep = new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize, 0);
-        borderList.add(rep);
-
-        rep = new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize,
-                mImageBorderSize);
-        borderList.add(rep);
-
-        int creamColor = Color.argb(255, 237, 237, 227);
-        rep = new FilterColorBorderRepresentation(creamColor, mImageBorderSize, 0);
-        borderList.add(rep);
-
-        rep = new FilterColorBorderRepresentation(creamColor, mImageBorderSize,
-                mImageBorderSize);
-        borderList.add(rep);
-
-        for (FilterRepresentation filter : borderList) {
-            filter.setSerializationName(serializationNames[i++]);
-            addRepresentation(filter);
-        }
-
-    }
-
     public static FiltersManager getHighresManager() {
         if (sHighresInstance == null) {
             sHighresInstance = new FiltersManager();
diff --git a/src_pd/com/android/gallery3d/util/CameraHelper.java b/src_pd/com/android/gallery3d/util/FilterShowHelper.java
similarity index 74%
rename from src_pd/com/android/gallery3d/util/CameraHelper.java
rename to src_pd/com/android/gallery3d/util/FilterShowHelper.java
index 87ca038..b30ebac 100644
--- a/src_pd/com/android/gallery3d/util/CameraHelper.java
+++ b/src_pd/com/android/gallery3d/util/FilterShowHelper.java
@@ -13,12 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.gallery3d.util;
 
-import android.content.Intent;
+public class FilterShowHelper {
 
-public class CameraHelper {
-
-    public static final Intent CAMERA_LAUNCHER_INTENT = new Intent(Intent.ACTION_MAIN)
-        .setClassName("com.android.camera2", "com.android.camera.CameraActivity");
+    /**
+     * Whether to utilize version info in editor.
+     */
+    public static final boolean shouldUseVersions() {
+        return true;
+    }
 }
diff --git a/src_pd/com/android/gallery3d/util/IntentHelper.java b/src_pd/com/android/gallery3d/util/IntentHelper.java
new file mode 100644
index 0000000..13f69eb
--- /dev/null
+++ b/src_pd/com/android/gallery3d/util/IntentHelper.java
@@ -0,0 +1,32 @@
+/*
+ * 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.gallery3d.util;
+
+import android.content.Context;
+import android.content.Intent;
+
+public class IntentHelper {
+
+    public static Intent getCameraIntent(Context context) {
+        return new Intent(Intent.ACTION_MAIN)
+            .setClassName("com.android.camera2", "com.android.camera.CameraActivity");
+    }
+
+    public static Intent getGalleryIntent(Context context) {
+        return new Intent(Intent.ACTION_MAIN)
+            .setClassName("com.android.gallery3d", "com.android.gallery3d.app.GalleryActivity");
+    }
+}