Merge "Fix 6519765 Ghosting is seen when hiding the actionbar in fullscreen video playback mode b:6519765" into jb-dev
diff --git a/res/layout/cropimage.xml b/res/layout/cropimage.xml
index aefebe8..c434fb6 100644
--- a/res/layout/cropimage.xml
+++ b/res/layout/cropimage.xml
@@ -17,9 +17,5 @@
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
-    <view class="com.android.gallery3d.ui.GLRootView"
-            android:id="@+id/gl_root_view"
-            android:background="@null"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent" />
+    <include layout="@layout/gl_root_group"/>
 </FrameLayout>
diff --git a/res/layout/dialog_picker.xml b/res/layout/dialog_picker.xml
index ba3f500..4a625a1 100644
--- a/res/layout/dialog_picker.xml
+++ b/res/layout/dialog_picker.xml
@@ -18,8 +18,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
-    <com.android.gallery3d.ui.GLRootView
-            android:id="@+id/gl_root_view"
+    <include layout="@layout/gl_root_group"
             android:layout_weight="1"
             android:layout_height="0dp"
             android:layout_width="match_parent"/>
diff --git a/res/layout/gl_root_group.xml b/res/layout/gl_root_group.xml
new file mode 100644
index 0000000..76ff33b
--- /dev/null
+++ b/res/layout/gl_root_group.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <com.android.gallery3d.ui.GLRootView
+            android:id="@+id/gl_root_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+    <View android:id="@+id/gl_root_cover"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:background="@android:color/black"/>
+</merge>
diff --git a/res/layout/main.xml b/res/layout/main.xml
index 7dfe57a..d367301 100644
--- a/res/layout/main.xml
+++ b/res/layout/main.xml
@@ -4,14 +4,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
-    <com.android.gallery3d.ui.GLRootView
-            android:id="@+id/gl_root_view"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_alignParentBottom="true"
-            android:layout_alignParentLeft="true"
-            android:layout_alignParentRight="true"
-            android:layout_alignParentTop="true"/>
+    <include layout="@layout/gl_root_group"/>
     <FrameLayout android:id="@+id/footer"
             android:visibility="gone"
             android:layout_alignParentBottom="true"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1772f08..529480c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -211,8 +211,6 @@
 
     <!-- This toast message is shown when failed to load the album data. [CHAR LIMIT=NONE] -->
     <string name="sync_album_error">Couldn\'t download the photos in this album. Retry later.</string>
-    <!-- This toast message is shown when failed to load the album list data. [CHAR LIMIT=NONE] -->
-    <string name="sync_album_set_error">Couldn\'t download the list of albums. Retry later.</string>
 
     <!-- The title of the menu item to let user choose the which portion of
          the media items the user wants to see. When pressed, a submenu will
diff --git a/src/com/android/gallery3d/app/AlbumPage.java b/src/com/android/gallery3d/app/AlbumPage.java
index b5ad2a5..e773691 100644
--- a/src/com/android/gallery3d/app/AlbumPage.java
+++ b/src/com/android/gallery3d/app/AlbumPage.java
@@ -176,9 +176,11 @@
                 if (!more) {
                     mResumeEffect = null;
                     mAlbumView.setSlotFilter(null);
-                } else {
-                    invalidate();
                 }
+                // We want to render one more time even when no more effect
+                // required. So that the animated thumbnails could be draw
+                // with declarations in super.render().
+                invalidate();
             }
             canvas.restore();
         }
@@ -650,7 +652,9 @@
                         mInitialSynced = true;
                     }
                     clearLoadingBit(BIT_LOADING_SYNC);
-                    if (resultCode == MediaSet.SYNC_RESULT_ERROR && mIsActive) {
+                    if (resultCode == MediaSet.SYNC_RESULT_ERROR && mIsActive
+                            && (mAlbumDataAdapter.size() == 0)) {
+                        // show error toast only if the album is empty
                         Toast.makeText((Context) mActivity, R.string.sync_album_error,
                                 Toast.LENGTH_LONG).show();
                     }
diff --git a/src/com/android/gallery3d/app/AlbumSetDataLoader.java b/src/com/android/gallery3d/app/AlbumSetDataLoader.java
index 819adcc..39d4a8b 100644
--- a/src/com/android/gallery3d/app/AlbumSetDataLoader.java
+++ b/src/com/android/gallery3d/app/AlbumSetDataLoader.java
@@ -329,7 +329,7 @@
             while (mActive) {
                 synchronized (this) {
                     if (mActive && !mDirty && updateComplete) {
-                        updateLoading(false);
+                        if (!mSource.isLoading()) updateLoading(false);
                         Utils.waitWithoutInterrupt(this);
                         continue;
                     }
diff --git a/src/com/android/gallery3d/app/AlbumSetPage.java b/src/com/android/gallery3d/app/AlbumSetPage.java
index c6e3596..aa72eb8 100644
--- a/src/com/android/gallery3d/app/AlbumSetPage.java
+++ b/src/com/android/gallery3d/app/AlbumSetPage.java
@@ -300,9 +300,9 @@
             // Only show toast when there's no album and we are going to finish
             // the page. Toast is redundant if we are going to stay on this page.
             if ((mAlbumSetDataAdapter.size() == 0)) {
-                Toast.makeText((Context) mActivity,
-                        R.string.empty_album, Toast.LENGTH_LONG).show();
                 if (mActivity.getStateManager().getStateCount() > 1) {
+                    Toast.makeText((Context) mActivity,
+                            R.string.empty_album, Toast.LENGTH_LONG).show();
                     mActivity.getStateManager().finishState(this);
                 }
             }
@@ -545,7 +545,7 @@
                 break;
             }
             case SelectionManager.SELECT_ALL_MODE: {
-                mActionModeHandler.setTitle(getSelectedString());
+                mActionModeHandler.updateSupportedOperation();
                 mRootPane.invalidate();
                 break;
             }
@@ -597,8 +597,7 @@
                     }
                     clearLoadingBit(BIT_LOADING_SYNC);
                     if (resultCode == MediaSet.SYNC_RESULT_ERROR && mIsActive) {
-                        Toast.makeText((Context) mActivity, R.string.sync_album_set_error,
-                                Toast.LENGTH_LONG).show();
+                        Log.w(TAG, "failed to load album set");
                     }
                 } finally {
                     root.unlockRenderThread();
diff --git a/src/com/android/gallery3d/app/Gallery.java b/src/com/android/gallery3d/app/Gallery.java
index dd30d7e..bdd4a0b 100644
--- a/src/com/android/gallery3d/app/Gallery.java
+++ b/src/com/android/gallery3d/app/Gallery.java
@@ -198,6 +198,9 @@
                     data.putString(PhotoPage.KEY_MEDIA_SET_PATH, albumPath.toString());
                 }
                 data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, itemPath.toString());
+                if (intent.getBooleanExtra(PhotoPage.KEY_TREAT_BACK_AS_UP, false)) {
+                    data.putBoolean(PhotoPage.KEY_TREAT_BACK_AS_UP, true);
+                }
 
                 // Displays the filename as title, reading the filename from the interface:
                 // {@link android.provider.OpenableColumns#DISPLAY_NAME}.
diff --git a/src/com/android/gallery3d/app/MovieActivity.java b/src/com/android/gallery3d/app/MovieActivity.java
index 8dd69e4..b0e53c4 100644
--- a/src/com/android/gallery3d/app/MovieActivity.java
+++ b/src/com/android/gallery3d/app/MovieActivity.java
@@ -96,6 +96,10 @@
         winParams.buttonBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF;
         winParams.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
         win.setAttributes(winParams);
