The first step of multiselect. Now, you can multiselect images, but can do nothing.
diff --git a/res/drawable/btn_check_buttonless_on.png b/res/drawable/btn_check_buttonless_on.png
new file mode 100755
index 0000000..adf09fb
--- /dev/null
+++ b/res/drawable/btn_check_buttonless_on.png
Binary files differ
diff --git a/res/drawable/ic_menu_multiselect_gallery.png b/res/drawable/ic_menu_multiselect_gallery.png
new file mode 100755
index 0000000..cb342c6
--- /dev/null
+++ b/res/drawable/ic_menu_multiselect_gallery.png
Binary files differ
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 29759e4..66bcfff 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -104,6 +104,9 @@
 
     <!-- menu pick to start a slide show -->
     <string name="slide_show">Slideshow</string>
+
+    <!-- menu pick to enter multiselect mode -->
+    <string name="multiselect">Multiselect</string>
     
     <!-- menu pick to go to camera mode to capture a picture -->
     <string name="capture_picture">Capture picture</string>
diff --git a/src/com/android/camera/GridViewSpecial.java b/src/com/android/camera/GridViewSpecial.java
index 1645fe5..d8becad 100644
--- a/src/com/android/camera/GridViewSpecial.java
+++ b/src/com/android/camera/GridViewSpecial.java
@@ -5,7 +5,6 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Paint;
@@ -28,7 +27,6 @@
 
     private static final String TAG = "GridViewSpecial";
     private IImageList mAllImages = ImageManager.emptyImageList();
-    private final Paint mPatin = new Paint();
 
     ImageBlockManager mImageBlockManager;
     private Handler mHandler;
@@ -39,6 +37,7 @@
     private boolean mCurrentSelectionPressed;
 
     private Listener mListener = null;
+    private DrawAdapter mDrawAdapter = null;
 
     long mVideoSizeLimit;
     private boolean mRunning = false;
@@ -73,14 +72,30 @@
     private GestureDetector mGestureDetector;
     private boolean mLayoutComplete = false;
 
