Merge "Lock rendering when switching state."
diff --git a/gallerycommon/src/com/android/gallery3d/common/BitmapUtils.java b/gallerycommon/src/com/android/gallery3d/common/BitmapUtils.java
index aaf4f66..c34e896 100644
--- a/gallerycommon/src/com/android/gallery3d/common/BitmapUtils.java
+++ b/gallerycommon/src/com/android/gallery3d/common/BitmapUtils.java
@@ -85,7 +85,7 @@
     // minSideLength long. If that's not possible, return 1.
     public static int computeSampleSizeLarger(int w, int h,
             int minSideLength) {
-        int initialSize = Math.min(w / minSideLength, h / minSideLength);
+        int initialSize = Math.max(w / minSideLength, h / minSideLength);
         if (initialSize <= 1) return 1;
 
         return initialSize <= 8
@@ -155,6 +155,17 @@
         return resizeBitmapByScale(bitmap, scale, recycle);
     }
 
+    // Resize the bitmap if each side is >= targetSize * 2
+    public static Bitmap resizeDownIfTooBig(
+            Bitmap bitmap, int targetSize, boolean recycle) {
+        int srcWidth = bitmap.getWidth();
+        int srcHeight = bitmap.getHeight();
+        float scale = Math.max(
+                (float) targetSize / srcWidth, (float) targetSize / srcHeight);
+        if (scale > 0.5f) return bitmap;
+        return resizeBitmapByScale(bitmap, scale, recycle);
+    }
+
     // Crops a square from the center of the original image.
     public static Bitmap cropCenter(Bitmap bitmap, boolean recycle) {
         int width = bitmap.getWidth();
diff --git a/gallerycommon/src/com/android/gallery3d/common/EntrySchema.java b/gallerycommon/src/com/android/gallery3d/common/EntrySchema.java
index d652ac9..46de03f 100644
--- a/gallerycommon/src/com/android/gallery3d/common/EntrySchema.java
+++ b/gallerycommon/src/com/android/gallery3d/common/EntrySchema.java
@@ -29,14 +29,14 @@
     @SuppressWarnings("unused")
     private static final String TAG = "EntrySchema";
 
-    private static final int TYPE_STRING = 0;
-    private static final int TYPE_BOOLEAN = 1;
-    private static final int TYPE_SHORT = 2;
-    private static final int TYPE_INT = 3;
-    private static final int TYPE_LONG = 4;
-    private static final int TYPE_FLOAT = 5;
-    private static final int TYPE_DOUBLE = 6;
-    private static final int TYPE_BLOB = 7;
+    public static final int TYPE_STRING = 0;
+    public static final int TYPE_BOOLEAN = 1;
+    public static final int TYPE_SHORT = 2;
+    public static final int TYPE_INT = 3;
+    public static final int TYPE_LONG = 4;
+    public static final int TYPE_FLOAT = 5;
+    public static final int TYPE_DOUBLE = 6;
+    public static final int TYPE_BLOB = 7;
     private static final String SQLITE_TYPES[] = {
             "TEXT", "INTEGER", "INTEGER", "INTEGER", "INTEGER", "REAL", "REAL", "NONE" };
 
@@ -91,7 +91,7 @@
         return -1;
     }
 