+
+        // We set the background in the theme to have the launching animation.
+        // But for the performance (and battery), we remove the background here.
+        win.setBackgroundDrawable(null);
     }
 
     private void initializeActionBar(Intent intent) {
diff --git a/src/com/android/gallery3d/app/PhotoDataAdapter.java b/src/com/android/gallery3d/app/PhotoDataAdapter.java
index 6ba1ff6..6a4961e 100644
--- a/src/com/android/gallery3d/app/PhotoDataAdapter.java
+++ b/src/com/android/gallery3d/app/PhotoDataAdapter.java
@@ -23,6 +23,7 @@
 
 import com.android.gallery3d.common.BitmapUtils;
 import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.BitmapPool;
 import com.android.gallery3d.data.ContentListener;
 import com.android.gallery3d.data.DataManager;
 import com.android.gallery3d.data.LocalMediaItem;
@@ -415,7 +416,7 @@
 
         // Create a default ScreenNail if the real one is not available yet.
         if (entry.screenNail == null) {
-            entry.screenNail = newDefaultScreenNail(item);
+            entry.screenNail = newPlaceholderScreenNail(item);
             if (offset == 0) updateTileProvider(entry);
         }
 
@@ -490,8 +491,8 @@
     }
 
     public Bitmap getTile(int level, int x, int y, int tileSize,
-            int borderSize) {
-        return mTileProvider.getTile(level, x, y, tileSize, borderSize);
+            int borderSize, BitmapPool pool) {
+        return mTileProvider.getTile(level, x, y, tileSize, borderSize, pool);
     }
 
     public boolean isEmpty() {
@@ -632,7 +633,7 @@
             // If this is a temporary item, don't try to get its bitmap because
             // it won't be available. We will get its bitmap after a data reload.
             if (isTemporaryItem(mItem)) {
-                return newDefaultScreenNail(mItem);
+                return newPlaceholderScreenNail(mItem);
             }
 
             Bitmap bitmap = mItem.requestImage(MediaItem.TYPE_THUMBNAIL).run(jc);
@@ -687,7 +688,7 @@
     // Create a default ScreenNail when a ScreenNail is needed, but we don't yet
     // have one available (because the image data is still being saved, or the
     // Bitmap is still being loaded.
-    private ScreenNail newDefaultScreenNail(MediaItem item) {
+    private ScreenNail newPlaceholderScreenNail(MediaItem item) {
         int width = item.getWidth();
         int height = item.getHeight();
         return new BitmapScreenNail(width, height);
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
index ea3badc..36ccc67 100644
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -75,6 +75,7 @@
     private static final int MSG_ON_FULL_SCREEN_CHANGED = 4;
     private static final int MSG_UPDATE_ACTION_BAR = 5;
     private static final int MSG_UNFREEZE_GLROOT = 6;
+    private static final int MSG_WANT_BARS = 7;
 
     private static final int HIDE_BARS_TIMEOUT = 3500;
     private static final int UNFREEZE_GLROOT_TIMEOUT = 250;
@@ -90,6 +91,7 @@
     public static final String KEY_INDEX_HINT = "index-hint";
     public static final String KEY_OPEN_ANIMATION_RECT = "open-animation-rect";
     public static final String KEY_APP_BRIDGE = "app-bridge";
+    public static final String KEY_TREAT_BACK_AS_UP = "treat-back-as-up";
 
     public static final String KEY_RETURN_INDEX_HINT = "return-index-hint";
 
@@ -110,8 +112,6 @@
     private int mCurrentIndex = 0;
     private Handler mHandler;
     private boolean mShowBars = true;
-    // The value of canShowBars() last time the bar updates state.
-    private boolean mCanShowBars = false;
     private volatile boolean mActionBarAllowed = true;
     private GalleryActionBar mActionBar;
     private MyMenuVisibilityListener mMenuVisibilityListener;
@@ -128,6 +128,7 @@
     private SnailAlbum mScreenNailSet;
     private OrientationManager mOrientationManager;
     private boolean mHasActivityResult;
+    private boolean mTreatBackAsUp;
 
     private NfcAdapter mNfcAdapter;
 
@@ -181,6 +182,7 @@
         mOriginalSetPathString = mSetPathString;
         mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity.getAndroidContext());
         Path itemPath = Path.fromString(data.getString(KEY_MEDIA_ITEM_PATH));
+        mTreatBackAsUp = data.getBoolean(KEY_TREAT_BACK_AS_UP, false);
 
         if (mSetPathString != null) {
             mAppBridge = (AppBridge) data.getParcelable(KEY_APP_BRIDGE);
@@ -212,6 +214,7 @@
             }
 
             mMediaSet = mActivity.getDataManager().getMediaSet(mSetPathString);
+            mSelectionManager.setSourceMediaSet(mMediaSet);
             mCurrentIndex = data.getInt(KEY_INDEX_HINT, 0);
             if (mMediaSet == null) {
                 Log.w(TAG, "failed to restore " + mSetPathString);
@@ -268,12 +271,10 @@
                     }
                     case MSG_LOCK_ORIENTATION: {
                         mOrientationManager.lockOrientation();
-                        updateBars();
                         break;
                     }
                     case MSG_UNLOCK_ORIENTATION: {
                         mOrientationManager.unlockOrientation();
-                        updateBars();
                         break;
                     }
                     case MSG_ON_FULL_SCREEN_CHANGED: {
@@ -284,6 +285,10 @@
                         updateBars();
                         break;
                     }
+                    case MSG_WANT_BARS: {
+                        wantBars();
+                        break;
+                    }
                     case MSG_UNFREEZE_GLROOT: {
                         mActivity.getGLRoot().unfreeze();
                         break;
@@ -377,6 +382,7 @@
     private void showBars() {
         if (mShowBars) return;
         mShowBars = true;
+        mOrientationManager.unlockOrientation();
         mActionBar.show();
         mActivity.getGLRoot().setLightsOutMode(false);
         refreshHidingMessage();
@@ -402,29 +408,24 @@
         if (mAppBridge != null && mCurrentIndex == 0) return false;
         // No bars if it's not allowed.
         if (!mActionBarAllowed) return false;
-        // No bars if the orientation is locked.
-        if (mOrientationManager.isOrientationLocked()) return false;
 
         return true;
     }
 
+    private void wantBars() {
+        if (canShowBars()) showBars();
+    }
+
     private void toggleBars() {
-        mCanShowBars = canShowBars();
         if (mShowBars) {
             hideBars();
         } else {
-            if (mCanShowBars) showBars();
+            if (canShowBars()) showBars();
         }
     }
 
     private void updateBars() {
-        boolean v = canShowBars();
-        if (mCanShowBars == v) return;
-        mCanShowBars = v;
-
-        if (mCanShowBars) {
-            showBars();
-        } else {
+        if (!canShowBars()) {
             hideBars();
         }
     }
@@ -441,7 +442,11 @@
         } else if (mAppBridge == null || !switchWithCaptureAnimation(-1)) {
             // We are leaving this page. Set the result now.
             setResult();
-            super.onBackPressed();
+            if (mTreatBackAsUp) {
+                onUpPressed();
+            } else {
+                super.onBackPressed();
+            }
         }
     }
 
@@ -686,6 +691,11 @@
     }
 
     @Override
+    public void onActionBarWanted() {
+        mHandler.sendEmptyMessage(MSG_WANT_BARS);
+    }
+
+    @Override
     public void onFullScreenChanged(boolean full) {
         Message m = mHandler.obtainMessage(
                 MSG_ON_FULL_SCREEN_CHANGED, full ? 1 : 0, 0);
diff --git a/src/com/android/gallery3d/data/ComboAlbumSet.java b/src/com/android/gallery3d/data/ComboAlbumSet.java
index 916b163..6d9a2a3 100644
--- a/src/com/android/gallery3d/data/ComboAlbumSet.java
+++ b/src/com/android/gallery3d/data/ComboAlbumSet.java
@@ -65,6 +65,14 @@
     }
 
     @Override
+    public boolean isLoading() {
+        for (int i = 0, n = mSets.length; i < n; ++i) {
+            if (mSets[i].isLoading()) return true;
+        }
+        return false;
+    }
+
+    @Override
     public long reload() {
         boolean changed = false;
         for (int i = 0, n = mSets.length; i < n; ++i) {
diff --git a/src/com/android/gallery3d/data/LocalAlbumSet.java b/src/com/android/gallery3d/data/LocalAlbumSet.java
index dbb5189..07741ef 100644
--- a/src/com/android/gallery3d/data/LocalAlbumSet.java
+++ b/src/com/android/gallery3d/data/LocalAlbumSet.java
@@ -100,6 +100,7 @@
     private final ChangeNotifier mNotifierVideo;
     private final String mName;
     private final Handler mHandler;
+    private boolean mIsLoading;
 
     private Future<ArrayList<MediaSet>> mLoadTask;
     private ArrayList<MediaSet> mLoadBuffer;
@@ -261,6 +262,11 @@
     }
 
     @Override
+    public synchronized boolean isLoading() {
+        return mIsLoading;
+    }
+
+    @Override
     // synchronized on this function for
     //   1. Prevent calling reload() concurrently.
     //   2. Prevent calling onFutureDone() and reload() concurrently
@@ -268,6 +274,7 @@
         // "|" is used instead of "||" because we want to clear both flags.
         if (mNotifierImage.isDirty() | mNotifierVideo.isDirty()) {
             if (mLoadTask != null) mLoadTask.cancel();
+            mIsLoading = true;
             mLoadTask = mApplication.getThreadPool().submit(new AlbumsLoader(), this);
         }
         if (mLoadBuffer != null) {
@@ -285,6 +292,7 @@
     public synchronized void onFutureDone(Future<ArrayList<MediaSet>> future) {
         if (mLoadTask != future) return; // ignore, wait for the latest task
         mLoadBuffer = future.get();
+        mIsLoading = false;
         if (mLoadBuffer == null) mLoadBuffer = new ArrayList<MediaSet>();
         mHandler.post(new Runnable() {
             @Override
diff --git a/src/com/android/gallery3d/data/MediaSet.java b/src/com/android/gallery3d/data/MediaSet.java
index ff9b8c3..67525b1 100644
--- a/src/com/android/gallery3d/data/MediaSet.java
+++ b/src/com/android/gallery3d/data/MediaSet.java
@@ -94,6 +94,14 @@
         return false;
     }
 
+    /**
+     * Method {@link #reload()} may process the loading task in background, this method tells
+     * its client whether the loading is still in process or not.
+     */
+    public boolean isLoading() {
+        return false;
+    }
+
     public int getTotalMediaItemCount() {
         int total = getMediaItemCount();
         for (int i = 0, n = getSubMediaSetCount(); i < n; i++) {
diff --git a/src/com/android/gallery3d/data/MtpDeviceSet.java b/src/com/android/gallery3d/data/MtpDeviceSet.java
index 6dcb0d2..1f26511 100644
--- a/src/com/android/gallery3d/data/MtpDeviceSet.java
+++ b/src/com/android/gallery3d/data/MtpDeviceSet.java
@@ -47,6 +47,7 @@
     private Future<ArrayList<MediaSet>> mLoadTask;
     private ArrayList<MediaSet> mDeviceSet = new ArrayList<MediaSet>();
     private ArrayList<MediaSet> mLoadBuffer;
+    private boolean mIsLoading;
 
     public MtpDeviceSet(Path path, GalleryApp application, MtpContext mtpContext) {
         super(path, nextVersionNumber());
@@ -113,9 +114,15 @@
     }
 
     @Override
+    public synchronized boolean isLoading() {
+        return mIsLoading;
+    }
+
+    @Override
     public synchronized long reload() {
         if (mNotifier.isDirty()) {
             if (mLoadTask != null) mLoadTask.cancel();
+            mIsLoading = true;
             mLoadTask = mApplication.getThreadPool().submit(new DevicesLoader(), this);
         }
         if (mLoadBuffer != null) {
@@ -133,6 +140,7 @@
     public synchronized void onFutureDone(Future<ArrayList<MediaSet>> future) {
         if (future != mLoadTask) return;
         mLoadBuffer = future.get();
+        mIsLoading = false;
         if (mLoadBuffer == null) mLoadBuffer = new ArrayList<MediaSet>();
 
         mHandler.post(new Runnable() {
diff --git a/src/com/android/gallery3d/gadget/WidgetClickHandler.java b/src/com/android/gallery3d/gadget/WidgetClickHandler.java
index 36575e4..35f9bec 100644
--- a/src/com/android/gallery3d/gadget/WidgetClickHandler.java
+++ b/src/com/android/gallery3d/gadget/WidgetClickHandler.java
@@ -26,6 +26,7 @@
 
 import com.android.gallery3d.R;
 import com.android.gallery3d.app.Gallery;
+import com.android.gallery3d.app.PhotoPage;
 
 public class WidgetClickHandler extends Activity {
     private static final String TAG = "PhotoAppWidgetClickHandler";
@@ -46,14 +47,19 @@
     @Override
     protected void onCreate(Bundle savedState) {
         super.onCreate(savedState);
-        Intent intent = getIntent();
-        if (isValidDataUri(intent.getData())) {
-            startActivity(new Intent(Intent.ACTION_VIEW, intent.getData()));
+        Uri uri = getIntent().getData();
+        Intent intent;
+        if (isValidDataUri(uri)) {
+            intent = new Intent(Intent.ACTION_VIEW, uri);
+            intent.putExtra(PhotoPage.KEY_TREAT_BACK_AS_UP, true);
         } else {
             Toast.makeText(this,
                     R.string.no_such_item, Toast.LENGTH_LONG).show();
-            startActivity(new Intent(this, Gallery.class));
+            intent = new Intent(this, Gallery.class);
         }
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
+                Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+        startActivity(intent);
         finish();
     }
 }
diff --git a/src/com/android/gallery3d/ui/ActionModeHandler.java b/src/com/android/gallery3d/ui/ActionModeHandler.java
index c5773ed..746d41d 100644
--- a/src/com/android/gallery3d/ui/ActionModeHandler.java
+++ b/src/com/android/gallery3d/ui/ActionModeHandler.java
@@ -283,6 +283,8 @@
             mMenuTask.cancel();
         }
 
+        updateSelectionMenu();
+
         // Disable share action until share intent is in good shape
         final MenuItem item = mShareActionProvider != null ?
                 mMenu.findItem(R.id.action_share) : null;
diff --git a/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java b/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
index cefb9cd..6561a23 100644
--- a/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
+++ b/src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
@@ -70,7 +70,8 @@
         public MediaSet album;
         public MediaItem coverItem;
         public Texture content;
-        public Texture label;
+        public BitmapTexture labelTexture;
+        public BitmapTexture bitmapTexture;
         public Path setPath;
         public String title;
         public int totalCount;
@@ -226,6 +227,8 @@
         AlbumSetEntry entry = mData[slotIndex % mData.length];
         if (entry.coverLoader != null) entry.coverLoader.recycle();
         if (entry.labelLoader != null) entry.labelLoader.recycle();
+        if (entry.labelTexture != null) entry.labelTexture.recycle();
+        if (entry.bitmapTexture != null) entry.bitmapTexture.recycle();
         mData[slotIndex % mData.length] = null;
     }
 
@@ -256,7 +259,7 @@
             if (entry.labelLoader != null) {
                 entry.labelLoader.recycle();
                 entry.labelLoader = null;
-                entry.label = null;
+                entry.labelTexture = null;
             }
             if (album != null) {
                 entry.labelLoader = new AlbumLabelLoader(
@@ -273,6 +276,7 @@
             if (entry.coverLoader != null) {
                 entry.coverLoader.recycle();
                 entry.coverLoader = null;
+                entry.bitmapTexture = null;
                 entry.content = null;
             }
             if (cover != null) {
@@ -296,11 +300,11 @@
     private void uploadBackgroundTextureInSlot(int index) {
         if (index < mContentStart || index >= mContentEnd) return;
         AlbumSetEntry entry = mData[index % mData.length];
-        if (entry.content instanceof BitmapTexture) {
-            mTextureUploader.addBgTexture((BitmapTexture) entry.content);
+        if (entry.bitmapTexture != null) {
+            mTextureUploader.addBgTexture(entry.bitmapTexture);
         }
-        if (entry.label instanceof BitmapTexture) {
-            mTextureUploader.addBgTexture((BitmapTexture) entry.label);
+        if (entry.labelTexture != null) {
+            mTextureUploader.addBgTexture(entry.labelTexture);
         }
     }
 
@@ -311,11 +315,11 @@
         // Upload foreground texture
         for (int i = mActiveStart, n = mActiveEnd; i < n; ++i) {
             AlbumSetEntry entry = mData[i % mData.length];
-            if (entry.content instanceof BitmapTexture) {
-                mTextureUploader.addFgTexture((BitmapTexture) entry.content);
+            if (entry.bitmapTexture != null) {
+                mTextureUploader.addFgTexture(entry.bitmapTexture);
             }
-            if (entry.label instanceof BitmapTexture) {
-                mTextureUploader.addFgTexture((BitmapTexture) entry.label);
+            if (entry.labelTexture != null) {
+                mTextureUploader.addFgTexture(entry.labelTexture);
             }
         }
 
@@ -440,6 +444,7 @@
 
             AlbumSetEntry entry = mData[mSlotIndex % mData.length];
             BitmapTexture texture = new BitmapTexture(bitmap);
+            entry.bitmapTexture = texture;
             entry.content = texture;
 
             if (isActiveSlot(mSlotIndex)) {
@@ -509,7 +514,7 @@
             AlbumSetEntry entry = mData[mSlotIndex % mData.length];
             BitmapTexture texture = new BitmapTexture(bitmap);
             texture.setOpaque(false);
-            entry.label = texture;
+            entry.labelTexture = texture;
 
             if (isActiveSlot(mSlotIndex)) {
                 mTextureUploader.addFgTexture(texture);
@@ -536,7 +541,7 @@
             if (entry.labelLoader != null) {
                 entry.labelLoader.recycle();
                 entry.labelLoader = null;
-                entry.label = null;
+                entry.labelTexture = null;
             }
             if (entry.album != null) {
                 entry.labelLoader = new AlbumLabelLoader(i,
diff --git a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
index b0e6153..7b4a866 100644
--- a/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
+++ b/src/com/android/gallery3d/ui/AlbumSetSlotRenderer.java
@@ -99,9 +99,9 @@
         }
     }
 
-    private static Texture checkTexture(GLCanvas canvas, Texture texture) {
-        return ((texture == null) || ((texture instanceof UploadedTexture)
-                && !((UploadedTexture) texture).isContentValid(canvas)))
+    private static Texture checkTexture(Texture texture) {
+        return ((texture instanceof UploadedTexture)
+                && ((UploadedTexture) texture).isUploading())
                 ? null
                 : texture;
     }
@@ -142,15 +142,14 @@
             GLCanvas canvas, AlbumSetEntry entry, int width, int height) {
         int renderRequestFlags = 0;
 
-        Texture content = checkTexture(canvas, entry.content);
+        Texture content = checkTexture(entry.content);
         if (content == null) {
             content = mWaitLoadingTexture;
             entry.isWaitLoadingDisplayed = true;
         } else if (entry.isWaitLoadingDisplayed) {
             entry.isWaitLoadingDisplayed = false;
-            entry.content = new FadeInTexture(
-                    PLACEHOLDER_COLOR, (BitmapTexture) entry.content);
-            content = entry.content;
+            content = new FadeInTexture(PLACEHOLDER_COLOR, entry.bitmapTexture);
+            entry.content = content;
         }
         drawContent(canvas, content, width, height, entry.rotation);
         if ((content instanceof FadeInTexture) &&
@@ -173,7 +172,7 @@
             GLCanvas canvas, AlbumSetEntry entry, int width, int height) {
         // We show the loading message only when the album is still loading
         // (Not when we are still preparing the label)
-        Texture content = checkTexture(canvas, entry.label);
+        Texture content = checkTexture(entry.labelTexture);
         if (entry.album == null) {
             content = mDataWindow.getLoadingTexture();
         }
diff --git a/src/com/android/gallery3d/ui/AlbumSlidingWindow.java b/src/com/android/gallery3d/ui/AlbumSlidingWindow.java
index 240cf34..deec171 100644
--- a/src/com/android/gallery3d/ui/AlbumSlidingWindow.java
+++ b/src/com/android/gallery3d/ui/AlbumSlidingWindow.java
@@ -48,6 +48,7 @@
         public int rotation;
         public int mediaType;
         public boolean isWaitDisplayed;
+        public BitmapTexture bitmapTexture;
         public Texture content;
         private BitmapLoader contentLoader;
     }
@@ -164,8 +165,8 @@
     private void uploadBgTextureInSlot(int index) {
         if (index < mContentEnd && index >= mContentStart) {
             AlbumEntry entry = mData[index % mData.length];
-            if (entry.content instanceof BitmapTexture) {
-                mTextureUploader.addBgTexture((BitmapTexture) entry.content);
+            if (entry.bitmapTexture != null) {
+                mTextureUploader.addBgTexture(entry.bitmapTexture);
             }
         }
     }
@@ -177,8 +178,8 @@
         // add foreground textures
         for (int i = mActiveStart, n = mActiveEnd; i < n; ++i) {
             AlbumEntry entry = mData[i % mData.length];
-            if (entry.content instanceof BitmapTexture) {
-                mTextureUploader.addFgTexture((BitmapTexture) entry.content);
+            if (entry.bitmapTexture != null) {
+                mTextureUploader.addFgTexture(entry.bitmapTexture);
             }
         }
 
@@ -234,9 +235,8 @@
         AlbumEntry data[] = mData;
         int index = slotIndex % data.length;
         AlbumEntry entry = data[index];
-        if (entry.contentLoader != null) {
-            entry.contentLoader.recycle();
-        }
+        if (entry.contentLoader != null) entry.contentLoader.recycle();
+        if (entry.bitmapTexture != null) entry.bitmapTexture.recycle();
         data[index] = null;
     }
 
@@ -296,15 +296,16 @@
             if (bitmap == null) return; // error or recycled
 
             AlbumEntry entry = mData[mSlotIndex % mData.length];
-            entry.content = new BitmapTexture(bitmap);
+            entry.bitmapTexture = new BitmapTexture(bitmap);
+            entry.content = entry.bitmapTexture;
 
             if (isActiveSlot(mSlotIndex)) {
-                mTextureUploader.addFgTexture((BitmapTexture) entry.content);
+                mTextureUploader.addFgTexture(entry.bitmapTexture);
                 --mActiveRequestCount;
                 if (mActiveRequestCount == 0) requestNonactiveImages();
                 if (mListener != null) mListener.onContentChanged();
             } else {
-                mTextureUploader.addBgTexture((BitmapTexture) entry.content);
+                mTextureUploader.addBgTexture(entry.bitmapTexture);
             }
         }
     }
diff --git a/src/com/android/gallery3d/ui/AlbumSlotRenderer.java b/src/com/android/gallery3d/ui/AlbumSlotRenderer.java
index 5f1e3a1..ab99766 100644
--- a/src/com/android/gallery3d/ui/AlbumSlotRenderer.java
+++ b/src/com/android/gallery3d/ui/AlbumSlotRenderer.java
@@ -89,9 +89,9 @@
         }
     }
 
-    private static Texture checkTexture(GLCanvas canvas, Texture texture) {
-        return ((texture == null) || ((texture instanceof UploadedTexture)
-                && !((UploadedTexture) texture).isContentValid(canvas)))
+    private static Texture checkTexture(Texture texture) {
+        return (texture instanceof UploadedTexture)
+                && ((UploadedTexture) texture).isUploading()
                 ? null
                 : texture;
     }
@@ -104,15 +104,14 @@
 
         int renderRequestFlags = 0;
 
-        Texture content = checkTexture(canvas, entry.content);
+        Texture content = checkTexture(entry.content);
         if (content == null) {
             content = mWaitLoadingTexture;
             entry.isWaitDisplayed = true;
         } else if (entry.isWaitDisplayed) {
             entry.isWaitDisplayed = false;
-            entry.content = new FadeInTexture(
-                    PLACEHOLDER_COLOR, (BitmapTexture) entry.content);
-            content = entry.content;
+            content = new FadeInTexture(PLACEHOLDER_COLOR, entry.bitmapTexture);
+            entry.content = content;
         }
         drawContent(canvas, content, width, height, entry.rotation);
         if ((content instanceof FadeInTexture) &&
diff --git a/src/com/android/gallery3d/ui/BasicTexture.java b/src/com/android/gallery3d/ui/BasicTexture.java
index 6a9a17d..7855927 100644
--- a/src/com/android/gallery3d/ui/BasicTexture.java
+++ b/src/com/android/gallery3d/ui/BasicTexture.java
@@ -33,6 +33,8 @@
     protected static final int STATE_LOADED = 1;
     protected static final int STATE_ERROR = -1;
 
+    private static final int MAX_TEXTURE_SIZE = 2048;
+
     protected int mId;
     protected int mState;
 
@@ -75,6 +77,10 @@
         mHeight = height;
         mTextureWidth = Utils.nextPowerOf2(width);
         mTextureHeight = Utils.nextPowerOf2(height);
+        if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) {
+            Log.w(TAG, String.format("texture is too large: %d x %d",
+                    mTextureWidth, mTextureHeight), new Exception());
+        }
     }
 
     public int getId() {
@@ -133,7 +139,7 @@
     // Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D).
     abstract protected int getTarget();
 
-    public boolean isLoaded(GLCanvas canvas) {
+    public boolean isLoaded() {
         return mState == STATE_LOADED;
     }
 
@@ -154,10 +160,10 @@
 
     private void freeResource() {
         GLCanvas canvas = mCanvasRef;
-        if (canvas != null && isLoaded(canvas)) {
+        if (canvas != null && isLoaded()) {
             canvas.unloadTexture(this);
         }
-        mState = BasicTexture.STATE_UNLOADED;
+        mState = STATE_UNLOADED;
         setAssociatedCanvas(null);
     }
 
diff --git a/src/com/android/gallery3d/ui/BitmapScreenNail.java b/src/com/android/gallery3d/ui/BitmapScreenNail.java
index 14d3f19..5c91757 100644
--- a/src/com/android/gallery3d/ui/BitmapScreenNail.java
+++ b/src/com/android/gallery3d/ui/BitmapScreenNail.java
@@ -18,7 +18,6 @@
 
 import android.graphics.Bitmap;
 import android.graphics.RectF;
-import android.util.Log;
 
 import com.android.gallery3d.common.Utils;
 import com.android.gallery3d.data.MediaItem;
@@ -35,6 +34,9 @@
     private static final int PLACEHOLDER_COLOR = 0xFF222222;
     // The duration of the fading animation in milliseconds
     private static final int DURATION = 180;
+
+    private static final int MAX_SIDE = 640;
+
     // These are special values for mAnimationStartTime
     private static final long ANIMATION_NOT_NEEDED = -1;
     private static final long ANIMATION_NEEDED = -2;
@@ -44,7 +46,6 @@
     private int mHeight;
     private Bitmap mBitmap;
     private BitmapTexture mTexture;
-    private FadeInTexture mFadeInTexture;
     private long mAnimationStartTime = ANIMATION_NOT_NEEDED;
 
     public BitmapScreenNail(Bitmap bitmap) {
@@ -56,12 +57,17 @@
     }
 
     public BitmapScreenNail(int width, int height) {
+        setSize(width, height);
+    }
+
+    private void setSize(int width, int height) {
         if (width == 0 || height == 0) {
             width = 640;
             height = 480;
         }
-        mWidth = width;
-        mHeight = height;
+        float scale = Math.min(1, (float) MAX_SIDE / Math.max(width, height));
+        mWidth = Math.round(scale * width);
+        mHeight = Math.round(scale * height);
     }
 
     // Combines the two ScreenNails.
@@ -101,8 +107,7 @@
     public void updatePlaceholderSize(int width, int height) {
         if (mBitmap != null) return;
         if (width == 0 || height == 0) return;
-        mWidth = width;
-        mHeight = height;
+        setSize(width, height);
     }
 
     @Override
@@ -189,4 +194,8 @@
         float r = (float)(now() - mAnimationStartTime) / DURATION;
         return Utils.clamp(1.0f - r, 0.0f, 1.0f);
     }
+
+    public boolean isShowingPlaceholder() {
+        return (mBitmap == null) || isAnimating();
+    }
 }
diff --git a/src/com/android/gallery3d/ui/BitmapTileProvider.java b/src/com/android/gallery3d/ui/BitmapTileProvider.java
index 320118e..d4c9b1d 100644
--- a/src/com/android/gallery3d/ui/BitmapTileProvider.java
+++ b/src/com/android/gallery3d/ui/BitmapTileProvider.java
@@ -21,6 +21,7 @@
 import android.graphics.Canvas;
 
 import com.android.gallery3d.common.BitmapUtils;
+import com.android.gallery3d.data.BitmapPool;
 
 import java.util.ArrayList;
 
@@ -49,44 +50,46 @@
         mConfig = Config.ARGB_8888;
     }
 
+    @Override
     public ScreenNail getScreenNail() {
         return mScreenNail;
     }
 
+    @Override
     public int getImageHeight() {
         return mImageHeight;
     }
 
+    @Override
     public int getImageWidth() {
         return mImageWidth;
     }
 
+    @Override
     public int getLevelCount() {
         return mMipmaps.length;
     }
 
+    @Override
     public Bitmap getTile(int level, int x, int y, int tileSize,
-            int borderSize) {
+            int borderSize, BitmapPool pool) {
         x >>= level;
         y >>= level;
         int size = tileSize + 2 * borderSize;
-        Bitmap result = Bitmap.createBitmap(size, size, mConfig);
+
+        Bitmap result = pool == null ? null : pool.getBitmap();
+        if (result == null) {
+            result = Bitmap.createBitmap(size, size, mConfig);
+        } else {
+            result.eraseColor(0);
+        }
+
         Bitmap mipmap = mMipmaps[level];
         Canvas canvas = new Canvas(result);
         int offsetX = -x + borderSize;
         int offsetY = -y + borderSize;
         canvas.drawBitmap(mipmap, offsetX, offsetY, null);
-
-        // If the valid region (covered by mipmap or border) is smaller than the
-        // result bitmap, subset it.
-        int endX = offsetX + mipmap.getWidth() + borderSize;
-        int endY = offsetY + mipmap.getHeight() + borderSize;
-        if (endX < size || endY < size) {
-            return Bitmap.createBitmap(result, 0, 0, Math.min(size, endX),
-                    Math.min(size, endY));
-        } else {
-            return result;
-        }
+        return result;
     }
 
     public void recycle() {
diff --git a/src/com/android/gallery3d/ui/ExtTexture.java b/src/com/android/gallery3d/ui/ExtTexture.java
index d8267d1..9319d8c 100644
--- a/src/com/android/gallery3d/ui/ExtTexture.java
+++ b/src/com/android/gallery3d/ui/ExtTexture.java
@@ -60,12 +60,12 @@
                 GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
 
         setAssociatedCanvas(canvas);
-        mState = UploadedTexture.STATE_LOADED;
+        mState = STATE_LOADED;
     }
 
     @Override
     protected boolean onBind(GLCanvas canvas) {
-        if (!isLoaded(canvas)) {
+        if (!isLoaded()) {
             uploadToCanvas(canvas);
         }
 
diff --git a/src/com/android/gallery3d/ui/FadeOutTexture.java b/src/com/android/gallery3d/ui/FadeOutTexture.java
index c438977..47e6acb 100644
--- a/src/com/android/gallery3d/ui/FadeOutTexture.java
+++ b/src/com/android/gallery3d/ui/FadeOutTexture.java
@@ -18,7 +18,7 @@
 
 // FadeOutTexture is a texture which begins with a given texture, then gradually animates
 // into fading out totally.
-public class FadeOutTexture extends FadeTexture implements Texture {
+public class FadeOutTexture extends FadeTexture {
     @SuppressWarnings("unused")
     private static final String TAG = "FadeOutTexture";
 
diff --git a/src/com/android/gallery3d/ui/FadeTexture.java b/src/com/android/gallery3d/ui/FadeTexture.java
index ad0d358..cbf5073 100644
--- a/src/com/android/gallery3d/ui/FadeTexture.java
+++ b/src/com/android/gallery3d/ui/FadeTexture.java
@@ -42,23 +42,22 @@
         mIsAnimating = true;
     }
 
+    @Override
     public void draw(GLCanvas canvas, int x, int y) {
         draw(canvas, x, y, mWidth, mHeight);
     }
 
-    /**
-     * Subclasses should implement how to fade the texture.
-     */
-    public abstract void draw(GLCanvas canvas, int x, int y, int w, int h);
-
+    @Override
     public boolean isOpaque() {
         return mIsOpaque;
     }
 
+    @Override
     public int getWidth() {
         return mWidth;
     }
 
+    @Override
     public int getHeight() {
         return mHeight;
     }
diff --git a/src/com/android/gallery3d/ui/GLCanvasImpl.java b/src/com/android/gallery3d/ui/GLCanvasImpl.java
index 1efe5af..ff3e9e5 100644
--- a/src/com/android/gallery3d/ui/GLCanvasImpl.java
+++ b/src/com/android/gallery3d/ui/GLCanvasImpl.java
@@ -661,7 +661,7 @@
     // so we synchronized on the mUnboundTextures object.
     public boolean unloadTexture(BasicTexture t) {
         synchronized (mUnboundTextures) {
-            if (!t.isLoaded(this)) return false;
+            if (!t.isLoaded()) return false;
             mUnboundTextures.add(t.mId);
             return true;
         }
@@ -786,7 +786,7 @@
         } else {
             setSize(texture.getWidth(), texture.getHeight());
 
-            if (!texture.isLoaded(this)) texture.prepare(this);
+            if (!texture.isLoaded()) texture.prepare(this);
 
             gl11ep.glFramebufferTexture2DOES(
                     GL11ExtensionPack.GL_FRAMEBUFFER_OES,
diff --git a/src/com/android/gallery3d/ui/GLRootView.java b/src/com/android/gallery3d/ui/GLRootView.java
index 987e9a2..99ed8cb 100644
--- a/src/com/android/gallery3d/ui/GLRootView.java
+++ b/src/com/android/gallery3d/ui/GLRootView.java
@@ -25,7 +25,9 @@
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.SurfaceHolder;
+import android.view.View;
 
+import com.android.gallery3d.R;
 import com.android.gallery3d.anim.CanvasAnimation;
 import com.android.gallery3d.common.Utils;
 import com.android.gallery3d.util.GalleryUtils;
@@ -104,6 +106,7 @@
 
     private long mLastDrawFinishTime;
     private boolean mInDownState = false;
+    private boolean mFirstDraw = true;
 
     public GLRootView(Context context) {
         this(context, null);
@@ -118,7 +121,7 @@
         getHolder().setFormat(PixelFormat.RGB_565);
 
         // Uncomment this to enable gl error check.
-        //setDebugFlags(DEBUG_CHECK_GL_ERROR);
+        // setDebugFlags(DEBUG_CHECK_GL_ERROR);
     }
 
     @Override
@@ -252,8 +255,15 @@
             // The GL Object has changed
             Log.i(TAG, "GLObject has changed from " + mGL + " to " + gl);
         }
-        mGL = gl;
-        mCanvas = new GLCanvasImpl(gl);
+        mRenderLock.lock();
+        try {
+            mGL = gl;
+            mCanvas = new GLCanvasImpl(gl);
+            BasicTexture.invalidateAllTextures();
+        } finally {
+            mRenderLock.unlock();
+        }
+
         if (DEBUG_FPS || DEBUG_PROFILE) {
             setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
         } else {
@@ -272,7 +282,6 @@
                 + ", gl10: " + gl1.toString());
         Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
         GalleryUtils.setRenderThread();
-        BasicTexture.invalidateAllTextures();
         if (DEBUG_PROFILE) {
             Log.d(TAG, "Start profiling");
             Profile.enable(20);  // take a sample every 20ms
@@ -316,6 +325,20 @@
             mRenderLock.unlock();
         }
 
+        // We put a black cover View in front of the SurfaceView and hide it
+        // after the first draw. This prevents the SurfaceView being transparent
+        // before the first draw.
+        if (mFirstDraw) {
+            mFirstDraw = false;
+            post(new Runnable() {
+                    public void run() {
+                        View root = getRootView();
+                        View cover = root.findViewById(R.id.gl_root_cover);
+                        cover.setVisibility(GONE);
+                    }
+                });
+        }
+
         if (DEBUG_PROFILE_SLOW_ONLY) {
             long t = System.nanoTime();
             long durationInMs = (t - mLastDrawFinishTime) / 1000000;
@@ -514,7 +537,11 @@
 
     @Override
     public void setLightsOutMode(boolean enabled) {
-        int flags = enabled ? SYSTEM_UI_FLAG_LOW_PROFILE : 0;
+        int flags = enabled
+                ? SYSTEM_UI_FLAG_LOW_PROFILE
+                | SYSTEM_UI_FLAG_FULLSCREEN
+                | SYSTEM_UI_FLAG_LAYOUT_STABLE
+                : 0;
         setSystemUiVisibility(flags);
     }
 
diff --git a/src/com/android/gallery3d/ui/NinePatchTexture.java b/src/com/android/gallery3d/ui/NinePatchTexture.java
index 957229e..fa0e9cd 100644
--- a/src/com/android/gallery3d/ui/NinePatchTexture.java
+++ b/src/com/android/gallery3d/ui/NinePatchTexture.java
@@ -157,7 +157,7 @@
 
     @Override
     public void draw(GLCanvas canvas, int x, int y, int w, int h) {
-        if (!isLoaded(canvas)) {
+        if (!isLoaded()) {
             mInstanceCache.clear();
         }
 
diff --git a/src/com/android/gallery3d/ui/PhotoFallbackEffect.java b/src/com/android/gallery3d/ui/PhotoFallbackEffect.java
index cd930bd..3ca09ab 100644
--- a/src/com/android/gallery3d/ui/PhotoFallbackEffect.java
+++ b/src/com/android/gallery3d/ui/PhotoFallbackEffect.java
@@ -87,7 +87,7 @@
     }
 
     private void drawEntry(GLCanvas canvas, Entry entry) {
-        if (!entry.texture.isLoaded(canvas)) return;
+        if (!entry.texture.isLoaded()) return;
 
         int w = entry.texture.getWidth();
         int h = entry.texture.getHeight();
diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java
index fd08e0e..4490569 100644
--- a/src/com/android/gallery3d/ui/PhotoView.java
+++ b/src/com/android/gallery3d/ui/PhotoView.java
@@ -90,33 +90,25 @@
         public void unlockOrientation();
         public void onFullScreenChanged(boolean full);
         public void onActionBarAllowed(boolean allowed);
+        public void onActionBarWanted();
         public void onCurrentImageUpdated();
     }
 
-    // Here is a graph showing the places we need to lock/unlock device
-    // orientation:
+    // The rules about orientation locking:
     //
-    //           +------------+ A  +------------+
-    // Page mode |   Camera   |<---|   Photo    |
-    //           |  [locked]  |--->| [unlocked] |
-    //           +------------+  B +------------+
-    //                ^                  ^
-    //                | C                | D
-    //           +------------+    +------------+
-    //           |   Camera   |    |   Photo    |
-    // Film mode |    [*]     |    |    [*]     |
-    //           +------------+    +------------+
+    // (1) We need to lock the orientation if we are in page mode camera
+    // preview, so there is no (unwanted) rotation animation when the user
+    // rotates the device.
     //
-    // In Page mode, we want to lock in Camera because we don't want the system
-    // rotation animation. We also want to unlock in Photo because we want to
-    // show the system action bar in the right place.
+    // (2) We need to unlock the orientation if we want to show the action bar
+    // because the action bar follows the system orientation.
     //
-    // We don't show action bar in Film mode, so it's fine for it to be locked
-    // or unlocked in Film mode.
+    // The rules about action bar:
     //
-    // There are four transitions we need to check if we need to
-    // lock/unlock. Marked as A to D above and in the code.
-
+    // (1) If we are in film mode, we don't show action bar.
+    //
+    // (2) If we go from camera to gallery with capture animation, we show
+    // action bar.
     private static final int MSG_CANCEL_EXTRA_SCALING = 2;
     private static final int MSG_SWITCH_FOCUS = 3;
     private static final int MSG_CAPTURE_ANIMATION_DONE = 4;
@@ -457,14 +449,9 @@
                 setFilmMode(false);
             }
 
-            if (isCenter && !mFilmMode) {
-                if (mIsCamera) {
-                    // move into camera, lock
-                    mListener.lockOrientation();  // Transition A
-                } else {
-                    // move out of camera, unlock
-                    mListener.unlockOrientation();  // Transition B
-                }
+            if (isCenter && !mFilmMode && mIsCamera) {
+                // Move into camera in page mode, lock
+                mListener.lockOrientation();
             }
 
             mWasCameraCenter = isCameraCenter;
@@ -931,13 +918,9 @@
         mModel.setNeedFullImage(!enabled);
         mListener.onActionBarAllowed(!enabled);
 
-        // If we leave filmstrip mode, we should lock/unlock
-        if (!enabled) {
-            if (mPictures.get(0).isCamera()) {
-                mListener.lockOrientation();  // Transition C
-            } else {
-                mListener.unlockOrientation();  // Transition D
-            }
+        // Move into camera in page mode, lock
+        if (!enabled && mPictures.get(0).isCamera()) {
+            mListener.lockOrientation();
         }
     }
 
@@ -1209,12 +1192,10 @@
 
     private void captureAnimationDone(int offset) {
         mHolding &= ~HOLD_CAPTURE_ANIMATION;
-        if (offset == 1) {
-            // move out of camera, unlock
-            if (!mFilmMode) {
-                // Now the capture animation is done, enable the action bar.
-                mListener.onActionBarAllowed(true);
-            }
+        if (offset == 1 && !mFilmMode) {
+            // Now the capture animation is done, enable the action bar.
+            mListener.onActionBarAllowed(true);
+            mListener.onActionBarWanted();
         }
         snapback();
     }
@@ -1323,7 +1304,6 @@
         return mPositionController.getPosition(index);
     }
 
-
     public PhotoFallbackEffect buildFallbackEffect(GLView root, GLCanvas canvas) {
         Rect location = new Rect();
         Utils.assertTrue(root.getBoundsOf(this, location));
@@ -1334,7 +1314,10 @@
             MediaItem item = mModel.getMediaItem(i);
             if (item == null) continue;
             ScreenNail sc = mModel.getScreenNail(i);
-            if (sc == null) continue;
+            if (!(sc instanceof BitmapScreenNail)
+                    || ((BitmapScreenNail) sc).isShowingPlaceholder()) continue;
+
+            // Now, sc is BitmapScreenNail and is not showing placeholder
             Rect rect = new Rect(getPhotoRect(i));
             if (!Rect.intersects(fullRect, rect)) continue;
             rect.offset(location.left, location.top);
diff --git a/src/com/android/gallery3d/ui/PositionController.java b/src/com/android/gallery3d/ui/PositionController.java
index bb2b83e..d085c4d 100644
--- a/src/com/android/gallery3d/ui/PositionController.java
+++ b/src/com/android/gallery3d/ui/PositionController.java
@@ -209,8 +209,13 @@
         }
 
         updateScaleAndGapLimit();
-        startOpeningAnimationIfNeeded();
-        snapAndRedraw();
+
+        // If we have the opening animation, do it. Otherwise go directly to the
+        // right position.
+        if (!startOpeningAnimationIfNeeded()) {
+            snapAndRedraw();
+            skipAnimation();
+        }
     }
 
     public void setConstrainedFrame(Rect f) {
@@ -280,10 +285,10 @@
         return true;
     }
 
-    private void startOpeningAnimationIfNeeded() {
-        if (mOpenAnimationRect == null) return;
+    private boolean startOpeningAnimationIfNeeded() {
+        if (mOpenAnimationRect == null) return false;
         Box b = mBoxes.get(0);
-        if (b.mUseViewSize) return;
+        if (b.mUseViewSize) return false;
 
         // Start animation from the saved rectangle if we have one.
         Rect r = mOpenAnimationRect;
@@ -303,6 +308,8 @@
             g.mCurrentGap = mViewW;
             g.doAnimation(g.mDefaultSize, ANIM_KIND_OPENING);
         }
+
+        return true;
     }
 
     public void setFilmMode(boolean enabled) {
diff --git a/src/com/android/gallery3d/ui/RawTexture.java b/src/com/android/gallery3d/ui/RawTexture.java
index e1af7d2..4c0d9d3 100644
--- a/src/com/android/gallery3d/ui/RawTexture.java
+++ b/src/com/android/gallery3d/ui/RawTexture.java
@@ -68,13 +68,13 @@
                 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, null);
 
         mId = sTextureId[0];
-        mState = UploadedTexture.STATE_LOADED;
+        mState = STATE_LOADED;
         setAssociatedCanvas(canvas);
     }
 
     @Override
     protected boolean onBind(GLCanvas canvas) {
-        if (isLoaded(canvas)) return true;
+        if (isLoaded()) return true;
         Log.w(TAG, "lost the content due to context change");
         return false;
     }
diff --git a/src/com/android/gallery3d/ui/SelectionManager.java b/src/com/android/gallery3d/ui/SelectionManager.java
index 1783b11..86e92da 100644
--- a/src/com/android/gallery3d/ui/SelectionManager.java
+++ b/src/com/android/gallery3d/ui/SelectionManager.java
@@ -107,15 +107,21 @@
         return mInverseSelection ^ mClickedSet.contains(itemId);
     }
 
+    private int getTotalCount() {
+        if (mSourceMediaSet == null) return -1;
+
+        if (mTotal < 0) {
+            mTotal = mIsAlbumSet
+                    ? mSourceMediaSet.getSubMediaSetCount()
+                    : mSourceMediaSet.getMediaItemCount();
+        }
+        return mTotal;
+    }
+
     public int getSelectedCount() {
         int count = mClickedSet.size();
         if (mInverseSelection) {
-            if (mTotal < 0) {
-                mTotal = mIsAlbumSet
-                        ? mSourceMediaSet.getSubMediaSetCount()
-                        : mSourceMediaSet.getMediaItemCount();
-            }
-            count = mTotal - count;
+            count = getTotalCount() - count;
         }
         return count;
     }
@@ -128,8 +134,14 @@
             mClickedSet.add(path);
         }
 
+        // Convert to inverse selection mode if everything is selected.
+        int count = getSelectedCount();
+        if (count == getTotalCount()) {
+            selectAll();
+        }
+
         if (mListener != null) mListener.onSelectionChange(path, isItemSelected(path));
-        if (getSelectedCount() == 0 && mAutoLeave) {
+        if (count == 0 && mAutoLeave) {
             leaveSelectionMode();
         }
     }
@@ -159,8 +171,8 @@
         ArrayList<Path> selected = new ArrayList<Path>();
         if (mIsAlbumSet) {
             if (mInverseSelection) {
-                int max = mSourceMediaSet.getSubMediaSetCount();
-                for (int i = 0; i < max; i++) {
+                int total = getTotalCount();
+                for (int i = 0; i < total; i++) {
                     MediaSet set = mSourceMediaSet.getSubMediaSet(i);
                     Path id = set.getPath();
                     if (!mClickedSet.contains(id)) {
@@ -182,8 +194,7 @@
             }
         } else {
             if (mInverseSelection) {
-
-                int total = mSourceMediaSet.getMediaItemCount();
+                int total = getTotalCount();
                 int index = 0;
                 while (index < total) {
                     int count = Math.min(total - index, MediaSet.MEDIAITEM_BATCH_FETCH_COUNT);
diff --git a/src/com/android/gallery3d/ui/TextureUploader.java b/src/com/android/gallery3d/ui/TextureUploader.java
index ff7830f..714b097 100644
--- a/src/com/android/gallery3d/ui/TextureUploader.java
+++ b/src/com/android/gallery3d/ui/TextureUploader.java
@@ -36,8 +36,12 @@
     }
 
     public synchronized void clear() {
-        mFgTextures.clear();
-        mBgTextures.clear();
+        while (!mFgTextures.isEmpty()) {
+            mFgTextures.pop().setIsUploading(false);
+        }
+        while (!mBgTextures.isEmpty()) {
+            mBgTextures.pop().setIsUploading(false);
+        }
     }
 
     // caller should hold synchronized on "this"
@@ -48,12 +52,16 @@
     }
 
     public synchronized void addBgTexture(UploadedTexture t) {
+        if (t.isContentValid()) return;
         mBgTextures.addLast(t);
+        t.setIsUploading(true);
         queueSelfIfNeed();
     }
 
     public synchronized void addFgTexture(UploadedTexture t) {
+        if (t.isContentValid()) return;
         mFgTextures.addLast(t);
+        t.setIsUploading(true);
         queueSelfIfNeed();
     }
 
@@ -64,7 +72,9 @@
             synchronized (this) {
                 if (deque.isEmpty()) break;
                 t = deque.removeFirst();
-                if (t.isContentValid(canvas)) continue;
+                t.setIsUploading(false);
+                if (t.isContentValid()) continue;
+
                 // this has to be protected by the synchronized block
                 // to prevent the inner bitmap get recycled
                 t.updateContent(canvas);
diff --git a/src/com/android/gallery3d/ui/TileImageView.java b/src/com/android/gallery3d/ui/TileImageView.java
index fb0e333..0c6086d 100644
--- a/src/com/android/gallery3d/ui/TileImageView.java
+++ b/src/com/android/gallery3d/ui/TileImageView.java
@@ -25,6 +25,7 @@
 
 import com.android.gallery3d.app.GalleryContext;
 import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.BitmapPool;
 import com.android.gallery3d.data.DecodeUtils;
 import com.android.gallery3d.util.Future;
 import com.android.gallery3d.util.ThreadPool;
@@ -43,8 +44,12 @@
     // texture to avoid seams between tiles.
     private static final int TILE_SIZE = 254;
     private static final int TILE_BORDER = 1;
+    private static final int BITMAP_SIZE = TILE_SIZE + TILE_BORDER * 2;
     private static final int UPLOAD_LIMIT = 1;
 
+    private static final BitmapPool sTilePool =
+            new BitmapPool(BITMAP_SIZE, BITMAP_SIZE, 128);
+
     /*
      *  This is the tile state in the CPU side.
      *  Life of a Tile:
@@ -96,9 +101,9 @@
     private final LongSparseArray<Tile> mActiveTiles = new LongSparseArray<Tile>();
 
     // The following three queue is guarded by TileImageView.this
-    private TileQueue mRecycledQueue = new TileQueue();
-    private TileQueue mUploadQueue = new TileQueue();
-    private TileQueue mDecodeQueue = new TileQueue();
+    private final TileQueue mRecycledQueue = new TileQueue();
+    private final TileQueue mUploadQueue = new TileQueue();
+    private final TileQueue mDecodeQueue = new TileQueue();
 
     // The width and height of the full-sized bitmap
     protected int mImageWidth = SIZE_UNKNOWN;
@@ -116,7 +121,7 @@
     private final TileUploader mTileUploader = new TileUploader();
     private boolean mIsTextureFreed;
     private Future<Void> mTileDecoder;
-    private ThreadPool mThreadPool;
+    private final ThreadPool mThreadPool;
     private boolean mBackgroundTileUploaded;
 
     public static interface Model {
@@ -138,7 +143,7 @@
         //
         // The method would be called in another thread.
         public Bitmap getTile(int level, int x, int y, int tileSize,
-                int borderSize);
+                int borderSize, BitmapPool pool);
     }
 
     public TileImageView(GalleryContext context) {
@@ -373,6 +378,7 @@
             }
         }
         setScreenNail(null);
+        sTilePool.clear();
     }
 
     public void prepareTextures() {
@@ -451,7 +457,7 @@
         int n = mActiveTiles.size();
         for (int i = 0; i < n; i++) {
             Tile tile = mActiveTiles.valueAt(i);
-            if (!tile.isContentValid(canvas)) queueForDecode(tile);
+            if (!tile.isContentValid()) queueForDecode(tile);
         }
     }
 
@@ -480,7 +486,10 @@
         synchronized (this) {
             if (tile.mTileState == STATE_RECYCLING) {
                 tile.mTileState = STATE_RECYCLED;
-                tile.mDecodedTile = null;
+                if (tile.mDecodedTile != null) {
+                    sTilePool.recycle(tile.mDecodedTile);
+                    tile.mDecodedTile = null;
+                }
                 mRecycledQueue.push(tile);
                 return false;
             }
@@ -505,7 +514,10 @@
             return;
         }
         tile.mTileState = STATE_RECYCLED;
-        tile.mDecodedTile = null;
+        if (tile.mDecodedTile != null) {
+            sTilePool.recycle(tile.mDecodedTile);
+            tile.mDecodedTile = null;
+        }
         mRecycledQueue.push(tile);
     }
 
@@ -546,7 +558,7 @@
                     tile = mUploadQueue.pop();
                 }
                 if (tile == null || quota <= 0) break;
-                if (!tile.isContentValid(canvas)) {
+                if (!tile.isContentValid()) {
                     Utils.assertTrue(tile.mTileState == STATE_DECODED);
                     tile.updateContent(canvas);
                     --quota;
@@ -568,7 +580,7 @@
 
         Tile tile = getTile(tx, ty, level);
         if (tile != null) {
-            if (!tile.isContentValid(canvas)) {
+            if (!tile.isContentValid()) {
                 if (tile.mTileState == STATE_DECODED) {
                     if (mUploadQuota > 0) {
                         --mUploadQuota;
@@ -597,7 +609,7 @@
     static boolean drawTile(
             Tile tile, GLCanvas canvas, RectF source, RectF target) {
         while (true) {
-            if (tile.isContentValid(canvas)) {
+            if (tile.isContentValid()) {
                 // offset source rectangle for the texture border.
                 source.offset(TILE_BORDER, TILE_BORDER);
                 canvas.drawTexture(tile, source, target);
@@ -626,12 +638,12 @@
     }
 
     private class Tile extends UploadedTexture {
-        int mX;
-        int mY;
-        int mTileLevel;
-        Tile mNext;
-        Bitmap mDecodedTile;
-        volatile int mTileState = STATE_ACTIVATED;
+        public int mX;
+        public int mY;
+        public int mTileLevel;
+        public Tile mNext;
+        public Bitmap mDecodedTile;
+        public volatile int mTileState = STATE_ACTIVATED;
 
         public Tile(int x, int y, int level) {
             mX = x;
@@ -641,7 +653,7 @@
 
         @Override
         protected void onFreeBitmap(Bitmap bitmap) {
-            bitmap.recycle();
+            sTilePool.recycle(bitmap);
         }
 
         boolean decode() {
@@ -649,7 +661,7 @@
             // by (1 << mTilelevel) from a region in the original image.
             try {
                 mDecodedTile = DecodeUtils.ensureGLCompatibleBitmap(mModel.getTile(
-                        mTileLevel, mX, mY, TILE_SIZE, TILE_BORDER));
+                        mTileLevel, mX, mY, TILE_SIZE, TILE_BORDER, sTilePool));
             } catch (Throwable t) {
                 Log.w(TAG, "fail to decode tile", t);
             }
@@ -659,6 +671,13 @@
         @Override
         protected Bitmap onGetBitmap() {
             Utils.assertTrue(mTileState == STATE_DECODED);
+
+            // We need to override the width and height, so that we won't
+            // draw beyond the boundaries.
+            int rightEdge = ((mImageWidth - mX) >> mTileLevel) + TILE_BORDER;
+            int bottomEdge = ((mImageHeight - mY) >> mTileLevel) + TILE_BORDER;
+            setSize(Math.min(BITMAP_SIZE, rightEdge), Math.min(BITMAP_SIZE, bottomEdge));
+
             Bitmap bitmap = mDecodedTile;
             mDecodedTile = null;
             mTileState = STATE_ACTIVATED;
diff --git a/src/com/android/gallery3d/ui/TileImageViewAdapter.java b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
index 5c92812..0b4ac03 100644
--- a/src/com/android/gallery3d/ui/TileImageViewAdapter.java
+++ b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
@@ -20,10 +20,10 @@
 import android.graphics.Bitmap.Config;
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapRegionDecoder;
-import android.graphics.Canvas;
 import android.graphics.Rect;
 
 import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.BitmapPool;
 
 public class TileImageViewAdapter implements TileImageView.Model {
     private static final String TAG = "TileImageViewAdapter";
@@ -94,70 +94,68 @@
                 (float) mImageWidth / mScreenNail.getWidth()));
     }
 
+    // Gets a sub image on a rectangle of the current photo. For example,
+    // getTile(1, 50, 50, 100, 3, pool) means to get the region located
+    // at (50, 50) with sample level 1 (ie, down sampled by 2^1) and the
+    // target tile size (after sampling) 100 with border 3.
+    //
+    // From this spec, we can infer the actual tile size to be
+    // 100 + 3x2 = 106, and the size of the region to be extracted from the
+    // photo to be 200 with border 6.
+    //
+    // As a result, we should decode region (50-6, 50-6, 250+6, 250+6) or
+    // (44, 44, 256, 256) from the original photo and down sample it to 106.
     @Override
     public Bitmap getTile(int level, int x, int y, int tileSize,
-            int borderSize) {
-        // wantRegion is the rectangle on the original image we want. askRegion
-        // is the rectangle on the original image that we will ask from
-        // mRegionDecoder. Both are in the coordinates of the original image,
-        // not the coordinates of the scaled-down images.
-        Rect wantRegion = new Rect();
-        Rect askRegion = new Rect();
-
+            int borderSize, BitmapPool pool) {
         int b = borderSize << level;
-        wantRegion.set(x - b, y - b, x + (tileSize << level) + b,
-                y + (tileSize << level) + b);
+        int t = tileSize << level;
 
+        Rect wantRegion = new Rect(x - b, y - b, x + t + b, y + t + b);
+
+        boolean needClear;
         BitmapRegionDecoder regionDecoder = null;
+
         synchronized (this) {
             regionDecoder = mRegionDecoder;
             if (regionDecoder == null) return null;
-            // askRegion is the intersection of wantRegion and the original image.
-            askRegion.set(0, 0, mImageWidth, mImageHeight);
+
+            // We need to clear a reused bitmap, if wantRegion is not fully
+            // within the image.
+            needClear = !new Rect(0, 0, mImageWidth, mImageHeight)
+                    .contains(wantRegion);
         }
 
-        Utils.assertTrue(askRegion.intersect(wantRegion));
+        Bitmap bitmap = pool == null ? null : pool.getBitmap();
+        if (bitmap != null) {
+            if (needClear) bitmap.eraseColor(0);
+        } else {
+            int s = tileSize + 2 * borderSize;
+            bitmap = Bitmap.createBitmap(s, s, Config.ARGB_8888);
+        }
 
         BitmapFactory.Options options = new BitmapFactory.Options();
         options.inPreferredConfig = Config.ARGB_8888;
         options.inPreferQualityOverSpeed = true;
         options.inSampleSize =  (1 << level);
+        options.inBitmap = bitmap;
 
-        Bitmap bitmap;
-
-        // In CropImage, we may call the decodeRegion() concurrently.
-        synchronized (regionDecoder) {
-            bitmap = regionDecoder.decodeRegion(askRegion, options);
+        try {
+            // In CropImage, we may call the decodeRegion() concurrently.
+            synchronized (regionDecoder) {
+                bitmap = regionDecoder.decodeRegion(wantRegion, options);
+            }
+        } finally {
+            if (options.inBitmap != bitmap && options.inBitmap != null) {
+                if (pool != null) pool.recycle(options.inBitmap);
+                options.inBitmap = null;
+            }
         }
 
         if (bitmap == null) {
             Log.w(TAG, "fail in decoding region");
-            return null;
         }
-
-        if (wantRegion.equals(askRegion)) return bitmap;
-
-        // Now the wantRegion does not match the askRegion. This means we are at
-        // a boundary tile, and we need to add paddings. Create a new Bitmap
-        // and copy over.
-        int size = tileSize + 2 * borderSize;
-        Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
-        Canvas canvas = new Canvas(result);
-        int offsetX = (askRegion.left - wantRegion.left) >> level;
-        int offsetY = (askRegion.top - wantRegion.top) >> level;
-        canvas.drawBitmap(bitmap, offsetX, offsetY, null);
-
-        // If the valid region (covered by bitmap or border) is smaller than the
-        // result bitmap, subset it.
-        int endX = offsetX + bitmap.getWidth() + borderSize;
-        int endY = offsetY + bitmap.getHeight() + borderSize;
-        bitmap.recycle();
-        if (endX < size || endY < size) {
-            return Bitmap.createBitmap(result, 0, 0, Math.min(size, endX),
-                    Math.min(size, endY));
-        } else {
-            return result;
-        }
+        return bitmap;
     }
 
     @Override
diff --git a/src/com/android/gallery3d/ui/UploadedTexture.java b/src/com/android/gallery3d/ui/UploadedTexture.java
index 85aa1c4..0fe5067 100644
--- a/src/com/android/gallery3d/ui/UploadedTexture.java
+++ b/src/com/android/gallery3d/ui/UploadedTexture.java
@@ -52,6 +52,9 @@
     @SuppressWarnings("unused")
     private static final String TAG = "Texture";
     private boolean mContentValid = true;
+
+    // indicate this textures is being uploaded in background
+    private boolean mIsUploading = false;
     private boolean mOpaque = true;
     private boolean mThrottled = false;
     private static int sUploadedCount;
@@ -72,6 +75,14 @@
         }
     }
 
+    protected void setIsUploading(boolean uploading) {
+        mIsUploading = uploading;
+    }
+
+    public boolean isUploading() {
+        return mIsUploading;
+    }
+
     private static class BorderKey implements Cloneable {
         public boolean vertical;
         public Config config;
@@ -128,10 +139,6 @@
             int h = mBitmap.getHeight() + mBorder * 2;
             if (mWidth == UNSPECIFIED) {
                 setSize(w, h);
-            } else if (mWidth != w || mHeight != h) {
-                throw new IllegalStateException(String.format(
-                        "cannot change size: this = %s, orig = %sx%s, new = %sx%s",
-                        toString(), mWidth, mHeight, w, h));
             }
         }
         return mBitmap;
@@ -169,8 +176,8 @@
     /**
      * Whether the content on GPU is valid.
      */
-    public boolean isContentValid(GLCanvas canvas) {
-        return isLoaded(canvas) && mContentValid;
+    public boolean isContentValid() {
+        return isLoaded() && mContentValid;
     }
 
     /**
@@ -178,7 +185,7 @@
      * @param canvas
      */
     public void updateContent(GLCanvas canvas) {
-        if (!isLoaded(canvas)) {
+        if (!isLoaded()) {
             if (mThrottled && ++sUploadedCount > UPLOAD_LIMIT) {
                 return;
             }
@@ -218,6 +225,9 @@
                 int height = bHeight + mBorder * 2;
                 int texWidth = getTextureWidth();
                 int texHeight = getTextureHeight();
+
+                Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
+
                 // Define a vertically flipped crop rectangle for
                 // OES_draw_texture.
                 // The four values in sCropRect are: left, bottom, width, and
@@ -285,7 +295,7 @@
             // Update texture state.
             setAssociatedCanvas(canvas);
             mId = sTextureId[0];
-            mState = UploadedTexture.STATE_LOADED;
+            mState = STATE_LOADED;
             mContentValid = true;
         } else {
             mState = STATE_ERROR;
@@ -296,7 +306,7 @@
     @Override
     protected boolean onBind(GLCanvas canvas) {
         updateContent(canvas);
-        return isContentValid(canvas);
+        return isContentValid();
     }
 
     @Override
diff --git a/tests/src/com/android/gallery3d/ui/TextureTest.java b/tests/src/com/android/gallery3d/ui/TextureTest.java
index be2356c..36446b3 100644
--- a/tests/src/com/android/gallery3d/ui/TextureTest.java
+++ b/tests/src/com/android/gallery3d/ui/TextureTest.java
@@ -20,10 +20,10 @@
 import android.graphics.Bitmap.Config;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import javax.microedition.khronos.opengles.GL11;
-
 import junit.framework.TestCase;
 
+import javax.microedition.khronos.opengles.GL11;
+
 @SmallTest
 public class TextureTest extends TestCase {
     @SuppressWarnings("unused")
@@ -34,7 +34,7 @@
         int mOpaqueCalled;
 
         MyBasicTexture(GLCanvas canvas, int id) {
-            super(canvas, id, BasicTexture.STATE_UNLOADED);
+            super(canvas, id, 0);
         }
 
         @Override
@@ -76,13 +76,13 @@
         assertEquals(4, texture.getTextureWidth());
         assertEquals(8, texture.getTextureHeight());
 
-        assertFalse(texture.isLoaded(canvas));
+        assertFalse(texture.isLoaded());
         texture.upload();
-        assertTrue(texture.isLoaded(canvas));
+        assertTrue(texture.isLoaded());
 
         // For a different GL, it's not loaded.
         GLCanvas canvas2 = new GLCanvasImpl(new GLStub());
-        assertFalse(texture.isLoaded(canvas2));
+        assertFalse(texture.isLoaded());
 
         assertEquals(0, texture.mOnBindCalled);
         assertEquals(0, texture.mOpaqueCalled);
@@ -143,20 +143,20 @@
         assertEquals(0, texture.mGetCalled);
         texture.draw(canvas, 0, 0);
         assertEquals(1, texture.mGetCalled);
-        assertTrue(texture.isLoaded(canvas));
-        assertTrue(texture.isContentValid(canvas));
+        assertTrue(texture.isLoaded());
+        assertTrue(texture.isContentValid());
 
         // invalidate content and it should be freed.
         texture.invalidateContent();
-        assertFalse(texture.isContentValid(canvas));
+        assertFalse(texture.isContentValid());
         assertEquals(1, texture.mFreeCalled);
-        assertTrue(texture.isLoaded(canvas));  // But it's still loaded
+        assertTrue(texture.isLoaded());  // But it's still loaded
 
         // draw it again and the bitmap should be fetched again.
         texture.draw(canvas, 0, 0);
         assertEquals(2, texture.mGetCalled);
-        assertTrue(texture.isLoaded(canvas));
-        assertTrue(texture.isContentValid(canvas));
+        assertTrue(texture.isLoaded());
+        assertTrue(texture.isContentValid());
 
         // recycle the texture and it should be freed again.
         texture.recycle();
@@ -168,7 +168,7 @@
 
     class MyTextureForMixed extends BasicTexture {
         MyTextureForMixed(GLCanvas canvas, int id) {
-            super(canvas, id, BasicTexture.STATE_UNLOADED);
+            super(canvas, id, 0);
         }
 
         @Override