-    public Listener setListener(Listener listener) {
-        Listener original = mListener;
+    public void setListener(Listener listener) {
         mListener = listener;
-        return original;
+    }
+
+    public void setDrawAdapter(DrawAdapter adapter) {
+        mDrawAdapter = adapter;
+    }
+
+    public static interface DrawAdapter {
+        public void drawImage(Canvas canvas, IImage image,
+                Bitmap b, int xPos, int yPos, int w, int h);
+    }
+
+    public void invalidateImage(int index) {
+        mImageBlockManager.invalidateImage(index);
+    }
+
+    public void invalidateAllImages() {
+        this.clearCache();
+        mImageBlockManager = new ImageBlockManager();
+        mImageBlockManager.moveDataWindow(true);
     }
 
     private void init(Context context) {
-        mPatin.setColor(0xFF000000);
 
         setVerticalScrollBarEnabled(true);
         initializeScrollbars(context.obtainStyledAttributes(
@@ -251,10 +266,7 @@
 
     public void stop() {
         mScroller = null;
-        if (mImageBlockManager != null) {
-            mImageBlockManager.onPause();
-            mImageBlockManager = null;
-        }
+        clearCache();
         mRunning = false;
     }
 
@@ -273,25 +285,26 @@
             return;
         }
         clearCache();
+
         mCurrentSpec = mCellSizeChoices[mSizeChoice];
-        int oldColumnCount = mCurrentSpec.mColumns;
+        LayoutSpec spec = mCurrentSpec;
 
+        int oldColumnCount = spec.mColumns;
         int width = right - left;
-        mCurrentSpec.mColumns = 1;
-        width -= mCurrentSpec.mCellWidth;
-        mCurrentSpec.mColumns += width
-                / (mCurrentSpec.mCellWidth + mCurrentSpec.mCellSpacing);
 
-        mCurrentSpec.mLeftEdgePadding = ((right - left)
-                - ((mCurrentSpec.mColumns - 1) * mCurrentSpec.mCellSpacing)
-                - (mCurrentSpec.mColumns * mCurrentSpec.mCellWidth)) / 2;
-        mCurrentSpec.mRightEdgePadding = mCurrentSpec.mLeftEdgePadding;
+        spec.mColumns = 1 + (width - spec.mCellWidth)
+                / (spec.mCellWidth + spec.mCellSpacing);
 
-        int rows = (mAllImages.getCount() + mCurrentSpec.mColumns - 1)
-                / mCurrentSpec.mColumns;
-        mMaxScrollY = mCurrentSpec.mCellSpacing
+        spec.mLeftEdgePadding = (width
+                - ((spec.mColumns - 1) * spec.mCellSpacing)
+                - (spec.mColumns * spec.mCellWidth)) / 2;
+        spec.mRightEdgePadding = spec.mLeftEdgePadding;
+
+        int rows = (mAllImages.getCount() + spec.mColumns - 1)
+                / spec.mColumns;
+        mMaxScrollY = spec.mCellSpacing
                 + (rows
-                * (mCurrentSpec.mCellSpacing + mCurrentSpec.mCellHeight))
+                * (spec.mCellSpacing + spec.mCellHeight))
                 - (bottom - top) ;
         if (mImageBlockManager == null) {
             mImageBlockManager = new ImageBlockManager();
@@ -320,11 +333,6 @@
         private boolean mDone = false;
 
         private Thread mWorkerThread;
-        private Bitmap mMissingImageThumbnailBitmap;
-        private Bitmap mMissingVideoThumbnailBitmap;
-
-        private Drawable mVideoOverlay;
-        private Drawable mVideoMmsErrorOverlay;
 
         ImageBlockManager() {
             mLoader = new ImageLoader(mHandler, 1);
@@ -380,25 +388,21 @@
             mWorkerThread.start();
         }
 
-        // Create this bitmap lazily, and only once for all the ImageBlocks to
-        // use
-        public Bitmap getErrorBitmap(IImage image) {
-            if (ImageManager.isImage(image)) {
-                if (mMissingImageThumbnailBitmap == null) {
-                    mMissingImageThumbnailBitmap =
-                            BitmapFactory.decodeResource(
-                            GridViewSpecial.this.getResources(),
-                            R.drawable.ic_missing_thumbnail_picture);
-                }
-                return mMissingImageThumbnailBitmap;
-            } else {
-                if (mMissingVideoThumbnailBitmap == null) {
-                    mMissingVideoThumbnailBitmap =
-                            BitmapFactory.decodeResource(
-                            GridViewSpecial.this.getResources(),
-                            R.drawable.ic_missing_thumbnail_video);
-                }
-                return mMissingVideoThumbnailBitmap;
+        public void invalidateImage(int index) {
+            ImageBlock block = getBlockForPos(index);
+            LayoutSpec spec = mCurrentSpec;
+            int column = spec.mColumns;
+            int blockIndex = index / column;
+            int base = blockIndex * column;
+            int col = index - base;
+            int spacing = spec.mCellSpacing;
+            final int yPos = spacing;
+            final int xPos = spec.mLeftEdgePadding
+                    + (col * (spec.mCellWidth + spacing));
+
+            IImage image = mAllImages.getImageAt(index);
+            if (image != null) {
+                block.loadImage(base, col, image, xPos, yPos);
             }
         }
 
@@ -443,30 +447,28 @@
             Log.v(TAG, "/ImageBlockManager.onPause");
         }
 
-        void getVisibleRange(int [] range) {
-            synchronized (ImageBlockManager.this) {
-                int blockLength = mBlockCache.length;
-                boolean lookingForStart = true;
-                ImageBlock prevBlock = null;
-                for (int i = 0; i < blockLength; i++) {
-                    int index = (mBlockCacheStartOffset + i) % blockLength;
-                    ImageBlock block = mBlockCache[index];
-                    if (lookingForStart) {
-                        if (block.mIsVisible) {
-                            range[0] = block.mBlockNumber
-                                    * mCurrentSpec.mColumns;
-                            lookingForStart = false;
-                        }
-                    } else {
-                        if (!block.mIsVisible || i == blockLength - 1) {
-                            range[1] = (prevBlock.mBlockNumber
-                                    * mCurrentSpec.mColumns)
-                                    + mCurrentSpec.mColumns - 1;
-                            break;
-                        }
+        synchronized void getVisibleRange(int [] range) {
+            int blockLength = mBlockCache.length;
+            boolean lookingForStart = true;
+            ImageBlock prevBlock = null;
+            for (int i = 0; i < blockLength; i++) {
+                int index = (mBlockCacheStartOffset + i) % blockLength;
+                ImageBlock block = mBlockCache[index];
+                if (lookingForStart) {
+                    if (block.mIsVisible) {
+                        range[0] = block.mBlockNumber
+                                * mCurrentSpec.mColumns;
+                        lookingForStart = false;
                     }
-                    prevBlock = block;
+                } else {
+                    if (!block.mIsVisible || i == blockLength - 1) {
+                        range[1] = (prevBlock.mBlockNumber
+                                * mCurrentSpec.mColumns)
+                                + mCurrentSpec.mColumns - 1;
+                        break;
+                    }
                 }
+                prevBlock = block;
             }
         }
 
@@ -590,7 +592,7 @@
                 int currentBlock = (scrollPos < 0)
                         ? ((scrollPos - height + 1) / height)
                         : (scrollPos / height);
-
+                Paint paint = new Paint();
                 while (true) {
                     final int yPos = currentBlock * height;
                     if (yPos >= scrollPos + thisHeight) {
@@ -598,7 +600,7 @@
                     }
 
                     if (currentBlock < 0) {
-                        canvas.drawRect(0, yPos, thisWidth, 0, mPatin);
+                        canvas.drawRect(0, yPos, thisWidth, 0, paint);
                         currentBlock += 1;
                         continue;
                     }
@@ -620,7 +622,7 @@
                         if (b == null) {
                             break;
                         }
-                        canvas.drawBitmap(b, 0, yPos, mPatin);
+                        canvas.drawBitmap(b, 0, yPos, paint);
                         blockCount += 1;
                     }
                 }
@@ -638,20 +640,19 @@
             Canvas mCanvas = new Canvas(mBitmap);
             Paint mPaint = new Paint();
 
-            int     mBlockNumber;
+            int mBlockNumber;
 
             // columns which have been requested to the loader
-            int     mRequestedMask;
+            int mRequestedMask;
 
             // columns which have been completed from the loader
-            int     mCompletedMask;
+            int mCompletedMask;
             boolean mIsVisible;
 
-            public void dump(StringBuilder line1, StringBuilder line2) {
-                synchronized (ImageBlock.this) {
-                    line2.append(mCompletedMask != 0xF ? 'L' : '_');
-                    line1.append(mIsVisible ? 'V' : ' ');
-                }
+            public synchronized void dump(
+                    StringBuilder line1, StringBuilder line2) {
+                line2.append(mCompletedMask != 0xF ? 'L' : '_');
+                line1.append(mIsVisible ? 'V' : ' ');
             }
 
             ImageBlock() {
@@ -728,46 +729,43 @@
                 }
             }
 
-            private int startLoading() {
-                synchronized (ImageBlock.this) {
-                    final int startRow = mBlockNumber;
-                    int count = mAllImages.getCount();
+            private synchronized int startLoading() {
+                final int startRow = mBlockNumber;
+                int count = mAllImages.getCount();
 
-                    if (startRow == REMOVE_SELCTION) {
-                        return 0;
-                    }
-
-                    if ((startRow * mCurrentSpec.mColumns) >= count) {
-                        return 0;
-                    }
-
-                    int retVal = 0;
-                    int base = (mBlockNumber * mCurrentSpec.mColumns);
-                    for (int col = 0; col < mCurrentSpec.mColumns; col++) {
-                        if ((mCompletedMask & (1 << col)) != 0) {
-                            continue;
-                        }
-
-                        int spacing = mCurrentSpec.mCellSpacing;
-                        int leftSpacing = mCurrentSpec.mLeftEdgePadding;
-                        final int yPos = spacing;
-                        final int xPos = leftSpacing
-                                + (col * (mCurrentSpec.mCellWidth + spacing));
-
-                        int pos = base + col;
-                        if (pos >= count) {
-                            break;
-                        }
-
-                        IImage image = mAllImages.getImageAt(pos);
-                        if (image != null) {
-                            loadImage(base, col, image, xPos, yPos);
-                            retVal += 1;
-                        }
-                    }
-                    return retVal;
-
+                if (startRow == -1) {
+                    return 0;
                 }
+
+                if ((startRow * mCurrentSpec.mColumns) >= count) {
+                    return 0;
+                }
+
+                int retVal = 0;
+                int base = (mBlockNumber * mCurrentSpec.mColumns);
+                for (int col = 0; col < mCurrentSpec.mColumns; col++) {
+                    if ((mCompletedMask & (1 << col)) != 0) {
+                        continue;
+                    }
+
+                    int spacing = mCurrentSpec.mCellSpacing;
+                    int leftSpacing = mCurrentSpec.mLeftEdgePadding;
+                    final int yPos = spacing;
+                    final int xPos = leftSpacing
+                            + (col * (mCurrentSpec.mCellWidth + spacing));
+
+                    int pos = base + col;
+                    if (pos >= count) {
+                        break;
+                    }
+
+                    IImage image = mAllImages.getImageAt(pos);
+                    if (image != null) {
+                        loadImage(base, col, image, xPos, yPos);
+                        retVal += 1;
+                    }
+                }
+                return retVal;
             }
 
             Bitmap resizeBitmap(Bitmap b) {
@@ -785,79 +783,14 @@
                 return b2;
             }
 
-            private void drawBitmap(IImage image, int base, int baseOffset,
-                    Bitmap b, int xPos, int yPos) {
+            private void drawBitmap(
+                    IImage image, int index, Bitmap b, int xPos, int yPos) {
                 mCanvas.setBitmap(mBitmap);
-                if (b != null) {
-                    // if the image is close to the target size then crop,
-                    // otherwise scale both the bitmap and the view should be
-                    // square but I suppose that could change in the future.
-                    int w = mCurrentSpec.mCellWidth;
-                    int h = mCurrentSpec.mCellHeight;
-
-                    int bw = b.getWidth();
-                    int bh = b.getHeight();
-
-                    int deltaW = bw - w;
-                    int deltaH = bh - h;
-
-                    if (deltaW < 10 && deltaH < 10) {
-                        int halfDeltaW = deltaW / 2;
-                        int halfDeltaH = deltaH / 2;
-                        Rect src = new Rect(0 + halfDeltaW,
-                                0 + halfDeltaH, bw - halfDeltaW,
-                                bh - halfDeltaH);
-                        Rect dst = new Rect(xPos, yPos,
-                                xPos + w, yPos + h);
-                        mCanvas.drawBitmap(b, src, dst, mPaint);
-                    } else {
-                        Rect src = new Rect(0, 0, bw, bh);
-                        Rect dst = new Rect(xPos, yPos, xPos + w, yPos + h);
-                        mCanvas.drawBitmap(b, src, dst, mPaint);
-                    }
-                } else {
-                    // If the thumbnail cannot be drawn, put up an error icon
-                    // instead
-                    Bitmap error = mImageBlockManager.getErrorBitmap(image);
-                    int width = error.getWidth();
-                    int height = error.getHeight();
-                    Rect source = new Rect(0, 0, width, height);
-                    int left = (mCurrentSpec.mCellWidth - width) / 2 + xPos;
-                    int top = (mCurrentSpec.mCellHeight - height) / 2 + yPos;
-                    Rect dest = new Rect(left, top, left + width, top + height);
-                    mCanvas.drawBitmap(error, source, dest, mPaint);
+                if (mDrawAdapter != null) {
+                    mDrawAdapter.drawImage(mCanvas, image, b, xPos, yPos,
+                            mCurrentSpec.mCellWidth, mCurrentSpec.mCellHeight);
                 }
-                if (ImageManager.isVideo(image)) {
-                    Drawable overlay = null;
-                    long size = MenuHelper.getImageFileSize(image);
-                    if (size >= 0 && size <= mVideoSizeLimit) {
-                        if (mVideoOverlay == null) {
-                            mVideoOverlay = getResources().getDrawable(
-                                    R.drawable.ic_gallery_video_overlay);
-                        }
-                        overlay = mVideoOverlay;
-                    } else {
-                        if (mVideoMmsErrorOverlay == null) {
-                            mVideoMmsErrorOverlay = getResources().getDrawable(
-                                    R.drawable.ic_error_mms_video_overlay);
-                        }
-                        overlay = mVideoMmsErrorOverlay;
-                        Paint paint = new Paint();
-                        paint.setARGB(0x80, 0x00, 0x00, 0x00);
-                        mCanvas.drawRect(xPos, yPos,
-                                xPos + mCurrentSpec.mCellWidth,
-                                yPos + mCurrentSpec.mCellHeight, paint);
-                    }
-                    int width = overlay.getIntrinsicWidth();
-                    int height = overlay.getIntrinsicHeight();
-                    int left = (mCurrentSpec.mCellWidth - width) / 2 + xPos;
-                    int top = (mCurrentSpec.mCellHeight - height) / 2 + yPos;
-                    Rect newBounds =
-                            new Rect(left, top, left + width, top + height);
-                    overlay.setBounds(newBounds);
-                    overlay.draw(mCanvas);
-                }
-                paintSel(base + baseOffset, xPos, yPos);
+                paintSel(index, xPos, yPos);
             }
 
             private void repaintSelection() {
@@ -906,57 +839,50 @@
                 mCellOutline.draw(mCanvas);
             }
 
-            private void loadImage(
+            private synchronized void loadImage(
                     final int base,
                     final int baseOffset,
                     final IImage image,
                     final int xPos,
                     final int yPos) {
-                synchronized (ImageBlock.this) {
-                    final int startBlock = mBlockNumber;
-                    final int pos = base + baseOffset;
-                    final ImageLoader.LoadedCallback r =
-                            new ImageLoader.LoadedCallback() {
-                        public void run(Bitmap b) {
-                            boolean more = false;
-                            synchronized (ImageBlock.this) {
-                                if (startBlock != mBlockNumber) {
-                                    return;
-                                }
-
-                                if (mBitmap == null) {
-                                    return;
-                                }
-
-                                drawBitmap(image, base, baseOffset, b, xPos,
-                                        yPos);
-
-                                int mask = (1 << baseOffset);
-                                mRequestedMask &= ~mask;
-                                mCompletedMask |= mask;
-
-                                if (mRequestedMask == 0) {
-                                    if (mIsVisible) {
-                                        postInvalidate();
-                                    }
-                                    more = true;
-                                }
-                            }
-                            if (b != null) {
-                                b.recycle();
+                final int startBlock = mBlockNumber;
+                final int pos = base + baseOffset;
+                final ImageLoader.LoadedCallback r =
+                        new ImageLoader.LoadedCallback() {
+                    public void run(Bitmap b) {
+                        boolean more = false;
+                        synchronized (ImageBlock.this) {
+                            if (startBlock != mBlockNumber || mBitmap == null) {
+                                return;
                             }
 
-                            if (more) {
-                                synchronized (ImageBlockManager.this) {
-                                    ImageBlockManager.this.notify();
-                                    mWorkCounter += 1;
+                            drawBitmap(image, pos, b, xPos, yPos);
+
+                            int mask = (1 << baseOffset);
+                            mRequestedMask &= ~mask;
+                            mCompletedMask |= mask;
+
+                            if (mRequestedMask == 0) {
+                                if (mIsVisible) {
+                                    postInvalidate();
                                 }
+                                more = true;
                             }
                         }
-                    };
-                    mRequestedMask |= (1 << baseOffset);
-                    mLoader.getBitmap(image, pos, r, mIsVisible, false);
-                }
+                        if (b != null) {
+                            b.recycle();
+                        }
+
+                        if (more) {
+                            synchronized (ImageBlockManager.this) {
+                                ImageBlockManager.this.notify();
+                                mWorkCounter += 1;
+                            }
+                        }
+                    }
+                };
+                mRequestedMask |= (1 << baseOffset);
+                mLoader.getBitmap(image, pos, r, mIsVisible, false);
             }
         }
     }
@@ -968,11 +894,6 @@
     @Override
     public void onDraw(Canvas canvas) {
         super.onDraw(canvas);
-        if (false) {
-            canvas.drawRect(0, 0, getWidth(), getHeight(), mPatin);
-            return;
-        }
-
         if (mImageBlockManager != null) {
             mImageBlockManager.doDraw(canvas);
             mImageBlockManager.moveDataWindow(false);
@@ -1147,6 +1068,4 @@
         }
         return super.onKeyUp(keyCode, event);
     }
-
-
 }
diff --git a/src/com/android/camera/HighlightView.java b/src/com/android/camera/HighlightView.java
index 4565bf8..ee2e129 100644
--- a/src/com/android/camera/HighlightView.java
+++ b/src/com/android/camera/HighlightView.java
@@ -408,7 +408,7 @@
     private Drawable mResizeDrawableHeight;
     private Drawable mResizeDrawableDiagonal;
 
-    private Paint mFocusPaint = new Paint();
-    private Paint mNoFocusPaint = new Paint();
-    private Paint mOutlinePaint = new Paint();
+    private final Paint mFocusPaint = new Paint();
+    private final Paint mNoFocusPaint = new Paint();
+    private final Paint mOutlinePaint = new Paint();
 }
diff --git a/src/com/android/camera/ImageGallery2.java b/src/com/android/camera/ImageGallery2.java
index ac0dff5..307e6eb 100644
--- a/src/com/android/camera/ImageGallery2.java
+++ b/src/com/android/camera/ImageGallery2.java
@@ -34,6 +34,11 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -50,7 +55,10 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
-public class ImageGallery2 extends Activity implements GridViewSpecial.Listener {
+import java.util.HashSet;
+
+public class ImageGallery2 extends Activity implements
+        GridViewSpecial.Listener, GridViewSpecial.DrawAdapter {
     private static final String TAG = "ImageGallery2";
     IImageList mAllImages;
     private int mInclusion;
@@ -79,6 +87,8 @@
     // The index of the first picture in GridViewSpecial.
     private int mFirstVisibleIndex = 0;
 
+    private HashSet<IImage> mMultiSelected = null;
+
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -336,6 +346,7 @@
         } else {
             mAllImages = allImages(!unmounted);
             mGvs.setImageList(mAllImages);
+            mGvs.setDrawAdapter(this);
             mGvs.init(mHandler);
             mGvs.start();
             mGvs.requestLayout();
@@ -484,16 +495,34 @@
             }
 
             MenuItem item = menu.add(0, 0, 1000, R.string.camerasettings);
-            item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+            item.setOnMenuItemClickListener(
+                    new MenuItem.OnMenuItemClickListener() {
                 public boolean onMenuItemClick(MenuItem item) {
                     Intent preferences = new Intent();
-                    preferences.setClass(ImageGallery2.this, GallerySettings.class);
+                    preferences.setClass(
+                            ImageGallery2.this, GallerySettings.class);
                     startActivity(preferences);
                     return true;
                 }
             });
             item.setAlphabeticShortcut('p');
             item.setIcon(android.R.drawable.ic_menu_preferences);
+
+            item = menu.add(R.string.multiselect);
+            item.setOnMenuItemClickListener(
+                    new MenuItem.OnMenuItemClickListener() {
+                public boolean onMenuItemClick(MenuItem item) {
+                    if (mMultiSelected == null) {
+                        mMultiSelected = new HashSet<IImage>();
+                    } else {
+                        mMultiSelected = null;
+                    }
+                    mGvs.invalidateAllImages();
+                    return true;
+                }
+            });
+            item.setIcon(R.drawable.ic_menu_multiselect_gallery);
+
         }
         return true;
     }
@@ -603,6 +632,13 @@
                 return;
             }
 
+            // if in multiselect mode
+            if (mMultiSelected != null) {
+                if (!mMultiSelected.add(img)) mMultiSelected.remove(img);
+                mGvs.invalidateImage(index);
+                return;
+            }
+
             if (isPickIntent()) {
                 launchCropperOrFinish(img);
             } else {
@@ -754,6 +790,120 @@
         mFirstVisibleIndex = index;
     }
 
+    private Drawable mVideoOverlay;
+    private Drawable mVideoMmsErrorOverlay;
+    private Drawable mMultiSelectTrue;
+
+    public void drawImage(Canvas canvas, IImage image,
+            Bitmap b, int xPos, int yPos, int w, int h) {
+        Paint paint = new Paint();
+
+        if (b != null) {
+            // if the image is close to the target size then crop,
+            // otherwise scale both the bitmap and the view should be
+            // square but I suppose that could change in the future.
+
+            int bw = b.getWidth();
+            int bh = b.getHeight();
+
+            int deltaW = bw - w;
+            int deltaH = bh - h;
+
+            if (deltaW < 10 && deltaH < 10) {
+                int halfDeltaW = deltaW / 2;
+                int halfDeltaH = deltaH / 2;
+                Rect src = new Rect(0 + halfDeltaW, 0 + halfDeltaH,
+                        bw - halfDeltaW, bh - halfDeltaH);
+                Rect dst = new Rect(xPos, yPos, xPos + w, yPos + h);
+                canvas.drawBitmap(b, src, dst, paint);
+            } else {
+                Rect src = new Rect(0, 0, bw, bh);
+                Rect dst = new Rect(xPos, yPos, xPos + w, yPos + h);
+                canvas.drawBitmap(b, src, dst, paint);
+            }
+        } else {
+            // If the thumbnail cannot be drawn, put up an error icon
+            // instead
+            Bitmap error = getErrorBitmap(image);
+            int width = error.getWidth();
+            int height = error.getHeight();
+            Rect source = new Rect(0, 0, width, height);
+            int left = (w - width) / 2 + xPos;
+            int top = (w - height) / 2 + yPos;
+            Rect dest = new Rect(left, top, left + width, top + height);
+            canvas.drawBitmap(error, source, dest, paint);
+        }
+        if (ImageManager.isVideo(image)) {
+            Drawable overlay = null;
+            long size = MenuHelper.getImageFileSize(image);
+            if (size >= 0 && size <= mVideoSizeLimit) {
+                if (mVideoOverlay == null) {
+                    mVideoOverlay = getResources().getDrawable(
+                            R.drawable.ic_gallery_video_overlay);
+                }
+                overlay = mVideoOverlay;
+            } else {
+                if (mVideoMmsErrorOverlay == null) {
+                    mVideoMmsErrorOverlay = getResources().getDrawable(
+                            R.drawable.ic_error_mms_video_overlay);
+                }
+                overlay = mVideoMmsErrorOverlay;
+                paint.setARGB(0x80, 0x00, 0x00, 0x00);
+                canvas.drawRect(xPos, yPos, xPos + w, yPos + h, paint);
+            }
+            int width = overlay.getIntrinsicWidth();
+            int height = overlay.getIntrinsicHeight();
+            int left = (w - width) / 2 + xPos;
+            int top = (h - height) / 2 + yPos;
+            Rect newBounds =
+                    new Rect(left, top, left + width, top + height);
+            overlay.setBounds(newBounds);
+            overlay.draw(canvas);
+        }
+
+        if (mMultiSelected != null && mMultiSelected.contains(image)) {
+            initializeMultiSelectDrawables();
+
+            Drawable checkBox = mMultiSelectTrue;
+            int width = checkBox.getIntrinsicWidth();
+            int height = checkBox.getIntrinsicHeight();
+            int left = 5 + xPos;
+            int top = h - height - 5 + yPos;
+            checkBox.setBounds(new Rect(
+                    left, top, left + width, top + height));
+            checkBox.draw(canvas);
+        }
+    }
+
+    private void initializeMultiSelectDrawables() {
+        if (mMultiSelectTrue == null) {
+            mMultiSelectTrue = getResources()
+                    .getDrawable(R.drawable.btn_check_buttonless_on);
+        }
+    }
+
+    private Bitmap mMissingImageThumbnailBitmap;
+    private Bitmap mMissingVideoThumbnailBitmap;
+
+    // Create this bitmap lazily, and only once for all the ImageBlocks to
+    // use
+    public Bitmap getErrorBitmap(IImage image) {
+        if (ImageManager.isImage(image)) {
+            if (mMissingImageThumbnailBitmap == null) {
+                mMissingImageThumbnailBitmap = BitmapFactory.decodeResource(
+                        getResources(),
+                        R.drawable.ic_missing_thumbnail_picture);
+            }
+            return mMissingImageThumbnailBitmap;
+        } else {
+            if (mMissingVideoThumbnailBitmap == null) {
+                mMissingVideoThumbnailBitmap = BitmapFactory.decodeResource(
+                        getResources(), R.drawable.ic_missing_thumbnail_video);
+            }
+            return mMissingVideoThumbnailBitmap;
+        }
+    }
+
 }