-    private ColumnInfo getColumn(String columnName) {
+    public ColumnInfo getColumn(String columnName) {
         int index = getColumnIndex(columnName);
         return (index < 0) ? null : mColumnInfo[index];
     }
diff --git a/res/drawable-hdpi/frame_overlay_gallery_ptp.png b/res/drawable-hdpi/frame_overlay_gallery_ptp.png
new file mode 100644
index 0000000..cb9ef50
--- /dev/null
+++ b/res/drawable-hdpi/frame_overlay_gallery_ptp.png
Binary files differ
diff --git a/res/drawable-hdpi/grid_pressed.9.png b/res/drawable-hdpi/grid_pressed.9.png
new file mode 100644
index 0000000..36486ae
--- /dev/null
+++ b/res/drawable-hdpi/grid_pressed.9.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_album_overlay_ptp_holo.png b/res/drawable-hdpi/ic_album_overlay_ptp_holo.png
deleted file mode 100644
index b725840..0000000
--- a/res/drawable-hdpi/ic_album_overlay_ptp_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_gallery_play.png b/res/drawable-hdpi/ic_gallery_play.png
index e475e1f..e4060e5 100644
--- a/res/drawable-hdpi/ic_gallery_play.png
+++ b/res/drawable-hdpi/ic_gallery_play.png
Binary files differ
diff --git a/res/drawable-hdpi/manage_bar.9.png b/res/drawable-hdpi/manage_bar.9.png
deleted file mode 100644
index 336c2d7..0000000
--- a/res/drawable-hdpi/manage_bar.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/frame_overlay_gallery_ptp.png b/res/drawable-mdpi/frame_overlay_gallery_ptp.png
new file mode 100644
index 0000000..95ba8f0
--- /dev/null
+++ b/res/drawable-mdpi/frame_overlay_gallery_ptp.png
Binary files differ
diff --git a/res/drawable-mdpi/grid_pressed.9.png b/res/drawable-mdpi/grid_pressed.9.png
new file mode 100644
index 0000000..ecb51bd
--- /dev/null
+++ b/res/drawable-mdpi/grid_pressed.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_album_overlay_ptp_holo.png b/res/drawable-mdpi/ic_album_overlay_ptp_holo.png
deleted file mode 100644
index adbd3d1..0000000
--- a/res/drawable-mdpi/ic_album_overlay_ptp_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_gallery_play.png b/res/drawable-mdpi/ic_gallery_play.png
index 183d75e..753231d 100644
--- a/res/drawable-mdpi/ic_gallery_play.png
+++ b/res/drawable-mdpi/ic_gallery_play.png
Binary files differ
diff --git a/res/drawable-mdpi/manage_bar.9.png b/res/drawable-mdpi/manage_bar.9.png
deleted file mode 100644
index e42b92b..0000000
--- a/res/drawable-mdpi/manage_bar.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/ic_pan_thumb.png b/res/drawable-sw600dp-mdpi/ic_pan_thumb.png
new file mode 100644
index 0000000..e249029
--- /dev/null
+++ b/res/drawable-sw600dp-mdpi/ic_pan_thumb.png
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/ic_video_thumb.png b/res/drawable-sw600dp-mdpi/ic_video_thumb.png
new file mode 100644
index 0000000..743cec2
--- /dev/null
+++ b/res/drawable-sw600dp-mdpi/ic_video_thumb.png
Binary files differ
diff --git a/res/drawable-xhdpi/frame_overlay_gallery_camera.png b/res/drawable-xhdpi/frame_overlay_gallery_camera.png
new file mode 100644
index 0000000..e9bc7cc
--- /dev/null
+++ b/res/drawable-xhdpi/frame_overlay_gallery_camera.png
Binary files differ
diff --git a/res/drawable-xhdpi/frame_overlay_gallery_folder.png b/res/drawable-xhdpi/frame_overlay_gallery_folder.png
new file mode 100644
index 0000000..cbafc85
--- /dev/null
+++ b/res/drawable-xhdpi/frame_overlay_gallery_folder.png
Binary files differ
diff --git a/res/drawable-xhdpi/frame_overlay_gallery_picasa.png b/res/drawable-xhdpi/frame_overlay_gallery_picasa.png
new file mode 100644
index 0000000..e7463a4
--- /dev/null
+++ b/res/drawable-xhdpi/frame_overlay_gallery_picasa.png
Binary files differ
diff --git a/res/drawable-xhdpi/frame_overlay_gallery_ptp.png b/res/drawable-xhdpi/frame_overlay_gallery_ptp.png
new file mode 100644
index 0000000..57fb7e4
--- /dev/null
+++ b/res/drawable-xhdpi/frame_overlay_gallery_ptp.png
Binary files differ
diff --git a/res/drawable-xhdpi/grid_pressed.9.png b/res/drawable-xhdpi/grid_pressed.9.png
new file mode 100644
index 0000000..f2fed2d
--- /dev/null
+++ b/res/drawable-xhdpi/grid_pressed.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/grid_selected.9.png b/res/drawable-xhdpi/grid_selected.9.png
new file mode 100644
index 0000000..d0f4a22
--- /dev/null
+++ b/res/drawable-xhdpi/grid_selected.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_gallery_play.png b/res/drawable-xhdpi/ic_gallery_play.png
new file mode 100644
index 0000000..70901e5
--- /dev/null
+++ b/res/drawable-xhdpi/ic_gallery_play.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_make_offline.png b/res/drawable-xhdpi/ic_menu_make_offline.png
new file mode 100644
index 0000000..808e2d1
--- /dev/null
+++ b/res/drawable-xhdpi/ic_menu_make_offline.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pan_thumb.png b/res/drawable-xhdpi/ic_pan_thumb.png
new file mode 100644
index 0000000..6e3405c
--- /dev/null
+++ b/res/drawable-xhdpi/ic_pan_thumb.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_video_thumb.png b/res/drawable-xhdpi/ic_video_thumb.png
new file mode 100644
index 0000000..e0f53b3
--- /dev/null
+++ b/res/drawable-xhdpi/ic_video_thumb.png
Binary files differ
diff --git a/res/drawable-xhdpi/thumb_selected.png b/res/drawable-xhdpi/thumb_selected.png
new file mode 100644
index 0000000..c6536bc
--- /dev/null
+++ b/res/drawable-xhdpi/thumb_selected.png
Binary files differ
diff --git a/res/layout-w480dp/manage_offline_bar.xml b/res/layout-w480dp/manage_offline_bar.xml
deleted file mode 100644
index d44ecf0..0000000
--- a/res/layout-w480dp/manage_offline_bar.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:background="@drawable/manage_bar">
-    <LinearLayout android:id="@+id/cache_bar"
-            android:gravity="center_vertical"
-            android:orientation="horizontal"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-        <TextView android:layout_width="wrap_content"
-                android:paddingLeft="2dp"
-                android:layout_height="wrap_content"
-                android:gravity="center_vertical|left"
-                android:drawablePadding="3dp"
-                android:text="@string/make_available_offline"
-                android:drawableLeft="@drawable/ic_manage_pin"/>
-        <ProgressBar android:id="@+id/progress"
-                style="?android:attr/progressBarStyleHorizontal"
-                android:max="100"
-                android:progress="30"
-                android:secondaryProgress="65"
-                android:layout_marginLeft="5dp"
-                android:layout_marginRight="5dp"
-                android:layout_weight="1"
-                android:layout_width="match_parent"
-                android:layout_height="10dp"/>
-        <TextView android:id="@+id/status"
-                android:paddingRight="2dp"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"/>
-        <View android:layout_width="2dp"
-                android:layout_height="match_parent"
-                android:background="@android:drawable/divider_horizontal_dark" />
-        <Button android:id="@+id/done"
-                style="?android:attr/borderlessButtonStyle"
-                android:text="@string/done"
-                android:layout_weight="0"
-                android:layout_alignParentBottom="true"
-                android:layout_alignParentLeft="true"
-                android:layout_alignParentRight="true"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"/>
-    </LinearLayout>
-</FrameLayout>
diff --git a/res/layout/manage_offline_bar.xml b/res/layout/manage_offline_bar.xml
index b4bcec2..81853ed 100644
--- a/res/layout/manage_offline_bar.xml
+++ b/res/layout/manage_offline_bar.xml
@@ -13,50 +13,44 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:background="@drawable/manage_bar">
-    <LinearLayout android:id="@+id/cache_bar"
-            android:orientation="horizontal"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-        <LinearLayout android:orientation="vertical"
-                android:paddingLeft="10dp"
-                android:paddingRight="5dp"
-                android:paddingBottom="5dp"
-                android:paddingTop="5dp"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_weight="1">
-            <TextView android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:gravity="bottom"
-                    android:drawablePadding="3dp"
-                    android:text="@string/make_available_offline"
-                    android:drawableRight="@drawable/ic_manage_pin_small"/>
-            <ProgressBar android:id="@+id/progress"
-                    style="?android:attr/progressBarStyleHorizontal"
-                    android:max="100"
-                    android:progress="30"
-                    android:secondaryProgress="65"
-                    android:layout_marginTop="2dp"
-                    android:layout_marginBottom="2dp"
-                    android:layout_width="match_parent"
-                    android:layout_height="3dp"/>
-            <TextView android:id="@+id/status"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"/>
-        </LinearLayout>
-        <View android:layout_width="2dp"
-                android:layout_height="match_parent"
-                android:background="@android:drawable/divider_horizontal_dark" />
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+    <RelativeLayout
+            android:layout_width="fill_parent"
+            android:layout_height="40dp">
+        <TextView android:id="@+id/status"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerVertical="true"
+            android:layout_centerHorizontal="true" />
+        <ProgressBar android:id="@+id/progress"
+            style="?android:attr/progressBarStyleHorizontal"
+            android:max="100"
+            android:progress="30"
+            android:secondaryProgress="65"
+            android:layout_marginTop="2dp"
+            android:layout_marginBottom="2dp"
+            android:layout_width="130dp"
+            android:layout_height="4dp"
+            android:layout_below="@id/status"
+            android:layout_centerHorizontal="true"/>
+    </RelativeLayout>
+    <RelativeLayout android:layout_width="fill_parent"
+                android:layout_height="@dimen/manage_cache_bottom_height"
+                android:background="#1f1f1f">
+        <TextView android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/make_available_offline"
+            android:layout_alignParentLeft="true"
+            android:layout_centerVertical="true"
+            android:drawableLeft="@drawable/ic_manage_pin_small"
+            android:drawablePadding="3dp"/>
         <Button android:id="@+id/done"
-                style="?android:attr/borderlessButtonStyle"
-                android:text="@string/done"
-                android:layout_weight="0"
-                android:layout_alignParentBottom="true"
-                android:layout_alignParentLeft="true"
-                android:layout_alignParentRight="true"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"/>
-    </LinearLayout>
-</FrameLayout>
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:text="@string/done"
+            android:layout_alignParentRight="true"/>
+    </RelativeLayout>
+</LinearLayout>
diff --git a/res/values-land/dimensions.xml b/res/values-land/dimensions.xml
new file mode 100644
index 0000000..b5c5ed3
--- /dev/null
+++ b/res/values-land/dimensions.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- for manage cache bar -->
+    <dimen name="manage_cache_bottom_height">39dp</dimen>
+</resources>
diff --git a/res/values-xlarge/dimensions.xml b/res/values-xlarge/dimensions.xml
index a016054..4fa398f 100644
--- a/res/values-xlarge/dimensions.xml
+++ b/res/values-xlarge/dimensions.xml
@@ -15,9 +15,9 @@
 -->
 <resources>
     <dimen name="appwidget_width">240dp</dimen>
-    <dimen name="appwidget_height">200dp</dimen>
-    <dimen name="stack_photo_width">230dp</dimen>
-    <dimen name="stack_photo_height">190dp</dimen>
+    <dimen name="appwidget_height">240dp</dimen>
+    <dimen name="stack_photo_width">220dp</dimen>
+    <dimen name="stack_photo_height">165dp</dimen>
 
     <!-- configuration for album set page -->
     <integer name="albumset_rows_land">3</integer>
diff --git a/res/values/dimensions.xml b/res/values/dimensions.xml
index 7cbd875..2804bc9 100644
--- a/res/values/dimensions.xml
+++ b/res/values/dimensions.xml
@@ -14,21 +14,21 @@
      limitations under the License.
 -->
 <resources>
-    <dimen name="appwidget_width">146dp</dimen>
-    <dimen name="appwidget_height">146dp</dimen>
-    <dimen name="stack_photo_width">140dp</dimen>
-    <dimen name="stack_photo_height">110dp</dimen>
+    <dimen name="appwidget_width">180dp</dimen>
+    <dimen name="appwidget_height">180dp</dimen>
+    <dimen name="stack_photo_width">160dp</dimen>
+    <dimen name="stack_photo_height">120dp</dimen>
 
     <!-- configuration for album set page -->
     <integer name="albumset_rows_land">2</integer>
     <integer name="albumset_rows_port">4</integer>
     <dimen name="albumset_slot_gap">1dp</dimen>
 
-    <dimen name="albumset_dark_strip_height">40dp</dimen>
+    <dimen name="albumset_label_background_height">40dp</dimen>
     <dimen name="albumset_title_offset">3dp</dimen>
-    <dimen name="albumset_number_offset">4dp</dimen>
+    <dimen name="albumset_count_offset">4dp</dimen>
     <dimen name="albumset_title_font_size">14sp</dimen>
-    <dimen name="albumset_number_font_size">12sp</dimen>
+    <dimen name="albumset_count_font_size">12sp</dimen>
     <dimen name="albumset_left_margin">6dp</dimen>
     <dimen name="albumset_icon_size">26dp</dimen>
 
@@ -54,4 +54,6 @@
     <dimen name="filmstrip_bar_size">10dp</dimen>
     <dimen name="filmstrip_grip_width">96dp</dimen>
 
+    <!-- for manage cache bar -->
+    <dimen name="manage_cache_bottom_height">48dp</dimen>
 </resources>
diff --git a/res/xml/widget_info.xml b/res/xml/widget_info.xml
index e03124c..4aa460f 100644
--- a/res/xml/widget_info.xml
+++ b/res/xml/widget_info.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
-        android:minWidth="180dp"
-        android:minHeight="180dp"
+        android:minWidth="@dimen/appwidget_width"
+        android:minHeight="@dimen/appwidget_height"
         android:updatePeriodMillis="86400000"
         android:previewImage="@drawable/preview"
         android:initialLayout="@layout/appwidget_main"
diff --git a/src/com/android/gallery3d/app/AlbumPage.java b/src/com/android/gallery3d/app/AlbumPage.java
index 7d70548..55feb38 100644
--- a/src/com/android/gallery3d/app/AlbumPage.java
+++ b/src/com/android/gallery3d/app/AlbumPage.java
@@ -148,6 +148,18 @@
         }
     }
 
+    private void onDown(int index) {
+        MediaItem item = mAlbumDataAdapter.get(index);
+        Path path = (item == null) ? null : item.getPath();
+        mSelectionManager.setPressedPath(path);
+        mAlbumView.invalidate();
+    }
+
+    private void onUp() {
+        mSelectionManager.setPressedPath(null);
+        mAlbumView.invalidate();
+    }
+
     public void onSingleTapUp(int slotIndex) {
         MediaItem item = mAlbumDataAdapter.get(slotIndex);
         if (item == null) {
@@ -363,9 +375,20 @@
         mRootPane.addComponent(mAlbumView);
         mAlbumView.setListener(new SlotView.SimpleListener() {
             @Override
+            public void onDown(int index) {
+                AlbumPage.this.onDown(index);
+            }
+
+            @Override
+            public void onUp() {
+                AlbumPage.this.onUp();
+            }
+
+            @Override
             public void onSingleTapUp(int slotIndex) {
                 AlbumPage.this.onSingleTapUp(slotIndex);
             }
+
             @Override
             public void onLongTap(int slotIndex) {
                 AlbumPage.this.onLongTap(slotIndex);
@@ -395,7 +418,8 @@
     private void showDetails() {
         mShowDetails = true;
         if (mDetailsHelper == null) {
-            mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext());
+            mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext(),
+                    mSelectionManager);
             mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
             mDetailsHelper.setCloseListener(new CloseListener() {
                 public void onClose() {
@@ -521,10 +545,6 @@
                 break;
             }
             case SelectionManager.SELECT_ALL_MODE: {
-                int count = mSelectionManager.getSelectedCount();
-                String format = mActivity.getResources().getQuantityString(
-                        R.plurals.number_of_items_selected, count);
-                mActionModeHandler.setTitle(String.format(format, count));
                 mActionModeHandler.updateSupportedOperation();
                 mRootPane.invalidate();
                 break;
diff --git a/src/com/android/gallery3d/app/AlbumSetPage.java b/src/com/android/gallery3d/app/AlbumSetPage.java
index eac1abc..0726ba1 100644
--- a/src/com/android/gallery3d/app/AlbumSetPage.java
+++ b/src/com/android/gallery3d/app/AlbumSetPage.java
@@ -210,6 +210,18 @@
         }
     }
 
+    private void onDown(int index) {
+        MediaSet set = mAlbumSetDataAdapter.getMediaSet(index);
+        Path path = (set == null) ? null : set.getPath();
+        mSelectionManager.setPressedPath(path);
+        mAlbumSetView.invalidate();
+    }
+
+    private void onUp() {
+        mSelectionManager.setPressedPath(null);
+        mAlbumSetView.invalidate();
+    }
+
     public void onLongTap(int slotIndex) {
         if (mGetContent || mGetAlbum) return;
         if (mShowDetails) {
@@ -317,9 +329,20 @@
                 config.slotViewSpec, config.labelSpec);
         mAlbumSetView.setListener(new SlotView.SimpleListener() {
             @Override
+            public void onDown(int index) {
+                AlbumSetPage.this.onDown(index);
+            }
+
+            @Override
+            public void onUp() {
+                AlbumSetPage.this.onUp();
+            }
+
+            @Override
             public void onSingleTapUp(int slotIndex) {
                 AlbumSetPage.this.onSingleTapUp(slotIndex);
             }
+
             @Override
             public void onLongTap(int slotIndex) {
                 AlbumSetPage.this.onLongTap(slotIndex);
@@ -518,7 +541,8 @@
     private void showDetails() {
         mShowDetails = true;
         if (mDetailsHelper == null) {
-            mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext());
+            mHighlightDrawer = new HighlightDrawer(mActivity.getAndroidContext(),
+                    mSelectionManager);
             mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
             mDetailsHelper.setCloseListener(new CloseListener() {
                 public void onClose() {
diff --git a/src/com/android/gallery3d/app/Config.java b/src/com/android/gallery3d/app/Config.java
index 0cea6c8..be1417a 100644
--- a/src/com/android/gallery3d/app/Config.java
+++ b/src/com/android/gallery3d/app/Config.java
@@ -46,16 +46,16 @@
             slotViewSpec.slotGap = r.getDimensionPixelSize(R.dimen.albumset_slot_gap);
 
             labelSpec = new AlbumSetView.LabelSpec();
-            labelSpec.darkStripHeight = r.getDimensionPixelSize(
-                    R.dimen.albumset_dark_strip_height);
+            labelSpec.labelBackgroundHeight = r.getDimensionPixelSize(
+                    R.dimen.albumset_label_background_height);
             labelSpec.titleOffset = r.getDimensionPixelSize(
                     R.dimen.albumset_title_offset);
-            labelSpec.numberOffset = r.getDimensionPixelSize(
-                    R.dimen.albumset_number_offset);
+            labelSpec.countOffset = r.getDimensionPixelSize(
+                    R.dimen.albumset_count_offset);
             labelSpec.titleFontSize = r.getDimensionPixelSize(
                     R.dimen.albumset_title_font_size);
-            labelSpec.numberFontSize = r.getDimensionPixelSize(
-                    R.dimen.albumset_number_font_size);
+            labelSpec.countFontSize = r.getDimensionPixelSize(
+                    R.dimen.albumset_count_font_size);
             labelSpec.leftMargin = r.getDimensionPixelSize(
                     R.dimen.albumset_left_margin);
             labelSpec.iconSize = r.getDimensionPixelSize(
diff --git a/src/com/android/gallery3d/app/ManageCachePage.java b/src/com/android/gallery3d/app/ManageCachePage.java
index 7e301f2..01efaea 100644
--- a/src/com/android/gallery3d/app/ManageCachePage.java
+++ b/src/com/android/gallery3d/app/ManageCachePage.java
@@ -113,10 +113,10 @@
             int slotViewTop = GalleryActionBar.getHeight(activity);
             int slotViewBottom = bottom - top;
 
-            View cacheBar = activity.findViewById(R.id.cache_bar);
-            if (cacheBar != null) {
+            View footer = activity.findViewById(R.id.footer);
+            if (footer != null) {
                 int location[] = {0, 0};
-                cacheBar.getLocationOnScreen(location);
+                footer.getLocationOnScreen(location);
                 slotViewBottom = location[1];
             }
 
@@ -143,6 +143,18 @@
         mRootPane.invalidate();
     }
 
+    private void onDown(int index) {
+        MediaSet set = mAlbumSetDataAdapter.getMediaSet(index);
+        Path path = (set == null) ? null : set.getPath();
+        mSelectionManager.setPressedPath(path);
+        mAlbumSetView.invalidate();
+    }
+
+    private void onUp() {
+        mSelectionManager.setPressedPath(null);
+        mAlbumSetView.invalidate();
+    }
+
     public void onSingleTapUp(int slotIndex) {
         MediaSet targetSet = mAlbumSetDataAdapter.getMediaSet(slotIndex);
         if (targetSet == null) return; // Content is dirty, we shall reload soon
@@ -286,6 +298,16 @@
                 config.slotViewSpec, config.labelSpec);
         mAlbumSetView.setListener(new SlotView.SimpleListener() {
             @Override
+            public void onDown(int index) {
+                ManageCachePage.this.onDown(index);
+            }
+
+            @Override
+            public void onUp() {
+                ManageCachePage.this.onUp();
+            }
+
+            @Override
             public void onSingleTapUp(int slotIndex) {
                 ManageCachePage.this.onSingleTapUp(slotIndex);
             }
diff --git a/src/com/android/gallery3d/app/MovieActivity.java b/src/com/android/gallery3d/app/MovieActivity.java
index 4c9be7e..d0383aa 100644
--- a/src/com/android/gallery3d/app/MovieActivity.java
+++ b/src/com/android/gallery3d/app/MovieActivity.java
@@ -52,7 +52,7 @@
         View rootView = findViewById(R.id.root);
         Intent intent = getIntent();
         setVideoTitle(intent);
-        mPlayer = new MoviePlayer(rootView, this, intent.getData()) {
+        mPlayer = new MoviePlayer(rootView, this, intent.getData(), savedInstanceState) {
             @Override
             public void onCompletion() {
                 if (mFinishOnCompletion) {
@@ -73,7 +73,6 @@
         WindowManager.LayoutParams winParams = win.getAttributes();
         winParams.buttonBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF;
         win.setAttributes(winParams);
-
     }
 
     private void setVideoTitle(Intent intent) {
@@ -124,6 +123,12 @@
     }
 
     @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        mPlayer.onSaveInstanceState(outState);
+    }
+
+    @Override
     public void onDestroy() {
         mPlayer.onDestroy();
         super.onDestroy();
diff --git a/src/com/android/gallery3d/app/MoviePlayer.java b/src/com/android/gallery3d/app/MoviePlayer.java
index 4239944..ee76fa5 100644
--- a/src/com/android/gallery3d/app/MoviePlayer.java
+++ b/src/com/android/gallery3d/app/MoviePlayer.java
@@ -33,6 +33,7 @@
 import android.media.AudioManager;
 import android.media.MediaPlayer;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.view.KeyEvent;
 import android.view.View;
@@ -49,11 +50,18 @@
     @SuppressWarnings("unused")
     private static final String TAG = "MoviePlayer";
 
+    private static final String KEY_VIDEO_POSITION = "video-position";
+    private static final String KEY_RESUMEABLE_TIME = "resumeable-timeout";
+
     // Copied from MediaPlaybackService in the Music Player app.
     private static final String SERVICECMD = "com.android.music.musicservicecommand";
     private static final String CMDNAME = "command";
     private static final String CMDPAUSE = "pause";
 
+    // If we resume the acitivty with in RESUMEABLE_TIMEOUT, we will keep playing.
+    // Otherwise, we pause the player.
+    private static final long RESUMEABLE_TIMEOUT = 3 * 60 * 1000; // 3 mins
+
     private Context mContext;
     private final VideoView mVideoView;
     private final View mProgressView;
@@ -62,10 +70,14 @@
     private final Handler mHandler = new Handler();
     private final AudioBecomingNoisyReceiver mAudioBecomingNoisyReceiver;
     private final ActionBar mActionBar;
+    private final MediaController mMediaController;
 
-    private boolean mHasPaused;
+    private long mResumeableTime = Long.MAX_VALUE;
+    private int mVideoPosition = 0;
+    private boolean mHasPaused = false;
 
     private final Runnable mPlayingChecker = new Runnable() {
+        @Override
         public void run() {
             if (mVideoView.isPlaying()) {
                 mProgressView.setVisibility(View.GONE);
@@ -75,7 +87,8 @@
         }
     };
 
-    public MoviePlayer(View rootView, final MovieActivity movieActivity, Uri videoUri) {
+    public MoviePlayer(View rootView, final MovieActivity movieActivity, Uri videoUri,
+            Bundle savedInstance) {
         mContext = movieActivity.getApplicationContext();
         mVideoView = (VideoView) rootView.findViewById(R.id.surface_view);
         mProgressView = rootView.findViewById(R.id.progress_indicator);
@@ -96,7 +109,7 @@
         mVideoView.setOnCompletionListener(this);
         mVideoView.setVideoURI(mUri);
 
-        MediaController mediaController = new MediaController(movieActivity) {
+        mMediaController = new MediaController(movieActivity) {
             @Override
             public void show() {
                 super.show();
@@ -109,8 +122,8 @@
                 mActionBar.hide();
             }
         };
-        mVideoView.setMediaController(mediaController);
-        mediaController.setOnKeyListener(new View.OnKeyListener() {
+        mMediaController.setOnKeyListener(new View.OnKeyListener() {
+            @Override
             public boolean onKey(View v, int keyCode, KeyEvent event) {
                 if (keyCode == KeyEvent.KEYCODE_BACK) {
                     if (event.getAction() == KeyEvent.ACTION_UP) {
@@ -121,6 +134,7 @@
                 return false;
             }
         });
+        mVideoView.setMediaController(mMediaController);
 
         mAudioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver();
         mAudioBecomingNoisyReceiver.register();
@@ -132,14 +146,27 @@
         i.putExtra(CMDNAME, CMDPAUSE);
         movieActivity.sendBroadcast(i);
 
-        final Integer bookmark = mBookmarker.getBookmark(mUri);
-        if (bookmark != null) {
-            showResumeDialog(movieActivity, bookmark);
-        } else {
+        if (savedInstance != null) { // this is a resumed activity
+            mVideoPosition = savedInstance.getInt(KEY_VIDEO_POSITION, 0);
+            mResumeableTime = savedInstance.getLong(KEY_RESUMEABLE_TIME, Long.MAX_VALUE);
             mVideoView.start();
+            mVideoView.suspend();
+            mHasPaused = true;
+        } else {
+            final Integer bookmark = mBookmarker.getBookmark(mUri);
+            if (bookmark != null) {
+                showResumeDialog(movieActivity, bookmark);
+            } else {
+                mVideoView.start();
+            }
         }
     }
 
+    public void onSaveInstanceState(Bundle outState) {
+        outState.putInt(KEY_VIDEO_POSITION, mVideoPosition);
+        outState.putLong(KEY_RESUMEABLE_TIME, mResumeableTime);
+    }
+
     private void showResumeDialog(Context context, final int bookmark) {
         AlertDialog.Builder builder = new AlertDialog.Builder(context);
         builder.setTitle(R.string.resume_playing_title);
@@ -147,12 +174,14 @@
                 context.getString(R.string.resume_playing_message),
                 GalleryUtils.formatDuration(context, bookmark / 1000)));
         builder.setOnCancelListener(new OnCancelListener() {
+            @Override
             public void onCancel(DialogInterface dialog) {
                 onCompletion();
             }
         });
         builder.setPositiveButton(
                 R.string.resume_playing_resume, new OnClickListener() {
+            @Override
             public void onClick(DialogInterface dialog, int which) {
                 mVideoView.seekTo(bookmark);
                 mVideoView.start();
@@ -160,6 +189,7 @@
         });
         builder.setNegativeButton(
                 R.string.resume_playing_restart, new OnClickListener() {
+            @Override
             public void onClick(DialogInterface dialog, int which) {
                 mVideoView.start();
             }
@@ -168,21 +198,25 @@
     }
 
     public void onPause() {
-        mHandler.removeCallbacksAndMessages(null);
-        mBookmarker.setBookmark(mUri, mVideoView.getCurrentPosition(),
-                mVideoView.getDuration());
-        mVideoView.suspend();
         mHasPaused = true;
+        mHandler.removeCallbacksAndMessages(null);
+        mVideoPosition = mVideoView.getCurrentPosition();
+        mBookmarker.setBookmark(mUri, mVideoPosition, mVideoView.getDuration());
+        mVideoView.suspend();
+        mResumeableTime = System.currentTimeMillis() + RESUMEABLE_TIMEOUT;
     }
 
     public void onResume() {
         if (mHasPaused) {
-            Integer bookmark = mBookmarker.getBookmark(mUri);
-            if (bookmark != null) {
-                mVideoView.seekTo(bookmark);
+            mVideoView.seekTo(mVideoPosition);
+            mVideoView.resume();
+
+            // If we have slept for too long, pause the play
+            if (System.currentTimeMillis() > mResumeableTime) {
+                mMediaController.show();
+                mVideoView.pause();
             }
         }
-        mVideoView.resume();
     }
 
     public void onDestroy() {
diff --git a/src/com/android/gallery3d/data/DecodeUtils.java b/src/com/android/gallery3d/data/DecodeUtils.java
index d2b4ebc..29b2aa7 100644
--- a/src/com/android/gallery3d/data/DecodeUtils.java
+++ b/src/com/android/gallery3d/data/DecodeUtils.java
@@ -102,15 +102,19 @@
         options.inSampleSize = BitmapUtils.computeSampleSizeLarger(
                 options.outWidth, options.outHeight, targetSize);
         options.inJustDecodeBounds = false;
-        return ensureGLCompatibleBitmap(
-                BitmapFactory.decodeFileDescriptor(fd, null, options));
+
+        Bitmap result = BitmapFactory.decodeFileDescriptor(fd, null, options);
+        // We need to resize down if the decoder does not support inSampleSize.
+        // (For example, GIF images.)
+        result = BitmapUtils.resizeDownIfTooBig(result, targetSize, true);
+        return ensureGLCompatibleBitmap(result);
     }
 
     /**
      * Decodes the bitmap from the given byte array if the image size is larger than the given
      * requirement.
      *
-     * Note: The returned image may be resized down. However, both width and heigh must be
+     * Note: The returned image may be resized down. However, both width and height must be
      * larger than the <code>targetSize</code>.
      */
     public static Bitmap requestDecodeIfBigEnough(JobContext jc, byte[] data,
diff --git a/src/com/android/gallery3d/data/LocalImage.java b/src/com/android/gallery3d/data/LocalImage.java
index 7ab04c5..ca87b94 100644
--- a/src/com/android/gallery3d/data/LocalImage.java
+++ b/src/com/android/gallery3d/data/LocalImage.java
@@ -306,4 +306,16 @@
     public int getRotation() {
         return rotation;
     }
+
+    @Override
+    public int getWidth() {
+        // TODO
+        return 0;
+    }
+
+    @Override
+    public int getHeight() {
+        // TODO
+        return 0;
+    }
 }
diff --git a/src/com/android/gallery3d/data/LocalVideo.java b/src/com/android/gallery3d/data/LocalVideo.java
index d1498e8..785bed1 100644
--- a/src/com/android/gallery3d/data/LocalVideo.java
+++ b/src/com/android/gallery3d/data/LocalVideo.java
@@ -210,4 +210,16 @@
         }
         return details;
     }
+
+    @Override
+    public int getWidth() {
+        // TODO
+        return 0;
+    }
+
+    @Override
+    public int getHeight() {
+        // TODO
+        return 0;
+    }
 }
diff --git a/src/com/android/gallery3d/data/MediaItem.java b/src/com/android/gallery3d/data/MediaItem.java
index 0906251..a0c6d8c 100644
--- a/src/com/android/gallery3d/data/MediaItem.java
+++ b/src/com/android/gallery3d/data/MediaItem.java
@@ -78,4 +78,9 @@
     }
 
     public abstract String getMimeType();
+
+    // Returns width and height of the media item.
+    // Returns 0, 0 if the information is not available.
+    public abstract int getWidth();
+    public abstract int getHeight();
 }
diff --git a/src/com/android/gallery3d/data/MtpImage.java b/src/com/android/gallery3d/data/MtpImage.java
index 218f704..211b2f2 100644
--- a/src/com/android/gallery3d/data/MtpImage.java
+++ b/src/com/android/gallery3d/data/MtpImage.java
@@ -157,4 +157,13 @@
         return details;
     }
 
+    @Override
+    public int getWidth() {
+        return mImageWidth;
+    }
+
+    @Override
+    public int getHeight() {
+        return mImageHeight;
+    }
 }
diff --git a/src/com/android/gallery3d/data/UriImage.java b/src/com/android/gallery3d/data/UriImage.java
index 3a7ed7c..e97b035 100644
--- a/src/com/android/gallery3d/data/UriImage.java
+++ b/src/com/android/gallery3d/data/UriImage.java
@@ -263,4 +263,14 @@
             super.finalize();
         }
     }
+
+    @Override
+    public int getWidth() {
+        return 0;
+    }
+
+    @Override
+    public int getHeight() {
+        return 0;
+    }
 }
diff --git a/src/com/android/gallery3d/gadget/WidgetConfigure.java b/src/com/android/gallery3d/gadget/WidgetConfigure.java
index 747cc3a..a871e24 100644
--- a/src/com/android/gallery3d/gadget/WidgetConfigure.java
+++ b/src/com/android/gallery3d/gadget/WidgetConfigure.java
@@ -48,6 +48,7 @@
     // Note: There is also a limit on the size of data that can be
     // passed in Binder's transaction.
     private static float WIDGET_SCALE_FACTOR = 1.5f;
+    private static int MAX_WIDGET_SIDE = 360;
 
     private int mAppWidgetId = -1;
     private int mWidgetType = 0;
@@ -115,10 +116,18 @@
 
     private void setChoosenPhoto(Intent data) {
         Resources res = getResources();
-        int widgetWidth = Math.round(WIDGET_SCALE_FACTOR
-                * res.getDimension(R.dimen.appwidget_width));
-        int widgetHeight = Math.round(WIDGET_SCALE_FACTOR
-                * res.getDimension(R.dimen.appwidget_height));
+
+        float width = res.getDimension(R.dimen.appwidget_width);
+        float height = res.getDimension(R.dimen.appwidget_height);
+
+        // We try to crop a larger image (by scale factor), but there is still
+        // a bound on the binder limit.
+        float scale = Math.min(WIDGET_SCALE_FACTOR,
+                MAX_WIDGET_SIDE / Math.max(width, height));
+
+        int widgetWidth = Math.round(width * scale);
+        int widgetHeight = Math.round(height * scale);
+
         mPickedItem = data.getData();
         Intent request = new Intent(CropImage.ACTION_CROP, mPickedItem)
                 .putExtra(CropImage.KEY_OUTPUT_X, widgetWidth)
diff --git a/src/com/android/gallery3d/ui/ActionModeHandler.java b/src/com/android/gallery3d/ui/ActionModeHandler.java
index 87dd3be..b8d049b 100644
--- a/src/com/android/gallery3d/ui/ActionModeHandler.java
+++ b/src/com/android/gallery3d/ui/ActionModeHandler.java
@@ -86,7 +86,7 @@
         mSelectionMenu = customMenu.addDropDownMenu(
                 (Button) customView.findViewById(R.id.selection_menu),
                 R.menu.selection);
-        updateSelectAllTitle();
+        updateSelectionMenu();
         customMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
             public boolean onMenuItemClick(MenuItem item) {
                 return onActionItemClicked(actionMode, item);
@@ -119,12 +119,17 @@
         result = mMenuExecutor.onMenuClicked(item, listener);
         if (item.getItemId() == R.id.action_select_all) {
             updateSupportedOperation();
-            updateSelectAllTitle();
+            updateSelectionMenu();
         }
         return result;
     }
 
-    private void updateSelectAllTitle() {
+    private void updateSelectionMenu() {
+        // update title
+        int count = mSelectionManager.getSelectedCount();
+        String format = mActivity.getResources().getQuantityString(
+                R.plurals.number_of_items_selected, count);
+        setTitle(String.format(format, count));
         // For clients who call SelectionManager.selectAll() directly, we need to ensure the
         // menu status is consistent with selection manager.
         MenuItem item = mSelectionMenu.findItem(R.id.action_select_all);
diff --git a/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java b/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
index 9ad0937..4253685 100644
--- a/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
+++ b/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
@@ -25,6 +25,7 @@
 import com.android.gallery3d.ui.AlbumSetView.AlbumSetItem;
 import com.android.gallery3d.util.Future;
 import com.android.gallery3d.util.FutureListener;
+import com.android.gallery3d.util.GalleryUtils;
 import com.android.gallery3d.util.MediaSetUtils;
 import com.android.gallery3d.util.ThreadPool;
 
@@ -337,6 +338,7 @@
         private final int mMediaType;
         private Texture mContent;
         private final long mDataVersion;
+        private boolean mIsPanorama;
 
         public GalleryDisplayItem(int slotIndex, int coverIndex, MediaItem item) {
             super(item);
@@ -344,6 +346,7 @@
             mCoverIndex = coverIndex;
             mMediaType = item.getMediaType();
             mDataVersion = item.getDataVersion();
+            mIsPanorama = GalleryUtils.isPanorama(item);
             updateContent(mWaitLoadingTexture);
         }
 
@@ -392,7 +395,7 @@
 
             mSelectionDrawer.draw(canvas, mContent, width, height,
                     getRotation(), path, mCoverIndex, sourceType, mMediaType,
-                    mLabelSpec.darkStripHeight,
+                    mIsPanorama, mLabelSpec.labelBackgroundHeight,
                     cacheFlag == MediaSet.CACHE_FLAG_FULL,
                     (cacheFlag == MediaSet.CACHE_FLAG_FULL)
                     && (cacheStatus != MediaSet.CACHE_STATUS_CACHED_FULL));
@@ -471,14 +474,15 @@
 
     private class LabelDisplayItem extends DisplayItem {
         private static final int FONT_COLOR_TITLE = Color.WHITE;
-        private static final int FONT_COLOR_NUMBER = 0x80FFFFFF;  // 50% white
+        private static final int FONT_COLOR_COUNT = 0x80FFFFFF;  // 50% white
 
         private StringTexture mTextureTitle;
-        private StringTexture mTextureNumber;
+        private StringTexture mTextureCount;
         private String mTitle;
-        private String mNumber;
+        private String mCount;
         private int mLastWidth;
         private final int mSlotIndex;
+        private boolean mHasIcon;
 
         public LabelDisplayItem(int slotIndex) {
             mSlotIndex = slotIndex;
@@ -486,27 +490,29 @@
 
         public boolean updateContent() {
             String title = mLoadingLabel;
-            String number = "";
+            String count = "";
             MediaSet set = mSource.getMediaSet(mSlotIndex);
             if (set != null) {
                 title = Utils.ensureNotNull(set.getName());
-                number = "" + set.getTotalMediaItemCount();
+                count = "" + set.getTotalMediaItemCount();
             }
             if (Utils.equals(title, mTitle)
-                    && Utils.equals(number, mNumber)
+                    && Utils.equals(count, mCount)
                     && Utils.equals(mBoxWidth, mLastWidth)) {
                     return false;
             }
             mTitle = title;
-            mNumber = number;
+            mCount = count;
             mLastWidth = mBoxWidth;
+            mHasIcon = (identifySourceType(set) !=
+                    SelectionDrawer.DATASOURCE_TYPE_NOT_CATEGORIZED);
 
             AlbumSetView.LabelSpec s = mLabelSpec;
             mTextureTitle = StringTexture.newInstance(
                     title, s.titleFontSize, FONT_COLOR_TITLE,
                     mBoxWidth - s.leftMargin, false);
-            mTextureNumber = StringTexture.newInstance(
-                    number, s.numberFontSize, FONT_COLOR_NUMBER,
+            mTextureCount = StringTexture.newInstance(
+                    count, s.countFontSize, FONT_COLOR_COUNT,
                     mBoxWidth - s.leftMargin, true);
 
             return true;
@@ -520,11 +526,12 @@
 
             AlbumSetView.LabelSpec s = mLabelSpec;
             int x = -mBoxWidth / 2;
-            int y = (mBoxHeight + 1) / 2 - s.darkStripHeight;
+            int y = (mBoxHeight + 1) / 2 - s.labelBackgroundHeight;
             y += s.titleOffset;
             mTextureTitle.draw(canvas, x + s.leftMargin, y);
-            y += s.titleFontSize + s.numberOffset;
-            mTextureNumber.draw(canvas, x + s.iconSize, y);
+            y += s.titleFontSize + s.countOffset;
+            x += mHasIcon ? s.iconSize : s.leftMargin;
+            mTextureCount.draw(canvas, x, y);
             return false;
         }
 
diff --git a/src/com/android/gallery3d/ui/AlbumSetView.java b/src/com/android/gallery3d/ui/AlbumSetView.java
index c122fe7..89dfe4a 100644
--- a/src/com/android/gallery3d/ui/AlbumSetView.java
+++ b/src/com/android/gallery3d/ui/AlbumSetView.java
@@ -64,11 +64,11 @@
     }
 
     public static class LabelSpec {
-        public int darkStripHeight;
+        public int labelBackgroundHeight;
         public int titleOffset;
-        public int numberOffset;
+        public int countOffset;
         public int titleFontSize;
-        public int numberFontSize;
+        public int countFontSize;
         public int leftMargin;
         public int iconSize;
     }
diff --git a/src/com/android/gallery3d/ui/AlbumSlidingWindow.java b/src/com/android/gallery3d/ui/AlbumSlidingWindow.java
index 46d9e9e..5184d1c 100644
--- a/src/com/android/gallery3d/ui/AlbumSlidingWindow.java
+++ b/src/com/android/gallery3d/ui/AlbumSlidingWindow.java
@@ -24,6 +24,7 @@
 import com.android.gallery3d.data.Path;
 import com.android.gallery3d.util.Future;
 import com.android.gallery3d.util.FutureListener;
+import com.android.gallery3d.util.GalleryUtils;
 import com.android.gallery3d.util.JobLimiter;
 import com.android.gallery3d.util.ThreadPool.Job;
 import com.android.gallery3d.util.ThreadPool.JobContext;
@@ -283,6 +284,7 @@
         private final int mSlotIndex;
         private final int mMediaType;
         private Texture mContent;
+        private boolean mIsPanorama;
 
         public AlbumDisplayItem(int slotIndex, MediaItem item) {
             super(item);
@@ -290,6 +292,7 @@
                     ? MediaItem.MEDIA_TYPE_UNKNOWN
                     : item.getMediaType();
             mSlotIndex = slotIndex;
+            mIsPanorama = GalleryUtils.isPanorama(item);
             updateContent(mWaitLoadingTexture);
         }
 
@@ -332,7 +335,7 @@
                 Path path = null;
                 if (mMediaItem != null) path = mMediaItem.getPath();
                 mSelectionDrawer.draw(canvas, mContent, width, height,
-                        getRotation(), path, mMediaType);
+                        getRotation(), path, mMediaType, mIsPanorama);
                 return (mFocusIndex == mSlotIndex);
             } else if (pass == 1) {
                 mSelectionDrawer.drawFocus(canvas, width, height);
diff --git a/src/com/android/gallery3d/ui/DialogDetailsView.java b/src/com/android/gallery3d/ui/DialogDetailsView.java
index 07ebc3c..adc9de1 100644
--- a/src/com/android/gallery3d/ui/DialogDetailsView.java
+++ b/src/com/android/gallery3d/ui/DialogDetailsView.java
@@ -146,7 +146,7 @@
                     case MediaDetails.INDEX_LOCATION: {
                         double[] latlng = (double[]) detail.getValue();
                         mLocationIndex = mItems.size();
-                        value = DetailsHelper.resolveAddress(mContext, latlng, mAdapter);
+                        value = DetailsHelper.resolveAddress(mContext, latlng, this);
                         break;
                     }
                     case MediaDetails.INDEX_SIZE: {
diff --git a/src/com/android/gallery3d/ui/FilmStripView.java b/src/com/android/gallery3d/ui/FilmStripView.java
index 6a80564..eaf041e 100644
--- a/src/com/android/gallery3d/ui/FilmStripView.java
+++ b/src/com/android/gallery3d/ui/FilmStripView.java
@@ -20,14 +20,16 @@
 import com.android.gallery3d.anim.AlphaAnimation;
 import com.android.gallery3d.app.AlbumDataAdapter;
 import com.android.gallery3d.app.GalleryActivity;
+import com.android.gallery3d.data.MediaItem;
 import com.android.gallery3d.data.MediaSet;
+import com.android.gallery3d.data.Path;
 
 import android.content.Context;
 import android.view.MotionEvent;
 import android.view.View.MeasureSpec;
 
-public class FilmStripView extends GLView implements SlotView.Listener,
-        ScrollBarView.Listener, UserInteractionListener {
+public class FilmStripView extends GLView implements ScrollBarView.Listener,
+        UserInteractionListener {
     @SuppressWarnings("unused")
     private static final String TAG = "FilmStripView";
 
@@ -79,7 +81,28 @@
         mAlbumView = new AlbumView(activity, spec, thumbSize);
         mAlbumView.setOverscrollEffect(SlotView.OVERSCROLL_SYSTEM);
         mAlbumView.setSelectionDrawer(mStripDrawer);
-        mAlbumView.setListener(this);
+        mAlbumView.setListener(new SlotView.SimpleListener() {
+            @Override
+            public void onDown(int index) {
+                FilmStripView.this.onDown(index);
+            }
+            @Override
+            public void onUp() {
+                FilmStripView.this.onUp();
+            }
+            @Override
+            public void onSingleTapUp(int slotIndex) {
+                FilmStripView.this.onSingleTapUp(slotIndex);
+            }
+            @Override
+            public void onLongTap(int slotIndex) {
+                FilmStripView.this.onLongTap(slotIndex);
+            }
+            @Override
+            public void onScrollPositionChanged(int position, int total) {
+                FilmStripView.this.onScrollPositionChanged(position, total);
+            }
+        });
         mAlbumView.setUserInteractionListener(this);
         mAlbumDataAdapter = new AlbumDataAdapter(activity, mediaSet);
         addComponent(mAlbumView);
@@ -172,20 +195,32 @@
         super.render(canvas);
     }
 
-    // Called by AlbumView
-    @Override
-    public void onSingleTapUp(int slotIndex) {
+    private void onDown(int index) {
+        MediaItem item = mAlbumDataAdapter.get(index);
+        Path path = (item == null) ? null : item.getPath();
+        mStripDrawer.setPressedPath(path);
+        mAlbumView.invalidate();
+    }
+
+    private void onUp() {
+        mStripDrawer.setPressedPath(null);
+        mAlbumView.invalidate();
+    }
+
+    private void onSingleTapUp(int slotIndex) {
         if (mListener.onSlotSelected(slotIndex)) {
             mAlbumView.setFocusIndex(slotIndex);
         }
     }
 
-    // Called by AlbumView
-    @Override
-    public void onLongTap(int slotIndex) {
+    private void onLongTap(int slotIndex) {
         onSingleTapUp(slotIndex);
     }
 
+    private void onScrollPositionChanged(int position, int total) {
+        mScrollBarView.setContentPosition(position, total);
+    }
+
     // Called by AlbumView
     @Override
     public void onUserInteractionBegin() {
@@ -204,12 +239,6 @@
         mUIListener.onUserInteraction();
     }
 
-    // Called by AlbumView
-    @Override
-    public void onScrollPositionChanged(int position, int total) {
-        mScrollBarView.setContentPosition(position, total);
-    }
-
     // Called by ScrollBarView
     @Override
     public void onScrollBarPositionChanged(int position) {
diff --git a/src/com/android/gallery3d/ui/GridDrawer.java b/src/com/android/gallery3d/ui/GridDrawer.java
index cc86184..394a6c7 100644
--- a/src/com/android/gallery3d/ui/GridDrawer.java
+++ b/src/com/android/gallery3d/ui/GridDrawer.java
@@ -47,10 +47,10 @@
     }
 
     @Override
-    public void draw(GLCanvas canvas, Texture content, int width, int height,
-            int rotation, Path path, int topIndex, int dataSourceType,
-            int mediaType, int darkStripHeight, boolean wantCache,
-            boolean isCaching) {
+    public void draw(GLCanvas canvas, Texture content, int width,
+            int height, int rotation, Path path, int topIndex,
+            int dataSourceType, int mediaType, boolean isPanorama,
+            int labelBackgroundHeight, boolean wantCache, boolean isCaching) {
 
         int x = -width / 2;
         int y = -height / 2;
@@ -66,19 +66,22 @@
             y = -height / 2;
         }
 
-        drawVideoOverlay(canvas, mediaType, x, y, width, height, topIndex);
-
-        if (mSelectionMode && mSelectionManager.isItemSelected(path)) {
-            drawFrame(canvas, mFrameSelected, x, y, width, height);
-        }
+        drawMediaTypeOverlay(canvas, mediaType, isPanorama, x, y, width, height,
+                topIndex);
 
         if (topIndex == 0) {
-            drawDarkStrip(canvas, width, height, darkStripHeight);
+            drawLabelBackground(canvas, width, height, labelBackgroundHeight);
             drawIcon(canvas, width, height, dataSourceType);
             if (dataSourceType == DATASOURCE_TYPE_MTP) {
                 drawImportLabel(canvas, width, height);
             }
         }
+
+        if (mSelectionManager.isPressedPath(path)) {
+            drawPressedFrame(canvas, x, y, width, height);
+        } else if (mSelectionMode && mSelectionManager.isItemSelected(path)) {
+            drawFrame(canvas, mFrameSelected, x, y, width, height);
+        }
     }
 
     // Draws the "click to import" label at the center of the frame
diff --git a/src/com/android/gallery3d/ui/HighlightDrawer.java b/src/com/android/gallery3d/ui/HighlightDrawer.java
index 52c31f7..ee37d26 100644
--- a/src/com/android/gallery3d/ui/HighlightDrawer.java
+++ b/src/com/android/gallery3d/ui/HighlightDrawer.java
@@ -25,19 +25,21 @@
     private SelectionManager mSelectionManager;
     private Path mHighlightItem;
 
-    public HighlightDrawer(Context context) {
+    public HighlightDrawer(Context context, SelectionManager selectionManager) {
         super(context);
         mFrameSelected = new NinePatchTexture(context, R.drawable.grid_selected);
+        mSelectionManager = selectionManager;
     }
 
     public void setHighlightItem(Path item) {
         mHighlightItem = item;
     }
 
-    public void draw(GLCanvas canvas, Texture content, int width, int height,
-            int rotation, Path path, int topIndex, int dataSourceType,
-            int mediaType, int darkStripHeight, boolean wantCache,
-            boolean isCaching) {
+    @Override
+    public void draw(GLCanvas canvas, Texture content, int width,
+            int height, int rotation, Path path, int topIndex,
+            int dataSourceType, int mediaType, boolean isPanorama,
+            int labelBackgroundHeight, boolean wantCache, boolean isCaching) {
         int x = -width / 2;
         int y = -height / 2;
 
@@ -52,15 +54,18 @@
             y = -height / 2;
         }
 
-        drawVideoOverlay(canvas, mediaType, x, y, width, height, topIndex);
-
-        if (path == mHighlightItem) {
-            drawFrame(canvas, mFrameSelected, x, y, width, height);
-        }
+        drawMediaTypeOverlay(canvas, mediaType, isPanorama, x, y, width, height,
+                topIndex);
 
         if (topIndex == 0) {
-            drawDarkStrip(canvas, width, height, darkStripHeight);
+            drawLabelBackground(canvas, width, height, labelBackgroundHeight);
             drawIcon(canvas, width, height, dataSourceType);
         }
+
+        if (mSelectionManager.isPressedPath(path)) {
+            drawPressedFrame(canvas, x, y, width, height);
+        } else  if (path == mHighlightItem) {
+            drawFrame(canvas, mFrameSelected, x, y, width, height);
+        }
     }
 }
diff --git a/src/com/android/gallery3d/ui/IconDrawer.java b/src/com/android/gallery3d/ui/IconDrawer.java
index 86fdf68..781046c 100644
--- a/src/com/android/gallery3d/ui/IconDrawer.java
+++ b/src/com/android/gallery3d/ui/IconDrawer.java
@@ -22,12 +22,14 @@
 
 public abstract class IconDrawer extends SelectionDrawer {
     private static final String TAG = "IconDrawer";
-    private static final int DARK_STRIP_COLOR = 0x99000000;  // 60% black
+    private static final int LABEL_BACKGROUND_COLOR = 0x99000000;  // 60% black
 
     private final ResourceTexture mLocalSetIcon;
     private final ResourceTexture mCameraIcon;
     private final ResourceTexture mPicasaIcon;
     private final ResourceTexture mMtpIcon;
+    private final NinePatchTexture mFramePressed;
+    private final ResourceTexture mPanoramaBorder;
     private final Texture mVideoOverlay;
     private final Texture mVideoPlayIcon;
     private final int mIconSize;
@@ -43,9 +45,11 @@
         mLocalSetIcon = new ResourceTexture(context, R.drawable.frame_overlay_gallery_folder);
         mCameraIcon = new ResourceTexture(context, R.drawable.frame_overlay_gallery_camera);
         mPicasaIcon = new ResourceTexture(context, R.drawable.frame_overlay_gallery_picasa);
-        mMtpIcon = new ResourceTexture(context, R.drawable.ic_album_overlay_ptp_holo);
+        mMtpIcon = new ResourceTexture(context, R.drawable.frame_overlay_gallery_ptp);
         mVideoOverlay = new ResourceTexture(context, R.drawable.ic_video_thumb);
         mVideoPlayIcon = new ResourceTexture(context, R.drawable.ic_gallery_play);
+        mPanoramaBorder = new ResourceTexture(context, R.drawable.ic_pan_thumb);
+        mFramePressed = new NinePatchTexture(context, R.drawable.grid_pressed);
         mIconSize = context.getResources().getDimensionPixelSize(
                 R.dimen.albumset_icon_size);
     }
@@ -95,14 +99,23 @@
         id.width = Math.round(scale * icon.getWidth());
         id.height = Math.round(scale * icon.getHeight());
         id.x = -width / 2;
-        id.y = height / 2 - id.height;
+        id.y = (height + 1) / 2 - id.height;
         return id;
     }
 
-    protected void drawVideoOverlay(GLCanvas canvas, int mediaType,
-            int x, int y, int width, int height, int topIndex) {
-        if (mediaType != MediaObject.MEDIA_TYPE_VIDEO) return;
+    protected void drawMediaTypeOverlay(GLCanvas canvas, int mediaType,
+            boolean isPanorama, int x, int y, int width, int height,
+            int topIndex) {
+        if (mediaType == MediaObject.MEDIA_TYPE_VIDEO) {
+            drawVideoOverlay(canvas, x, y, width, height, topIndex);
+        }
+        if (isPanorama) {
+            drawPanoramaBorder(canvas, x, y, width, height);
+        }
+    }
 
+    protected void drawVideoOverlay(GLCanvas canvas, int x, int y,
+            int width, int height, int topIndex) {
         // Scale the video overlay to the height of the thumbnail and put it
         // on the left side.
         float scale = (float) height / mVideoOverlay.getHeight();
@@ -116,11 +129,27 @@
         }
     }
 
-    protected void drawDarkStrip(GLCanvas canvas, int width, int height,
-            int darkStripHeight) {
+    protected void drawPanoramaBorder(GLCanvas canvas, int x, int y,
+            int width, int height) {
+        float scale = (float) width / mPanoramaBorder.getWidth();
+        int w = Math.round(scale * mPanoramaBorder.getWidth());
+        int h = Math.round(scale * mPanoramaBorder.getHeight());
+        // draw at the top
+        mPanoramaBorder.draw(canvas, x, y, w, h);
+        // draw at the bottom
+        mPanoramaBorder.draw(canvas, x, y + width - h, w, h);
+    }
+
+    protected void drawLabelBackground(GLCanvas canvas, int width, int height,
+            int drawLabelBackground) {
         int x = -width / 2;
-        int y = (height + 1) / 2 - darkStripHeight;
-        canvas.fillRect(x, y, width, darkStripHeight, DARK_STRIP_COLOR);
+        int y = (height + 1) / 2 - drawLabelBackground;
+        canvas.fillRect(x, y, width, drawLabelBackground, LABEL_BACKGROUND_COLOR);
+    }
+
+    protected void drawPressedFrame(GLCanvas canvas, int x, int y, int width,
+            int height) {
+        drawFrame(canvas, mFramePressed, x, y, width, height);
     }
 
     @Override
diff --git a/src/com/android/gallery3d/ui/ManageCacheDrawer.java b/src/com/android/gallery3d/ui/ManageCacheDrawer.java
index e25e779..ed84a18 100644
--- a/src/com/android/gallery3d/ui/ManageCacheDrawer.java
+++ b/src/com/android/gallery3d/ui/ManageCacheDrawer.java
@@ -23,14 +23,13 @@
 import android.content.Context;
 
 public class ManageCacheDrawer extends IconDrawer {
-    private static final int COLOR_CACHING_BACKGROUND = 0x7F000000;
     private static final int ICON_SIZE = 36;
     private final ResourceTexture mCheckedItem;
     private final ResourceTexture mUnCheckedItem;
     private final SelectionManager mSelectionManager;
 
     private final ResourceTexture mLocalAlbumIcon;
-    private final StringTexture mCaching;
+    private final StringTexture mCachingText;
 
     public ManageCacheDrawer(Context context, SelectionManager selectionManager) {
         super(context);
@@ -38,7 +37,7 @@
         mUnCheckedItem = new ResourceTexture(context, R.drawable.btn_make_offline_normal_off_holo_dark);
         mLocalAlbumIcon = new ResourceTexture(context, R.drawable.btn_make_offline_disabled_on_holo_dark);
         String cachingLabel = context.getString(R.string.caching_label);
-        mCaching = StringTexture.newInstance(cachingLabel, 12, 0xffffffff);
+        mCachingText = StringTexture.newInstance(cachingLabel, 12, 0xffffffff);
         mSelectionManager = selectionManager;
     }
 
@@ -51,13 +50,11 @@
     }
 
     @Override
-    public void draw(GLCanvas canvas, Texture content, int width, int height,
-            int rotation, Path path, int topIndex, int dataSourceType,
-            int mediaType, int darkStripHeight, boolean wantCache,
-            boolean isCaching) {
+    public void draw(GLCanvas canvas, Texture content, int width,
+            int height, int rotation, Path path, int topIndex,
+            int dataSourceType, int mediaType, boolean isPanorama,
+            int labelBackgroundHeight, boolean wantCache, boolean isCaching) {
 
-        boolean selected = mSelectionManager.isItemSelected(path);
-        boolean chooseToCache = wantCache ^ selected;
 
         int x = -width / 2;
         int y = -height / 2;
@@ -73,48 +70,53 @@
             y = -height / 2;
         }
 
-        drawVideoOverlay(canvas, mediaType, x, y, width, height, topIndex);
+        drawMediaTypeOverlay(canvas, mediaType, isPanorama, x, y, width, height,
+                topIndex);
 
         if (topIndex == 0) {
-            drawDarkStrip(canvas, width, height, darkStripHeight);
+            drawLabelBackground(canvas, width, height, labelBackgroundHeight);
             drawIcon(canvas, width, height, dataSourceType);
         }
 
         if (topIndex == 0) {
-            ResourceTexture icon = null;
-            if (isLocal(dataSourceType)) {
-                icon = mLocalAlbumIcon;
-            } else if (chooseToCache) {
-                icon = mCheckedItem;
-            } else {
-                icon = mUnCheckedItem;
-            }
+            drawCachingIcon(canvas, path, dataSourceType, isCaching, wantCache,
+                    width, height);
+        }
 
-            int w = ICON_SIZE;
-            int h = ICON_SIZE;
-            x = width / 2 - w;
-            y = -height / 2;
+        if (mSelectionManager.isPressedPath(path)) {
+            drawPressedFrame(canvas, x, y, width, height);
+        }
+    }
 
-            icon.draw(canvas, x, y, w, h);
+    private void drawCachingIcon(GLCanvas canvas, Path path, int dataSourceType,
+            boolean isCaching, boolean wantCache, int width, int height) {
+        boolean selected = mSelectionManager.isItemSelected(path);
+        boolean chooseToCache = wantCache ^ selected;
 
-            if (isCaching) {
-                int textWidth = mCaching.getWidth();
-                int textHeight = mCaching.getHeight();
-                x = -textWidth / 2;
-                y = height / 2 - textHeight;
+        ResourceTexture icon = null;
+        if (isLocal(dataSourceType)) {
+            icon = mLocalAlbumIcon;
+        } else if (chooseToCache) {
+            icon = mCheckedItem;
+        } else {
+            icon = mUnCheckedItem;
+        }
 
-                // Leave a few pixels of margin in the background rect.
-                float sideMargin = Utils.clamp(textWidth * 0.1f, 2.0f,
-                        6.0f);
-                float clearance = Utils.clamp(textHeight * 0.1f, 2.0f,
-                        6.0f);
+        int w = ICON_SIZE;
+        int h = ICON_SIZE;
+        int right = (width + 1) / 2;
+        int bottom = (height + 1) / 2;
+        int x = right - w;
+        int y = bottom - h;
 
-                // Overlay the "Caching" wording at the bottom-center of the content.
-                canvas.fillRect(x - sideMargin, y - clearance,
-                        textWidth + sideMargin * 2, textHeight + clearance,
-                        COLOR_CACHING_BACKGROUND);
-                mCaching.draw(canvas, x, y);
-            }
+        icon.draw(canvas, x, y, w, h);
+
+        if (isCaching) {
+            int textWidth = mCachingText.getWidth();
+            int textHeight = mCachingText.getHeight();
+            x = right - ICON_SIZE - textWidth;
+            y = bottom - textHeight;
+            mCachingText.draw(canvas, x, y);
         }
     }
 
diff --git a/src/com/android/gallery3d/ui/SelectionDrawer.java b/src/com/android/gallery3d/ui/SelectionDrawer.java
index 05f346c..70d8ad5 100644
--- a/src/com/android/gallery3d/ui/SelectionDrawer.java
+++ b/src/com/android/gallery3d/ui/SelectionDrawer.java
@@ -34,14 +34,14 @@
     public abstract void prepareDrawing();
     public abstract void draw(GLCanvas canvas, Texture content,
             int width, int height, int rotation, Path path,
-            int topIndex, int dataSourceType, int mediaType,
-            int darkStripHeight, boolean wantCache, boolean isCaching);
+            int topIndex, int dataSourceType, int mediaType, boolean isPanorama,
+            int labelBackgroundHeight, boolean wantCache, boolean isCaching);
     public abstract void drawFocus(GLCanvas canvas, int width, int height);
 
     public void draw(GLCanvas canvas, Texture content, int width, int height,
-            int rotation, Path path, int mediaType) {
+            int rotation, Path path, int mediaType, boolean isPanorama) {
         draw(canvas, content, width, height, rotation, path, 0,
-                DATASOURCE_TYPE_NOT_CATEGORIZED, mediaType,
+                DATASOURCE_TYPE_NOT_CATEGORIZED, mediaType, isPanorama,
                 0, false, false);
     }
 
diff --git a/src/com/android/gallery3d/ui/SelectionManager.java b/src/com/android/gallery3d/ui/SelectionManager.java
index 9599f5b..0ab69d2 100644
--- a/src/com/android/gallery3d/ui/SelectionManager.java
+++ b/src/com/android/gallery3d/ui/SelectionManager.java
@@ -47,6 +47,7 @@
     private boolean mInSelectionMode;
     private boolean mAutoLeave = true;
     private int mTotal;
+    private Path mPressedPath;
 
     public interface SelectionListener {
         public void onSelectionModeChange(int mode);
@@ -141,6 +142,14 @@
         }
     }
 
+    public void setPressedPath(Path path) {
+        mPressedPath = path;
+    }
+
+    public boolean isPressedPath(Path path) {
+        return path != null && path == mPressedPath;
+    }
+
     private static void expandMediaSet(ArrayList<Path> items, MediaSet set) {
         int subCount = set.getSubMediaSetCount();
         for (int i = 0; i < subCount; i++) {
diff --git a/src/com/android/gallery3d/ui/SlotView.java b/src/com/android/gallery3d/ui/SlotView.java
index 11dbfaa..3eb3f17 100644
--- a/src/com/android/gallery3d/ui/SlotView.java
+++ b/src/com/android/gallery3d/ui/SlotView.java
@@ -39,12 +39,16 @@
     private static final int INDEX_NONE = -1;
 
     public interface Listener {
+        public void onDown(int index);
+        public void onUp();
         public void onSingleTapUp(int index);
         public void onLongTap(int index);
         public void onScrollPositionChanged(int position, int total);
     }
 
     public static class SimpleListener implements Listener {
+        public void onDown(int index) {}
+        public void onUp() {}
         public void onSingleTapUp(int index) {}
         public void onLongTap(int index) {}
         public void onScrollPositionChanged(int position, int total) {}
@@ -597,12 +601,37 @@
         }
     }
 
-    private class MyGestureListener
-            extends GestureDetector.SimpleOnGestureListener {
+    private class MyGestureListener implements
+            GestureDetector.OnGestureListener {
+        private boolean isDown;
+
+        // We call the listener's onDown() when our onShowPress() is called and
+        // call the listener's onUp() when we receive any further event.
+        @Override
+        public void onShowPress(MotionEvent e) {
+            if (isDown) return;
+            int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY());
+            if (index != INDEX_NONE) {
+                isDown = true;
+                mListener.onDown(index);
+            }
+        }
+
+        private void cancelDown() {
+            if (!isDown) return;
+            isDown = false;
+            mListener.onUp();
+        }
+
+        @Override
+        public boolean onDown(MotionEvent e) {
+            return false;
+        }
 
         @Override
         public boolean onFling(MotionEvent e1,
                 MotionEvent e2, float velocityX, float velocityY) {
+            cancelDown();
             int scrollLimit = mLayout.getScrollLimit();
             if (scrollLimit == 0) return false;
             float velocity = WIDE ? velocityX : velocityY;
@@ -615,6 +644,7 @@
         @Override
         public boolean onScroll(MotionEvent e1,
                 MotionEvent e2, float distanceX, float distanceY) {
+            cancelDown();
             float distance = WIDE ? distanceX : distanceY;
             boolean canMove = mScroller.startScroll(
                     Math.round(distance), 0, mLayout.getScrollLimit());
@@ -627,6 +657,7 @@
 
         @Override
         public boolean onSingleTapUp(MotionEvent e) {
+            cancelDown();
             if (mDownInScrolling) return true;
             int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY());
             if (index != INDEX_NONE) mListener.onSingleTapUp(index);
@@ -635,6 +666,7 @@
 
         @Override
         public void onLongPress(MotionEvent e) {
+            cancelDown();
             if (mDownInScrolling) return;
             lockRendering();
             try {
diff --git a/src/com/android/gallery3d/ui/StripDrawer.java b/src/com/android/gallery3d/ui/StripDrawer.java
index be98ae0..5120a0c 100644
--- a/src/com/android/gallery3d/ui/StripDrawer.java
+++ b/src/com/android/gallery3d/ui/StripDrawer.java
@@ -23,28 +23,43 @@
 import android.graphics.Rect;
 
 public class StripDrawer extends SelectionDrawer {
+    private NinePatchTexture mFramePressed;
     private NinePatchTexture mFocusBox;
     private Rect mFocusBoxPadding;
+    private Path mPressedPath;
 
     public StripDrawer(Context context) {
+        mFramePressed = new NinePatchTexture(context, R.drawable.grid_pressed);
         mFocusBox = new NinePatchTexture(context, R.drawable.focus_box);
         mFocusBoxPadding = mFocusBox.getPaddings();
     }
 
+    public void setPressedPath(Path path) {
+        mPressedPath = path;
+    }
+
+    private boolean isPressedPath(Path path) {
+        return path != null && path == mPressedPath;
+    }
+
     @Override
     public void prepareDrawing() {
     }
 
     @Override
-    public void draw(GLCanvas canvas, Texture content, int width, int height,
-            int rotation, Path path, int topIndex, int dataSourceType,
-            int mediaType, int darkStripHeight, boolean wantCache,
-            boolean isCaching) {
+    public void draw(GLCanvas canvas, Texture content,
+            int width, int height, int rotation, Path path, int topIndex,
+            int dataSourceType, int mediaType, boolean isPanorama,
+            int labelBackgroundHeight, boolean wantCache, boolean isCaching) {
 
         int x = -width / 2;
         int y = -height / 2;
 
         drawWithRotation(canvas, content, x, y, width, height, rotation);
+
+        if (isPressedPath(path)) {
+            drawFrame(canvas, mFramePressed, x, y, width, height);
+        }
     }
 
     @Override
diff --git a/src/com/android/gallery3d/util/GalleryUtils.java b/src/com/android/gallery3d/util/GalleryUtils.java
index 9c08dea..6803611 100644
--- a/src/com/android/gallery3d/util/GalleryUtils.java
+++ b/src/com/android/gallery3d/util/GalleryUtils.java
@@ -352,4 +352,11 @@
             output[0] = number;
         }
     }
+
+    public static boolean isPanorama(MediaItem item) {
+        if (item == null) return false;
+        int w = item.getWidth();
+        int h = item.getHeight();
+        return (h > 0 && w / h >= 2);
+    }
 }
diff --git a/tests/src/com/android/gallery3d/data/MockItem.java b/tests/src/com/android/gallery3d/data/MockItem.java
index bd6dcd9..2901979 100644
--- a/tests/src/com/android/gallery3d/data/MockItem.java
+++ b/tests/src/com/android/gallery3d/data/MockItem.java
@@ -40,4 +40,14 @@
     public String getMimeType() {
         return null;
     }
+
+    @Override
+    public int getWidth() {
+        return 0;
+    }
+
+    @Override
+    public int getHeight() {
+        return 0;
+    }
 }