Code drop from //branches/cupcake/...@124589
diff --git a/src/com/android/camera/ActionMenuButton.java b/src/com/android/camera/ActionMenuButton.java
new file mode 100644
index 0000000..65e1f0e
--- /dev/null
+++ b/src/com/android/camera/ActionMenuButton.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.text.Layout;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
+ * because we want to make the bubble taller than the text and TextView's clip is
+ * too aggressive.
+ */
+public class ActionMenuButton extends TextView {
+    private static final float CORNER_RADIUS = 8.0f;
+    private static final float PADDING_H = 5.0f;
+    private static final float PADDING_V = 1.0f;
+
+    private final RectF mRect = new RectF();
+    private Paint mPaint;
+
+    public ActionMenuButton(Context context) {
+        super(context);
+        init();
+    }
+
+    public ActionMenuButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public ActionMenuButton(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        init();
+    }
+
+    private void init() {
+        setFocusable(true);
+
+        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mPaint.setColor(getContext().getResources().getColor(R.color.bubble_dark_background));
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        invalidate();
+        super.drawableStateChanged();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        final Layout layout = getLayout();
+        final RectF rect = mRect;
+        final int left = getCompoundPaddingLeft();
+        final int top = getExtendedPaddingTop();
+
+        rect.set(left + layout.getLineLeft(0) - PADDING_H,
+                top + layout.getLineTop(0) - PADDING_V,
+                Math.min(left + layout.getLineRight(0) + PADDING_H, mScrollX + mRight - mLeft),
+                top + layout.getLineBottom(0) + PADDING_V);
+        canvas.drawRoundRect(rect, CORNER_RADIUS, CORNER_RADIUS, mPaint);
+
+        super.draw(canvas);
+    }
+}
diff --git a/src/com/android/camera/BufferedInputStream.java b/src/com/android/camera/BufferedInputStream.java
deleted file mode 100644
index 1505e87..0000000
--- a/src/com/android/camera/BufferedInputStream.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-
-package com.android.camera;
-/*
-*  This file derives from the Apache version of the BufferedInputStream.
-*  Mods to support passing in the buffer rather than creating one directly.
-*/
-import java.io.InputStream;
-import java.io.FilterInputStream;
-import java.io.IOException;
-
-public class BufferedInputStream extends FilterInputStream {
-    protected byte[] buf;
-    protected int count;
-    protected int marklimit;
-    protected int markpos = -1;
-    protected int pos;
-
-    private boolean closed = false;
-
-    public BufferedInputStream(InputStream in, byte [] buffer) {
-        super(in);
-        buf = buffer;
-        if (buf == null) {
-            throw new java.security.InvalidParameterException();
-        }
-    }
-
-    @Override
-    public synchronized int available() throws IOException {
-        return count - pos + in.available();
-    }
-
-    @Override
-    public synchronized void close() throws IOException {
-        if (null != in) {
-            super.close();
-            in = null;
-        }
-        buf = null;
-        closed = true;
-    }
-
-    private int fillbuf() throws IOException {
-        if (markpos == -1 || (pos - markpos >= marklimit)) {
-            /* Mark position not set or exceeded readlimit */
-            int result = in.read(buf);
-            if (result > 0) {
-                markpos = -1;
-                pos = 0;
-                count = result == -1 ? 0 : result;
-            }
-            return result;
-        }
-        if (markpos == 0 && marklimit > buf.length) {
-            /* Increase buffer size to accomodate the readlimit */
-            int newLength = buf.length * 2;
-            if (newLength > marklimit) {
-                newLength = marklimit;
-            }
-            byte[] newbuf = new byte[newLength];
-            System.arraycopy(buf, 0, newbuf, 0, buf.length);
-            buf = newbuf;
-        } else if (markpos > 0) {
-            System.arraycopy(buf, markpos, buf, 0, buf.length - markpos);
-        }
-        /* Set the new position and mark position */
-        pos -= markpos;
-        count = markpos = 0;
-        int bytesread = in.read(buf, pos, buf.length - pos);
-        count = bytesread <= 0 ? pos : pos + bytesread;
-        return bytesread;
-    }
-
-    @Override
-    public synchronized void mark(int readlimit) {
-        marklimit = readlimit;
-        markpos = pos;
-    }
-
-    @Override
-    public boolean markSupported() {
-        return true;
-    }
-
-    @Override
-    public synchronized int read() throws IOException {
-        if (in == null) {
-            // K0059=Stream is closed
-            throw new IOException(); //$NON-NLS-1$
-        }
-
-        /* Are there buffered bytes available? */
-        if (pos >= count && fillbuf() == -1) {
-            return -1; /* no, fill buffer */
-        }
-
-        /* Did filling the buffer fail with -1 (EOF)? */
-        if (count - pos > 0) {
-            return buf[pos++] & 0xFF;
-        }
-        return -1;
-    }
-
-    @Override
-    public synchronized int read(byte[] buffer, int offset, int length)
-            throws IOException {
-        if (closed) {
-            // K0059=Stream is closed
-            throw new IOException(); //$NON-NLS-1$
-        }
-        // avoid int overflow
-        if (offset > buffer.length - length || offset < 0 || length < 0) {
-            throw new IndexOutOfBoundsException();
-        }
-        if (length == 0) {
-            return 0;
-        }
-        if (null == buf) {
-            throw new IOException(); //$NON-NLS-1$
-        }
-
-        int required;
-        if (pos < count) {
-            /* There are bytes available in the buffer. */
-            int copylength = count - pos >= length ? length : count - pos;
-            System.arraycopy(buf, pos, buffer, offset, copylength);
-            pos += copylength;
-            if (copylength == length || in.available() == 0) {
-                return copylength;
-            }
-            offset += copylength;
-            required = length - copylength;
-        } else {
-            required = length;
-        }
-
-        while (true) {
-            int read;
-            /*
-             * If we're not marked and the required size is greater than the
-             * buffer, simply read the bytes directly bypassing the buffer.
-             */
-            if (markpos == -1 && required >= buf.length) {
-                read = in.read(buffer, offset, required);
-                if (read == -1) {
-                    return required == length ? -1 : length - required;
-                }
-            } else {
-                if (fillbuf() == -1) {
-                    return required == length ? -1 : length - required;
-                }
-                read = count - pos >= required ? required : count - pos;
-                System.arraycopy(buf, pos, buffer, offset, read);
-                pos += read;
-            }
-            required -= read;
-            if (required == 0) {
-                return length;
-            }
-            if (in.available() == 0) {
-                return length - required;
-            }
-            offset += read;
-        }
-    }
-
-    @Override
-    public synchronized void reset() throws IOException {
-        if (closed) {
-            // K0059=Stream is closed
-            throw new IOException(); //$NON-NLS-1$    
-        }
-        if (-1 == markpos) {
-            // K005a=Mark has been invalidated.
-            throw new IOException(); //$NON-NLS-1$
-        }
-        pos = markpos;
-    }
-
-    @Override
-    public synchronized long skip(long amount) throws IOException {
-        if (null == in) {
-            // K0059=Stream is closed
-            throw new IOException(); //$NON-NLS-1$
-        }
-        if (amount < 1) {
-            return 0;
-        }
-
-        if (count - pos >= amount) {
-            pos += amount;
-            return amount;
-        }
-        long read = count - pos;
-        pos = count;
-
-        if (markpos != -1) {
-            if (amount <= marklimit) {
-                if (fillbuf() == -1) {
-                    return read;
-                }
-                if (count - pos >= amount - read) {
-                    pos += amount - read;
-                    return amount;
-                }
-                // Couldn't get all the bytes, skip what we read
-                read += (count - pos);
-                pos = count;
-                return read;
-            }
-            markpos = -1;
-        }
-        return read + in.skip(amount - read);
-    }
-}
diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java
index ef99842..b79e7d1 100644
--- a/src/com/android/camera/Camera.java
+++ b/src/com/android/camera/Camera.java
@@ -16,8 +16,16 @@
 
 package com.android.camera;
 
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+
 import android.app.Activity;
 import android.app.ProgressDialog;
+import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -46,11 +54,10 @@
 import android.os.Message;
 import android.os.StatFs;
 import android.os.SystemClock;
-import android.pim.DateFormat;
 import android.preference.PreferenceManager;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Images;
-import android.provider.MediaStore.Video;
+import android.text.format.DateFormat;
 import android.util.Config;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -69,57 +76,60 @@
 import android.widget.ImageView;
 import android.widget.Toast;
 
-import java.util.ArrayList;
-
 public class Camera extends Activity implements View.OnClickListener, SurfaceHolder.Callback {
-    
+
     private static final String TAG = "camera";
-    
+
     private static final boolean DEBUG = false;
-    
+
     private static final int CROP_MSG = 1;
     private static final int KEEP = 2;
     private static final int RESTART_PREVIEW = 3;
     private static final int CLEAR_SCREEN_DELAY = 4;
-    
+
     private static final int SCREEN_DELAY = 2 * 60 * 1000;
+    private static final int POST_PICTURE_ALERT_TIMEOUT = 6 * 1000;
     private static final int FOCUS_BEEP_VOLUME = 100;
 
     private static final int NO_STORAGE_ERROR = -1;
     private static final int CANNOT_STAT_ERROR = -2;
-    
+
     public static final int MENU_SWITCH_TO_VIDEO = 0;
-    public static final int MENU_FLASH_SETTING = 1;
-    public static final int MENU_FLASH_AUTO = 2;
-    public static final int MENU_FLASH_ON = 3;
-    public static final int MENU_FLASH_OFF = 4;
-    public static final int MENU_SETTINGS = 5;
-    public static final int MENU_GALLERY_PHOTOS = 6;
+    public static final int MENU_SWITCH_TO_CAMERA = 1;
+    public static final int MENU_FLASH_SETTING = 2;
+    public static final int MENU_FLASH_AUTO = 3;
+    public static final int MENU_FLASH_ON = 4;
+    public static final int MENU_FLASH_OFF = 5;
+    public static final int MENU_SETTINGS = 6;
+    public static final int MENU_GALLERY_PHOTOS = 7;
+    public static final int MENU_GALLERY_VIDEOS = 8;
     public static final int MENU_SAVE_SELECT_PHOTOS = 30;
     public static final int MENU_SAVE_NEW_PHOTO = 31;
     public static final int MENU_SAVE_SELECTVIDEO = 32;
     public static final int MENU_SAVE_TAKE_NEW_VIDEO = 33;
     public static final int MENU_SAVE_GALLERY_PHOTO = 34;
-    public static final int MENU_SAVE_GALLERY_VIDEO_PHOTO = 35; 
-    public static final int MENU_SAVE_CAMERA_DONE = 36; 
+    public static final int MENU_SAVE_GALLERY_VIDEO_PHOTO = 35;
+    public static final int MENU_SAVE_CAMERA_DONE = 36;
     public static final int MENU_SAVE_CAMERA_VIDEO_DONE = 37;
-    
+
     Toast mToast;
     OrientationListener mOrientationListener;
     int mLastOrientation = OrientationListener.ORIENTATION_UNKNOWN;
     SharedPreferences mPreferences;
-    
+
     static final int IDLE = 1;
     static final int SNAPSHOT_IN_PROGRESS = 2;
     static final int SNAPSHOT_COMPLETED = 3;
-    
+
     int mStatus = IDLE;
+    static final String sTempCropFilename = "crop-temp";
 
     android.hardware.Camera mCameraDevice;
-    SurfaceView mSurfaceView;
+    VideoPreview mSurfaceView;
     SurfaceHolder mSurfaceHolder = null;
     ImageView mBlackout = null;
 
+    int mOriginalViewFinderWidth, mOriginalViewFinderHeight;
     int mViewFinderWidth, mViewFinderHeight;
     boolean mPreviewing = false;
 
@@ -127,7 +137,7 @@
 
     Capturer mCaptureObject;
     ImageCapture mImageCapture = null;
-    
+
     boolean mPausing = false;
 
     boolean mIsFocusing = false;
@@ -139,43 +149,39 @@
     boolean mDidRegister = false;
 
     int mCurrentZoomIndex = 0;
-    
-    private static final int STILL_MODE = 1;
-    private static final int VIDEO_MODE = 2;
-    private int mMode = STILL_MODE;
 
     ArrayList<MenuItem> mGalleryItems = new ArrayList<MenuItem>();
-    
+
     boolean mMenuSelectionMade;
-    
+
     View mPostPictureAlert;
     LocationManager mLocationManager = null;
 
     private Animation mFocusBlinkAnimation;
     private View mFocusIndicator;
     private ToneGenerator mFocusToneGenerator;
-    
+
+
     private ShutterCallback mShutterCallback = new ShutterCallback();
     private RawPictureCallback mRawPictureCallback = new RawPictureCallback();
-    private JpegPictureCallback mJpegPictureCallback = new JpegPictureCallback();
     private AutoFocusCallback mAutoFocusCallback = new AutoFocusCallback();
     private long mShutterPressTime;
     private int mPicturesRemaining;
 
     private boolean mKeepAndRestartPreview;
 
-    private Handler mHandler = new MainHandler(); 
+    private Handler mHandler = new MainHandler();
     private ProgressDialog mSavingProgress;
-    
+
     interface Capturer {
-    	Uri getLastCaptureUri();
-    	void onSnap();
+        Uri getLastCaptureUri();
+        void onSnap();
         void dismissFreezeFrame(boolean keep);
-    	void cancelSave();
-    	void cancelAutoDismiss();
-    	void setDone(boolean wait);
+        void cancelSave();
+        void cancelAutoDismiss();
+        void setDone(boolean wait);
     }
-    
+
     private void cancelSavingNotification() {
         if (mToast != null) {
             mToast.cancel();
@@ -194,15 +200,15 @@
                         mSavingProgress.cancel();
                         mSavingProgress = null;
                     }
-                    
+
                     mKeepAndRestartPreview = true;
-                    
+
                     if (msg.obj != null) {
                         mHandler.post((Runnable)msg.obj);
                     }
                     break;
                 }
-            
+
                 case RESTART_PREVIEW: {
                     if (mStatus == SNAPSHOT_IN_PROGRESS) {
                         // We are still in the processing of taking the picture, wait.
@@ -210,11 +216,12 @@
                         // TODO remove polling
                         mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, 100);
                     } else if (mStatus == SNAPSHOT_COMPLETED){
+                        hidePostPictureAlert();
                         mCaptureObject.dismissFreezeFrame(true);
                     }
                     break;
                 }
-                
+
                 case CLEAR_SCREEN_DELAY: {
                     getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                     break;
@@ -222,13 +229,13 @@
             }
         }
     };
-    
+
     LocationListener [] mLocationListeners = new LocationListener[] {
             new LocationListener(LocationManager.GPS_PROVIDER),
             new LocationListener(LocationManager.NETWORK_PROVIDER)
     };
 
-    
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -248,17 +255,17 @@
             }
         }
     };
-    
+
     private class LocationListener implements android.location.LocationListener {
         Location mLastLocation;
         boolean mValid = false;
         String mProvider;
-        
+
         public LocationListener(String provider) {
             mProvider = provider;
             mLastLocation = new Location(mProvider);
         }
-        
+
         public void onLocationChanged(Location newLocation) {
             if (newLocation.getLatitude() == 0.0 && newLocation.getLongitude() == 0.0) {
                 // Hack to filter out 0.0,0.0 locations
@@ -267,7 +274,7 @@
             mLastLocation.set(newLocation);
             mValid = true;
         }
-        
+
         public void onProviderEnabled(String provider) {
         }
 
@@ -280,12 +287,12 @@
                 mValid = false;
             }
         }
-        
+
         public Location current() {
             return mValid ? mLastLocation : null;
         }
     };
-    
+
     private long mRawPictureCallbackTime;
 
     private boolean mImageSavingItem = false;
@@ -302,25 +309,31 @@
             }
         }
     };
-    
-    private final class RawPictureCallback implements PictureCallback { 
+
+    private final class RawPictureCallback implements PictureCallback {
         public void onPictureTaken(byte [] rawData, android.hardware.Camera camera) {
             if (Config.LOGV)
                 Log.v(TAG, "got RawPictureCallback...");
             mRawPictureCallbackTime = System.currentTimeMillis();
             mBlackout.setVisibility(View.INVISIBLE);
             if (!isPickIntent() && mPreferences.getBoolean("pref_camera_postpicturemenu_key", true)) {
-                mPostPictureAlert.setVisibility(View.VISIBLE);
+                showPostPictureAlert();
             }
         }
     };
-    
+
     private final class JpegPictureCallback implements PictureCallback {
+        Location mLocation;
+
+        public JpegPictureCallback(Location loc) {
+            mLocation = loc;
+        }
+
         public void onPictureTaken(byte [] jpegData, android.hardware.Camera camera) {
             if (Config.LOGV)
                 Log.v(TAG, "got JpegPictureCallback...");
 
-            mImageCapture.storeImage(jpegData, camera);
+            mImageCapture.storeImage(jpegData, camera, mLocation);
 
             mStatus = SNAPSHOT_COMPLETED;
 
@@ -335,15 +348,15 @@
             if (mKeepAndRestartPreview) {
                 mKeepAndRestartPreview = false;
                 mPostPictureAlert.setVisibility(View.INVISIBLE);
-                
+
                 // Post this message so that we can finish processing the request. This also
                 // prevents the preview from showing up before mPostPictureAlert is dismissed.
                 mHandler.sendEmptyMessage(RESTART_PREVIEW);
             }
-            
+
         }
     };
-    
+
     private final class AutoFocusCallback implements android.hardware.Camera.AutoFocusCallback {
         public void onAutoFocus(boolean focused, android.hardware.Camera camera) {
             mIsFocusing = false;
@@ -354,42 +367,44 @@
                     mCaptureObject.onSnap();
                     clearFocus();
                 } else {
-                    mFocusToneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP2);
+                    ToneGenerator tg = mFocusToneGenerator;
+                    if (tg != null)
+                       tg.startTone(ToneGenerator.TONE_PROP_BEEP2);
                 }
                 mCaptureOnFocus = false;
             }
             updateFocusIndicator();
         }
     };
-    
+
     private class ImageCapture implements Capturer {
-        
+
         private boolean mCancel = false;
         private boolean mCapturing = false;
-        
+
         private Uri mLastContentUri;
         private ImageManager.IAddImage_cancelable mAddImageCancelable;
 
         Bitmap mCaptureOnlyBitmap;
-        
+
         /** These member variables are used for various debug timings */
         private long mThreadTimeStart;
         private long mThreadTimeEnd;
         private long mWallTimeStart;
         private long mWallTimeEnd;
-        
+
 
         public ImageCapture() {
         }
 
-        /** 
+        /**
          * This method sets whether or not we are capturing a picture. This method must be called
          * with the ImageCapture.this lock held.
          */
         public void setCapturingLocked(boolean capturing) {
             mCapturing = capturing;
         }
-        
+
         /*
          * Tell the ImageCapture thread to exit when possible.
          */
@@ -409,7 +424,7 @@
             } else {
                 Toast.makeText(Camera.this, R.string.camera_tossing, Toast.LENGTH_SHORT).show();
             }
-            
+
             if (mStatus == SNAPSHOT_IN_PROGRESS) {
                 // If we are still in the process of taking a picture, then just post a message.
                 mHandler.sendEmptyMessage(RESTART_PREVIEW);
@@ -417,31 +432,31 @@
                 restartPreview();
             }
         }
-        
+
         private void startTiming() {
             mWallTimeStart = SystemClock.elapsedRealtime();
             mThreadTimeStart = Debug.threadCpuTimeNanos();
         }
-        
+
         private void stopTiming() {
             mThreadTimeEnd = Debug.threadCpuTimeNanos();
             mWallTimeEnd = SystemClock.elapsedRealtime();
         }
-        
-        private void storeImage(byte[] data) {
+
+        private void storeImage(byte[] data, Location loc) {
             try {
                 if (DEBUG) {
                     startTiming();
                 }
-                    
+                long dateTaken = System.currentTimeMillis();
                 mLastContentUri = ImageManager.instance().addImage(
                         Camera.this,
                         mContentResolver,
-                        DateFormat.format("yyyy-MM-dd kk.mm.ss", System.currentTimeMillis()).toString(), 
+                        createName(dateTaken),
                         "",
-                        System.currentTimeMillis(),
+                        dateTaken,
                         // location for the database goes here
-                        null,
+                        loc,
                         0,   // the dsp will use the right orientation so don't "double set it"
                         ImageManager.CAMERA_IMAGE_BUCKET_NAME,
                         null);
@@ -456,46 +471,46 @@
                     mAddImageCancelable.get();
                     mAddImageCancelable = null;
                 }
-                
+
                 if (DEBUG) {
                     stopTiming();
                     Log.d(TAG, "Storing image took " + (mWallTimeEnd - mWallTimeStart) + " ms. " +
-                            "Thread time was " + ((mThreadTimeEnd - mThreadTimeStart) / 1000000) + 
+                            "Thread time was " + ((mThreadTimeEnd - mThreadTimeStart) / 1000000) +
                             " ms.");
                 }
             } catch (Exception ex) {
                 Log.e(TAG, "Exception while compressing image.", ex);
             }
         }
-        
-        public void storeImage(byte[] data, android.hardware.Camera camera) {
+
+        public void storeImage(byte[] data, android.hardware.Camera camera, Location loc) {
             boolean captureOnly = isPickIntent();
-            
+
             if (!captureOnly) {
-                storeImage(data);
+                storeImage(data, loc);
                 sendBroadcast(new Intent("com.android.camera.NEW_PICTURE", mLastContentUri));
             } else {
                 BitmapFactory.Options options = new BitmapFactory.Options();
                 options.inSampleSize = 4;
-                
+
                 if (DEBUG) {
                     startTiming();
                 }
-                
+
                 mCaptureOnlyBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);
-                
+
                 if (DEBUG) {
                     stopTiming();
                     Log.d(TAG, "Decoded mCaptureOnly bitmap (" + mCaptureOnlyBitmap.getWidth() +
-                            "x" + mCaptureOnlyBitmap.getHeight() + " ) in " + 
-                            (mWallTimeEnd - mWallTimeStart) + " ms. Thread time was " +  
+                            "x" + mCaptureOnlyBitmap.getHeight() + " ) in " +
+                            (mWallTimeEnd - mWallTimeStart) + " ms. Thread time was " +
                             ((mThreadTimeEnd - mThreadTimeStart) / 1000000) + " ms.");
                 }
-                
+
                 openOptionsMenu();
             }
-            
-            
+
+
             mCapturing = false;
             if (mPausing) {
                 closeCamera();
@@ -510,9 +525,9 @@
             if (!mCapturing) {
                 return;
             }
-            
+
             mCancel = true;
-            
+
             if (mAddImageCancelable != null) {
                 mAddImageCancelable.cancel();
             }
@@ -526,25 +541,25 @@
             if (mCameraDevice == null) {
                 return;
             }
-            
+
             mCancel = false;
             mCapturing = true;
 
             capture(captureOnly);
         }
-      
+
         public Uri getLastCaptureUri() {
             return mLastContentUri;
         }
-        
+
         public Bitmap getLastBitmap() {
             return mCaptureOnlyBitmap;
         }
-        
+
         private void capture(boolean captureOnly) {
             mPreviewing = false;
             mCaptureOnlyBitmap = null;
-            
+
             final int latchedOrientation = ImageManager.roundOrientation(mLastOrientation + 90);
 
             Boolean recordLocation = mPreferences.getBoolean("pref_camera_recordlocation_key", false);
@@ -554,35 +569,38 @@
             // get large. 85 is a good compromise between the two.
             parameters.set("jpeg-quality", 85);
             parameters.set("rotation", latchedOrientation);
+
+            parameters.remove("gps-latitude");
+            parameters.remove("gps-longitude");
+            parameters.remove("gps-altitude");
+            parameters.remove("gps-timestamp");
+
             if (loc != null) {
-                parameters.set("gps-latitude",  String.valueOf(loc.getLatitude()));
-                parameters.set("gps-longitude", String.valueOf(loc.getLongitude()));
-                parameters.set("gps-altitude",  String.valueOf(loc.getAltitude()));
-                parameters.set("gps-timestamp", String.valueOf(loc.getTime()));
-            } else {
-                parameters.remove("gps-latitude");
-                parameters.remove("gps-longitude");
-                parameters.remove("gps-altitude");
-                parameters.remove("gps-timestamp");
+                double lat = loc.getLatitude();
+                double lon = loc.getLongitude();
+
+                if (lat != 0D && lon != 0d) {
+                    parameters.set("gps-latitude",  String.valueOf(lat));
+                    parameters.set("gps-longitude", String.valueOf(lon));
+                    if (loc.hasAltitude())
+                        parameters.set("gps-altitude",  String.valueOf(loc.getAltitude()));
+                    if (loc.getTime() != 0)
+                        parameters.set("gps-timestamp", String.valueOf(loc.getTime()));
+                } else {
+                    loc = null;
+                }
             }
 
             Size pictureSize = parameters.getPictureSize();
-            Size previewSize = parameters.getPreviewSize();
 
             // resize the SurfaceView to the aspect-ratio of the still image
             // and so that we can see the full image that was taken
-            ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();
-            if (pictureSize.width*previewSize.height < previewSize.width*pictureSize.height) {
-                lp.width = (pictureSize.width * previewSize.height) / pictureSize.height; 
-            } else {
-                lp.height = (pictureSize.height * previewSize.width) / pictureSize.width; 
-            }
-            mSurfaceView.requestLayout();
+            mSurfaceView.setAspectRatio(pictureSize.width, pictureSize.height);
 
             mCameraDevice.setParameters(parameters);
-       
-            mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
-            
+
+            mCameraDevice.takePicture(mShutterCallback, mRawPictureCallback, new JpegPictureCallback(loc));
+
             mBlackout.setVisibility(View.VISIBLE);
             // Comment this out for now until we can decode the preview frame. This currently
             // just animates black-on-black because the surface flinger blacks out the surface
@@ -612,7 +630,7 @@
                 showStorageToast();
                 return;
             }
-            
+
             mStatus = SNAPSHOT_IN_PROGRESS;
 
             mKeepAndRestartPreview = !mPreferences.getBoolean("pref_camera_postpicturemenu_key", true);
@@ -626,6 +644,11 @@
         }
     }
 
+
+    static private String createName(long dateTaken) {
+        return DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken).toString();
+    }
+
     static public Matrix GetDisplayMatrix(Bitmap b, ImageView v) {
         Matrix m = new Matrix();
         float bw = (float)b.getWidth();
@@ -649,12 +672,12 @@
 
     private void postAfterKeep(final Runnable r) {
         Resources res = getResources();
-        
+
         if (mSavingProgress != null) {
-            mSavingProgress = ProgressDialog.show(this, res.getString(R.string.savingImage), 
+            mSavingProgress = ProgressDialog.show(this, res.getString(R.string.savingImage),
                     res.getString(R.string.wait));
         }
-        
+
         Message msg = mHandler.obtainMessage(KEEP);
         msg.obj = r;
         msg.sendToTarget();
@@ -672,38 +695,35 @@
 
         //setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
         requestWindowFeature(Window.FEATURE_PROGRESS);
-        
+
         Window win = getWindow();
         win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
         setContentView(R.layout.camera);
 
-        mSurfaceView = (SurfaceView) findViewById(R.id.camera_preview);
-        
+        mSurfaceView = (VideoPreview) findViewById(R.id.camera_preview);
+
         // don't set mSurfaceHolder here. We have it set ONLY within
         // surfaceCreated / surfaceDestroyed, other parts of the code
         // assume that when it is set, the surface is also set.
         SurfaceHolder holder = mSurfaceView.getHolder();
         holder.addCallback(this);
         holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
-        
+
         mBlackout = (ImageView) findViewById(R.id.blackout);
         mBlackout.setBackgroundDrawable(new ColorDrawable(0xFF000000));
 
         mPostPictureAlert = findViewById(R.id.post_picture_panel);
         View b;
-        
-        b = findViewById(R.id.save);
-        b.setOnClickListener(this);
-        
+
         b = findViewById(R.id.discard);
         b.setOnClickListener(this);
-        
+
         b = findViewById(R.id.share);
         b.setOnClickListener(this);
 
         b = findViewById(R.id.setas);
         b.setOnClickListener(this);
-        
+
         try {
             mClickSound = new MediaPlayer();
             AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.camera_click);
@@ -719,27 +739,27 @@
         } catch (Exception ex) {
             Log.w(TAG, "Couldn't create click sound", ex);
         }
-        
+
         mOrientationListener = new OrientationListener(this) {
             public void onOrientationChanged(int orientation) {
                 mLastOrientation = orientation;
             }
         };
-        
+
         mFocusIndicator = findViewById(R.id.focus_indicator);
         mFocusBlinkAnimation = AnimationUtils.loadAnimation(this, R.anim.auto_focus_blink);
         mFocusBlinkAnimation.setRepeatCount(Animation.INFINITE);
         mFocusBlinkAnimation.setRepeatMode(Animation.REVERSE);
     }
-    
+
     @Override
     public void onStart() {
         super.onStart();
-        
+
         final View hintView = findViewById(R.id.hint_toast);
         if (hintView != null)
             hintView.setVisibility(View.GONE);
-        
+
         Thread t = new Thread(new Runnable() {
             public void run() {
                 final boolean storageOK = calculatePicturesRemaining() > 0;
@@ -761,7 +781,7 @@
                             hintView.setVisibility(View.GONE);
                         }
                     }, 3000);
-                } else {            
+                } else {
                     mHandler.post(new Runnable() {
                         public void run() {
                             hintView.setVisibility(View.GONE);
@@ -773,15 +793,17 @@
         });
         t.start();
     }
-    
+
     public void onClick(View v) {
         switch (v.getId()) {
+        /*
             case R.id.save: {
-                mPostPictureAlert.setVisibility(View.INVISIBLE);
+                mPostPictureAlert.setVisibility(View.GONE);
                 postAfterKeep(null);
                 break;
             }
-                
+         */
+
             case R.id.discard: {
                 if (mCaptureObject != null) {
                     mCaptureObject.cancelSave();
@@ -791,12 +813,12 @@
                     }
                     mCaptureObject.dismissFreezeFrame(true);
                 }
-                mPostPictureAlert.setVisibility(View.INVISIBLE);
+                mPostPictureAlert.setVisibility(View.GONE);
                 break;
             }
-            
+
             case R.id.share: {
-                mPostPictureAlert.setVisibility(View.INVISIBLE);
+                mPostPictureAlert.setVisibility(View.GONE);
                 postAfterKeep(new Runnable() {
                     public void run() {
                         Uri u = mCaptureObject.getLastCaptureUri();
@@ -807,15 +829,15 @@
                         try {
                             startActivity(Intent.createChooser(intent, getText(R.string.sendImage)));
                         } catch (android.content.ActivityNotFoundException ex) {
-                            Toast.makeText(Camera.this, R.string.no_way_to_share_video, Toast.LENGTH_SHORT).show();
+                            Toast.makeText(Camera.this, R.string.no_way_to_share_image, Toast.LENGTH_SHORT).show();
                         }
                     }
                 });
                 break;
             }
-            
+
             case R.id.setas: {
-                mPostPictureAlert.setVisibility(View.INVISIBLE);
+                mPostPictureAlert.setVisibility(View.GONE);
                 postAfterKeep(new Runnable() {
                     public void run() {
                         Uri u = mCaptureObject.getLastCaptureUri();
@@ -831,9 +853,6 @@
             }
         }
     }
-    
-    void keepVideo() {
-    };
 
     private void showStorageToast() {
         String noStorageText = null;
@@ -854,14 +873,10 @@
     public void onResume() {
         super.onResume();
         mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
-        
+
         mPausing = false;
         mOrientationListener.enable();
 
-        if (isPickIntent()) {
-            mMode = STILL_MODE;
-        }
-
         // install an intent filter to receive SD card related events.
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
         intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
@@ -874,14 +889,19 @@
         mImageCapture = new ImageCapture();
 
         restartPreview();
-        
+
         if (mPreferences.getBoolean("pref_camera_recordlocation_key", false))
             startReceivingLocationUpdates();
-        
+
         updateFocusIndicator();
 
-        mFocusToneGenerator = new ToneGenerator(AudioManager.STREAM_SYSTEM, FOCUS_BEEP_VOLUME);
-        
+        try {
+            mFocusToneGenerator = new ToneGenerator(AudioManager.STREAM_SYSTEM, FOCUS_BEEP_VOLUME);
+        } catch (RuntimeException e) {
+            Log.w(TAG, "Exception caught while creating local tone generator: " + e);
+            mFocusToneGenerator = null;
+        }
+
         mBlackout.setVisibility(View.INVISIBLE);
     }
 
@@ -905,7 +925,7 @@
 
         mPausing = true;
         mOrientationListener.disable();
-        
+
         stopPreview();
 
         if (!mImageCapture.mCapturing) {
@@ -916,8 +936,11 @@
             mDidRegister = false;
         }
         stopReceivingLocationUpdates();
-        mFocusToneGenerator.release();
-        mFocusToneGenerator = null;
+
+        if (mFocusToneGenerator != null) {
+            mFocusToneGenerator.release();
+            mFocusToneGenerator = null;
+        }
 
         super.onPause();
     }
@@ -935,6 +958,10 @@
                 }
                 setResult(resultCode, intent);
                 finish();
+
+                File path = getFileStreamPath(sTempCropFilename);
+                path.delete();
+
                 break;
             }
         }
@@ -950,13 +977,13 @@
             }
         }
     }
-    
+
     private void clearFocus() {
         mIsFocusing = false;
         mIsFocused = false;
         mIsFocusButtonPressed = false;
     }
-    
+
     private void updateFocusIndicator() {
         mHandler.post(new Runnable() {
             public void run() {
@@ -976,10 +1003,12 @@
         });
     }
 
+
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
         getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
         switch (keyCode) {
             case KeyEvent.KEYCODE_BACK:
                 if (mStatus == SNAPSHOT_IN_PROGRESS || mStatus == SNAPSHOT_COMPLETED) {
@@ -1049,12 +1078,10 @@
     }
 
     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
-        if (mMode == STILL_MODE) {
-            // if we're creating the surface, start the preview as well.
-            boolean preview = holder.isCreating();
-            setViewFinder(w, h, preview);
-            mCaptureObject = mImageCapture;
-        }
+        // if we're creating the surface, start the preview as well.
+        boolean preview = holder.isCreating();
+        setViewFinder(w, h, preview);
+        mCaptureObject = mImageCapture;
     }
 
     public void surfaceCreated(SurfaceHolder holder) {
@@ -1065,7 +1092,7 @@
         stopPreview();
         mSurfaceHolder = null;
     }
-    
+
     private void closeCamera() {
         if (mCameraDevice != null) {
             mCameraDevice.release();
@@ -1073,26 +1100,23 @@
             mPreviewing = false;
         }
     }
-    
+
     private boolean ensureCameraDevice() {
         if (mCameraDevice == null) {
             mCameraDevice = android.hardware.Camera.open();
         }
         return mCameraDevice != null;
     }
-    
+
     private void restartPreview() {
-        SurfaceView surfaceView = mSurfaceView;
-        if (surfaceView == null || 
+        VideoPreview surfaceView = mSurfaceView;
+        if (surfaceView == null ||
                 surfaceView.getWidth() == 0 || surfaceView.getHeight() == 0) {
             return;
         }
         // make sure the surfaceview fills the whole screen when previewing
-        ViewGroup.LayoutParams lp = surfaceView.getLayoutParams();
-        lp.width = ViewGroup.LayoutParams.FILL_PARENT;
-        lp.height = ViewGroup.LayoutParams.FILL_PARENT;
-        surfaceView.requestLayout();
-        setViewFinder(mViewFinderWidth, mViewFinderHeight, true);
+        surfaceView.setAspectRatio(VideoPreview.DONT_CARE);
+        setViewFinder(mOriginalViewFinderWidth, mOriginalViewFinderHeight, true);
         mStatus = IDLE;
 
         // Calculate this in advance of each shot so we don't add to shutter latency. It's true that
@@ -1102,37 +1126,41 @@
         // let the user take a picture, and delete that file if needed to save the new photo.
         calculatePicturesRemaining();
     }
-    
+
     private void setViewFinder(int w, int h, boolean startPreview) {
         if (mPausing)
             return;
-        
-        if (mPreviewing && 
-                w == mViewFinderWidth && 
+
+        if (mPreviewing &&
+                w == mViewFinderWidth &&
                 h == mViewFinderHeight) {
             return;
         }
-        
+
         if (!ensureCameraDevice())
             return;
-        
+
         if (mSurfaceHolder == null)
             return;
-        
+
         if (isFinishing())
             return;
-        
+
         if (mPausing)
             return;
-        
+
         // remember view finder size
         mViewFinderWidth = w;
         mViewFinderHeight = h;
+        if (mOriginalViewFinderHeight == 0) {
+            mOriginalViewFinderWidth = w;
+            mOriginalViewFinderHeight = h;
+        }
 
         if (startPreview == false)
             return;
 
-        /* 
+        /*
          * start the preview if we're asked to...
          */
 
@@ -1140,21 +1168,31 @@
         // stop the preview first (this will blank the screen).
         if (mPreviewing)
             stopPreview();
-        
+
         // this blanks the screen if the surface changed, no-op otherwise
-        mCameraDevice.setPreviewDisplay(mSurfaceHolder);
-        
+        try {
+            mCameraDevice.setPreviewDisplay(mSurfaceHolder);
+        } catch (IOException exception) {
+            mCameraDevice.release();
+            mCameraDevice = null;
+            // TODO: add more exception handling logic here
+            return;
+        }
 
         // request the preview size, the hardware may not honor it,
         // if we depended on it we would have to query the size again
         android.hardware.Camera.Parameters p = mCameraDevice.getParameters();
         p.setPreviewSize(w, h);
-        mCameraDevice.setParameters(p);
-        
-        
+        try {
+            mCameraDevice.setParameters(p);
+        } catch (IllegalArgumentException e) {
+            // Ignore this error, it happens in the simulator.
+        }
+
+
         final long wallTimeStart = SystemClock.elapsedRealtime();
         final long threadTimeStart = Debug.threadCpuTimeNanos();
-    
+
         final Object watchDogSync = new Object();
         Thread watchDog = new Thread(new Runnable() {
             public void run() {
@@ -1169,7 +1207,7 @@
                     }
                     if (mPreviewing)
                         break;
-                    
+
                     int delay = (int) (SystemClock.elapsedRealtime() - wallTimeStart) / 1000;
                     if (delay >= next_warning) {
                         if (delay < 120) {
@@ -1194,19 +1232,24 @@
 
         if (Config.LOGV)
             Log.v(TAG, "calling mCameraDevice.startPreview");
-        mCameraDevice.startPreview();
+        try {
+            mCameraDevice.startPreview();
+        } catch (Throwable e) {
+            // TODO: change Throwable to IOException once android.hardware.Camera.startPreview
+            // properly declares that it throws IOException.
+        }
         mPreviewing = true;
-        
+
         synchronized (watchDogSync) {
             watchDogSync.notify();
         }
-        
+
         long threadTimeEnd = Debug.threadCpuTimeNanos();
         long wallTimeEnd = SystemClock.elapsedRealtime();
         if ((wallTimeEnd - wallTimeStart) > 3000) {
             Log.w(TAG, "startPreview() to " + (wallTimeEnd - wallTimeStart) + " ms. Thread time was"
                     + (threadTimeEnd - threadTimeStart) / 1000000 + " ms.");
-        }        
+        }
     }
 
     private void stopPreview() {
@@ -1217,10 +1260,13 @@
     }
 
     void gotoGallery() {
-        Uri target = mMode == STILL_MODE ? Images.Media.INTERNAL_CONTENT_URI
-                                                : Video.Media.INTERNAL_CONTENT_URI;
+        Uri target = Images.Media.INTERNAL_CONTENT_URI;
         Intent intent = new Intent(Intent.ACTION_VIEW, target);
-        startActivity(intent);
+        try {
+            startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Could not start gallery activity", e);
+        }
     }
 
     void keep() {
@@ -1236,27 +1282,27 @@
             mCaptureObject.cancelSave();
         }
     };
-    
+
     private ImageManager.IImage getImageForURI(Uri uri) {
         ImageManager.IImageList list = ImageManager.instance().allImages(
                 this,
-                mContentResolver, 
-                dataLocation(), 
-                ImageManager.INCLUDE_IMAGES, 
+                mContentResolver,
+                dataLocation(),
+                ImageManager.INCLUDE_IMAGES,
                 ImageManager.SORT_ASCENDING);
         ImageManager.IImage image = list.getImageForUri(uri);
         list.deactivate();
         return image;
     }
-    
-    
+
+
     private void startReceivingLocationUpdates() {
         if (mLocationManager != null) {
             try {
                 mLocationManager.requestLocationUpdates(
-                        LocationManager.NETWORK_PROVIDER, 
-                        1000, 
-                        0F, 
+                        LocationManager.NETWORK_PROVIDER,
+                        1000,
+                        0F,
                         mLocationListeners[1]);
             } catch (java.lang.SecurityException ex) {
                 // ok
@@ -1267,9 +1313,9 @@
             }
             try {
                 mLocationManager.requestLocationUpdates(
-                        LocationManager.GPS_PROVIDER, 
-                        1000, 
-                        0F, 
+                        LocationManager.GPS_PROVIDER,
+                        1000,
+                        0F,
                         mLocationListeners[0]);
             } catch (java.lang.SecurityException ex) {
                 // ok
@@ -1280,7 +1326,7 @@
             }
         }
     }
-    
+
     private void stopReceivingLocationUpdates() {
         if (mLocationManager != null) {
             for (int i = 0; i < mLocationListeners.length; i++) {
@@ -1292,18 +1338,20 @@
             }
         }
     }
-    
+
     private Location getCurrentLocation() {
         Location l = null;
-        
-        // go in worst to best order
-        for (int i = 0; i < mLocationListeners.length && l == null; i++) {
+
+        // go in best to worst order
+        for (int i = 0; i < mLocationListeners.length; i++) {
             l = mLocationListeners[i].current();
+            if (l != null)
+                break;
         }
-        
+
         return l;
     }
-    
+
     @Override
     public void onOptionsMenuClosed(Menu menu) {
         super.onOptionsMenuClosed(menu);
@@ -1315,7 +1363,7 @@
             mHandler.sendEmptyMessage(RESTART_PREVIEW);
         }
     }
-    
+
     @Override
     public boolean onMenuOpened(int featureId, Menu menu) {
         if (featureId == Window.FEATURE_OPTIONS_PANEL) {
@@ -1327,28 +1375,25 @@
         }
         return super.onMenuOpened(featureId, menu);
     }
-    
+
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
         super.onPrepareOptionsMenu(menu);
 
         mMenuSelectionMade = false;
-        
+
         for (int i = 1; i <= MenuHelper.MENU_ITEM_MAX; i++) {
             if (i != MenuHelper.GENERIC_ITEM) {
                 menu.setGroupVisible(i, false);
             }
         }
-        
-        if (mMode == STILL_MODE) {
-            if (mStatus == SNAPSHOT_IN_PROGRESS || mStatus == SNAPSHOT_COMPLETED) {
-                menu.setGroupVisible(MenuHelper.IMAGE_SAVING_ITEM, true);
-                mImageSavingItem = true;
-            } else {
-                menu.setGroupVisible(MenuHelper.IMAGE_MODE_ITEM, true);
-                mImageSavingItem = false;
-            }
-        } else if (mMode == VIDEO_MODE) {
+
+        if (mStatus == SNAPSHOT_IN_PROGRESS || mStatus == SNAPSHOT_COMPLETED) {
+            menu.setGroupVisible(MenuHelper.IMAGE_SAVING_ITEM, true);
+            mImageSavingItem = true;
+        } else {
+            menu.setGroupVisible(MenuHelper.IMAGE_MODE_ITEM, true);
+            mImageSavingItem = false;
         }
 
         if (mCaptureObject != null)
@@ -1361,7 +1406,7 @@
         String action = getIntent().getAction();
         return (Intent.ACTION_PICK.equals(action) || MediaStore.ACTION_IMAGE_CAPTURE.equals(action));
     }
-    
+
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         super.onCreateOptionsMenu(menu);
@@ -1372,39 +1417,106 @@
                     Bitmap bitmap = mImageCapture.getLastBitmap();
                     mCaptureObject.setDone(true);
 
-                    // TODO scale the image down to something ridiculous until IPC gets straightened out
-                    float scale = .5F;
-                    Matrix m = new Matrix();
-                    m.setScale(scale, scale);
+                    String cropValue = null;
+                    Uri saveUri = null;
 
-                    bitmap = Bitmap.createBitmap(bitmap, 0, 0,
-                            bitmap.getWidth(),
-                            bitmap.getHeight(), 
-                            m, true);
-                    
                     Bundle myExtras = getIntent().getExtras();
-                    String cropValue = myExtras != null ? myExtras.getString("crop") : null;
-                    if (cropValue != null) {
+                    if (myExtras != null) {
+                        saveUri = (Uri) myExtras.getParcelable("output");
+                        cropValue = myExtras.getString("crop");
+                    }
+
+
+                    if (cropValue == null) {
+                        /*
+                         * First handle the no crop case -- just return the value.  If the caller
+                         * specifies a "save uri" then write the data to it's stream.  Otherwise,
+                         * pass back a scaled down version of the bitmap directly in the extras.
+                         */
+                        if (saveUri != null) {
+                            OutputStream outputStream = null;
+                            try {
+                                outputStream = mContentResolver.openOutputStream(saveUri);
+                                bitmap.compress(Bitmap.CompressFormat.JPEG, 75, outputStream);
+                                outputStream.close();
+
+                                setResult(RESULT_OK);
+                                finish();
+                            } catch (IOException ex) {
+                                //
+                            } finally {
+                                if (outputStream != null) {
+                                    try {
+                                        outputStream.close();
+                                    } catch (IOException ex) {
+
+                                    }
+                                }
+                            }
+                        } else {
+                            float scale = .5F;
+                            Matrix m = new Matrix();
+                            m.setScale(scale, scale);
+
+                            bitmap = Bitmap.createBitmap(bitmap, 0, 0,
+                                    bitmap.getWidth(),
+                                    bitmap.getHeight(),
+                                    m, true);
+
+                            setResult(RESULT_OK, new Intent("inline-data").putExtra("data", bitmap));
+                            finish();
+                        }
+                    }
+                    else {
+                        /*
+                         * Save the image to a temp file and invoke the cropper
+                         */
+                        Uri tempUri = null;
+                        FileOutputStream tempStream = null;
+                        try {
+                            File path = getFileStreamPath(sTempCropFilename);
+                            path.delete();
+                            tempStream = openFileOutput(sTempCropFilename, 0);
+                            bitmap.compress(Bitmap.CompressFormat.JPEG, 75, tempStream);
+                            tempStream.close();
+                            tempUri = Uri.fromFile(path);
+                        } catch (FileNotFoundException ex) {
+                            setResult(Activity.RESULT_CANCELED);
+                            finish();
+                            return true;
+                        } catch (IOException ex) {
+                            setResult(Activity.RESULT_CANCELED);
+                            finish();
+                            return true;
+                        } finally {
+                            if (tempStream != null) {
+                                try {
+                                    tempStream.close();
+                                } catch (IOException ex) {
+
+                                }
+                            }
+                        }
+
                         Bundle newExtras = new Bundle();
                         if (cropValue.equals("circle"))
                             newExtras.putString("circleCrop", "true");
-                        newExtras.putParcelable("data", bitmap);
+                        if (saveUri != null)
+                            newExtras.putParcelable("output", saveUri);
+                        else
+                            newExtras.putBoolean("return-data", true);
 
                         Intent cropIntent = new Intent();
                         cropIntent.setClass(Camera.this, CropImage.class);
+                        cropIntent.setData(tempUri);
                         cropIntent.putExtras(newExtras);
+
                         startActivityForResult(cropIntent, CROP_MSG);
-                    } else {
-                        Bundle extras = new Bundle();
-                        extras.putParcelable("data", bitmap);
-                        setResult(RESULT_OK, new Intent("inline-data")
-                                .putExtra("data", bitmap));
-                        finish();
                     }
                     return true;
                 }
             });
-            
+
             menu.add(MenuHelper.IMAGE_SAVING_ITEM, MENU_SAVE_NEW_PHOTO, 0, R.string.camera_takenewphoto).setOnMenuItemClickListener(new OnMenuItemClickListener() {
                 public boolean onMenuItemClick(MenuItem item) {
                     keep();
@@ -1422,9 +1534,10 @@
             MenuHelper.addImageMenuItems(
                     menu,
                     MenuHelper.INCLUDE_ALL & ~MenuHelper.INCLUDE_ROTATE_MENU,
+                    true,
                     Camera.this,
                     mHandler,
-                    
+
                     // Handler for deletion
                     new Runnable() {
                         public void run() {
@@ -1461,20 +1574,12 @@
                 }
             });
             gallery.setIcon(android.R.drawable.ic_menu_gallery);
-
-            mGalleryItems.add(menu.add(MenuHelper.VIDEO_SAVING_ITEM, MENU_SAVE_GALLERY_VIDEO_PHOTO, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                public boolean onMenuItemClick(MenuItem item) {
-                    keepVideo();
-                    gotoGallery();
-                    return true;
-                }
-            }));
-        }       
+        }
         return true;
     }
-    
+
     SelectedImageGetter mSelectedImageGetter =
-        new SelectedImageGetter() { 
+        new SelectedImageGetter() {
             public ImageManager.IImage getCurrentImage() {
                 return getImageForURI(getCurrentImageUri());
             }
@@ -1502,16 +1607,29 @@
         }
         return mPicturesRemaining;
     }
-    
+
     private void addBaseMenuItems(Menu menu) {
-        MenuItem gallery = menu.add(MenuHelper.IMAGE_MODE_ITEM, MENU_GALLERY_PHOTOS, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new OnMenuItemClickListener() {
-            public boolean onMenuItemClick(MenuItem item) {
-                gotoGallery();
-                return true;
-            }
-        });
-        gallery.setIcon(android.R.drawable.ic_menu_gallery);
-        mGalleryItems.add(gallery);
+        MenuHelper.addSwitchModeMenuItem(menu, this, true);
+        {
+            MenuItem gallery = menu.add(MenuHelper.IMAGE_MODE_ITEM, MENU_GALLERY_PHOTOS, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new OnMenuItemClickListener() {
+                public boolean onMenuItemClick(MenuItem item) {
+                    gotoGallery();
+                    return true;
+                }
+            });
+            gallery.setIcon(android.R.drawable.ic_menu_gallery);
+            mGalleryItems.add(gallery);
+        }
+        {
+            MenuItem gallery = menu.add(MenuHelper.VIDEO_MODE_ITEM, MENU_GALLERY_VIDEOS, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new OnMenuItemClickListener() {
+                public boolean onMenuItemClick(MenuItem item) {
+                    gotoGallery();
+                    return true;
+                }
+            });
+            gallery.setIcon(android.R.drawable.ic_menu_gallery);
+            mGalleryItems.add(gallery);
+        }
 
         MenuItem item = menu.add(MenuHelper.GENERIC_ITEM, MENU_SETTINGS, 0, R.string.settings).setOnMenuItemClickListener(new OnMenuItemClickListener() {
             public boolean onMenuItemClick(MenuItem item) {
@@ -1523,5 +1641,20 @@
         });
         item.setIcon(android.R.drawable.ic_menu_preferences);
     }
+
+    private void showPostPictureAlert() {
+        mPostPictureAlert.setVisibility(View.VISIBLE);
+        mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, POST_PICTURE_ALERT_TIMEOUT);
+    }
+
+    private void hidePostPictureAlert() {
+        cancelRestartPreviewTimeout();
+        mPostPictureAlert.setVisibility(View.INVISIBLE);
+    }
+
+    private void cancelRestartPreviewTimeout() {
+        mHandler.removeMessages(RESTART_PREVIEW);
+    }
+
 }
 
diff --git a/src/com/android/camera/CameraButtonIntentReceiver.java b/src/com/android/camera/CameraButtonIntentReceiver.java
index ccf5821..5e4d3c3 100644
--- a/src/com/android/camera/CameraButtonIntentReceiver.java
+++ b/src/com/android/camera/CameraButtonIntentReceiver.java
@@ -19,11 +19,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.BroadcastReceiver;
-import android.util.Config;
-import android.util.Log;
 import android.view.KeyEvent;
 
-class CameraButtonIntentReceiver extends BroadcastReceiver {
+public class CameraButtonIntentReceiver extends BroadcastReceiver {
     public CameraButtonIntentReceiver() {
     }
     
diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java
index fb77e34..0c0f31b 100644
--- a/src/com/android/camera/CameraSettings.java
+++ b/src/com/android/camera/CameraSettings.java
@@ -16,50 +16,29 @@
 
 package com.android.camera;
 
-import android.content.SharedPreferences;
 import android.os.Bundle;
-import android.preference.Preference;
 import android.preference.PreferenceActivity;
-import android.content.Context;
 
 /**
  *  CameraSettings
  */
-class CameraSettings extends PreferenceActivity
+public class CameraSettings extends PreferenceActivity
 {
     public CameraSettings()
     {
     }
-    
+
     protected int resourceId() {
         return R.xml.camera_preferences;
     }
-    
+
     /** Called with the activity is first created. */
     @Override
     public void onCreate(Bundle icicle)
     {
         super.onCreate(icicle);
         addPreferencesFromResource(resourceId());
-        
-        Preference p = findPreference("pref_camera_upload_albumname_key");
-        if (p != null) {
-            SharedPreferences sp = p.getSharedPreferences();
-            p.setSummary(
-                    String.format(getResources().getString(R.string.pref_camera_upload_albumname_summary), 
-                            p.getSharedPreferences().getString(p.getKey(), UploadService.sUploadAlbumName)));
-            p.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
-                public boolean onPreferenceChange(Preference p, Object newObjValue) {
-                    String newValue = (String) newObjValue;
-                    if (newValue == null || newValue.length() == 0)
-                        return false;
-                    if (android.util.Config.LOGV)
-                        android.util.Log.v("camera", "onPreferenceChange ... " + newValue);
-                    p.setSummary(String.format(getResources().getString(R.string.pref_camera_upload_albumname_summary), newValue));
-                    return true;
-                }
-            });
-        }
+
     }
 }
 
diff --git a/src/com/android/camera/ExifInterface.java b/src/com/android/camera/ExifInterface.java
index de2fd5c..2db021a 100644
--- a/src/com/android/camera/ExifInterface.java
+++ b/src/com/android/camera/ExifInterface.java
@@ -59,7 +59,7 @@
     private HashMap<String, String> mCachedAttributes = null;
 
     static {
-	    System.loadLibrary("exif");
+        System.loadLibrary("exif");
     }
 
     public ExifInterface(String fileName) {
diff --git a/src/com/android/camera/GalleryPicker.java b/src/com/android/camera/GalleryPicker.java
index 9f1664b..da946a0 100644
--- a/src/com/android/camera/GalleryPicker.java
+++ b/src/com/android/camera/GalleryPicker.java
@@ -29,6 +29,7 @@
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
@@ -56,29 +57,31 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Map;
 
 public class GalleryPicker extends Activity {
     static private final String TAG = "GalleryPicker";
-    
+
     GridView mGridView;
     Drawable mFrameGalleryMask;
     Drawable mCellOutline;
+    Drawable mVideoOverlay;
 
     BroadcastReceiver mReceiver;
     GalleryPickerAdapter mAdapter;
-    
+
     Dialog mMediaScanningDialog;
-    
+
     MenuItem mFlipItem;
     SharedPreferences mPrefs;
-   
+
     boolean mPausing = false;
-    
+
     private static long LOW_STORAGE_THRESHOLD = 1024 * 1024 * 2;
-    
+
     public GalleryPicker() {
     }
-    
+
     private void rebake(boolean unmounted, boolean scanning) {
         if (mMediaScanningDialog != null) {
             mMediaScanningDialog.cancel();
@@ -86,16 +89,16 @@
         }
         if (scanning) {
             mMediaScanningDialog = ProgressDialog.show(
-                    this, 
-                    null, 
-                    getResources().getString(R.string.wait), 
-                    true, 
+                    this,
+                    null,
+                    getResources().getString(R.string.wait),
+                    true,
                     true);
         }
         mAdapter.notifyDataSetChanged();
         mAdapter.init(!unmounted && !scanning);
     }
-    
+
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -103,12 +106,12 @@
         mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
 
         setContentView(R.layout.gallerypicker);
-        
-        mGridView = (GridView) findViewById(R.id.albums);   
+
+        mGridView = (GridView) findViewById(R.id.albums);
         mGridView.setSelector(android.R.color.transparent);
 
         mReceiver = new BroadcastReceiver() {
-            
+
             @Override
             public void onReceive(Context context, Intent intent) {
                 if (Config.LOGV) Log.v(TAG, "onReceiveIntent " + intent.getAction());
@@ -136,7 +139,7 @@
                 }
             }
         };
-        
+
         mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                 launchFolderGallery(position);
@@ -144,34 +147,35 @@
         });
         mGridView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
             public void onCreateContextMenu(ContextMenu menu, View v, final ContextMenu.ContextMenuInfo menuInfo) {
-                menu.setHeaderTitle(
-                        mAdapter.baseTitleForPosition(((AdapterContextMenuInfo)menuInfo).position));
-                menu.add(0, 207, 0, R.string.slide_show)
-                .setOnMenuItemClickListener(new OnMenuItemClickListener() {
-                    public boolean onMenuItemClick(MenuItem item) {
-                        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
-                        int position = info.position;
-                        
-                        Uri targetUri;
-                        synchronized (mAdapter.mFirstImageUris) {
-                            if (position >= mAdapter.mFirstImageUris.size()) {
-                                // the list of ids does not include the "all" list
-                                targetUri = mAdapter.firstImageUri(mAdapter.mIds.get(position-1));
-                            } else {
+                int position = ((AdapterContextMenuInfo)menuInfo).position;
+                menu.setHeaderTitle(mAdapter.baseTitleForPosition(position));
+                if ((mAdapter.getIncludeMediaTypes(position) & ImageManager.INCLUDE_IMAGES) != 0) {
+                    menu.add(0, 207, 0, R.string.slide_show)
+                    .setOnMenuItemClickListener(new OnMenuItemClickListener() {
+                        public boolean onMenuItemClick(MenuItem item) {
+                            AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)menuInfo;
+                            int position = info.position;
+
+                            Uri targetUri;
+                            synchronized (mAdapter.mItems) {
+                                if (position < 0 || position >= mAdapter.mItems.size()) {
+                                    return true;
+                                }
                                 // the mFirstImageUris list includes the "all" uri
-                                targetUri = mAdapter.mFirstImageUris.get(position);
+                                targetUri = mAdapter.mItems.get(position).mFirstImageUri;
                             }
+                            if (targetUri != null && position > 0) {
+                                targetUri = targetUri.buildUpon().appendQueryParameter("bucketId",
+                                        mAdapter.mItems.get(info.position).mId).build();
+                            }
+    //                      Log.v(TAG, "URI to launch slideshow " + targetUri);
+                            Intent intent = new Intent(Intent.ACTION_VIEW, targetUri);
+                            intent.putExtra("slideshow", true);
+                            startActivity(intent);
+                            return true;
                         }
-                        if (targetUri != null && position > 0) {
-                            targetUri = targetUri.buildUpon().appendQueryParameter("bucketId", mAdapter.mIds.get(info.position-1)).build();
-                        }
-//                      Log.v(TAG, "URI to launch slideshow " + targetUri);
-                        Intent intent = new Intent(Intent.ACTION_VIEW, targetUri);
-                        intent.putExtra("slideshow", true);
-                        startActivity(intent);
-                        return true;
-                    }
-                });
+                    });
+                }
                 menu.add(0, 208, 0, R.string.view)
                 .setOnMenuItemClickListener(new OnMenuItemClickListener() {
                     public boolean onMenuItemClick(MenuItem item) {
@@ -183,56 +187,118 @@
             }
         });
     }
-    
+
     private void launchFolderGallery(int position) {
-        android.net.Uri uri = Images.Media.INTERNAL_CONTENT_URI;
-        if (position > 0) {
-            uri = uri.buildUpon().appendQueryParameter("bucketId", mAdapter.mIds.get(position-1)).build();
-        }
-        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
-        if (position > 0) {
-            intent.putExtra("windowTitle", mAdapter.mNames.get(position-1));
-        }
-        startActivity(intent);
+        mAdapter.mItems.get(position).launch(this);
     }
-    
+
     class ItemInfo {
         Bitmap bitmap;
         int count;
-        int overlayId;
     }
-    
+
+    static class Item implements Comparable<Item>{
+        // The type is also used as the sort order
+        public final static int TYPE_ALL_IMAGES = 0;
+        public final static int TYPE_ALL_VIDEOS = 1;
+        public final static int TYPE_CAMERA_IMAGES = 2;
+        public final static int TYPE_CAMERA_VIDEOS = 3;
+        public final static int TYPE_NORMAL_FOLDERS = 4;
+
+        public int mType;
+        public String mId;
+        public String mName;
+        public Uri mFirstImageUri;
+        public ItemInfo mThumb;
+
+        public Item(int type, String id, String name) {
+            mType = type;
+            mId = id;
+            mName = name;
+        }
+
+        public boolean needsBucketId() {
+            return mType >= TYPE_CAMERA_IMAGES;
+        }
+
+        public void launch(Activity activity) {
+            android.net.Uri uri = Images.Media.INTERNAL_CONTENT_URI;
+            if (needsBucketId()) {
+                uri = uri.buildUpon().appendQueryParameter("bucketId",mId).build();
+            }
+            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+            intent.putExtra("windowTitle", mName);
+            intent.putExtra("mediaTypes", getIncludeMediaTypes());
+            activity.startActivity(intent);
+        }
+
+        public int getIncludeMediaTypes() {
+            return convertItemTypeToIncludedMediaType(mType);
+        }
+
+        public static int convertItemTypeToIncludedMediaType(int itemType) {
+            switch (itemType) {
+            case TYPE_ALL_IMAGES:
+            case TYPE_CAMERA_IMAGES:
+                return ImageManager.INCLUDE_IMAGES;
+            case TYPE_ALL_VIDEOS:
+            case TYPE_CAMERA_VIDEOS:
+                return ImageManager.INCLUDE_VIDEOS;
+            case TYPE_NORMAL_FOLDERS:
+            default:
+                return     ImageManager.INCLUDE_IMAGES | ImageManager.INCLUDE_VIDEOS;
+            }
+        }
+
+        public int getOverlay() {
+            switch (mType) {
+            case TYPE_ALL_IMAGES:
+            case TYPE_CAMERA_IMAGES:
+                return R.drawable.frame_overlay_gallery_camera;
+            case TYPE_ALL_VIDEOS:
+            case TYPE_CAMERA_VIDEOS:
+                return R.drawable.frame_overlay_gallery_video;
+            case TYPE_NORMAL_FOLDERS:
+                return R.drawable.frame_overlay_gallery_folder;
+            default:
+                return     -1;
+            }
+        }
+
+        // sort based on the sort order, then the case-insensitive display name, then the id.
+        public int compareTo(Item other) {
+            int x = mType - other.mType;
+            if (x == 0) {
+                x = mName.compareToIgnoreCase(other.mName);
+                if (x == 0) {
+                    x = mId.compareTo(other.mId);
+                }
+            }
+            return x;
+        }
+    }
+
     class GalleryPickerAdapter extends BaseAdapter {
-        ArrayList<String> mIds = new ArrayList<String>();
-        ArrayList<String> mNames = new ArrayList<String>();
-        ArrayList<Uri> mFirstImageUris = new ArrayList<Uri>();
-        
-        ArrayList<View> mAllViews = new ArrayList<View>();
-        SparseArray<ItemInfo> mThumbs = new SparseArray<ItemInfo>();
-        
+        ArrayList<Item> mItems = new ArrayList<Item>();
+
         boolean mDone = false;
         CameraThread mWorkerThread;
 
         public void init(boolean assumeMounted) {
-            mAllViews.clear();
-            mThumbs.clear();
-            
+            mItems.clear();
+
             ImageManager.IImageList images;
             if (assumeMounted) {
                 images = ImageManager.instance().allImages(
                         GalleryPicker.this,
-                        getContentResolver(), 
-                        ImageManager.DataLocation.ALL, 
-                        ImageManager.INCLUDE_IMAGES, 
+                        getContentResolver(),
+                        ImageManager.DataLocation.ALL,
+                        ImageManager.INCLUDE_IMAGES | ImageManager.INCLUDE_VIDEOS,
                         ImageManager.SORT_DESCENDING);
             } else {
                 images = ImageManager.instance().emptyImageList();
             }
 
-            mIds.clear();
-            mNames.clear();
-            mFirstImageUris.clear();
-
             if (mWorkerThread != null) {
                 try {
                     mDone = true;
@@ -243,72 +309,56 @@
                     mWorkerThread = null;
                 }
             }
-            
+
             String cameraItem = ImageManager.CAMERA_IMAGE_BUCKET_ID;
             final HashMap<String, String> hashMap = images.getBucketIds();
             String cameraBucketId = null;
-            for (String key : hashMap.keySet()) {
+            for (Map.Entry<String, String> entry: hashMap.entrySet()) {
+                String key = entry.getKey();
                 if (key.equals(cameraItem)) {
                     cameraBucketId = key;
                 } else {
-                    mIds.add(key);
+                    mItems.add(new Item(Item.TYPE_NORMAL_FOLDERS, key, entry.getValue()));
                 }
             }
             images.deactivate();
             notifyDataSetInvalidated();
-            
-            // sort baesd on the display name.  if two display names compare equal
-            // then sort based on the id
-            java.util.Collections.sort(mIds, new java.util.Comparator<String>() {
-                public int compare(String first, String second) {
-                    int x = hashMap.get(first).compareTo(hashMap.get(second));
-                    if (x == 0)
-                        x = first.compareTo(second);
-                    return x;
-                }
-            });
 
-            for (String s : mIds) {
-                mNames.add(hashMap.get(s));
-            }
+            // If just one
+            addBucketIfNotEmpty(Item.TYPE_ALL_IMAGES, null, R.string.all_images);
+            addBucketIfNotEmpty(Item.TYPE_ALL_VIDEOS, null, R.string.all_videos);
 
             if (cameraBucketId != null) {
-                mIds.add(0, cameraBucketId);
-                mNames.add(0, "Camera");
+                addBucketIfNotEmpty(Item.TYPE_CAMERA_IMAGES, cameraBucketId,
+                        R.string.gallery_camera_bucket_name);
+                addBucketIfNotEmpty(Item.TYPE_CAMERA_VIDEOS, cameraBucketId,
+                        R.string.gallery_camera_videos_bucket_name);
             }
-            final boolean foundCameraBucket = cameraBucketId != null;
-            
+
+            java.util.Collections.sort(mItems);
+
             mDone = false;
             mWorkerThread = new CameraThread(new Runnable() {
                 public void run() {
                     try {
                         // no images, nothing to do
-                        if (mIds.size() == 0)
+                        if (mItems.size() == 0)
                             return;
-                        
-                        for (int i = 0; i < mIds.size() + 1 && !mDone; i++) {
-                            String id = i == 0 ? null : mIds.get(i-1);
-                            ImageManager.IImageList list = ImageManager.instance().allImages(
-                                    GalleryPicker.this, 
-                                    getContentResolver(), 
-                                    ImageManager.DataLocation.ALL,
-                                    ImageManager.INCLUDE_IMAGES, 
-                                    ImageManager.SORT_DESCENDING,
-                                    id);
+
+                        for (int i = 0; i < mItems.size() && !mDone; i++) {
+                            final Item item = mItems.get(i);
+                            ImageManager.IImageList list = createImageList(
+                                    item.getIncludeMediaTypes(), item.mId);
                             try {
                                 if (mPausing) {
                                     break;
                                 }
                                 if (list.getCount() > 0)
-                                    mFirstImageUris.add(i, list.getImageAt(0).fullSizeImageUri());
+                                    item.mFirstImageUri = list.getImageAt(0).fullSizeImageUri();
 
-                                int overlay = -1;
-                                if (i == 1 && foundCameraBucket)
-                                    overlay = R.drawable.frame_overlay_gallery_camera;
                                 final Bitmap b = makeMiniThumbBitmap(142, 142, list);
                                 final int pos = i;
                                 final int count = list.getCount();
-                                final int overlayId = overlay;
                                 final Thread currentThread = Thread.currentThread();
                                 mHandler.post(new Runnable() {
                                     public void run() {
@@ -318,19 +368,18 @@
                                             }
                                             return;
                                         }
-                                        
+
                                         ItemInfo info = new ItemInfo();
                                         info.bitmap = b;
                                         info.count = count;
-                                        info.overlayId = overlayId;
-                                        mThumbs.put(pos, info); 
-                                        
+                                        item.mThumb = info;
+
                                         final GridView grid = GalleryPicker.this.mGridView;
                                         final int firstVisible = grid.getFirstVisiblePosition();
-                                        
+
                                         // Minor optimization -- only notify if the specified position is visible
                                         if ((pos >= firstVisible) && (pos < firstVisible + grid.getChildCount())) {
-                                        	GalleryPickerAdapter.this.notifyDataSetChanged();
+                                            GalleryPickerAdapter.this.notifyDataSetChanged();
                                         }
                                     }
                                 });
@@ -339,29 +388,22 @@
                             }
                         }
                     } catch (Exception ex) {
-                        Log.e(TAG, "got exception generating collage views " + ex.toString());
+                        Log.e(TAG, "got exception generating collage views ", ex);
                     }
                 }
             });
             mWorkerThread.start();
             mWorkerThread.toBackground();
         }
-        
-        Uri firstImageUri(String id) {
-            ImageManager.IImageList list = ImageManager.instance().allImages(
-                    GalleryPicker.this, 
-                    getContentResolver(), 
-                    ImageManager.DataLocation.ALL,
-                    ImageManager.INCLUDE_IMAGES, 
-                    ImageManager.SORT_DESCENDING,
-                    id);
-            Uri uri = list.getImageAt(0).fullSizeImageUri();
-            list.deactivate();
-            return uri;
+
+        private void addBucketIfNotEmpty(int itemType, String bucketId, int labelId) {
+            if (!isEmptyBucket(Item.convertItemTypeToIncludedMediaType(itemType), bucketId)) {
+                mItems.add(new Item(itemType, bucketId, getResources().getString(labelId)));
+            }
         }
 
         public int getCount() {
-            return mIds.size() + 1;  // add 1 for the everything bucket
+            return mItems.size();
         }
 
         public Object getItem(int position) {
@@ -371,41 +413,39 @@
         public long getItemId(int position) {
             return position;
         }
-        
+
         private String baseTitleForPosition(int position) {
-            if (position == 0) {
-                return getResources().getString(R.string.all_images);
-            } else {
-                return mNames.get(position-1);
-            }
+            return mItems.get(position).mName;
+        }
+
+        private int getIncludeMediaTypes(int position) {
+            return mItems.get(position).getIncludeMediaTypes();
         }
 
         public View getView(final int position, View convertView, ViewGroup parent) {
             View v;
-            
+
             if (convertView == null) {
                 LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                 v = vi.inflate(R.layout.gallery_picker_item, null);
             } else {
                 v = convertView;
             }
-            
+
             TextView titleView = (TextView) v.findViewById(R.id.title);
 
-            
             GalleryPickerItem iv = (GalleryPickerItem) v.findViewById(R.id.thumbnail);
-            ItemInfo info = mThumbs.get(position);
+            iv.setOverlay(mItems.get(position).getOverlay());
+            ItemInfo info = mItems.get(position).mThumb;
             if (info != null) {
                 iv.setImageBitmap(info.bitmap);
-                iv.setOverlay(info.overlayId);
                 String title = baseTitleForPosition(position) + " (" + info.count + ")";
                 titleView.setText(title);
             } else {
                 iv.setImageResource(android.R.color.transparent);
-                iv.setOverlay(-1);
                 titleView.setText(baseTitleForPosition(position));
             }
-            
+
             return v;
         }
     };
@@ -415,13 +455,13 @@
         super.onPause();
         mPausing = true;
         unregisterReceiver(mReceiver);
-        
+
         // free up some ram
         mAdapter = null;
         mGridView.setAdapter(null);
         System.gc();
     }
-    
+
     @Override
     public void onResume() {
         super.onResume();
@@ -433,7 +473,7 @@
 
         boolean scanning = ImageManager.isMediaScannerScanning(this);
         rebake(false, scanning);
-        
+
         // install an intent filter to receive SD card related events.
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
         intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
@@ -455,10 +495,10 @@
                     StatFs stat = new StatFs(storageDirectory);
                     long remaining = (long)stat.getAvailableBlocks() * (long)stat.getBlockSize();
                     if (remaining < LOW_STORAGE_THRESHOLD) {
-    
+
                         mHandler.post(new Runnable() {
                             public void run() {
-                                Toast.makeText(GalleryPicker.this.getApplicationContext(), 
+                                Toast.makeText(GalleryPicker.this.getApplicationContext(),
                                     R.string.not_enough_space, 5000).show();
                             }
                         });
@@ -467,8 +507,10 @@
             }
         });
         t.start();
-        
-        if (!scanning && mAdapter.mIds.size() <= 1) {
+
+        // If we just have one folder, open it. (Probably never triggered because we always have
+        // At least two folders now.)
+        if (!scanning && mAdapter.mItems.size() <= 1) {
             android.net.Uri uri = Images.Media.INTERNAL_CONTENT_URI;
             Intent intent = new Intent(Intent.ACTION_VIEW, uri);
             startActivity(intent);
@@ -476,26 +518,27 @@
             return;
         }
     }
-    
+
 
     private void setBackgrounds(Resources r) {
         mFrameGalleryMask = r.getDrawable(R.drawable.frame_gallery_preview_album_mask);
 
         mCellOutline = r.getDrawable(android.R.drawable.gallery_thumb);
+        mVideoOverlay = r.getDrawable(R.drawable.ic_gallery_video_overlay);
     }
-    
+
     Handler mHandler = new Handler();
-    
+
     private void placeImage(Bitmap image, Canvas c, Paint paint, int imageWidth, int widthPadding, int imageHeight, int heightPadding, int offsetX, int offsetY, int pos) {
         int row = pos / 2;
         int col = pos - (row * 2);
-        
+
         int xPos = (col * (imageWidth + widthPadding)) - offsetX;
         int yPos = (row * (imageHeight + heightPadding)) - offsetY;
-        
+
         c.drawBitmap(image, xPos, yPos, paint);
     }
-    
+
     private Bitmap makeMiniThumbBitmap(int width, int height, ImageManager.IImageList images) {
         int count = images.getCount();
         // We draw three different version of the folder image depending on the number of images in the folder.
@@ -507,30 +550,20 @@
         int imageHeight = height;
         int offsetWidth = 0;
         int offsetHeight = 0;
-        if (count < 4) {
-            count = 1;
-        // uncomment for 2 pictures per frame
-//        if (count == 2 || count == 3) {
-//            count = 2;
-//            imageWidth = imageWidth * 2 / 3;
-//            imageHeight = imageHeight * 2 / 3;
-//            offsetWidth = imageWidth / 3 - padding;
-//            offsetHeight = -imageHeight / 3 + padding * 2;
-        } else if (count >= 4) {
-            count = 4;
-            imageWidth = (imageWidth - padding) / 2;     // 2 here because we show two images
-            imageHeight = (imageHeight - padding) / 2;   // per row and column
-        }
+
+        imageWidth = (imageWidth - padding) / 2;     // 2 here because we show two images
+        imageHeight = (imageHeight - padding) / 2;   // per row and column
+
         final Paint  p = new Paint();
         final Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         final Canvas c = new Canvas(b);
-        
+
         final Matrix m = new Matrix();
-        
+
         // draw the whole canvas as transparent
         p.setColor(0x00000000);
         c.drawPaint(p);
-        
+
         // draw the mask normally
         p.setColor(0xFFFFFFFF);
         mFrameGalleryMask.setBounds(0, 0, width, height);
@@ -542,17 +575,34 @@
 
         pdpaint.setStyle(Paint.Style.FILL);
         c.drawRect(0, 0, width, height, pdpaint);
-        
-        for (int i = 0; i < count; i++) {
+
+        for (int i = 0; i < 4; i++) {
             if (mPausing) {
                 return null;
             }
+
+            Bitmap temp = null;
             ImageManager.IImage image = i < count ? images.getImageAt(i) : null;
-            if (image == null) {
-                break;
+
+            if (image != null) {
+                temp = image.miniThumbBitmap();
             }
-            Bitmap temp = image.miniThumbBitmap();
+
             if (temp != null) {
+                if (ImageManager.isVideo(image)) {
+                    Bitmap newMap = temp.copy(temp.getConfig(), true);
+                    Canvas overlayCanvas = new Canvas(newMap);
+                    int overlayWidth = mVideoOverlay.getIntrinsicWidth();
+                    int overlayHeight = mVideoOverlay.getIntrinsicHeight();
+                    int left = (newMap.getWidth() - overlayWidth) / 2;
+                    int top = (newMap.getHeight() - overlayHeight) / 2;
+                    Rect newBounds = new Rect(left, top, left + overlayWidth, top + overlayHeight);
+                    mVideoOverlay.setBounds(newBounds);
+                    mVideoOverlay.draw(overlayCanvas);
+                    temp.recycle();
+                    temp = newMap;
+                }
+
                 Bitmap temp2 = ImageLoader.transform(m, temp, imageWidth, imageHeight, true);
                 if (temp2 != temp)
                     temp.recycle();
@@ -567,22 +617,24 @@
             mCellOutline.draw(tempCanvas);
 
             placeImage(thumb, c, pdpaint, imageWidth, padding, imageHeight, padding, offsetWidth, offsetHeight, i);
-            
+
             thumb.recycle();
-            
+
             if (temp != null)
                 temp.recycle();
         }
+
         return b;
     }
-    
+
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         super.onCreateOptionsMenu(menu);
-        
+
+        MenuHelper.addCaptureMenuItems(menu, this);
         mFlipItem = MenuHelper.addFlipOrientation(menu, this, mPrefs);
 
-        menu.add(0, 0, 0, R.string.camerasettings)
+        menu.add(0, 0, 5, R.string.camerasettings)
         .setOnMenuItemClickListener(new OnMenuItemClickListener() {
             public boolean onMenuItemClick(MenuItem item) {
                 Intent preferences = new Intent();
@@ -597,11 +649,32 @@
         return true;
     }
 
-    @Override 
+    @Override
     public boolean onPrepareOptionsMenu(android.view.Menu menu) {
         int keyboard = getResources().getConfiguration().keyboardHidden;
         mFlipItem.setEnabled(keyboard == android.content.res.Configuration.KEYBOARDHIDDEN_YES);
 
         return true;
     }
+
+    private boolean isEmptyBucket(int mediaTypes, String bucketId) {
+        // TODO: Find a more efficient way of calculating this
+        ImageManager.IImageList list = createImageList(mediaTypes, bucketId);
+        try {
+            return list.isEmpty();
+        }
+        finally {
+            list.deactivate();
+        }
+    }
+
+    private ImageManager.IImageList createImageList(int mediaTypes, String bucketId) {
+        return ImageManager.instance().allImages(
+                this,
+                getContentResolver(),
+                ImageManager.DataLocation.ALL,
+                mediaTypes,
+                ImageManager.SORT_DESCENDING,
+                bucketId);
+    }
 }
diff --git a/src/com/android/camera/GallerySettings.java b/src/com/android/camera/GallerySettings.java
index 3af6867..8cbeba2 100644
--- a/src/com/android/camera/GallerySettings.java
+++ b/src/com/android/camera/GallerySettings.java
@@ -25,7 +25,7 @@
 /**
  *  GallerySettings
  */
-class GallerySettings extends CameraSettings
+public class GallerySettings extends CameraSettings
 {
     public GallerySettings()
     {
diff --git a/src/com/android/camera/HighlightView.java b/src/com/android/camera/HighlightView.java
index 594bab6..408beab 100644
--- a/src/com/android/camera/HighlightView.java
+++ b/src/com/android/camera/HighlightView.java
@@ -376,35 +376,35 @@
         float heightUnits = widthUnits;
 
         switch (keyCode)
-    	{    	
-		case KeyEvent.KEYCODE_DPAD_LEFT:
-			if (mMode == ModifyMode.Move)
-				moveBy(-widthUnits, 0);
-			else if (mMode == ModifyMode.Grow)
-				growBy(-widthUnits, 0);
-			break;
-		
-		case KeyEvent.KEYCODE_DPAD_RIGHT:
-			if (mMode == ModifyMode.Move)
-				moveBy(widthUnits, 0);
+        {       
+        case KeyEvent.KEYCODE_DPAD_LEFT:
+            if (mMode == ModifyMode.Move)
+                moveBy(-widthUnits, 0);
             else if (mMode == ModifyMode.Grow)
-				growBy(widthUnits, 0);
-			break;
-	
-		case KeyEvent.KEYCODE_DPAD_UP:
-			if (mMode == ModifyMode.Move)
-				moveBy(0, -heightUnits);
+                growBy(-widthUnits, 0);
+            break;
+        
+        case KeyEvent.KEYCODE_DPAD_RIGHT:
+            if (mMode == ModifyMode.Move)
+                moveBy(widthUnits, 0);
             else if (mMode == ModifyMode.Grow)
-				growBy(0, -heightUnits);
-			break;
+                growBy(widthUnits, 0);
+            break;
+    
+        case KeyEvent.KEYCODE_DPAD_UP:
+            if (mMode == ModifyMode.Move)
+                moveBy(0, -heightUnits);
+            else if (mMode == ModifyMode.Grow)
+                growBy(0, -heightUnits);
+            break;
 
-		case KeyEvent.KEYCODE_DPAD_DOWN:
-			if (mMode == ModifyMode.Move)
-				moveBy(0, heightUnits);
+        case KeyEvent.KEYCODE_DPAD_DOWN:
+            if (mMode == ModifyMode.Move)
+                moveBy(0, heightUnits);
             else if (mMode == ModifyMode.Grow)
-				growBy(0, heightUnits);
-			break;
-    	}
+                growBy(0, heightUnits);
+            break;
+        }
     }
     
     enum ModifyMode { None, Move,Grow };
diff --git a/src/com/android/camera/ImageGallery2.java b/src/com/android/camera/ImageGallery2.java
index c8abdae..44d297b 100644
--- a/src/com/android/camera/ImageGallery2.java
+++ b/src/com/android/camera/ImageGallery2.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
@@ -51,11 +52,14 @@
 import android.widget.TextView;
 import android.widget.Toast;
 import android.preference.PreferenceManager;
+import android.provider.MediaStore;
 import android.widget.Scroller;
 
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 
+import com.android.camera.ImageManager.IImage;
+
 public class ImageGallery2 extends Activity {
     private static final String TAG = "ImageGallery2";
     private ImageManager.IImageList mAllImages;
@@ -66,10 +70,11 @@
     public final static int VIEW_MSG = 3;
 
     private static final String INSTANCE_STATE_TAG = "scrollY";
-    
+
     private Dialog mMediaScanningDialog;
-    
+
     private MenuItem mFlipItem;
+    private MenuItem mSlideShowItem;
     private SharedPreferences mPrefs;
 
     public ImageGallery2() {
@@ -115,11 +120,15 @@
                         }
                     });
 
-                    menu.setHeaderTitle(R.string.context_menu_header);
-                    if ((mInclusion & ImageManager.INCLUDE_IMAGES) != 0) {
+                    boolean isImage = ImageManager.isImage(mSelectedImageGetter.getCurrentImage());
+
+                    menu.setHeaderTitle(isImage ? R.string.context_menu_header
+                            : R.string.video_context_menu_header);
+                    if ((mInclusion & (ImageManager.INCLUDE_IMAGES | ImageManager.INCLUDE_VIDEOS)) != 0) {
                         MenuHelper.MenuItemsResult r = MenuHelper.addImageMenuItems(
                                 menu,
                                 MenuHelper.INCLUDE_ALL,
+                                isImage,
                                 ImageGallery2.this,
                                 mHandler,
                                 mDeletePhotoRunnable,
@@ -136,37 +145,15 @@
                         if (r != null)
                             r.gettingReadyToOpen(menu, mSelectedImageGetter.getCurrentImage());
 
-                        addSlideShowMenu(menu, 1000);
-                    }
-
-                    if ((mInclusion & ImageManager.INCLUDE_VIDEOS) != 0) {
-                        MenuHelper.MenuItemsResult r = MenuHelper.addVideoMenuItems(
-                                menu,
-                                MenuHelper.INCLUDE_ALL,
-                                ImageGallery2.this,
-                                mHandler,
-                                mSelectedImageGetter,
-                                new Runnable() {
-                                    public void run() {
-                                        ImageManager.IImage image = mSelectedImageGetter.getCurrentImage();
-                                        if (image != null) {
-                                            mGvs.clearCache();
-                                            mAllImages.removeImage(mSelectedImageGetter.getCurrentImage());
-                                            mGvs.invalidate();
-                                            mGvs.start();
-                                            mNoImagesView.setVisibility(mAllImages.getCount() > 0 ? View.GONE : View.VISIBLE);
-                                        }
-                                    }
-                                },
-                                null, null);
-                        if (r != null)
-                            r.gettingReadyToOpen(menu, mSelectedImageGetter.getCurrentImage());
+                        if (isImage) {
+                            addSlideShowMenu(menu, 1000);
+                        }
                     }
                 }
             });
         }
     }
-    
+
     private MenuItem addSlideShowMenu(Menu menu, int position) {
         return menu.add(0, 207, position, R.string.slide_show)
         .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@@ -194,7 +181,7 @@
         })
         .setIcon(android.R.drawable.ic_menu_slideshow);
     }
-    
+
     private Runnable mDeletePhotoRunnable = new Runnable() {
         public void run() {
             mGvs.clearCache();
@@ -204,7 +191,7 @@
             mNoImagesView.setVisibility(mAllImages.getCount() > 0 ? View.GONE : View.VISIBLE);
         }
     };
-    
+
     private SelectedImageGetter mSelectedImageGetter = new SelectedImageGetter() {
         public Uri getCurrentImageUri() {
             ImageManager.IImage image = getCurrentImage();
@@ -227,20 +214,20 @@
         super.onConfigurationChanged(newConfig);
         mTargetScroll = mGvs.getScrollY();
     }
-        
+
     private Runnable mLongPressCallback = new Runnable() {
         public void run() {
             mGvs.showContextMenu();
         }
     };
-    
+
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
             // The keyUp doesn't get called when the longpress menu comes up. We only get here when the user
             // lets go of the center key before the longpress menu comes up.
             mHandler.removeCallbacks(mLongPressCallback);
-            
+
             // open the photo
             if (mSelectedImageGetter.getCurrentImage() != null) {
                 mGvs.onSelect(mGvs.mCurrentSelection);
@@ -282,7 +269,8 @@
                     mHandler.postDelayed(mLongPressCallback, ViewConfiguration.getLongPressTimeout());
                     break;
                 case KeyEvent.KEYCODE_DEL:
-                    MenuHelper.deletePhoto(this, mDeletePhotoRunnable);
+                    MenuHelper.deleteImage(this, mDeletePhotoRunnable,
+                            mSelectedImageGetter.getCurrentImage());
                     break;
                 default:
                     handled = false;
@@ -402,10 +390,10 @@
         }
         if (scanning) {
             mMediaScanningDialog = ProgressDialog.show(
-                    this, 
-                    null, 
-                    getResources().getString(R.string.wait), 
-                    true, 
+                    this,
+                    null,
+                    getResources().getString(R.string.wait),
+                    true,
                     true);
             mAllImages = ImageManager.instance().emptyImageList();
         } else {
@@ -418,7 +406,7 @@
             checkThumbnails();
         }
     }
-    
+
     @Override
     protected void onSaveInstanceState(Bundle state) {
         super.onSaveInstanceState(state);
@@ -431,7 +419,7 @@
         super.onRestoreInstanceState(state);
         mTargetScroll = state.getInt(INSTANCE_STATE_TAG, 0);
     }
-    
+
     int mTargetScroll;
 
     @Override
@@ -474,7 +462,7 @@
                     rebake(true, false);
                 } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_STARTED)) {
                     Toast.makeText(ImageGallery2.this, getResources().getString(R.string.wait), 5000);
-                    rebake(false, true);   
+                    rebake(false, true);
                 } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
                     if (Config.LOGV)
                         Log.v(TAG, "rebake because of ACTION_MEDIA_SCANNER_FINISHED");
@@ -489,10 +477,10 @@
         registerReceiver(mReceiver, intentFilter);
 
         MenuHelper.requestOrientation(this, mPrefs);
-        
+
         rebake(false, ImageManager.isMediaScannerScanning(this));
     }
-    
+
     private void stopCheckingThumbnails() {
         mStopThumbnailChecking = true;
         if (mThumbnailCheckThread != null) {
@@ -523,7 +511,7 @@
                         if (mStopThumbnailChecking) {
                             return false;
                         }
-                        
+
                         if (!mLayoutComplete) {
                             return true;
                         }
@@ -563,11 +551,11 @@
                     Log.v(TAG, "check thumbnails thread finishing; took " + (t2-t1));
             }
         });
-        
+
         mThumbnailCheckThread.setName("check_thumbnails");
         mThumbnailCheckThread.start();
         mThumbnailCheckThread.toBackground();
-        
+
         ImageManager.IImageList list = allImages(true);
         mNoImagesView.setVisibility(list.getCount() > 0 ? View.GONE : View.VISIBLE);
     }
@@ -575,21 +563,12 @@
     @Override
     public boolean onCreateOptionsMenu(android.view.Menu menu) {
         MenuItem item;
-        if (false) {
-            if ((mInclusion & ImageManager.INCLUDE_IMAGES) != 0) {
-                item = menu.add(0, 0, 0, R.string.upload_all);
-                item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                    public boolean onMenuItemClick(MenuItem item) {
-                        UploadAction.uploadImage(ImageGallery2.this, null);
-                        return true;
-                    }
-                });
-                item.setIcon(android.R.drawable.ic_menu_upload);
-            }
-        }
-        addSlideShowMenu(menu, 0);
+        MenuHelper.addCaptureMenuItems(menu, this);
+        if ((mInclusion & ImageManager.INCLUDE_IMAGES) != 0) {
+            mSlideShowItem = addSlideShowMenu(menu, 5);
 
-        mFlipItem = MenuHelper.addFlipOrientation(menu, this, mPrefs);
+            mFlipItem = MenuHelper.addFlipOrientation(menu, this, mPrefs);
+        }
 
         item = menu.add(0, 0, 1000, R.string.camerasettings);
         item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@@ -605,15 +584,32 @@
 
         return true;
     }
-    
-    @Override 
+
+    @Override
     public boolean onPrepareOptionsMenu(android.view.Menu menu) {
-        int keyboard = getResources().getConfiguration().keyboardHidden;
-        mFlipItem.setEnabled(keyboard == android.content.res.Configuration.KEYBOARDHIDDEN_YES);
+        if ((mInclusion & ImageManager.INCLUDE_IMAGES) != 0) {
+            boolean imageSelected = isImageSelected();
+            boolean videoSelected = isVideoSelected();
+            int keyboard = getResources().getConfiguration().keyboardHidden;
+            mFlipItem.setEnabled(imageSelected
+                    && (keyboard == android.content.res.Configuration.KEYBOARDHIDDEN_YES));
+            // TODO: Only enable slide show if there is at least one image in the folder.
+            mSlideShowItem.setEnabled(!videoSelected);
+        }
 
         return true;
     }
 
+    private boolean isImageSelected() {
+        IImage image = mSelectedImageGetter.getCurrentImage();
+        return (image != null) && ImageManager.isImage(image);
+    }
+
+    private boolean isVideoSelected() {
+        IImage image = mSelectedImageGetter.getCurrentImage();
+        return (image != null) && ImageManager.isVideo(image);
+    }
+
     private synchronized ImageManager.IImageList allImages(boolean assumeMounted) {
         if (mAllImages == null) {
             mNoImagesView = findViewById(R.id.no_images);
@@ -636,6 +632,11 @@
                             leftText.setText(R.string.photos_gallery_title);
                     }
                     if (type.equals("vnd.android.cursor.dir/video") || type.equals("video/*")) {
+                        mInclusion = ImageManager.INCLUDE_VIDEOS;
+                        if (isPickIntent())
+                            leftText.setText(R.string.pick_videos_gallery_title);
+                        else
+                            leftText.setText(R.string.videos_gallery_title);
                     }
                 }
                 Bundle extras = intent.getExtras();
@@ -644,6 +645,11 @@
                     leftText.setText(title);
                 }
 
+                if (extras != null) {
+                    mInclusion = (ImageManager.INCLUDE_IMAGES | ImageManager.INCLUDE_VIDEOS)
+                        & extras.getInt("mediaTypes", mInclusion);
+                }
+
                 if (extras != null && extras.getBoolean("pick-drm")) {
                     Log.d(TAG, "pick-drm is true");
                     mInclusion = ImageManager.INCLUDE_DRM_IMAGES;
@@ -651,7 +657,8 @@
                 }
             }
             if (Config.LOGV)
-                Log.v(TAG, "computing images... mSortAscending is " + mSortAscending + "; assumeMounted is " + assumeMounted);
+                Log.v(TAG, "computing images... mSortAscending is " + mSortAscending
+                        + "; assumeMounted is " + assumeMounted);
             Uri uri = getIntent().getData();
             if (!assumeMounted) {
                 mAllImages = ImageManager.instance().emptyImageList();
@@ -758,7 +765,7 @@
                         velocityY = maxVelocity;
                     else if (velocityY < -maxVelocity)
                         velocityY = -maxVelocity;
-                    
+
                     select(-1);
                     if (mFling) {
                         mScroller = new Scroller(getContext());
@@ -891,8 +898,8 @@
 
             if (mGallery.isFinishing() || mGallery.mPausing) {
                 return;
-            }            
-            
+            }
+
             clearCache();
 
             mCurrentSpec = mCellSizeChoices[mSizeChoice];
@@ -905,7 +912,7 @@
 
             mCurrentSpec.mLeftEdgePadding = ((right - left) - ((mCurrentSpec.mColumns - 1) * mCurrentSpec.mCellSpacing) - (mCurrentSpec.mColumns * mCurrentSpec.mCellWidth)) / 2;
             mCurrentSpec.mRightEdgePadding = mCurrentSpec.mLeftEdgePadding;
-            
+
             int rows = (mGallery.mAllImages.getCount() + mCurrentSpec.mColumns - 1) / mCurrentSpec.mColumns;
             mMaxScrollY = mCurrentSpec.mCellSpacing + (rows * (mCurrentSpec.mCellSpacing + mCurrentSpec.mCellHeight)) - (bottom - top) + mMaxOvershoot;
             mMinScrollY = 0 - mMaxOvershoot;
@@ -950,9 +957,10 @@
 
             private int mWorkCounter = 0;
             private boolean mDone = false;
-            
+
             private Thread mWorkerThread;
-            private Bitmap mErrorBitmap;
+            private Bitmap mMissingImageThumbnailBitmap;
+            private Bitmap mMissingVideoThumbnailBitmap;
 
             public void dump() {
                 synchronized (ImageBlockManager.this) {
@@ -1027,12 +1035,20 @@
             }
 
             // Create this bitmap lazily, and only once for all the ImageBlocks to use
-            public Bitmap getErrorBitmap() {
-                if (mErrorBitmap == null) {
-                    mErrorBitmap = BitmapFactory.decodeResource(GridViewSpecial.this.getResources(),
-                            android.R.drawable.ic_menu_report_image);
+            public Bitmap getErrorBitmap(ImageManager.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;
                 }
-                return mErrorBitmap;
             }
 
             private ImageBlock getBlockForPos(int pos) {
@@ -1299,6 +1315,7 @@
                 int     mRequestedMask;   // columns which have been requested to the loader
                 int     mCompletedMask;   // columns which have been completed from the loader
                 boolean mIsVisible;
+                Drawable mVideoOverlay;
 
                 public void dump(StringBuilder line1, StringBuilder line2) {
                     synchronized (ImageBlock.this) {
@@ -1315,7 +1332,7 @@
                     mBlockNumber = -1;
                     mCellOutline = GridViewSpecial.this.getResources().getDrawable(android.R.drawable.gallery_thumb);
                 }
-                
+
                 private void recycleBitmaps() {
                     synchronized (ImageBlock.this) {
                         mBitmap.recycle();
@@ -1434,13 +1451,13 @@
                         // 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;
@@ -1460,7 +1477,7 @@
                         }
                     } else {
                         // If the thumbnail cannot be drawn, put up an error icon instead
-                        Bitmap error = mImageBlockManager.getErrorBitmap();
+                        Bitmap error = mImageBlockManager.getErrorBitmap(image);
                         int width = error.getWidth();
                         int height = error.getHeight();
                         Rect source = new Rect(0, 0, width, height);
@@ -1469,6 +1486,19 @@
                         Rect dest = new Rect(left, top, left + width, top + height);
                         mCanvas.drawBitmap(error, source, dest, mPaint);
                     }
+                    if (ImageManager.isVideo(image)) {
+                        if (mVideoOverlay == null) {
+                            mVideoOverlay = getResources().getDrawable(
+                                    R.drawable.ic_gallery_video_overlay);
+                        }
+                        int width = mVideoOverlay.getIntrinsicWidth();
+                        int height = mVideoOverlay.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);
+                        mVideoOverlay.setBounds(newBounds);
+                        mVideoOverlay.draw(mCanvas);
+                    }
                     paintSel(base + baseOffset, xPos, yPos);
                 }
 
@@ -1524,7 +1554,7 @@
 //                                      Log.v(TAG, "wanted block " + mBlockNumber + " but got " + startBlock);
                                         return;
                                     }
-                                    
+
                                     if (mBitmap == null) {
                                         return;
                                     }
@@ -1644,11 +1674,11 @@
                     }
                     Intent intent = new Intent(Intent.ACTION_VIEW, targetUri);
 
-                    // this should be unnecessary but if you remove this line then executing 
-                    // the subsequent startActivity causes the user to have to choose among
-                    // ViewImage and a number of bogus entries (like attaching the image to
-                    // a contact).
-                    intent.setClass(mContext, ViewImage.class);
+                    if (img instanceof ImageManager.VideoObject) {
+                        intent.putExtra(MediaStore.EXTRA_SCREEN_ORIENTATION,
+                                ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+                    }
+
                     try {
                         mContext.startActivity(intent);
                     } catch (Exception ex) {
diff --git a/src/com/android/camera/ImageManager.java b/src/com/android/camera/ImageManager.java
index 8d3f90a..1fe93b5 100755
--- a/src/com/android/camera/ImageManager.java
+++ b/src/com/android/camera/ImageManager.java
@@ -25,8 +25,11 @@
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.location.Location;
+import android.media.MediaMetadataRetriever;
+import android.media.MediaPlayer;
 import android.net.Uri;
 import android.os.Environment;
 import android.os.Handler;
@@ -36,8 +39,10 @@
 import android.provider.MediaStore;
 import android.provider.MediaStore.Images.ImageColumns;
 import android.provider.MediaStore.Images.Thumbnails;
+import android.provider.MediaStore.Video.VideoColumns;
 import android.provider.MediaStore.Images;
 import android.provider.MediaStore.MediaColumns;
+import android.provider.MediaStore.Video;
 import android.util.Config;
 import android.util.Log;
 
@@ -69,7 +74,7 @@
     private static final String TAG = "ImageManager";
 
     private static final int MINI_THUMB_DATA_FILE_VERSION = 3;
-    
+
     static public void debug_where(String tag, String msg) {
         try {
             throw new Exception();
@@ -98,11 +103,11 @@
     private static int computeSampleSize(BitmapFactory.Options options, int target) {
         int w = options.outWidth;
         int h = options.outHeight;
-        
+
         int candidateW = w / target;
         int candidateH = h / target;
         int candidate = Math.max(candidateW, candidateH);
-        
+
         if (candidate == 0)
             return 1;
 
@@ -110,7 +115,7 @@
             if ((w > target) && (w / candidate) < target)
                 candidate -= 1;
         }
-        
+
         if (candidate > 1) {
             if ((h > target) && (h / candidate) < target)
                 candidate -= 1;
@@ -121,7 +126,7 @@
 
         return candidate;
     }
-    /* 
+    /*
      * All implementors of ICancelable should inherit from BaseCancelable
      * since it provides some convenience methods such as acknowledgeCancel
      * and checkCancel.
@@ -129,10 +134,10 @@
     public abstract class BaseCancelable implements ICancelable {
         boolean mCancel = false;
         boolean mFinished = false;
-        
+
         /*
          * Subclasses should call acknowledgeCancel when they're finished with
-         * their operation.  
+         * their operation.
          */
         protected void acknowledgeCancel() {
             synchronized (this) {
@@ -161,7 +166,7 @@
                 } catch (InterruptedException ex) {
                     // now what???  TODO
                 }
-                
+
                 return retVal;
             }
         }
@@ -205,13 +210,13 @@
         protected BaseImage(long id, long miniThumbId, ContentResolver cr, BaseImageList container, int cursorRow) {
             mContentResolver = cr;
             mId              = id;
-            mMiniThumbMagic     = miniThumbId;
+            mMiniThumbMagic  = miniThumbId;
             mContainer       = container;
             mCursorRow       = cursorRow;
         }
-        
+
         abstract Bitmap.CompressFormat compressionType();
-        
+
         public void commitChanges() {
             Cursor c = getCursor();
             synchronized (c) {
@@ -221,7 +226,7 @@
                 }
             }
         }
-        
+
         /**
          * Take a given bitmap and compress it to a file as described
          * by the Uri parameter.
@@ -248,7 +253,7 @@
                     }
                     return false;
                 }
-                
+
                 public boolean get() {
                     try {
                         long t1 = System.currentTimeMillis();
@@ -297,7 +302,7 @@
                 return false;
             if (!(other instanceof Image))
                 return false;
-            
+
             return fullSizeImageUri().equals(((Image)other).fullSizeImageUri());
         }
 
@@ -327,11 +332,11 @@
                 ParcelFileDescriptor mPFD;
                 BitmapFactory.Options mOptions = new BitmapFactory.Options();
                 long mCancelInitiationTime;
-                
+
                 public LoadBitmapCancelable(ParcelFileDescriptor pfdInput) {
                     mPFD = pfdInput;
                 }
-                
+
                 public boolean doCancelWork() {
                     if (VERBOSE)
                         Log.v(TAG, "requesting bitmap load cancel");
@@ -339,7 +344,7 @@
                     mOptions.requestCancelDecode();
                     return true;
                 }
-                
+
                 public Bitmap get() {
                     try {
                         Bitmap b = makeBitmap(targetWidthHeight, fullSizeImageUri(), mPFD, mOptions);
@@ -386,7 +391,7 @@
                 return null;
             }
         }
-        
+
         public long fullSizeImageId() {
             return mId;
         }
@@ -394,11 +399,11 @@
         public Uri fullSizeImageUri() {
             return mContainer.contentUri(mId);
         }
-        
+
         public IImageList getContainer() {
             return mContainer;
         }
-        
+
         Cursor getCursor() {
             return mContainer.getCursor();
         }
@@ -411,7 +416,7 @@
                 return c.getLong(mContainer.indexDateTaken());
             }
         }
-        
+
         protected int getDegreesRotated() {
             return 0;
         }
@@ -597,7 +602,7 @@
                 }
             }
         }
-        
+
         public int getHeight() {
             ParcelFileDescriptor input = null;
             try {
@@ -660,7 +665,7 @@
         protected Bitmap makeBitmap(int targetWidthHeight, Uri uri, ParcelFileDescriptor pfdInput, BitmapFactory.Options options) {
             return mContainer.makeBitmap(targetWidthHeight, uri, pfdInput, options);
         }
-        
+
         /* (non-Javadoc)
          * @see com.android.camera.IImage#thumb1()
          */
@@ -702,7 +707,7 @@
                 return null;
             }
         }
-        
+
         public void onRemove() {
             mContainer.mCache.remove(mId);
         }
@@ -736,7 +741,7 @@
                 }
             }
         }
-        
+
         /* (non-Javadoc)
          * @see com.android.camera.IImage#setName()
          */
@@ -748,7 +753,7 @@
                 }
             }
         }
-        
+
         public void setPicasaId(String id) {
             Cursor c = null;
             try {
@@ -772,7 +777,7 @@
                     c.close();
             }
         }
-        
+
         /* (non-Javadoc)
          * @see com.android.camera.IImage#thumbUri()
          */
@@ -782,7 +787,7 @@
             uri = uri.buildUpon().appendQueryParameter("thumb", "1").build();
             return uri;
         }
-        
+
         @Override
         public String toString() {
             return fullSizeImageUri().toString();
@@ -810,16 +815,17 @@
             mSort = sort;
             mUri = uri;
             mBaseUri = uri;
+            mBucketId = bucketId;
 
             mContentResolver = cr;
         }
-        
+
         String randomAccessFilePath(int version) {
             String directoryName = Environment.getExternalStorageDirectory().toString() + "/dcim/.thumbnails";
             String path = directoryName + "/.thumbdata" + version + "-" + mUri.hashCode();
             return path;
         }
-        
+
         RandomAccessFile miniThumbDataFile() {
             if (mMiniThumbData == null) {
                 String path = randomAccessFilePath(MINI_THUMB_DATA_FILE_VERSION);
@@ -834,7 +840,7 @@
                 try {
                     mMiniThumbData = new RandomAccessFile(f, "rw");
                 } catch (IOException ex) {
-                    
+
                 }
             }
             return mMiniThumbData;
@@ -853,14 +859,14 @@
                     return thumb;
                 }
                 OutputStream thumbOut = mContentResolver.openOutputStream(uri);
-                thumb.compress(Bitmap.CompressFormat.JPEG, 60, thumbOut);  
+                thumb.compress(Bitmap.CompressFormat.JPEG, 60, thumbOut);
                 thumbOut.close();
                 return thumb;
             }
             catch (Exception ex) {
                 Log.d(TAG, "unable to store thumbnail: " + ex);
                 return thumb;
-            } 
+            }
         }
 
         /**
@@ -924,11 +930,11 @@
             }
             return uri;
         }
-        
+
         java.util.Random mRandom = new java.util.Random(System.currentTimeMillis());
 
         protected SomewhatFairLock mLock = new SomewhatFairLock();
-        
+
         class SomewhatFairLock {
             private Object mSync = new Object();
             private boolean mLocked = false;
@@ -954,7 +960,7 @@
                     mLocked = true;
                 }
             }
-            
+
             void unlock() {
 //              if (VERBOSE) Log.v(TAG, "unlocking... thread " + Thread.currentThread().getId());
                 synchronized (mSync) {
@@ -991,7 +997,7 @@
                             // which will produce much better scaling quality
                             // and is significantly faster.
                             options.inSampleSize = computeSampleSize(options, THUMBNAIL_TARGET_SIZE);
-                                
+
                             if (VERBOSE) {
                                 Log.v(TAG, "in createThumbnailFromExif using inSampleSize of " + options.inSampleSize);
                             }
@@ -1019,7 +1025,20 @@
             }
             return bitmap;
         }
-        
+
+        private Bitmap createVideoThumbnail(String filePath) {
+            Bitmap bitmap = null;
+            MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+            try {
+                retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
+                retriever.setDataSource(filePath);
+                bitmap = retriever.captureFrame();
+            } finally {
+                retriever.release();
+            }
+            return bitmap;
+        }
+
         // returns id
         public long checkThumbnail(BaseImage existingImage, Cursor c, int i) {
             long magic, fileMagic = 0, id;
@@ -1042,7 +1061,7 @@
                 }
 
                 if (magic != 0) {
-                    // check the mini thumb file for the right data.  Right is defined as 
+                    // check the mini thumb file for the right data.  Right is defined as
                     // having the right magic number at the offset reserved for this "id".
                     RandomAccessFile r = miniThumbDataFile();
                     if (r != null) {
@@ -1072,18 +1091,23 @@
                 // If we can't retrieve the thumbnail, first check if there is one embedded in the
                 // EXIF data. If not, or it's not big enough, decompress the full size image.
                 Bitmap bitmap = null;
-                
+
                 String filePath = null;
                 synchronized (c) {
                     if (c.moveToPosition(i)) {
                         filePath = c.getString(indexData());
                     }
                 }
-                
                 if (filePath != null) {
                     bitmap = createThumbnailFromEXIF(filePath, id);
                     if (bitmap == null) {
-                        bitmap = createThumbnailFromUri(c, id);
+                        String mimeType = c.getString(indexMimeType());
+                        boolean isVideo = isVideoMimeType(mimeType);
+                        if (isVideo) {
+                            bitmap = createVideoThumbnail(filePath);
+                        } else {
+                            bitmap = createThumbnailFromUri(c, id);
+                        }
                     }
                     synchronized (c) {
                         int degrees = 0;
@@ -1141,7 +1165,7 @@
                 Log.v(TAG, ">>>>>>>>>>> need to check " + c.getCount() + " rows");
 
             c.close();
-            
+
             if (!ImageManager.hasStorage()) {
                 if (VERBOSE)
                     Log.v(TAG, "bailing from the image checker thread -- no storage");
@@ -1158,7 +1182,7 @@
                     return;
                 }
             }
-            
+
             c = getCursor();
             try {
                 if (VERBOSE) Log.v(TAG, "checkThumbnails found " + c.getCount());
@@ -1188,7 +1212,7 @@
                 }
             }
         }
-        
+
         public void commitChanges() {
             synchronized (mCursor) {
                 mCursor.commitUpdates();
@@ -1208,16 +1232,21 @@
                 return ContentUris.withAppendedId(mBaseUri, id);
             }
         }
-        
+
         public void deactivate() {
             mCursorDeactivated = true;
-            mCursor.deactivate();
+            try {
+                mCursor.deactivate();
+            } catch (IllegalStateException e) {
+                // IllegalStateException may be thrown if the cursor is stale.
+                Log.e(TAG, "Caught exception while deactivating cursor.", e);
+            }
             if (mMiniThumbData != null) {
                 try {
                     mMiniThumbData.close();
                     mMiniThumbData = null;
                 } catch (IOException ex) {
-                    
+
                 }
             }
         }
@@ -1244,6 +1273,11 @@
                 return 0;
             }
         }
+
+        public boolean isEmpty() {
+            return getCount() == 0;
+        }
+
         protected Cursor getCursor() {
             synchronized (mCursor) {
                 if (mCursorDeactivated) {
@@ -1252,11 +1286,11 @@
                 return mCursor;
             }
         }
-        
+
         protected void activateCursor() {
             requery();
         }
-        
+
         public IImage getImageAt(int i) {
             Cursor c = getCursor();
             synchronized (c) {
@@ -1307,7 +1341,7 @@
             RandomAccessFile r = miniThumbDataFile();
             if (r == null)
                 return null;
-            
+
             long pos = id * sBytesPerMiniThumb;
             RandomAccessFile f = r;
             synchronized (f) {
@@ -1349,7 +1383,7 @@
                         }
                         index += 1;
                     } while (c.moveToNext());
-                }   
+                }
                 return -1;
             }
         }
@@ -1368,7 +1402,7 @@
         protected abstract int indexTitle();
         protected abstract int indexDisplayName();
         protected abstract int indexThumbId();
-        
+
         protected IImage make(long id, long miniThumbId, ContentResolver cr, IImageList list, long timestamp, int index, int rotation) {
             return null;
         }
@@ -1400,7 +1434,7 @@
             return false;
         }
 
-        
+
         /* (non-Javadoc)
          * @see com.android.camera.IImageList#removeImageAt(int)
          */
@@ -1460,7 +1494,7 @@
                         }
                         r.seek(pos);
                         r.writeByte(0);     // we have no data in this slot
-                        
+
                         // if magic is 0 then leave it alone
                         if (magic == 0)
                             r.skipBytes(8);
@@ -1468,11 +1502,11 @@
                             r.writeLong(magic);
                         r.writeInt(data.length);
                         r.write(data);
-                        //                      f.flush();  
+                        //                      f.flush();
                         r.seek(pos);
                         r.writeByte(1);  // we have data in this slot
                         long t3 = System.currentTimeMillis();
-                        
+
                         if (VERBOSE) Log.v(TAG, "saveMiniThumbToFile took " + (t3-t0) + "; " + (t1-t0) + " " + (t2-t1) + " " + (t3-t2));
                     }
                 } catch (IOException ex) {
@@ -1486,9 +1520,9 @@
             mHandler = h;
         }
     }
-    
+
     public class CanceledException extends Exception {
-        
+
     }
     public enum DataLocation { NONE, INTERNAL, EXTERNAL, ALL }
 
@@ -1502,17 +1536,17 @@
      * from an ICancelable can be retrieved using the get* method.  If the
      * operation was canceled then null is returned.  The act of canceling
      * is to call "cancel" -- from another thread.
-     * 
-     * In general an object which implements ICancelable will need to 
+     *
+     * In general an object which implements ICancelable will need to
      * check, periodically, whether they are canceled or not.  This works
      * well for some things and less well for others.
-     * 
+     *
      * Right now the actual jpeg encode does not check cancelation but
      * the part of encoding which writes the data to disk does.  Note,
      * though, that there is what appears to be a bug in the jpeg encoder
      * in that if the stream that's being written is closed it crashes
      * rather than returning an error.  TODO fix that.
-     * 
+     *
      * When an object detects that it is canceling it must, before exiting,
      * call acknowledgeCancel.  This is necessary because the caller of
      * cancel() will block until acknowledgeCancel is called.
@@ -1526,7 +1560,7 @@
          */
         public boolean cancel();
     }
-    
+
     public interface IGetBitmap_cancelable extends ICancelable {
         // returns the bitmap or null if there was an error or we were canceled
         public Bitmap get();
@@ -1543,9 +1577,9 @@
          * @return  the bitmap for the full size image.
          */
         public abstract Bitmap fullSizeBitmap(int targetWidthOrHeight);
-        
+
         /**
-         * 
+         *
          * @return an object which can be canceled while the bitmap is loading
          */
         public abstract IGetBitmap_cancelable fullSizeBitmap_cancelable(int targetWidthOrHeight);
@@ -1589,7 +1623,7 @@
         public abstract String getDisplayName();
 
         public abstract String getPicasaId();
-        
+
         public abstract int getRow();
 
         public abstract int getWidth();
@@ -1599,13 +1633,13 @@
         public abstract long imageId();
 
         public abstract boolean isReadonly();
-        
+
         public abstract boolean isDrm();
-        
+
         public abstract Bitmap miniThumbBitmap();
-        
+
         public abstract void onRemove();
-        
+
         public abstract boolean rotateImageBy(int degrees);
 
         /**
@@ -1622,26 +1656,27 @@
          * Sets the name of the image.
          */
         public abstract void setName(String name);
-        
+
         public abstract void setPicasaId(String id);
-        
+
         /**
          * Get the bitmap for the medium thumbnail.
          * @return  the bitmap for the medium thumbnail.
          */
         public abstract Bitmap thumbBitmap();
-        
+
         public abstract Uri thumbUri();
-        
+
         public abstract String getDataPath();
     }
+
     public interface IImageList {
         public HashMap<String, String> getBucketIds();
 
         public interface OnChange {
             public void onChange(IImageList list);
         }
-        
+
         public interface ThumbCheckCallback {
             public boolean checking(int current, int count);
         }
@@ -1658,7 +1693,13 @@
          * @return       the number of images
          */
         public abstract int getCount();
-        
+
+        /**
+         * @return true if the count of image objects is zero.
+         */
+
+        public abstract boolean isEmpty();
+
         /**
          * Returns the image at the ith position.
          *
@@ -1666,7 +1707,7 @@
          * @return      the image at the ith position
          */
         public abstract IImage getImageAt(int i);
-        
+
         /**
          * Returns the image with a particular Uri.
          *
@@ -1674,40 +1715,26 @@
          * @return      the image with a particular Uri.
          */
         public abstract IImage getImageForUri(Uri uri);;
-        
+
         public abstract boolean removeImage(IImage image);
         /**
          * Removes the image at the ith position.
          * @param i     the position
          */
         public abstract void removeImageAt(int i);
-        
+
         public abstract void removeOnChangeListener(OnChange changeCallback);
         public abstract void setOnChangeListener(OnChange changeCallback, Handler h);
     }
-    
-    class VideoObject extends Image {
-        public VideoObject() {
-            super(0, 0, null, null, 0, 0);
-        }
-        
-        public String getTags() {
-            return null;
-        }
-        
-        public String setTags(String tags) {
-            return null;
-        }
-    }
 
     class Image extends BaseImage implements IImage {
         int mRotation;
-        
+
         protected Image(long id, long miniThumbId, ContentResolver cr, BaseImageList container, int cursorRow, int rotation) {
             super(id, miniThumbId, cr, container, cursorRow);
             mRotation = rotation;
         }
-        
+
         public String getDataPath() {
             String path = null;
             Cursor c = getCursor();
@@ -1724,7 +1751,7 @@
         protected int getDegreesRotated() {
             return mRotation;
         }
-        
+
         protected void setDegreesRotated(int degrees) {
             Cursor c = getCursor();
             mRotation = degrees;
@@ -1743,12 +1770,12 @@
             String mimeType = getMimeType();
             if (mimeType == null)
                 return Bitmap.CompressFormat.JPEG;
-            
+
             if (mimeType.equals("image/png"))
                 return Bitmap.CompressFormat.PNG;
             else if (mimeType.equals("image/png"))
                 return Bitmap.CompressFormat.PNG;
-            
+
             return Bitmap.CompressFormat.JPEG;
         }
 
@@ -1782,16 +1809,16 @@
             }
             return 0;
         }
-        
+
         public boolean isReadonly() {
             String mimeType = getMimeType();
             return !"image/jpeg".equals(mimeType) && !"image/png".equals(mimeType);
         }
-        
+
         public boolean isDrm() {
             return false;
         }
-        
+
         /**
          * Remove tag if already there. Otherwise, does nothing.
          * @param tag
@@ -1822,17 +1849,17 @@
          * @see com.android.camera.IImage#saveModifiedImage(android.graphics.Bitmap)
          */
         public IGetBoolean_cancelable saveImageContents(
-                final Bitmap image, 
+                final Bitmap image,
                 final byte [] jpegData,
                 final int orientation,
                 final boolean newFile,
                 final Cursor cursor) {
             final class SaveImageContentsCancelable extends BaseCancelable implements IGetBoolean_cancelable {
                 IGetBoolean_cancelable mCurrentCancelable = null;
-                
+
                 SaveImageContentsCancelable() {
                 }
-                
+
                 public boolean doCancelWork() {
                     synchronized (this) {
                         if (mCurrentCancelable != null)
@@ -1840,22 +1867,22 @@
                     }
                     return true;
                 }
-                
+
                 public boolean get() {
                     try {
                         Bitmap thumbnail = null;
-                        
+
                         long t1 = System.currentTimeMillis();
                         Uri uri = mContainer.contentUri(mId);
                         synchronized (this) {
                             checkCanceled();
                             mCurrentCancelable = compressImageToFile(image, jpegData, uri);
                         }
-                        
+
                         long t2 = System.currentTimeMillis();
                         if (!mCurrentCancelable.get())
                             return false;
-        
+
                         synchronized (this) {
                             String filePath;
                             synchronized (cursor) {
@@ -1890,7 +1917,7 @@
                         saveMiniThumb(rotate(thumbnail, orientation));
                         long t5 = System.currentTimeMillis();
                         checkCanceled();
-                        
+
                         if (VERBOSE) Log.v(TAG, String.format("Timing data %d %d %d %d", t2-t1, t3-t2, t4-t3, t5-t4));
                         return true;
                     } catch (CanceledException ex) {
@@ -1911,7 +1938,7 @@
             }
             return new SaveImageContentsCancelable();
         }
-        
+
         private void setExifRotation(int degrees) {
             try {
                 Cursor c = getCursor();
@@ -1952,7 +1979,7 @@
                 Log.e(TAG, "unable to save exif data with new orientation " + fullSizeImageUri());
             }
         }
-        
+
         /**
          * Save the rotated image by updating the Exif "Orientation" tag.
          * @param degrees
@@ -1979,10 +2006,10 @@
             if (mContainer.mThumbUri != null) {
                 try {
                     c = mContentResolver.query(
-                            mContainer.mThumbUri, 
-                            THUMB_PROJECTION, 
-                            Thumbnails.IMAGE_ID + "=?", 
-                            new String[] { String.valueOf(fullSizeImageId()) }, 
+                            mContainer.mThumbUri,
+                            THUMB_PROJECTION,
+                            Thumbnails.IMAGE_ID + "=?",
+                            new String[] { String.valueOf(fullSizeImageId()) },
                             null);
                     if (c != null && c.moveToFirst()) {
                         Uri thumbUri = ContentUris.withAppendedId(mContainer.mThumbUri, c.getLong(((ImageList)mContainer).INDEX_THUMB_ID));
@@ -2011,7 +2038,7 @@
                         c.close();
                 }
             }
-            
+
             if (bitmap == null) {
                 bitmap = fullSizeBitmap(THUMBNAIL_TARGET_SIZE, false);
                 if (VERBOSE) {
@@ -2038,7 +2065,7 @@
 
     final static private String sWhereClause = "(" + Images.Media.MIME_TYPE + "=? or " + Images.Media.MIME_TYPE + "=?" + ")";
     final static private String[] sAcceptableImageTypes = new String[] { "image/jpeg", "image/png" };
-    
+
     private static final String[] IMAGE_PROJECTION = new String[] {
             "_id",
             "_data",
@@ -2047,7 +2074,7 @@
             ImageColumns.ORIENTATION,
             ImageColumns.MIME_TYPE
         };
-        
+
     /**
      * Represents an ordered collection of Image objects.
      * Provides an api to add and remove an image.
@@ -2059,7 +2086,7 @@
         final int INDEX_DATE_TAKEN       = indexOf(IMAGE_PROJECTION, ImageColumns.DATE_TAKEN);
         final int INDEX_MINI_THUMB_MAGIC = indexOf(IMAGE_PROJECTION, ImageColumns.MINI_THUMB_MAGIC);
         final int INDEX_ORIENTATION      = indexOf(IMAGE_PROJECTION, ImageColumns.ORIENTATION);
-        
+
         final int INDEX_THUMB_ID         = indexOf(THUMB_PROJECTION, BaseColumns._ID);
         final int INDEX_THUMB_IMAGE_ID   = indexOf(THUMB_PROJECTION, Images.Thumbnails.IMAGE_ID);
         final int INDEX_THUMB_WIDTH      = indexOf(THUMB_PROJECTION, Images.Thumbnails.WIDTH);
@@ -2068,12 +2095,12 @@
         boolean mIsRegistered = false;
         ContentObserver mContentObserver;
         DataSetObserver mDataSetObserver;
-        
+
         public HashMap<String, String> getBucketIds() {
             Cursor c = Images.Media.query(
                         mContentResolver,
                         mBaseUri.buildUpon().appendQueryParameter("distinct", "true").build(),
-                        new String[] { 
+                        new String[] {
                             ImageColumns.BUCKET_DISPLAY_NAME,
                             ImageColumns.BUCKET_ID
                         },
@@ -2098,7 +2125,6 @@
             mBaseUri = imageUri;
             mThumbUri = thumbUri;
             mSort = sort;
-            mBucketId = bucketId;
 
             mContentResolver = cr;
 
@@ -2118,7 +2144,7 @@
                     // For now ignore them since there shouldn't be anyone modifying the database on the fly.
                     if (true)
                         return;
-                    
+
                     synchronized (mCursor) {
                         requery();
                     }
@@ -2126,7 +2152,7 @@
                         mListener.onChange(ImageList.this);
                 }
             };
-            
+
             mContentObserver = new ContentObserver(null) {
                 @Override
                 public boolean deliverSelfNotifications() {
@@ -2139,7 +2165,7 @@
                     updateRunnable.run();
                 }
             };
-            
+
             mDataSetObserver = new DataSetObserver() {
                 @Override
                 public void onChanged() {
@@ -2152,23 +2178,23 @@
                     if (VERBOSE) Log.v(TAG, "MyDataSetObserver.onInvalidated: " + mCursorDeactivated);
                 }
             };
-            
+
             registerObservers();
         }
-        
+
         private void registerObservers() {
             if (mIsRegistered)
                 return;
-            
+
             mCursor.registerContentObserver(mContentObserver);
             mCursor.registerDataSetObserver(mDataSetObserver);
             mIsRegistered = true;
         }
-        
+
         private void unregisterObservers() {
             if (!mIsRegistered)
                 return;
-            
+
             mCursor.unregisterContentObserver(mContentObserver);
             mCursor.unregisterDataSetObserver(mDataSetObserver);
             mIsRegistered = false;
@@ -2183,21 +2209,21 @@
             super.activateCursor();
             registerObservers();
         }
-        
+
         protected String whereClause() {
             if (mBucketId != null) {
-                return sWhereClause + " and " + Images.Media.BUCKET_ID + " = " + mBucketId; 
+                return sWhereClause + " and " + Images.Media.BUCKET_ID + " = '" + mBucketId + "'";
             } else {
                 return sWhereClause;
             }
         }
-        
+
         protected String[] whereClauseArgs() {
             return sAcceptableImageTypes;
         }
-        
+
         protected Cursor createCursor() {
-            Cursor c = 
+            Cursor c =
                 Images.Media.query(
                     mContentResolver,
                     mBaseUri,
@@ -2209,7 +2235,7 @@
                 Log.v(TAG, "createCursor got cursor with count " + (c == null ? -1 : c.getCount()));
             return c;
         }
-        
+
         protected int indexOrientation() {  return INDEX_ORIENTATION;      }
         protected int indexDateTaken()   {  return INDEX_DATE_TAKEN;       }
         protected int indexDescription() {  return -1;                     }
@@ -2225,7 +2251,7 @@
         protected int indexTitle()       {  return -1;                     }
         protected int indexDisplayName() {  return -1;                     }
         protected int indexThumbId()     {  return INDEX_THUMB_ID;        }
-        
+
         protected IImage make(long id, long miniThumbId, ContentResolver cr, IImageList list, long timestamp, int index, int rotation) {
             return new Image(id, miniThumbId, mContentResolver, this, index, rotation);
         }
@@ -2234,12 +2260,12 @@
             Bitmap b = null;
 
             try {
-                if (pfd == null) 
+                if (pfd == null)
                     pfd = makeInputStream(uri);
 
                 if (pfd == null)
                     return null;
-                
+
                 if (options == null)
                     options = new BitmapFactory.Options();
 
@@ -2272,7 +2298,7 @@
             }
             return b;
         }
-        
+
         private ParcelFileDescriptor makeInputStream(Uri uri) {
             try {
                 return mContentResolver.openFileDescriptor(uri, "r");
@@ -2286,11 +2312,11 @@
             // which could happen, I suppose, if the first two values were
             // duplicated
             String ascending = (mSort == SORT_ASCENDING ? " ASC" : " DESC");
-            return 
+            return
                 Images.Media.DATE_TAKEN + ascending + "," +
                 Images.Media._ID + ascending;
         }
-        
+
     }
 
     /**
@@ -2301,11 +2327,11 @@
             DrmStore.Audio._ID,
             DrmStore.Audio.DATA,
             DrmStore.Audio.MIME_TYPE,
-        };      
-        
+        };
+
         final int INDEX_ID            = indexOf(DRM_IMAGE_PROJECTION, DrmStore.Audio._ID);
         final int INDEX_MIME_TYPE     = indexOf(DRM_IMAGE_PROJECTION, DrmStore.Audio.MIME_TYPE);
- 
+
         public DrmImageList(Context ctx, ContentResolver cr, Uri imageUri, int sort, String bucketId) {
             super(ctx, cr, imageUri, null, sort, bucketId);
         }
@@ -2323,16 +2349,16 @@
         public long checkThumbnail(BaseImage existingImage, Cursor c, int i) {
             return 0;
         }
-        
+
         class DrmImage extends Image {
             protected DrmImage(long id, ContentResolver cr, BaseImageList container, int cursorRow) {
                 super(id, 0, cr, container, cursorRow, 0);
             }
-            
+
             public boolean isDrm() {
                 return true;
             }
-            
+
             public boolean isReadonly() {
                 return true;
             }
@@ -2340,16 +2366,21 @@
             public Bitmap miniThumbBitmap() {
                 return fullSizeBitmap(MINI_THUMB_TARGET_SIZE);
             }
-            
+
             public Bitmap thumbBitmap() {
                 return fullSizeBitmap(THUMBNAIL_TARGET_SIZE);
             }
+
+            public String getDisplayName() {
+                return getTitle();
+            }
         }
-        
-        protected IImage make(long id, long miniThumbId, ContentResolver cr, IImageList list, long timestamp, int index) {
+
+        @Override
+        protected IImage make(long id, long miniThumbId, ContentResolver cr, IImageList list, long timestamp, int index, int rotation) {
             return new DrmImage(id, mContentResolver, this, index);
         }
-        
+
         protected int indexOrientation() {  return -1; }
         protected int indexDateTaken()   {  return -1; }
         protected int indexDescription() {  return -1; }
@@ -2363,11 +2394,11 @@
         protected int indexTitle()       {  return -1; }
         protected int indexDisplayName() {  return -1; }
         protected int indexThumbId()     {  return -1; }
-        
+
         // TODO review this probably should be based on DATE_TAKEN same as images
         private String sortOrder() {
             String ascending = (mSort == SORT_ASCENDING ? " ASC" : " DESC");
-            return 
+            return
                 DrmStore.Images.TITLE  + ascending + "," +
                 DrmStore.Images._ID;
         }
@@ -2385,7 +2416,7 @@
         // The second component indicates which sublist we're referring
         // to (an int which is used to index into mSubList).
         ArrayList<Long> mSkipList = null;
-        
+
         int [] mSkipCounts = null;
 
         public HashMap<String, String> getBucketIds() {
@@ -2395,7 +2426,7 @@
             }
             return hashMap;
         }
-        
+
         public ImageListUber(IImageList [] sublist, int sort) {
             mSubList = sublist.clone();
             mSort = sort;
@@ -2412,7 +2443,7 @@
                 }
             }
         }
-        
+
         public void checkThumbnails(ThumbCheckCallback cb) {
             // TODO this isn't quite right because we need to get the
             // total from each sub item and provide that in the callback
@@ -2421,7 +2452,7 @@
             for (int i = 0; i < length; i++)
                 sublist[i].checkThumbnails(cb);
         }
-        
+
         public void commitChanges() {
             final IImageList sublist[] = mSubList;
             final int length = sublist.length;
@@ -2448,6 +2479,17 @@
             return count;
         }
 
+        public boolean isEmpty() {
+            final IImageList sublist[] = mSubList;
+            final int length = sublist.length;
+            for (int i = 0; i < length; i++) {
+                if (! sublist[i].isEmpty()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
         // mSkipCounts is used to tally the counts as we traverse
         // the mSkipList.  It's a member variable only so that
         // we don't have to allocate each time through.  Otherwise
@@ -2456,7 +2498,7 @@
         public synchronized IImage getImageAt(int index) {
             if (index < 0 || index > getCount())
                 throw new IndexOutOfBoundsException("index " + index + " out of range max is " + getCount());
-            
+
             // first make sure our allocations are in order
             if (mSkipCounts == null || mSubList.length > mSkipCounts.length)
                 mSkipCounts = new int[mSubList.length];
@@ -2610,7 +2652,7 @@
             if (changeCallback == mListener)
                 mListener = null;
         }
-        
+
         public void setOnChangeListener(OnChange changeCallback, Handler h) {
             mListener = changeCallback;
             mHandler = h;
@@ -2642,7 +2684,7 @@
         public long getDateTaken() {
             return 0;
         }
-        
+
         public String getMimeType() {
             throw new UnsupportedOperationException();
         }
@@ -2658,7 +2700,7 @@
         public double getLatitude() {
             return 0D;
         }
-        
+
         public double getLongitude() {
             return 0D;
         }
@@ -2678,7 +2720,7 @@
         public int getRow() {
             throw new UnsupportedOperationException();
         }
-        
+
         public int getHeight() {
             return 0;
         }
@@ -2686,7 +2728,7 @@
         public int getWidth() {
             return 0;
         }
-        
+
         public boolean hasLatLong() {
             return false;
         }
@@ -2694,7 +2736,7 @@
         public boolean isReadonly() {
             return true;
         }
-        
+
         public boolean isDrm() {
             return false;
         }
@@ -2706,7 +2748,7 @@
         public boolean rotateImageBy(int degrees) {
             return false;
         }
-        
+
         public void setDescription(String description) {
             throw new UnsupportedOperationException();
         }
@@ -2718,13 +2760,13 @@
         public void setName(String name) {
             throw new UnsupportedOperationException();
         }
-        
+
         public void setPicasaId(long id) {
         }
 
         public void setPicasaId(String id) {
         }
-        
+
         public Uri thumbUri() {
             throw new UnsupportedOperationException();
         }
@@ -2739,11 +2781,11 @@
 
             UriImage() {
             }
-            
+
             public String getDataPath() {
                 return mUri.getPath();
             }
-            
+
             InputStream getInputStream() {
                 try {
                     if (mUri.getScheme().equals("file")) {
@@ -2758,7 +2800,7 @@
                     return null;
                 }
             }
-            
+
             ParcelFileDescriptor getPFD() {
                 try {
                     if (mUri.getScheme().equals("file")) {
@@ -2773,7 +2815,7 @@
                     return null;
                 }
             }
-            
+
             /* (non-Javadoc)
              * @see com.android.camera.ImageManager.IImage#fullSizeBitmap(int)
              */
@@ -2790,7 +2832,7 @@
                     options.inJustDecodeBounds = false;
                     options.inDither = false;
                     options.inPreferredConfig = Bitmap.Config.ARGB_8888;
-                    
+
                     Bitmap b = BitmapFactory.decodeFileDescriptor(pfdInput.getFileDescriptor(), null, options);
                     if (VERBOSE) {
                         Log.v(TAG, "B: got bitmap " + b + " with sampleSize " + options.inSampleSize);
@@ -2808,11 +2850,11 @@
                     ParcelFileDescriptor pfdInput;
                     BitmapFactory.Options mOptions = new BitmapFactory.Options();
                     long mCancelInitiationTime;
-                    
+
                     public LoadBitmapCancelable(ParcelFileDescriptor pfd) {
                         pfdInput = pfd;
                     }
-                    
+
                     public boolean doCancelWork() {
                         if (VERBOSE)
                             Log.v(TAG, "requesting bitmap load cancel");
@@ -2820,7 +2862,7 @@
                         mOptions.requestCancelDecode();
                         return true;
                     }
-                    
+
                     public Bitmap get() {
                         try {
                             Bitmap b = makeBitmap(targetWidthOrHeight, fullSizeImageUri(), pfdInput, mOptions);
@@ -2848,12 +2890,12 @@
                     return null;
                 }
             }
-            
+
             @Override
             public Uri fullSizeImageUri() {
                 return mUri;
             }
-            
+
             @Override
             public InputStream fullSizeImageData() {
                 return getInputStream();
@@ -2862,26 +2904,26 @@
             public long imageId() {
                 return 0;
             }
-            
+
             public Bitmap miniThumbBitmap() {
                 return thumbBitmap();
             }
-            
+
             @Override
             public String getTitle() {
                 return mUri.toString();
             }
-            
+
             @Override
             public String getDisplayName() {
                 return getTitle();
             }
-            
-            @Override 
+
+            @Override
             public String getDescription() {
                 return "";
             }
-            
+
             public Bitmap thumbBitmap() {
                 Bitmap b = fullSizeBitmap(THUMBNAIL_TARGET_SIZE);
                 if (b != null) {
@@ -2894,7 +2936,7 @@
                     return null;
                 }
             }
-            
+
             private BitmapFactory.Options snifBitmapOptions() {
                 ParcelFileDescriptor input = getPFD();
                 if (input == null)
@@ -2920,13 +2962,13 @@
                 BitmapFactory.Options options = snifBitmapOptions();
                 return (options!=null) ? options.outMimeType : "";
             }
-            
+
             @Override
             public int getHeight() {
                 BitmapFactory.Options options = snifBitmapOptions();
                 return (options!=null) ? options.outHeight : 0;
             }
-            
+
             @Override
             public int getWidth() {
                 BitmapFactory.Options options = snifBitmapOptions();
@@ -2948,11 +2990,15 @@
         public void deactivate() {
             // nothing to do here
         }
-        
+
         public int getCount() {
             return 1;
         }
 
+        public boolean isEmpty() {
+            return false;
+        }
+
         public IImage getImageAt(int i) {
             if (i == 0)
                 return mSingleImage;
@@ -2980,7 +3026,7 @@
         protected int indexDateTaken() {
             return -1;
         }
-        
+
         @Override
         protected int indexMimeType() {
             return -1;
@@ -2990,12 +3036,12 @@
         protected int indexDescription() {
             return -1;
         }
-        
+
         @Override
         protected int indexId() {
             return -1;
         }
-        
+
         @Override
         protected int indexData() {
             return -1;
@@ -3040,7 +3086,7 @@
         protected int indexThumbId() {
             return -1;
         }
-        
+
         private InputStream makeInputStream(Uri uri) {
             InputStream input = null;
             try {
@@ -3087,14 +3133,14 @@
         public ThreadSafeOutputStream(OutputStream delegate) {
             mDelegateStream = delegate;
         }
-        
+
         @Override
         synchronized public void close() throws IOException {
             try {
                 mClosed = true;
                 mDelegateStream.close();
             } catch (IOException ex) {
-                
+
             }
         }
 
@@ -3113,7 +3159,7 @@
                 synchronized (this) {
                     if (mClosed)
                         return;
-                    
+
                     int writeLength = Math.min(8192, length);
                     mDelegateStream.write(b, offset, writeLength);
                     offset += writeLength;
@@ -3129,14 +3175,349 @@
             mDelegateStream.write(oneByte);
         }
     }
-    
+
+    class VideoList extends BaseImageList implements IImageList {
+        private final String[] sProjection = new String[] {
+                Video.Media._ID,
+                Video.Media.DATA,
+                Video.Media.DATE_TAKEN,
+                Video.Media.TITLE,
+                Video.Media.DISPLAY_NAME,
+                Video.Media.DESCRIPTION,
+                Video.Media.IS_PRIVATE,
+                Video.Media.TAGS,
+                Video.Media.CATEGORY,
+                Video.Media.LANGUAGE,
+                Video.Media.LATITUDE,
+                Video.Media.LONGITUDE,
+                Video.Media.MINI_THUMB_MAGIC,
+                Video.Media.MIME_TYPE,
+        };
+
+        final int INDEX_ID               = indexOf(sProjection, Video.Media._ID);
+        final int INDEX_DATA             = indexOf(sProjection, Video.Media.DATA);
+        final int INDEX_DATE_TAKEN       = indexOf(sProjection, Video.Media.DATE_TAKEN);
+        final int INDEX_TITLE            = indexOf(sProjection, Video.Media.TITLE);
+        final int INDEX_DISPLAY_NAME     = indexOf(sProjection, Video.Media.DISPLAY_NAME);
+        final int INDEX_MIME_TYPE        = indexOf(sProjection, Video.Media.MIME_TYPE);
+        final int INDEX_DESCRIPTION      = indexOf(sProjection, Video.Media.DESCRIPTION);
+        final int INDEX_PRIVATE          = indexOf(sProjection, Video.Media.IS_PRIVATE);
+        final int INDEX_TAGS             = indexOf(sProjection, Video.Media.TAGS);
+        final int INDEX_CATEGORY         = indexOf(sProjection, Video.Media.CATEGORY);
+        final int INDEX_LANGUAGE         = indexOf(sProjection, Video.Media.LANGUAGE);
+        final int INDEX_LATITUDE         = indexOf(sProjection, Video.Media.LATITUDE);
+        final int INDEX_LONGITUDE        = indexOf(sProjection, Video.Media.LONGITUDE);
+        final int INDEX_MINI_THUMB_MAGIC = indexOf(sProjection, Video.Media.MINI_THUMB_MAGIC);
+        final int INDEX_THUMB_ID         = indexOf(sProjection, BaseColumns._ID);
+
+        public VideoList(Context ctx, ContentResolver cr, Uri uri, Uri thumbUri,
+                int sort, String bucketId) {
+            super(ctx, cr, uri, sort, bucketId);
+
+            mCursor = createCursor();
+            if (mCursor == null) {
+                Log.e(TAG, "unable to create video cursor for " + mBaseUri);
+                throw new UnsupportedOperationException();
+            }
+
+            if (Config.LOGV) {
+                Log.v(TAG, "for " + mUri.toString() + " got cursor " + mCursor + " with length "
+                        + (mCursor != null ? mCursor.getCount() : -1));
+            }
+
+            if (mCursor == null) {
+                throw new UnsupportedOperationException();
+            }
+            if (mCursor != null && mCursor.moveToFirst()) {
+                int row = 0;
+                do {
+                    long imageId = mCursor.getLong(indexId());
+                    long dateTaken = mCursor.getLong(indexDateTaken());
+                    long miniThumbId = mCursor.getLong(indexMiniThumbId());
+                    mCache.put(imageId, new VideoObject(imageId, miniThumbId, mContentResolver,
+                            this, dateTaken, row++));
+                } while (mCursor.moveToNext());
+            }
+        }
+
+        public HashMap<String, String> getBucketIds() {
+            Cursor c = Images.Media.query(
+                    mContentResolver,
+                    mBaseUri.buildUpon().appendQueryParameter("distinct", "true").build(),
+                    new String[] {
+                        VideoColumns.BUCKET_DISPLAY_NAME,
+                        VideoColumns.BUCKET_ID
+                    },
+                    whereClause(),
+                    whereClauseArgs(),
+                    sortOrder());
+
+        HashMap<String, String> hash = new HashMap<String, String>();
+        if (c != null && c.moveToFirst()) {
+            do {
+                Log.e(TAG, "id: " + c.getString(1) + " display_name: " + c.getString(0));
+                hash.put(c.getString(1), c.getString(0));
+            } while (c.moveToNext());
+        }
+        return hash;
+        }
+
+        protected String whereClause() {
+            if (mBucketId != null) {
+                return Images.Media.BUCKET_ID + " = '" + mBucketId + "'";
+            } else {
+                return null;
+            }
+        }
+
+        protected String[] whereClauseArgs() {
+            return null;
+        }
+
+        protected Cursor createCursor() {
+            Cursor c =
+                Images.Media.query(
+                    mContentResolver,
+                    mBaseUri,
+                    sProjection,
+                    whereClause(),
+                    whereClauseArgs(),
+                    sortOrder());
+            if (VERBOSE)
+                Log.v(TAG, "createCursor got cursor with count " + (c == null ? -1 : c.getCount()));
+            return c;
+        }
+
+        protected int indexOrientation() {  return -1;                    }
+        protected int indexDateTaken()   {  return INDEX_DATE_TAKEN;      }
+        protected int indexDescription() {  return INDEX_DESCRIPTION;     }
+        protected int indexMimeType()    {  return INDEX_MIME_TYPE;       }
+        protected int indexData()        {  return INDEX_DATA;            }
+        protected int indexId()          {  return INDEX_ID;              }
+        protected int indexLatitude()    {  return INDEX_LATITUDE;        }
+        protected int indexLongitude()   {  return INDEX_LONGITUDE;       }
+        protected int indexMiniThumbId() {  return INDEX_MINI_THUMB_MAGIC;   }
+        protected int indexPicasaWeb()   {  return -1;                    }
+        protected int indexPrivate()     {  return INDEX_PRIVATE;         }
+        protected int indexTitle()       {  return INDEX_TITLE;           }
+        protected int indexDisplayName() {  return -1;                    }
+        protected int indexThumbId()     {  return INDEX_THUMB_ID;        }
+
+        protected IImage make(long id, long miniThumbId, ContentResolver cr, IImageList list,
+                long timestamp, int index) {
+            return new VideoObject(id, miniThumbId, mContentResolver, this, timestamp, index);
+        }
+
+        @Override
+        protected Bitmap makeBitmap(int targetWidthHeight, Uri uri, ParcelFileDescriptor pfdInput,
+                BitmapFactory.Options options) {
+            MediaPlayer mp = new MediaPlayer();
+            Bitmap thumbnail = sDefaultThumbnail;
+            try {
+                mp.setDataSource(mContext, uri);
+//              int duration = mp.getDuration();
+//              int at = duration > 2000 ? 1000 : duration / 2;
+                int at = 1000;
+                thumbnail = mp.getFrameAt(at);
+                if (Config.LOGV) {
+                    if ( thumbnail != null) {
+                        Log.v(TAG, "getFrameAt @ " + at + " returned " + thumbnail + "; " +
+                                thumbnail.getWidth() + " " + thumbnail.getHeight());
+                    } else {
+                        Log.v(TAG, "getFrame @ " + at + " failed for " + uri);
+                    }
+                }
+            } catch (IOException ex) {
+            } catch (IllegalArgumentException ex) {
+            } catch (SecurityException ex) {
+            } finally {
+                mp.release();
+            }
+            return thumbnail;
+        }
+
+        private final Bitmap sDefaultThumbnail = Bitmap.createBitmap(32, 32, Bitmap.Config.RGB_565);
+
+        private String sortOrder() {
+            return Video.Media.DATE_MODIFIED + (mSort == SORT_ASCENDING ? " ASC " : " DESC");
+        }
+    }
+
+    /**
+     * Represents a particular video and provides access
+     * to the underlying data and two thumbnail bitmaps
+     * as well as other information such as the id, and
+     * the path to the actual video data.
+     */
+    class VideoObject extends BaseImage implements IImage {
+        /**
+         * Constructor.
+         *
+         * @param id        the image id of the image
+         * @param cr        the content resolver
+         */
+        protected VideoObject(long id, long miniThumbId, ContentResolver cr, VideoList container,
+                long dateTaken, int row) {
+            super(id, miniThumbId, cr, container, row);
+        }
+
+        protected Bitmap.CompressFormat compressionType() {
+            return Bitmap.CompressFormat.JPEG;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other == null)
+                return false;
+            if (!(other instanceof VideoObject))
+                return false;
+
+            return fullSizeImageUri().equals(((VideoObject)other).fullSizeImageUri());
+        }
+
+        public String getDataPath() {
+            String path = null;
+            Cursor c = getCursor();
+            synchronized (c) {
+                if (c.moveToPosition(getRow())) {
+                    int column = ((VideoList)getContainer()).indexData();
+                    if (column >= 0)
+                        path = c.getString(column);
+                }
+            }
+            return path;
+        }
+
+        /* (non-Javadoc)
+         * @see com.android.camera.IImage#fullSizeBitmap()
+         */
+        public Bitmap fullSizeBitmap(int targetWidthHeight) {
+            return sNoImageBitmap;
+        }
+
+        public IGetBitmap_cancelable fullSizeBitmap_cancelable(int targetWidthHeight) {
+            return null;
+        }
+
+        /* (non-Javadoc)
+         * @see com.android.camera.IImage#fullSizeImageData()
+         */
+        public InputStream fullSizeImageData() {
+            try {
+                InputStream input = mContentResolver.openInputStream(
+                        fullSizeImageUri());
+                return input;
+            } catch (IOException ex) {
+                return null;
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see com.android.camera.IImage#fullSizeImageId()
+         */
+        public long fullSizeImageId() {
+            return mId;
+        }
+
+        public String getCategory() {
+             return getStringEntry(((VideoList)mContainer).INDEX_CATEGORY);
+         }
+
+        public int getHeight() {
+             return 0;
+         }
+
+        public String getLanguage() {
+             return getStringEntry(((VideoList)mContainer).INDEX_LANGUAGE);
+         }
+
+        public String getPicasaId() {
+            return null;
+        }
+
+        private String getStringEntry(int entryName) {
+            String entry = null;
+            Cursor c = getCursor();
+            synchronized(c) {
+                if (c.moveToPosition(getRow())) {
+                    entry = c.getString(entryName);
+                }
+            }
+            return entry;
+        }
+
+        public String getTags() {
+             return getStringEntry(((VideoList)mContainer).INDEX_TAGS);
+         }
+
+        public int getWidth() {
+             return 0;
+         }
+
+         /* (non-Javadoc)
+         * @see com.android.camera.IImage#imageId()
+         */
+        public long imageId() {
+            return mId;
+        }
+
+         public boolean isReadonly() {
+             return false;
+         }
+
+         public boolean isDrm() {
+             return false;
+         }
+
+         public boolean rotateImageBy(int degrees) {
+            return false;
+        }
+
+         public void setCategory(String category) {
+             setStringEntry(category, ((VideoList)mContainer).INDEX_CATEGORY);
+         }
+
+         public void setLanguage(String language) {
+             setStringEntry(language, ((VideoList)mContainer).INDEX_LANGUAGE);
+         }
+
+         private void setStringEntry(String entry, int entryName) {
+            Cursor c = getCursor();
+            synchronized (c) {
+                if (c.moveToPosition(getRow())) {
+                    c.updateString(entryName, entry);
+                }
+            }
+        }
+
+         public void setTags(String tags) {
+             setStringEntry(tags, ((VideoList)mContainer).INDEX_TAGS);
+         }
+
+         /* (non-Javadoc)
+         * @see com.android.camera.IImage#thumb1()
+         */
+        public Bitmap thumbBitmap() {
+            return fullSizeBitmap(320);
+        }
+
+         @Override
+         public String toString() {
+             StringBuilder sb = new StringBuilder();
+             sb.append("" + mId);
+             return sb.toString();
+         }
+
+         private final Bitmap sNoImageBitmap = Bitmap.createBitmap(128, 128, Bitmap.Config.RGB_565);
+    }
+
     /*
      * How much quality to use when storing the thumbnail.
      */
     private static ImageManager sInstance = null;
     private static final int MINI_THUMB_TARGET_SIZE = 96;
     private static final int THUMBNAIL_TARGET_SIZE = 320;
-    
+
     private static final String[] THUMB_PROJECTION = new String[] {
         BaseColumns._ID,           // 0
         Images.Thumbnails.IMAGE_ID,      // 1
@@ -3147,6 +3528,10 @@
     private static Uri sStorageURI   = Images.Media.EXTERNAL_CONTENT_URI;
 
     private static Uri sThumbURI     = Images.Thumbnails.EXTERNAL_CONTENT_URI;
+
+    private static Uri sVideoStorageURI = Uri.parse("content://media/external/video/media");
+
+    private static Uri sVideoThumbURI = Uri.parse("content://media/external/video/thumbnails");
     /**
      * Returns an ImageList object that contains
      * all of the images.
@@ -3157,13 +3542,13 @@
      * @return the singleton ImageList
      */
     static final public int SORT_ASCENDING = 1;
-    
+
     static final public int SORT_DESCENDING = 2;
 
     static final public int INCLUDE_IMAGES     = (1 << 0);
     static final public int INCLUDE_DRM_IMAGES = (1 << 1);
     static final public int INCLUDE_VIDEOS     = (1 << 2);
-    
+
     static public DataLocation getDefaultDataLocation() {
         return DataLocation.EXTERNAL;
     }
@@ -3190,7 +3575,7 @@
     static public byte [] miniThumbData(Bitmap source) {
         if (source == null)
             return null;
-        
+
         float scale;
         if (source.getWidth() < source.getHeight()) {
             scale = MINI_THUMB_TARGET_SIZE / (float)source.getWidth();
@@ -3199,7 +3584,7 @@
         }
         Matrix matrix = new Matrix();
         matrix.setScale(scale, scale);
-        Bitmap miniThumbnail = ImageLoader.transform(matrix, source, 
+        Bitmap miniThumbnail = ImageLoader.transform(matrix, source,
                 MINI_THUMB_TARGET_SIZE, MINI_THUMB_TARGET_SIZE, false);
 
         if (miniThumbnail != source) {
@@ -3223,7 +3608,7 @@
         if (degrees != 0 && b != null) {
             Matrix m = new Matrix();
             m.setRotate(degrees, (float) b.getWidth() / 2, (float) b.getHeight() / 2);
-            
+
             Bitmap b2 = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, true);
             // TODO should recycle here but that needs more testing/verification
 //            b.recycle();
@@ -3231,12 +3616,12 @@
         }
         return b;
     }
-    
+
     public static int roundOrientation(int orientationInput) {
         int orientation = orientationInput;
         if (orientation == -1)
             orientation = 0;
-        
+
         orientation = orientation % 360;
         int retVal;
         if (orientation < (0*90) + 45) {
@@ -3255,6 +3640,35 @@
         return retVal;
     }
 
+
+    /**
+     * @return true if the mimetype is an image mimetype.
+     */
+    public static boolean isImageMimeType(String mimeType) {
+        return mimeType.startsWith("image/");
+    }
+
+    /**
+     * @return true if the mimetype is a video mimetype.
+     */
+    public static boolean isVideoMimeType(String mimeType) {
+        return mimeType.startsWith("video/");
+    }
+
+    /**
+     * @return true if the image is an image.
+     */
+    public static boolean isImage(IImage image) {
+        return isImageMimeType(image.getMimeType());
+    }
+
+    /**
+     * @return true if the image is a video.
+     */
+    public static boolean isVideo(IImage image) {
+        return isVideoMimeType(image.getMimeType());
+    }
+
     public Uri addImage(
             final Context ctx,
             final ContentResolver cr,
@@ -3274,11 +3688,12 @@
         values.put(Images.Media.ORIENTATION, orientation);
 
         File parentFile = new File(directory);
+        // Lowercase the path for hashing. This avoids duplicate buckets if the filepath
+        // case is changed externally.
+        // Keep the original case for display.
         String path = parentFile.toString().toLowerCase();
-        String name = parentFile.getName().toLowerCase();
-        
-        values.put(Images.ImageColumns.BUCKET_ID, path.hashCode());
-        values.put(Images.ImageColumns.BUCKET_DISPLAY_NAME, name);
+        String name = parentFile.getName();
+
         if (VERBOSE) Log.v(TAG, "addImage id is " + path.hashCode() + "; name " + name + "; path is " + path);
 
         if (location != null) {
@@ -3288,24 +3703,24 @@
             values.put(Images.Media.LATITUDE, location.getLatitude());
             values.put(Images.Media.LONGITUDE, location.getLongitude());
         }
-        
+
         if (directory != null && filename != null) {
             String value = directory + "/" + filename;
             values.put("_data", value);
         }
-        
+
         long t3 = System.currentTimeMillis();
         Uri uri = cr.insert(sStorageURI, values);
-        
+
         // The line above will create a filename that ends in .jpg
         // That filename is what will be handed to gmail when a user shares a photo.
         // Gmail gets the name of the picture attachment from the "DISPLAY_NAME" field.
         // Extract the filename and jam it into the display name.
         Cursor c = cr.query(
-                uri, 
-                new String [] { ImageColumns._ID, Images.Media.DISPLAY_NAME, "_data" }, 
-                null, 
-                null, 
+                uri,
+                new String [] { ImageColumns._ID, Images.Media.DISPLAY_NAME, "_data" },
+                null,
+                null,
                 null);
         if (c.moveToFirst()) {
             String filePath = c.getString(2);
@@ -3331,7 +3746,7 @@
                            final byte [] jpegData) {
         class AddImageCancelable extends BaseCancelable implements IAddImage_cancelable {
             private IGetBoolean_cancelable mSaveImageCancelable;
-            
+
             public boolean doCancelWork() {
                 if (VERBOSE) {
                     Log.v(TAG, "calling AddImageCancelable.cancel() " + mSaveImageCancelable);
@@ -3347,7 +3762,7 @@
                 if (source == null && jpegData == null) {
                     throw new IllegalArgumentException("source cannot be null");
                 }
-                
+
                 try {
                     long t1 = System.currentTimeMillis();
                     synchronized (this) {
@@ -3356,15 +3771,15 @@
                         }
                     }
                     long id = ContentUris.parseId(uri);
-    
+
                     BaseImageList il = new ImageList(ctx, cr, sStorageURI, sThumbURI, SORT_ASCENDING, null);
                     ImageManager.Image image = new Image(id, 0, cr, il, il.getCount(), 0);
                     long t5 = System.currentTimeMillis();
                     Cursor c = cr.query(
-                            uri, 
-                            new String [] { ImageColumns._ID, ImageColumns.MINI_THUMB_MAGIC, "_data" }, 
-                            null, 
-                            null, 
+                            uri,
+                            new String [] { ImageColumns._ID, ImageColumns.MINI_THUMB_MAGIC, "_data" },
+                            null,
+                            null,
                             null);
                     c.moveToPosition(0);
 
@@ -3372,7 +3787,7 @@
                         checkCanceled();
                         mSaveImageCancelable = image.saveImageContents(source, jpegData, orientation, true, c);
                     }
-                    
+
                     if (mSaveImageCancelable.get()) {
                         long t6 = System.currentTimeMillis();
                         if (VERBOSE) Log.v(TAG, "saveImageContents took " + (t6-t5));
@@ -3403,7 +3818,7 @@
         }
         return new AddImageCancelable();
     }
-    
+
     static public IImageList makeImageList(Uri uri, Context ctx, int sort) {
         ContentResolver cr = ctx.getContentResolver();
         String uriString = (uri != null) ? uri.toString() : "";
@@ -3411,7 +3826,7 @@
         // DRM images in a better way.  Is there a constant
         // for content://drm somewhere??
         IImageList imageList;
-        
+
         if (uriString.startsWith("content://drm")) {
             imageList = ImageManager.instance().allImages(
                     ctx,
@@ -3435,7 +3850,7 @@
         }
         return imageList;
     }
-    
+
     public IImageList emptyImageList() {
         return
         new IImageList() {
@@ -3456,6 +3871,10 @@
                 return 0;
             }
 
+            public boolean isEmpty() {
+                return true;
+            }
+
             public IImage getImageAt(int i) {
                 return null;
             }
@@ -3476,10 +3895,10 @@
 
             public void setOnChangeListener(com.android.camera.ImageManager.IImageList.OnChange changeCallback, Handler h) {
             }
-            
+
         };
     }
-    
+
     public IImageList allImages(Context ctx, ContentResolver cr, DataLocation location, int inclusion, int sort) {
         return allImages(ctx, cr, location, inclusion, sort, null, null);
     }
@@ -3487,12 +3906,12 @@
     public IImageList allImages(Context ctx, ContentResolver cr, DataLocation location, int inclusion, int sort, String bucketId) {
         return allImages(ctx, cr, location, inclusion, sort, bucketId, null);
     }
-    
+
     public IImageList allImages(Context ctx, ContentResolver cr, DataLocation location, int inclusion, int sort, String bucketId, Uri specificImageUri) {
         if (VERBOSE) {
             Log.v(TAG, "allImages " + location + " " + ((inclusion&INCLUDE_IMAGES)!=0) + " + v=" + ((inclusion&INCLUDE_VIDEOS)!=0));
         }
-        
+
         if (cr == null) {
             return null;
         } else {
@@ -3510,7 +3929,7 @@
                     try {
                         if (specificImageUri.getScheme().equalsIgnoreCase("content"))
                             l.add(new ImageList(ctx, cr, specificImageUri, sThumbURI, sort, bucketId));
-                        else 
+                        else
                             l.add(new SingleImageList(cr, specificImageUri));
                     } catch (UnsupportedOperationException ex) {
                     }
@@ -3522,11 +3941,17 @@
                             } catch (UnsupportedOperationException ex) {
                             }
                         }
+                        if ((inclusion & INCLUDE_VIDEOS) != 0) {
+                            try {
+                                l.add(new VideoList(ctx, cr, sVideoStorageURI, sVideoThumbURI, sort, bucketId));
+                            } catch (UnsupportedOperationException ex) {
+                            }
+                        }
                     }
                     if (location == DataLocation.INTERNAL || location == DataLocation.ALL) {
                         if ((inclusion & INCLUDE_IMAGES) != 0) {
                             try {
-                                l.add(new ImageList(ctx, cr, Images.Media.INTERNAL_CONTENT_URI, 
+                                l.add(new ImageList(ctx, cr, Images.Media.INTERNAL_CONTENT_URI,
                                         Images.Thumbnails.INTERNAL_CONTENT_URI, sort, bucketId));
                             } catch (UnsupportedOperationException ex) {
                             }
@@ -3546,7 +3971,7 @@
                 if (haveSdCard && location != DataLocation.INTERNAL) {
                     return new ImageList(ctx, cr, sStorageURI, sThumbURI, sort, bucketId);
                 } else  {
-                    return new ImageList(ctx, cr, Images.Media.INTERNAL_CONTENT_URI, 
+                    return new ImageList(ctx, cr, Images.Media.INTERNAL_CONTENT_URI,
                             Images.Thumbnails.INTERNAL_CONTENT_URI, sort, bucketId);
                 }
             }
@@ -3577,7 +4002,7 @@
             return false;
         }
     }
-    
+
     static public boolean hasStorage() {
         return hasStorage(true);
     }
@@ -3610,20 +4035,20 @@
          } catch (UnsupportedOperationException ex) {
             return null;
         }
-        
+
     }
-    
+
     public static boolean isMediaScannerScanning(Context context) {
         boolean result = false;
-        Cursor cursor = query(context, MediaStore.getMediaScannerUri(), 
+        Cursor cursor = query(context, MediaStore.getMediaScannerUri(),
                 new String [] { MediaStore.MEDIA_SCANNER_VOLUME }, null, null, null);
         if (cursor != null) {
             if (cursor.getCount() == 1) {
                 cursor.moveToFirst();
                 result = "external".equals(cursor.getString(0));
             }
-            cursor.close(); 
-        } 
+            cursor.close();
+        }
 
         if (VERBOSE)
             Log.v(TAG, ">>>>>>>>>>>>>>>>>>>>>>>>> isMediaScannerScanning returning " + result);
diff --git a/src/com/android/camera/ImageViewTouchBase.java b/src/com/android/camera/ImageViewTouchBase.java
index 9993373..7cdf55e 100644
--- a/src/com/android/camera/ImageViewTouchBase.java
+++ b/src/com/android/camera/ImageViewTouchBase.java
@@ -13,6 +13,7 @@
 import android.util.Log;
 import android.view.animation.Animation;
 import android.view.animation.TranslateAnimation;
+import android.view.KeyEvent;
 import android.widget.ImageView;
 
 abstract public class ImageViewTouchBase extends ImageView {
@@ -91,6 +92,17 @@
         }
     }
 
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BACK && getScale() > 1.0f) {
+            // If we're zoomed in, pressing Back jumps out to show the entire image, otherwise Back
+            // returns the user to the gallery.
+            zoomTo(1.0f);
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
     protected Handler mHandler = new Handler();
     
     protected int mLastXTouchPos;
diff --git a/src/com/android/camera/MenuHelper.java b/src/com/android/camera/MenuHelper.java
index 033fc9c..9e4fb82 100644
--- a/src/com/android/camera/MenuHelper.java
+++ b/src/com/android/camera/MenuHelper.java
@@ -23,12 +23,14 @@
 import android.content.SharedPreferences;
 import android.net.Uri;
 import android.os.Handler;
+import android.provider.MediaStore;
 import android.util.Config;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.SubMenu;
 import android.view.View;
+import android.view.MenuItem.OnMenuItemClickListener;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.EditText;
@@ -39,16 +41,18 @@
 
 import java.util.ArrayList;
 
+import com.android.camera.ImageManager.IImage;
+
 public class MenuHelper {
     static private final String TAG = "MenuHelper";
-    
+
     static public final int GENERIC_ITEM      = 1;
     static public final int IMAGE_SAVING_ITEM = 2;
     static public final int VIDEO_SAVING_ITEM = 3;
     static public final int IMAGE_MODE_ITEM   = 4;
     static public final int VIDEO_MODE_ITEM   = 5;
     static public final int MENU_ITEM_MAX     = 5;
-    
+
     static public final int INCLUDE_ALL           = 0xFFFFFFFF;
     static public final int INCLUDE_VIEWPLAY_MENU = (1 << 0);
     static public final int INCLUDE_SHARE_MENU    = (1 << 1);
@@ -57,84 +61,52 @@
     static public final int INCLUDE_DELETE_MENU   = (1 << 4);
     static public final int INCLUDE_ROTATE_MENU   = (1 << 5);
     static public final int INCLUDE_DETAILS_MENU   = (1 << 5);
-    
+
+    static public final int MENU_SWITCH_CAMERA_MODE = 0;
+    static public final int MENU_CAPTURE_PICTURE = 1;
+    static public final int MENU_CAPTURE_VIDEO = 2;
     static public final int MENU_IMAGE_SHARE = 10;
-    static public final int MENU_IMAGE_SHARE_EMAIL = 11;
-    static public final int MENU_IMAGE_SHARE_MMS = 12;
-    static public final int MENU_IMAGE_SHARE_PICASA =13;
     static public final int MENU_IMAGE_SET = 14;
     static public final int MENU_IMAGE_SET_WALLPAPER = 15;
     static public final int MENU_IMAGE_SET_CONTACT = 16;
     static public final int MENU_IMAGE_SET_MYFAVE = 17;
     static public final int MENU_IMAGE_CROP = 18;
     static public final int MENU_IMAGE_ROTATE = 19;
-    static public final int MENU_IMAGE_ROTATE_LEFT = 20; 
+    static public final int MENU_IMAGE_ROTATE_LEFT = 20;
     static public final int MENU_IMAGE_ROTATE_RIGHT = 21;
     static public final int MENU_IMAGE_TOSS = 22;
     static public final int MENU_VIDEO_PLAY = 23;
     static public final int MENU_VIDEO_SHARE = 24;
-    static public final int MENU_VIDEO_SHARE_MMS = 25;
-    static public final int MENU_VIDEO_SHARE_YOUTUBE = 26;
     static public final int MENU_VIDEO_TOSS = 27;
-    static public final int MENU_IMAGE_SHARE_PICASA_ALL =28;
-    
+
     public interface MenuItemsResult {
         public void gettingReadyToOpen(Menu menu, ImageManager.IImage image);
         public void aboutToCall(MenuItem item, ImageManager.IImage image);
     }
-    
+
     public interface MenuInvoker {
         public void run(MenuCallback r);
     }
-    
+
     public interface MenuCallback {
         public void run(Uri uri, ImageManager.IImage image);
     }
 
     static MenuItemsResult addImageMenuItems(
-            Menu menu, 
+            Menu menu,
             int inclusions,
+            final boolean isImage,
             final Activity activity,
             final Handler handler,
             final Runnable onDelete,
             final MenuInvoker onInvoke) {
-        final ArrayList<MenuItem> requiresWriteAccessItems = new ArrayList<MenuItem>(); 
-        final ArrayList<MenuItem> requiresNoDrmAccessItems = new ArrayList<MenuItem>(); 
-        if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
-            if (Config.LOGV)
-                Log.v(TAG, ">>>>> add share");
-            MenuItem item = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SHARE, 10,
-                    R.string.camera_share).setOnMenuItemClickListener(
-                    new MenuItem.OnMenuItemClickListener() {
-                public boolean onMenuItemClick(MenuItem item) {
-                    onInvoke.run(new MenuCallback() {
-                        public void run(Uri u, ImageManager.IImage image) {
-                            if (image == null)
-                                return;
-                            
-                            Intent intent = new Intent();
-                            intent.setAction(Intent.ACTION_SEND);
-                            intent.setType(image.getMimeType());
-                            intent.putExtra(Intent.EXTRA_STREAM, u);
-                            try {
-                                activity.startActivity(Intent.createChooser(intent,
-                                        activity.getText(R.string.sendImage)));
-                            } catch (android.content.ActivityNotFoundException ex) {
-                                Toast.makeText(activity, R.string.no_way_to_share_image, Toast.LENGTH_SHORT).show();
-                            }
-                        }
-                    });
-                    return true;
-                }
-            });
-            item.setIcon(android.R.drawable.ic_menu_share);
-            requiresNoDrmAccessItems.add(item);
-        }
+        final ArrayList<MenuItem> requiresWriteAccessItems = new ArrayList<MenuItem>();
+        final ArrayList<MenuItem> requiresNoDrmAccessItems = new ArrayList<MenuItem>();
 
-        if ((inclusions & INCLUDE_ROTATE_MENU) != 0) {
+        if (isImage && ((inclusions & INCLUDE_ROTATE_MENU) != 0)) {
             SubMenu rotateSubmenu = menu.addSubMenu(IMAGE_SAVING_ITEM, MENU_IMAGE_ROTATE,
                     40, R.string.rotate).setIcon(android.R.drawable.ic_menu_rotate);
-            // Don't show the rotate submenu if the item at hand is read only 
+            // Don't show the rotate submenu if the item at hand is read only
             // since the items within the submenu won't be shown anyway.  This is
             // really a framework bug in that it shouldn't show the submenu if
             // the submenu has no visible items.
@@ -158,7 +130,7 @@
                             public void run(Uri u, ImageManager.IImage image) {
                                 if (image == null || image.isReadonly())
                                     return;
-                        
+
                                 image.rotateImageBy(90);
                             }
                         });
@@ -167,21 +139,8 @@
                 }).setAlphabeticShortcut('r'));
             }
         }
-        
-        if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
-            MenuItem deleteItem = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_TOSS, 70, R.string.camera_toss);
-            requiresWriteAccessItems.add(deleteItem);
-            deleteItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                public boolean onMenuItemClick(MenuItem item) {
-                    deletePhoto(activity, onDelete);
-                    return true;
-                }
-            })
-            .setAlphabeticShortcut('d')
-            .setIcon(android.R.drawable.ic_menu_delete);
-        }
-        
-        if ((inclusions & INCLUDE_CROP_MENU) != 0) {
+
+        if (isImage && ((inclusions & INCLUDE_CROP_MENU) != 0)) {
             MenuItem autoCrop = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_CROP, 73,
                     R.string.camera_crop).setOnMenuItemClickListener(
                             new MenuItem.OnMenuItemClickListener() {
@@ -190,7 +149,7 @@
                         public void run(Uri u, ImageManager.IImage image) {
                             if (u == null)
                                 return;
-                            
+
                             Intent cropIntent = new Intent();
                             cropIntent.setClass(activity, CropImage.class);
                             cropIntent.setData(u);
@@ -203,8 +162,8 @@
             autoCrop.setIcon(android.R.drawable.ic_menu_crop);
             requiresWriteAccessItems.add(autoCrop);
         }
-        
-        if ((inclusions & INCLUDE_SET_MENU) != 0) {
+
+        if (isImage && ((inclusions & INCLUDE_SET_MENU) != 0)) {
             MenuItem setMenu = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SET, 75, R.string.camera_set);
             setMenu.setIcon(android.R.drawable.ic_menu_set_as);
 
@@ -214,7 +173,7 @@
                         public void run(Uri u, ImageManager.IImage image) {
                             if (u == null || image == null)
                                 return;
-                            
+
                             if (Config.LOGV)
                                 Log.v(TAG, "in callback u is " + u + "; mime type is " + image.getMimeType());
                             Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
@@ -228,6 +187,56 @@
             });
         }
 
+        if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
+            if (Config.LOGV)
+                Log.v(TAG, ">>>>> add share");
+            MenuItem item1 = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_SHARE, 10,
+                    R.string.camera_share).setOnMenuItemClickListener(
+                    new MenuItem.OnMenuItemClickListener() {
+                public boolean onMenuItemClick(MenuItem item) {
+                    onInvoke.run(new MenuCallback() {
+                        public void run(Uri u, ImageManager.IImage image) {
+                            if (image == null)
+                                return;
+                            Intent intent = new Intent();
+                            intent.setAction(Intent.ACTION_SEND);
+                            String mimeType = image.getMimeType();
+                            intent.setType(mimeType);
+                            intent.putExtra(Intent.EXTRA_STREAM, u);
+                            boolean isImage = ImageManager.isImageMimeType(mimeType);
+                            try {
+                                activity.startActivity(Intent.createChooser(intent,
+                                        activity.getText(
+                                                isImage ? R.string.sendImage : R.string.sendVideo)));
+                            } catch (android.content.ActivityNotFoundException ex) {
+                                Toast.makeText(activity,
+                                        isImage ? R.string.no_way_to_share_image
+                                                : R.string.no_way_to_share_video,
+                                                Toast.LENGTH_SHORT).show();
+                            }
+                        }
+                    });
+                    return true;
+                }
+            });
+            item1.setIcon(android.R.drawable.ic_menu_share);
+            MenuItem item = item1;
+            requiresNoDrmAccessItems.add(item);
+        }
+
+        if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
+            MenuItem deleteItem = menu.add(IMAGE_SAVING_ITEM, MENU_IMAGE_TOSS, 70, R.string.camera_toss);
+            requiresWriteAccessItems.add(deleteItem);
+            deleteItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+                public boolean onMenuItemClick(MenuItem item) {
+                    deleteImageImpl(activity, onDelete, isImage);
+                    return true;
+                }
+            })
+            .setAlphabeticShortcut('d')
+            .setIcon(android.R.drawable.ic_menu_delete);
+        }
+
         if ((inclusions & INCLUDE_DETAILS_MENU) != 0) {
             MenuItem detailsMenu = menu.add(0, 0, 80, R.string.details).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
                 public boolean onMenuItemClick(MenuItem item) {
@@ -237,9 +246,9 @@
                                 return;
 
                             AlertDialog.Builder builder = new AlertDialog.Builder(activity);
-                            
+
                             final View d = View.inflate(activity, R.layout.detailsview, null);
-                            
+
                             ImageView imageView = (ImageView) d.findViewById(R.id.details_thumbnail_image);
                             imageView.setImageBitmap(image.miniThumbBitmap());
 
@@ -250,8 +259,8 @@
                             String lengthString = "";
                             try {
                                 long length = data.available();
-                                lengthString = 
-                                    android.content.Formatter.formatFileSize(activity, length);
+                                lengthString =
+                                    android.text.format.Formatter.formatFileSize(activity, length);
                                 data.close();
                             } catch (java.io.IOException ex) {
 
@@ -260,9 +269,14 @@
                             ((TextView)d.findViewById(R.id.details_attrname_1)).setText(R.string.details_file_size);
                             ((TextView)d.findViewById(R.id.details_attrvalu_1)).setText(lengthString);
 
-                            String dimensionsString = String.valueOf(image.getWidth() + " X " + image.getHeight());
-                            ((TextView)d.findViewById(R.id.details_attrname_2)).setText(R.string.details_image_resolution);
-                            ((TextView)d.findViewById(R.id.details_attrvalu_2)).setText(dimensionsString);
+                            if (isImage) {
+                                String dimensionsString = String.valueOf(image.getWidth() + " X " + image.getHeight());
+                                ((TextView)d.findViewById(R.id.details_attrname_2)).setText(R.string.details_image_resolution);
+                                ((TextView)d.findViewById(R.id.details_attrvalu_2)).setText(dimensionsString);
+                            } else {
+                                d.findViewById(R.id.details_attrname_2).setVisibility(View.GONE);
+                                d.findViewById(R.id.details_attrvalu_2).setVisibility(View.GONE);
+                            }
 
                             String dateString = "";
                             long dateTaken = image.getDateTaken();
@@ -270,19 +284,19 @@
                                 java.util.Date date = new java.util.Date(image.getDateTaken());
                                 java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat();
                                 dateString = dateFormat.format(date);
-                                
+
                                 ((TextView)d.findViewById(R.id.details_attrname_3)).setText(R.string.details_date_taken);
                                 ((TextView)d.findViewById(R.id.details_attrvalu_3)).setText(dateString);
                             } else {
                                 d.findViewById(R.id.details_daterow).setVisibility(View.GONE);
                             }
 
-                            
+
                             builder.setIcon(android.R.drawable.ic_dialog_info)
                                 .setTitle(R.string.details_panel_title)
                                 .setView(d)
                                 .show();
-                          
+
                         }
                     });
                     return true;
@@ -290,7 +304,23 @@
             });
             detailsMenu.setIcon(R.drawable.ic_menu_view_details);
         }
-        
+
+        if ((!isImage) && ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0)) {
+            menu.add(VIDEO_SAVING_ITEM, MENU_VIDEO_PLAY, 0, R.string.video_play)
+                .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+                public boolean onMenuItemClick(MenuItem item) {
+                    onInvoke.run(new MenuCallback() {
+                        public void run(Uri uri, IImage image) {
+                            Intent intent = new Intent(Intent.ACTION_VIEW,
+                                    image.fullSizeImageUri());
+                            activity.startActivity(intent);
+                        }});
+                    return true;
+                }
+            });
+        }
+
+
         return new MenuItemsResult() {
             public void gettingReadyToOpen(Menu menu, ImageManager.IImage image) {
                 // protect against null here.  this isn't strictly speaking required
@@ -321,94 +351,25 @@
         };
     }
 
-    static MenuItemsResult addVideoMenuItems(
-            Menu menu,
-            int inclusions,
-            final Activity activity,
-            final Handler handler,
-            final SelectedImageGetter mGetter,
-            final Runnable onDelete, 
-            final Runnable preWork, 
-            final Runnable postWork) {
-        
-        if ((inclusions & INCLUDE_VIEWPLAY_MENU) != 0) {
-            menu.add(VIDEO_SAVING_ITEM, MENU_VIDEO_PLAY, 0, R.string.video_play).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                public boolean onMenuItemClick(MenuItem item) {
-                    if (preWork != null)
-                        preWork.run();
-
-                    Intent intent = new Intent(Intent.ACTION_VIEW, mGetter.getCurrentImageUri());
-                    activity.startActivity(intent);
-
-                    // don't do the postWork since we're launching another activity
-                    return true;
-                }
-            });
-        }
-        
-        if ((inclusions & INCLUDE_SHARE_MENU) != 0) {
-            MenuItem item = menu.add(VIDEO_SAVING_ITEM, 0, 0, R.string.camera_share).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                public boolean onMenuItemClick(MenuItem item) {
-                    Uri u = mGetter.getCurrentImageUri();
-                    if (u == null)
-                        return true;
-                    
-                    if (preWork != null)
-                        preWork.run();
-
-                    Intent intent = new Intent();
-                    intent.setAction(Intent.ACTION_SEND);
-                    intent.setType(mGetter.getCurrentImage().getMimeType());
-                    intent.putExtra(Intent.EXTRA_STREAM, u);
-                    try {
-                        activity.startActivity(Intent.createChooser(intent,
-                                activity.getText(R.string.sendVideo)));
-                    } catch (android.content.ActivityNotFoundException ex) {
-                        Toast.makeText(activity, R.string.no_way_to_share_video, Toast.LENGTH_SHORT).show();
-
-                        if (postWork != null)
-                            postWork.run();
-                    }
-                    return true;
-                }
-            });
-            item.setIcon(android.R.drawable.ic_menu_share);
-        }     
-
-        if ((inclusions & INCLUDE_DELETE_MENU) != 0) {
-            MenuItem deleteMenu = menu.add(VIDEO_SAVING_ITEM, MENU_VIDEO_TOSS, 0, R.string.camera_toss).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
-                public boolean onMenuItemClick(MenuItem item) {
-                    if (preWork != null)
-                        preWork.run();
-
-                    activity.getContentResolver().delete(mGetter.getCurrentImageUri(), null, null);
-
-                    if (onDelete != null)
-                        onDelete.run();
-
-                    if (postWork != null)
-                        postWork.run();
-                    
-                    return true;
-                }
-            });
-            deleteMenu.setIcon(android.R.drawable.ic_menu_delete);
-            deleteMenu.setAlphabeticShortcut('d');
-        }
-
-        return null;
+    static void deletePhoto(Activity activity, Runnable onDelete) {
+        deleteImageImpl(activity, onDelete, true);
     }
-    
-    static void deletePhoto(Activity activity, final Runnable onDelete) {
+
+    static void deleteImage(Activity activity, Runnable onDelete, IImage image) {
+        deleteImageImpl(activity, onDelete, ImageManager.isImage(image));
+    }
+
+    private static void deleteImageImpl(Activity activity, final Runnable onDelete, boolean isPhoto) {
         boolean confirm = android.preference.PreferenceManager.getDefaultSharedPreferences(activity).getBoolean("pref_gallery_confirm_delete_key", true);
         if (!confirm) {
             if (onDelete != null)
                 onDelete.run();
         } else {
             android.app.AlertDialog.Builder b = new android.app.AlertDialog.Builder(activity);
-            b.setIcon(R.drawable.delete_image);
+            b.setIcon(android.R.drawable.ic_dialog_alert);
             b.setTitle(R.string.confirm_delete_title);
-            b.setMessage(R.string.confirm_delete_message);
+            b.setMessage(isPhoto? R.string.confirm_delete_message
+                    : R.string.confirm_delete_video_message);
             b.setPositiveButton(android.R.string.ok, new android.content.DialogInterface.OnClickListener() {
                 public void onClick(android.content.DialogInterface v, int x) {
                     if (onDelete != null)
@@ -423,7 +384,62 @@
             b.create().show();
         }
     }
-    
+
+    static void addSwitchModeMenuItem(Menu menu, final Activity activity,
+            final boolean switchToVideo) {
+        int group = switchToVideo ? MenuHelper.IMAGE_MODE_ITEM : MenuHelper.VIDEO_MODE_ITEM;
+        int labelId = switchToVideo ? R.string.switch_to_video_lable
+                : R.string.switch_to_camera_lable;
+        int iconId = switchToVideo ? R.drawable.ic_menu_camera_video_view
+                : R.drawable.ic_menu_camera;
+        MenuItem item = menu.add(group, MENU_SWITCH_CAMERA_MODE, 0,
+                labelId).setOnMenuItemClickListener(
+                        new OnMenuItemClickListener() {
+            public boolean onMenuItemClick(MenuItem item) {
+                String action = switchToVideo ? MediaStore.INTENT_ACTION_VIDEO_CAMERA
+                        : MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA;
+                Intent intent = new Intent(action);
+                intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+                activity.finish();
+                activity.startActivity(intent);
+                return true;
+             }
+        });
+        item.setIcon(iconId);
+    }
+
+    static void addCaptureMenuItems(Menu menu, final Activity activity) {
+
+        menu.add(0, MENU_CAPTURE_PICTURE, 1, R.string.capture_picture)
+            .setOnMenuItemClickListener(
+                 new MenuItem.OnMenuItemClickListener() {
+                     public boolean onMenuItemClick(MenuItem item) {
+                        Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+                        try {
+                               activity.startActivity(intent);
+                        } catch (android.content.ActivityNotFoundException e) {
+                            // Ignore exception
+                        }
+                return true;
+            }
+        })
+        .setIcon(R.drawable.ic_menu_camera);
+
+        menu.add(0, MENU_CAPTURE_VIDEO, 2, R.string.capture_video)
+            .setOnMenuItemClickListener(
+                 new MenuItem.OnMenuItemClickListener() {
+                     public boolean onMenuItemClick(MenuItem item) {
+                         Intent intent = new Intent(MediaStore.INTENT_ACTION_VIDEO_CAMERA);
+                         try {
+                             activity.startActivity(intent);
+                         } catch (android.content.ActivityNotFoundException e) {
+                             // Ignore exception
+                         }
+                return true;
+            }
+        })
+        .setIcon(R.drawable.ic_menu_camera_video_view);
+    }
     static MenuItem addFlipOrientation(Menu menu, final Activity activity, final SharedPreferences prefs) {
         // position 41 after rotate
         return menu
@@ -457,65 +473,5 @@
                         ? android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER
                         : req);
     }
-
-    static public class YouTubeUploadInfoDialog extends Dialog {
-        private CheckBox mPrivate;
-        private ImageManager.VideoObject mVideo;
-        private EditText mTitle;
-        private EditText mTags;
-        private EditText mDescription;
-        private Spinner mCategory;
-        private Button mUpload;
-
-        public YouTubeUploadInfoDialog(final Activity activity,
-                    final ArrayList<String> categoriesShort,
-                    final ArrayList<String> categoriesLong,
-                    ImageManager.VideoObject video,
-                    final Runnable postRunnable) {
-            super(activity, android.R.style.Theme_Dialog);
-            mVideo = video;
-            setContentView(R.layout.youtube_upload_info);
-            setTitle(R.string.upload_dialog_title);
-
-            mPrivate = (CheckBox)findViewById(R.id.public_or_private);
-            if (!mPrivate.isChecked()) {
-                mPrivate.setChecked(true);
-            }
-            
-            mTitle = (EditText)findViewById(R.id.video_title);
-            mTags = (EditText)findViewById(R.id.video_tags);
-            mDescription = (EditText)findViewById(R.id.video_description);
-            mCategory = (Spinner)findViewById(R.id.category);
-            
-            if (Config.LOGV)
-                Log.v(TAG, "setting categories in adapter");
-            android.widget.ArrayAdapter<String> categories = new android.widget.ArrayAdapter<String>(activity, android.R.layout.simple_spinner_item, categoriesLong);
-            categories.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-            mCategory.setAdapter(categories);
-
-            if (mVideo != null) {
-                mTitle.setText(mVideo.getTitle());
-                mTags.setText(mVideo.getTags());
-                mDescription.setText(mVideo.getDescription());
-            }
-            
-            mUpload = (Button)findViewById(R.id.do_upload);
-            mUpload.setOnClickListener(new View.OnClickListener() {
-                public void onClick(View v)
-                {
-                    if (mVideo != null) {
-                        mVideo.setName(mTitle.getText().toString());
-                        mVideo.setDescription(mDescription.getText().toString());
-                        mVideo.setTags(mTags.getText().toString());
-                    }
-                    
-                    YouTubeUploadInfoDialog.this.dismiss();
-                    UploadAction.uploadImage(activity, mVideo);
-                    if (postRunnable != null)
-                        postRunnable.run();
-                }
-            });
-        }
-    }
 }
 
diff --git a/src/com/android/camera/MovieView.java b/src/com/android/camera/MovieView.java
new file mode 100644
index 0000000..58e80df
--- /dev/null
+++ b/src/com/android/camera/MovieView.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera;
+
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.MediaStore;
+import android.view.View;
+import android.widget.MediaController;
+import android.widget.VideoView;
+
+public class MovieView extends Activity implements MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener
+{
+    private static final String TAG = "MovieView";
+    // Copied from MediaPlaybackService in the Music Player app. Should be public, but isn't.
+    private static final String SERVICECMD = "com.android.music.musicservicecommand";
+    private static final String CMDNAME = "command";
+    private static final String CMDPAUSE = "pause";
+
+    private VideoView   mVideoView;
+    private View        mProgressView;
+    private boolean		mFinishOnCompletion;
+    public MovieView()
+    {
+    }
+
+    @Override
+    public void onCreate(Bundle icicle)
+    {
+        super.onCreate(icicle);
+
+        setContentView(R.layout.movie_view);
+
+        mVideoView = (VideoView) findViewById(R.id.surface_view);
+        mProgressView = findViewById(R.id.progress_indicator);
+        Intent intent = getIntent();
+        if (intent.hasExtra(MediaStore.EXTRA_SCREEN_ORIENTATION)) {
+            int orientation = intent.getIntExtra(MediaStore.EXTRA_SCREEN_ORIENTATION,
+                    ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+            if (orientation != getRequestedOrientation()) {
+                setRequestedOrientation(orientation);
+            }
+        }
+        mFinishOnCompletion = intent.getBooleanExtra(MediaStore.EXTRA_FINISH_ON_COMPLETION, true);
+        Uri uri = intent.getData();
+
+        // For streams that we expect to be slow to start up, show a
+        // progress spinner until playback starts.
+        String scheme = uri.getScheme();
+        if ("http".equalsIgnoreCase(scheme) ||
+                "rtsp".equalsIgnoreCase(scheme)) {
+            mHandler.postDelayed(mPlayingChecker, 250);
+        } else {
+            mProgressView.setVisibility(View.GONE);
+        }
+
+        mVideoView.setOnErrorListener(this);
+        mVideoView.setOnCompletionListener(this);
+        mVideoView.setVideoURI(uri);
+        mVideoView.setMediaController(new MediaController(this));
+        mVideoView.requestFocus(); // make the video view handle keys for seeking and pausing
+
+        Intent i = new Intent(SERVICECMD);
+        i.putExtra(CMDNAME, CMDPAUSE);
+        sendBroadcast(i);
+
+        mVideoView.start();
+    }
+
+    @Override
+    public void onPause() {
+        mHandler.removeCallbacksAndMessages(null);
+        super.onPause();
+    }
+
+    Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+        }
+    };
+
+    Runnable mPlayingChecker = new Runnable() {
+        public void run() {
+            if (mVideoView.isPlaying()) {
+                mProgressView.setVisibility(View.GONE);
+            } else {
+                mHandler.postDelayed(mPlayingChecker, 250);
+            }
+        }
+    };
+
+    public boolean onError(MediaPlayer player, int arg1, int arg2) {
+        mHandler.removeCallbacksAndMessages(null);
+        mProgressView.setVisibility(View.GONE);
+        return false;
+    }
+
+    public void onCompletion(MediaPlayer mp) {
+        if (mFinishOnCompletion) {
+            finish();
+        }
+    }
+}
diff --git a/src/com/android/camera/PwaUpload.java b/src/com/android/camera/PwaUpload.java
deleted file mode 100644
index 8df08df..0000000
--- a/src/com/android/camera/PwaUpload.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera;
-
-import android.content.Intent;
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-import android.net.Uri;
-
-/**
- *
- */
-public class PwaUpload extends Activity
-{
-    private static final String TAG = "camera";
-    
-    @Override public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        ImageManager.IImageList imageList = ImageManager.instance().allImages(
-                this, 
-                getContentResolver(), 
-                ImageManager.DataLocation.ALL, 
-                ImageManager.INCLUDE_IMAGES|ImageManager.INCLUDE_VIDEOS, 
-                ImageManager.SORT_ASCENDING);
-        Uri uri = (Uri) getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
-        if (android.util.Config.LOGV)
-            Log.v(TAG, "uri is " + uri);
-        ImageManager.IImage imageObj = imageList.getImageForUri(uri);
-
-        if (android.util.Config.LOGV)
-            Log.v(TAG, "imageObj is " + imageObj);
-        if (imageObj != null) {
-            UploadAction.uploadImage(this, imageObj);
-        }
-        finish();
-    }
-    
-    @Override public void onResume() {
-        super.onResume();
-    } 
-}
diff --git a/src/com/android/camera/SlideShow.java b/src/com/android/camera/SlideShow.java
index ee6c7be..2be99ac 100644
--- a/src/com/android/camera/SlideShow.java
+++ b/src/com/android/camera/SlideShow.java
@@ -60,21 +60,21 @@
     private int mCurrentPosition = 0;
     private ImageView mSwitcher;
     private boolean mPosted = false;
-    
+
     @Override
     protected void onCreate(Bundle icicle)
     {
         super.onCreate(icicle);
         Window wp = getWindow();
         wp.setFlags(FLAG_KEEP_SCREEN_ON, FLAG_KEEP_SCREEN_ON);
-        
+
         setContentView(R.layout.slide_show);
-        
+
         mSwitcher = (ImageView)findViewById(R.id.imageview);
         if (android.util.Config.LOGV)
             Log.v(TAG, "mSwitcher " + mSwitcher);
     }
-    
+
     @Override
     protected void onResume()
     {
@@ -86,13 +86,13 @@
         }
         loadImage();
     }
-    
+
     @Override
     protected void onPause() {
         super.onPause();
         cancelPost();
     }
-    
+
     static public class ImageViewTouch extends ImageView {
         class xy {
             public xy(float xIn, float yIn) {
@@ -108,26 +108,26 @@
             float x,y;
             long timeAdded;
         }
-        
+
         SlideShow mSlideShow;
         Paint mPaints[] = new Paint[1];
         ArrayList<xy> mPoints = new ArrayList<xy>();
         boolean mDown;
-        
+
         public ImageViewTouch(Context context) {
             super(context);
             mSlideShow = (SlideShow) context;
             setScaleType(ImageView.ScaleType.CENTER);
             setupPaint();
         }
-        
+
         public ImageViewTouch(Context context, AttributeSet attrs) {
             super(context, attrs);
             mSlideShow = (SlideShow) context;
             setScaleType(ImageView.ScaleType.CENTER);
             setupPaint();
         }
-        
+
         private void setupPaint() {
             for (int i = 0; i < mPaints.length; i++) {
                 Paint p = new Paint();
@@ -138,7 +138,7 @@
                 mPaints[i] = p;
             }
         }
-        
+
         private void addEvent(MotionEvent event) {
             long now = System.currentTimeMillis();
             mPoints.add(new xy(event));
@@ -151,7 +151,7 @@
                 mPoints.remove(0);
             }
         }
-        
+
         public boolean onTouchEvent(MotionEvent event) {
             addEvent(event);
             switch (event.getAction()) {
@@ -182,7 +182,7 @@
                 long delta = now - ev.timeAdded;
                 if (delta > sLag)
                     continue;
-                
+
                 int alpha2 = Math.max(0, 255 - (255 * (int)delta / sLag));
                 if (alpha2 == 0)
                     continue;
@@ -195,7 +195,7 @@
         }
     }
 
-    
+
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         switch (keyCode) {
@@ -203,14 +203,14 @@
                 cancelPost();
                 loadPreviousImage();
                 return true;
-                
+
             case KeyEvent.KEYCODE_DPAD_RIGHT:
                 cancelPost();
                 loadNextImage();
                 return true;
-                
+
             case KeyEvent.KEYCODE_DPAD_CENTER:
-                if (mPosted) 
+                if (mPosted)
                     cancelPost();
                 else
                     loadNextImage();
@@ -218,12 +218,12 @@
         }
         return super.onKeyDown(keyCode, event);
     }
-    
+
     private void cancelPost() {
         mHandler.removeCallbacks(mNextImageRunnable);
         mPosted = false;
     }
-    
+
     private void post() {
         mHandler.postDelayed(mNextImageRunnable, sNextImageInterval);
         mPosted = true;
@@ -233,7 +233,7 @@
         ImageManager.IImage image = mImageList.getImageAt(mCurrentPosition);
         if (image == null)
             return;
-        
+
         Bitmap bitmap = image.thumbBitmap();
         if (bitmap == null)
             return;
@@ -241,7 +241,7 @@
         mSwitcher.setImageDrawable(new BitmapDrawable(bitmap));
         post();
     }
-    
+
     private Runnable mNextImageRunnable = new Runnable() {
         public void run() {
             if (android.util.Config.LOGV)
@@ -249,13 +249,13 @@
             loadNextImage();
         }
     };
-    
+
     private void loadNextImage() {
         if (++mCurrentPosition >= mImageList.getCount())
             mCurrentPosition = 0;
         loadImage();
     }
-    
+
     private void loadPreviousImage() {
         if (mCurrentPosition == 0)
             mCurrentPosition = mImageList.getCount() - 1;
@@ -272,7 +272,7 @@
         i.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
         return i;
     }
-    
+
     private final Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -286,22 +286,22 @@
 
         public void checkThumbnails(ThumbCheckCallback cb) {
             // TODO Auto-generated method stub
-            
+
         }
 
         public void commitChanges() {
             // TODO Auto-generated method stub
-            
+
         }
 
         public void removeOnChangeListener(OnChange changeCallback) {
             // TODO Auto-generated method stub
-            
+
         }
 
         public void setOnChangeListener(OnChange changeCallback, Handler h) {
             // TODO Auto-generated method stub
-            
+
         }
 
         private ArrayList<FileImage> mImages = new ArrayList<FileImage>();
@@ -320,7 +320,7 @@
             public long imageId() {
                 return mId;
             }
-            
+
             public String getDataPath() {
                 return mPath;
             }
@@ -332,7 +332,7 @@
             public IGetBitmap_cancelable fullSizeBitmap_cancelable(int targetWidthOrHeight) {
                 return null;
             }
-            
+
             public Bitmap thumbBitmap() {
                 Bitmap b = fullSizeBitmap(320);
                 Matrix m = new Matrix();
@@ -341,7 +341,7 @@
                 Bitmap scaledBitmap = Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, true);
                 return scaledBitmap;
             }
-            
+
             public Bitmap miniThumbBitmap() {
                 return thumbBitmap();
             }
@@ -416,7 +416,11 @@
         public int getCount() {
             return mImages.size();
         }
-        
+
+        public boolean isEmpty() {
+            return mImages.isEmpty();
+        }
+
         public void deactivate() {
             // nothing to do here
         }
diff --git a/src/com/android/camera/UploadAction.java b/src/com/android/camera/UploadAction.java
deleted file mode 100644
index 41cc351..0000000
--- a/src/com/android/camera/UploadAction.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Config;
-import android.util.Log;
-
-public class UploadAction {
-    static private final String TAG = "UploadAction";
-    
-    static public void uploadImage(Activity activity, ImageManager.IImage image) {
-        Bundle args = new Bundle();
-        if (image != null)
-            args.putString("imageuri", image.fullSizeImageUri().toString());
-        activity.startService(new Intent(activity, UploadService.class).putExtras(args));        
-    }
-}
diff --git a/src/com/android/camera/UploadService.java b/src/com/android/camera/UploadService.java
deleted file mode 100644
index 9c7d2b0..0000000
--- a/src/com/android/camera/UploadService.java
+++ /dev/null
@@ -1,1181 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera;
-
-import com.android.camera.ImageManager.IImage;
-import com.android.internal.http.multipart.Part;
-import com.android.internal.http.multipart.MultipartEntity;
-import com.android.internal.http.multipart.PartBase;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.preference.PreferenceManager;
-import android.sax.Element;
-import android.sax.ElementListener;
-import android.sax.EndTextElementListener;
-import android.sax.RootElement;
-import android.util.Config;
-import android.util.Log;
-import android.util.Xml;
-
-import com.google.android.googleapps.GoogleLoginCredentialsResult;
-import com.google.android.googlelogin.GoogleLoginServiceBlockingHelper;
-import com.google.android.googlelogin.GoogleLoginServiceConstants;
-import com.google.android.googlelogin.GoogleLoginServiceNotFoundException;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-import android.net.http.AndroidHttpClient;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpGet;
-import com.android.internal.http.multipart.StringPart;
-
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.message.BasicHeader;
-import org.apache.http.params.HttpParams;
-import org.apache.http.protocol.HTTP;
-import org.apache.http.util.EncodingUtils;
-
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.HashMap;
-
-public class UploadService extends Service implements Runnable {
-    private static final String TAG = "UploadService";
-
-    static final boolean DEBUG = false;
-    private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
-
-    private GoogleLoginServiceBlockingHelper mGls;
-    static public final int MSG_STATUS = 3;
-    static public final int EVENT_UPLOAD_ERROR = 400;
-
-    static public final String sPicasaService = "lh2";
-    static public final String sYouTubeService = "youtube";
-    static public final String sYouTubeUserService = "YouTubeUser";
-
-    static public final String sUploadAlbumName = "android_upload";
-    HashMap<String, Album> mAlbums;
-    ArrayList<String> mAndroidUploadAlbumPhotos = null;
-    HashMap<String, String> mGDataAuthTokenMap = new HashMap<String, String>();
-
-    int mStartId;
-    Thread mThread;
-
-    android.os.Handler mHandler = new android.os.Handler() {
-
-    };
-
-    ArrayList<Runnable> mStatusListeners = new ArrayList<Runnable>();
-
-    ArrayList<Uri> mUploadList = new ArrayList<Uri>();
-
-    ImageManager.IImageList mImageList = null;
-
-    String mPicasaUsername;
-    String mPicasaAuthToken;
-    String mYouTubeUsername;
-    String mYouTubeAuthToken;
-
-    AndroidHttpClient mClient = AndroidHttpClient.newInstance("Android-Camera/0.1");
-
-    private static final ComponentName sLogin = new ComponentName(
-            "com.google.android.googleapps",
-            "com.google.android.googleapps.GoogleLoginService");
-
-    public UploadService() {
-        if (LOCAL_LOGV)
-            Log.v(TAG, "UploadService Constructor !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
-    }
-
-    private void computeAuthToken() {
-        if (LOCAL_LOGV) Log.v(TAG, "computeAuthToken()");
-        if (mPicasaAuthToken != null) return;
-
-        try {
-            String account = mGls.getAccount(GoogleLoginServiceConstants.REQUIRE_GOOGLE);
-            GoogleLoginCredentialsResult result =
-                mGls.getCredentials(account, sPicasaService, true);
-            mPicasaAuthToken = result.getCredentialsString();
-            mPicasaUsername = result.getAccount();
-            if (Config.LOGV)
-                Log.v(TAG, "mPicasaUsername is " + mPicasaUsername);
-        } catch (GoogleLoginServiceNotFoundException e) {
-            Log.e(TAG, "Could not get auth token", e);
-        }
-    }
-
-    private void computeYouTubeAuthToken() {
-        if (LOCAL_LOGV) Log.v(TAG, "computeYouTubeAuthToken()");
-        if (mYouTubeAuthToken != null) return;
-
-        try {
-            String account = mGls.getAccount(GoogleLoginServiceConstants.REQUIRE_GOOGLE);
-            GoogleLoginCredentialsResult result =
-                mGls.getCredentials(account, sYouTubeService, true);
-            mYouTubeAuthToken = result.getCredentialsString();
-            mYouTubeUsername = result.getAccount();
-            if (mYouTubeAuthToken.equals("NoLinkedYouTubeAccount")) {
-                // we successfully logged in to the google account, but it
-                // is not linked to a YouTube username.
-                if (Config.LOGV)
-                    Log.v(TAG, "account " + mYouTubeUsername + " is not linked to a youtube account");
-                mYouTubeAuthToken = null;
-                return;
-            }
-
-            mYouTubeUsername = mGls.peekCredentials(mYouTubeUsername, sYouTubeUserService);
-            // now mYouTubeUsername is the YouTube username linked to the
-            // google account, which is probably what we want to display.
-
-            if (Config.LOGV)
-                Log.v(TAG, "3 mYouTubeUsername: " + mYouTubeUsername);
-        } catch (GoogleLoginServiceNotFoundException e) {
-            Log.e(TAG, "Could not get auth token", e);
-        }
-    }
-
-    NotificationManager mNotificationManager;
-
-    @Override
-    public void onCreate() {
-
-        try {
-            mGls = new GoogleLoginServiceBlockingHelper(this);
-        } catch (GoogleLoginServiceNotFoundException e) {
-            Log.e(TAG, "Could not find google login service, stopping service");
-            stopSelf();
-        }
-
-        if (mThread == null) {
-            mThread = new Thread(this);
-            mThread.start();
-        }
-        mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
-
-        IntentFilter intentFilter = new IntentFilter("com.android.camera.NEW_PICTURE");
-        b = new android.content.BroadcastReceiver() {
-            public void onReceive(android.content.Context ctx, Intent intent) {
-                android.content.SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
-                if (prefs.getBoolean("pref_camera_autoupload_key", false)) {
-                    if (Config.LOGV)
-                        Log.v(TAG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> auto upload " + intent.getData());
-                }
-            }
-        };
-        registerReceiver(b, intentFilter);
-    }
-    
-    android.content.BroadcastReceiver b = null;
-
-    @Override
-    public void onDestroy() {
-        mGls.close();
-        if (b != null) {
-            unregisterReceiver(b);
-        }
-    }
-
-    @Override
-    public void onStart(Intent intent, int startId) {
-        if (LOCAL_LOGV)
-            Log.v(TAG, "UploadService.onStart; this is " + hashCode());
-
-        if (mImageList == null) {
-            mImageList = ImageManager.instance().allImages(
-                this,
-                getContentResolver(),
-                ImageManager.DataLocation.ALL,
-                ImageManager.INCLUDE_IMAGES | ImageManager.INCLUDE_VIDEOS,
-                ImageManager.SORT_ASCENDING);
-            mImageList.setOnChangeListener(new ImageManager.IImageList.OnChange() {
-                public void onChange(ImageManager.IImageList list) {
-                    /*
-                    Log.v(TAG, "onChange <<<<<<<<<<<<<<<<<<<<<<<<<");
-                    for (int i = 0; i < list.getCount(); i++) {
-                        ImageManager.IImage img = list.getImageAt(i);
-                        Log.v(TAG, "pos " + i + " " + img.fullSizeImageUri());
-                        String picasaId = img.getPicasaId();
-                        if (picasaId == null || picasaId.length() == 0) {
-                            synchronized (mUploadList) {
-                                Uri uri = img.fullSizeImageUri();
-                                if (mUploadList.contains(uri)) {
-                                    mUploadList.add(img.fullSizeImageUri());
-                                    mUploadList.notify();
-                                }
-                            }
-                        }
-                    }
-                    */
-                }
-            }, mHandler);
-        }
-
-        if (LOCAL_LOGV)
-            Log.v(TAG, "got image list with count " + mImageList.getCount());
-
-        synchronized (mUploadList) {
-            mStartId = startId;
-            String uriString = intent.getStringExtra("imageuri");
-
-            if (LOCAL_LOGV)
-                Log.v(TAG, "starting UploadService; startId = " + startId + " start uri: " + uriString);
-
-            if (uriString != null) {
-                Uri uri = Uri.parse(uriString);
-                IImage image = mImageList.getImageForUri(uri);
-                if (!mUploadList.contains(uri)) {
-                    if (LOCAL_LOGV)
-                        Log.v(TAG, "queing upload of " + image.fullSizeImageUri());
-                    mUploadList.add(uri);
-                }
-            } else {
-                // for now upload all applies to images only, not videos
-                for (int i = 0; i < mImageList.getCount(); i++) {
-                    IImage image = mImageList.getImageAt(i);
-                    if (image instanceof ImageManager.Image) {
-                        Uri uri = image.fullSizeImageUri();
-                        if (!mUploadList.contains(uri)) {
-                            if (LOCAL_LOGV)
-                                Log.v(TAG, "queing upload of " + image.fullSizeImageUri());
-                            mUploadList.add(uri);
-                        }
-                    }
-                }
-            }
-            updateNotification();
-        }
-
-        synchronized(mUploadList) {
-            mUploadList.notify();
-        }
-    }
-
-    void updateNotification() {
-        int videosCount = 0, imagesCount = 0;
-        for (int i = 0;i < mUploadList.size(); i++) {
-            // TODO yes this is a hack
-            Uri uri = mUploadList.get(i);
-            if (uri.toString().contains("video"))
-                videosCount += 1;
-            else
-                imagesCount += 1;
-        }
-        updateNotification(imagesCount, videosCount);
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return mBinder;
-    }
-
-    // This is the object that recieves interactions from clients.
-    private final IBinder mBinder = new Binder() {
-        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
-            return true;
-        }
-    };
-
-    private void updateNotification(int pendingImagesCount, int pendingVideosCount) {
-        final int mVideoUploadId = 1;
-        final int mImageUploadId = 2;
-        if (pendingImagesCount == 0) {
-            if (mNotificationManager != null)
-                mNotificationManager.cancel(mImageUploadId);
-        } else {
-            String detailedMsg = String.format(getResources().getString(R.string.uploadingNPhotos), pendingImagesCount);
-            Notification n = new Notification(
-                    this,
-                    android.R.drawable.stat_sys_upload,
-                    getResources().getString(R.string.uploading_photos),
-                    System.currentTimeMillis(),
-                    getResources().getString(R.string.uploading_photos_2),
-                    detailedMsg,
-                    null);
-            mNotificationManager.notify(mImageUploadId, n);
-        }
-        if (pendingVideosCount == 0) {
-            if (mNotificationManager != null)
-                mNotificationManager.cancel(mVideoUploadId);
-        } else {
-            String detailedMsg = String.format(getResources().getString(R.string.uploadingNVideos), pendingImagesCount);
-            Notification n = new Notification(
-                    this,
-                    android.R.drawable.stat_sys_upload,
-                    getResources().getString(R.string.uploading_videos),
-                    System.currentTimeMillis(),
-                    getResources().getString(R.string.uploading_videos_2),
-                    detailedMsg,
-                    null);
-            mNotificationManager.notify(mVideoUploadId, n);
-        }
-    }
-
-    public void run() {
-        try {
-            if (Config.LOGV)
-                Log.v(TAG, "running upload thread...");
-            while (true) {
-                IImage image = null;
-                synchronized (mUploadList) {
-                    if (LOCAL_LOGV)
-                        Log.v(TAG, "mUploadList.size() is " + mUploadList.size());
-                    if (mUploadList.size() == 0) {
-                        try {
-                            updateNotification(0, 0);
-                            if (Config.LOGV)
-                                Log.v(TAG, "waiting...");
-                            mUploadList.wait(60000);
-                            if (Config.LOGV)
-                                Log.v(TAG, "done waiting...");
-                        } catch (InterruptedException ex) {
-                        }
-                        if (mUploadList.size() == 0) {
-//                          if (LOCAL_LOGV) Log.v(TAG, "exiting run, stoping service");
-//                          stopSelf(mStartId);
-//                          break;
-                            continue;
-                        }
-                    }
-                    Uri uri = mUploadList.get(0);
-                    image = mImageList.getImageForUri(uri);
-                    if (Config.LOGV)
-                        Log.v(TAG, "got uri " + uri + " " + image);
-                }
-
-                boolean success = false;
-                if (image != null) {
-                    updateNotification();
-
-                    long t1 = System.currentTimeMillis();
-                    success = uploadItem(image);
-                    long t2 = System.currentTimeMillis();
-                    if (LOCAL_LOGV) Log.v(TAG, "upload took " + (t2-t1) + "; success = " + success);
-                }
-
-                synchronized (mUploadList) {
-                    mUploadList.remove(0);
-                    if (!success && image != null) {
-                        mUploadList.add(image.fullSizeImageUri());
-                    }
-                }
-                if (!success) {
-                    int retryDelay = 30000;
-                    if (LOCAL_LOGV)
-                        Log.v(TAG, "failed to upload " + image.fullSizeImageUri() + " trying again in " + retryDelay + " ms");
-                    try {
-                        synchronized (mUploadList) {
-                            long t1x = System.currentTimeMillis();
-                            mUploadList.wait(retryDelay);
-                            long t2x = System.currentTimeMillis();
-                            if (Config.LOGV)
-                                Log.v(TAG, "retry waited " + (t2x-t1x));
-                        }
-                    } catch (InterruptedException ex) {
-                        if (Config.LOGV)
-                            Log.v(TAG, "ping, was waiting but now retry again");
-                    };
-                }
-            }
-        } catch (Exception ex) {
-            Log.e(TAG, "got exception in upload thread", ex);
-        }
-        finally {
-            if (LOCAL_LOGV)
-                Log.v(TAG, "finished task");
-        }
-    }
-
-    private String getLatLongString(IImage image) {
-        if (image.hasLatLong()) {
-            return  "<georss:where><gml:Point><gml:pos>"
-            +   image.getLatitude()
-            +   " "
-            +   image.getLongitude()
-            +   "</gml:pos></gml:Point></georss:where>";
-        } else {
-            return "";
-        }
-    }
-
-    private String uploadAlbumName() {
-        android.content.SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
-        String s = prefs.getString("pref_camera_upload_albumname_key", sUploadAlbumName);
-        return s;
-    }
-
-    private boolean uploadItem(IImage image) {
-        if (LOCAL_LOGV)
-            Log.v(TAG, "starting work on " + image);
-
-        if (image instanceof ImageManager.VideoObject) {
-            if (LOCAL_LOGV)
-                Log.v(TAG, "Uploading video");
-            computeYouTubeAuthToken();
-            return (new VideoUploadTask(image)).upload();
-        } else {
-            if (LOCAL_LOGV)
-                Log.v(TAG, "Uploading photo");
-
-            computeAuthToken();
-            // handle photos
-            if (mAlbums == null)
-                mAlbums = getAlbums();
-
-            String albumName = uploadAlbumName();
-            if (mAlbums == null || !mAlbums.containsKey(albumName)) {
-                Album a = createAlbum(albumName, uploadAlbumName());
-                if (a == null) {
-                    return false;
-                }
-                if (LOCAL_LOGV)
-                    Log.v(TAG, "made new album: " + a.getAlbumName() + "; " + a.getAlbumId());
-                mAlbums.put(a.getAlbumName(), a);
-            }
-
-            if (mAndroidUploadAlbumPhotos == null)
-                mAndroidUploadAlbumPhotos = getAlbumContents(albumName);
-
-            if (mAndroidUploadAlbumPhotos != null) {
-                String previousUploadId = image.getPicasaId();
-                if (previousUploadId != null) {
-                    if (mAndroidUploadAlbumPhotos.contains(previousUploadId)) {
-                        if (Config.LOGV)
-                            Log.v(TAG, "already have id " + previousUploadId);
-                        return true;
-                    }
-                }
-            }
-            Album album = mAlbums.get(albumName);
-            return (new ImageUploadTask(image)).upload(album);
-        }
-    }
-
-//    void broadcastError(int error) {
-//        HashMap map = new HashMap();
-//        map.put("error", new Integer(error));
-//
-//        Message send = Message.obtain();
-//        send.what = EVENT_UPLOAD_ERROR;
-//        send.setData(map);
-//
-//        if (mBroadcaster == null) {
-//            mBroadcaster = new Broadcaster();
-//        }
-//        mBroadcaster.broadcast(send);
-//    }
-
-    class Album {
-        String mAlbumName;
-
-        String mAlbumId;
-
-        public Album() {
-        }
-
-        public void setAlbumName(String albumName) {
-            mAlbumName = albumName;
-        }
-
-        public void setAlbumId(String albumId) {
-            mAlbumId = albumId;
-        }
-
-        public String getAlbumName() {
-            return mAlbumName;
-        }
-
-        public String getAlbumId() {
-            return mAlbumId;
-        }
-    }
-
-    static private String stringFromResponse(HttpResponse response) {
-        try {
-            HttpEntity entity = response.getEntity();
-            InputStream inputStream = entity.getContent();
-            StringWriter s = new StringWriter();
-            while (true) {
-                int c = inputStream.read();
-                if (c == -1)
-                    break;
-                s.write((char)c);
-            }
-            inputStream.close();
-            String retval = s.toString();
-            if (Config.LOGV)
-                Log.v(TAG, "got resposne " + retval);
-            return retval;
-        } catch (Exception ex) {
-            return null;
-        }
-    }
-
-    abstract class UploadTask {
-        IImage mImageObj;
-
-        public UploadTask(IImage image) {
-            mImageObj = image;
-        }
-
-        public class UploadResponse {
-            private HttpResponse mStatus;
-            private String mBody;
-
-            public UploadResponse(HttpResponse status) {
-                mStatus = status;
-                mBody = stringFromResponse(status);
-            }
-
-            public int getStatus() {
-                return mStatus.getStatusLine().getStatusCode();
-            }
-
-            public String getResponse() {
-                return mBody;
-            }
-        }
-
-        class StreamPart extends PartBase {
-            InputStream mInputStream;
-            long mLength;
-
-            StreamPart(String name, InputStream inputStream, String contentType) {
-                super(name,
-                      contentType == null ? "application/octet-stream" : contentType,
-                      "ISO-8859-1",
-                      "binary"
-                    );
-                mInputStream = inputStream;
-                try {
-                    mLength = inputStream.available();
-                } catch (IOException ex) {
-
-                }
-            }
-
-            @Override
-            protected long lengthOfData() throws IOException {
-                return mLength;
-            }
-
-            @Override
-            protected void sendData(OutputStream out) throws IOException {
-                byte [] buffer = new byte[4096];
-                while (true) {
-                    int got = mInputStream.read(buffer);
-                    if (got == -1)
-                        break;
-                    out.write(buffer, 0, got);
-                }
-                mInputStream.close();
-            }
-
-            @Override
-            protected void sendDispositionHeader(OutputStream out) throws IOException {
-            }
-
-            @Override
-            protected void sendContentTypeHeader(OutputStream out) throws IOException {
-                String contentType = getContentType();
-                if (contentType != null) {
-                    out.write(CONTENT_TYPE_BYTES);
-                    out.write(EncodingUtils.getAsciiBytes(contentType));
-                    String charSet = getCharSet();
-                    if (charSet != null) {
-                        out.write(CHARSET_BYTES);
-                        out.write(EncodingUtils.getAsciiBytes(charSet));
-                    }
-                }
-            }
-        }
-
-        public class StringPartX extends StringPart {
-            public StringPartX(String name, String value, String charset) {
-                super(name, value, charset);
-                setContentType("application/atom+xml");
-            }
-
-            @Override
-            protected void sendDispositionHeader(OutputStream out) throws IOException {
-            }
-
-            @Override
-            protected void sendContentTypeHeader(OutputStream out) throws IOException {
-                String contentType = getContentType();
-                if (contentType != null) {
-                    out.write(CONTENT_TYPE_BYTES);
-                    out.write(EncodingUtils.getAsciiBytes(contentType));
-                    String charSet = getCharSet();
-                    if (charSet != null) {
-                        out.write(CHARSET_BYTES);
-                        out.write(EncodingUtils.getAsciiBytes(charSet));
-                    }
-                }
-            }
-        }
-
-        public class MultipartEntityX extends MultipartEntity {
-            public MultipartEntityX(Part[] parts, HttpParams params) {
-                super(parts, params);
-            }
-
-            @Override
-            public Header getContentType() {
-                StringBuilder buffer = new StringBuilder();
-                buffer.append("multipart/related; boundary=");
-                buffer.append(EncodingUtils.getAsciiString(getMultipartBoundary()));
-                return new BasicHeader(HTTP.CONTENT_TYPE, buffer.toString());
-            }
-
-        }
-
-        protected UploadResponse doUpload(String uploadUrl,
-                              String mimeType,
-                              String data,
-                              IImage imageObj,
-                              String authToken,
-                              String title,
-                              String filename,
-                              boolean youTubeAuthenticate) {
-            if (authToken == null)
-                return null;
-
-            FileInputStream inputStream = (FileInputStream)mImageObj.fullSizeImageData();
-            try {
-                HttpPost post = new HttpPost(uploadUrl);
-                post.addHeader(new BasicHeader("Authorization", "GoogleLogin auth=" + authToken));
-                if (youTubeAuthenticate) {
-                    // TODO: remove hardwired key? - This is our official YouTube issued developer key to Android.
-                    String youTubeDeveloperKey = "key=AI39si5Cr35CiD1IgDqD9Ua6N4dSbY-oibnLUPITmBN_rFW6qRz-hd8sTqNzRf1gzNwSYZbDuS31Txa4iKyjAV77507O4tq7JA";
-                    post.addHeader("X-GData-Key", youTubeDeveloperKey);
-                    post.addHeader("Slug", filename);
-                }
-
-                Part p1 = new StringPartX("param_name", data, null);
-                Part p2 = new StreamPart("field_uploadfile", inputStream, mimeType);
-
-                MultipartEntity mpe = new MultipartEntityX(new Part[] { p1, p2 }, post.getParams());
-                post.setEntity(mpe);
-                HttpResponse status = mClient.execute(post);
-                if (LOCAL_LOGV) Log.v(TAG, "doUpload response is " + status.getStatusLine());
-                return new UploadResponse(status);
-            } catch (java.io.IOException ex) {
-                if (LOCAL_LOGV) Log.v(TAG, "IOException in doUpload", ex);
-                return null;
-            }
-        }
-
-        class ResponseHandler implements ElementListener {
-            private static final String ATOM_NAMESPACE
-                    = "http://www.w3.org/2005/Atom";
-            private static final String PICASSA_NAMESPACE
-                    = "http://schemas.google.com/photos/2007";
-
-            private ContentHandler mHandler = null;
-            private String mId = null;
-
-            public ResponseHandler() {
-                RootElement root = new RootElement(ATOM_NAMESPACE, "entry");
-                Element entry = root;
-                entry.setElementListener(this);
-
-                entry.getChild(PICASSA_NAMESPACE, "id")
-                .setEndTextElementListener(new EndTextElementListener() {
-                    public void end(String body) {
-                        mId = body;
-                    }
-                });
-
-                mHandler = root.getContentHandler();
-            }
-
-            public void start(Attributes attributes) {
-            }
-
-            public void end() {
-            }
-
-            ContentHandler getContentHandler() {
-                return mHandler;
-            }
-
-            public String getId() {
-                return mId;
-            }
-        }
-    }
-
-    private class VideoUploadTask extends UploadTask {
-        public VideoUploadTask(IImage image) {
-            super(image);
-        }
-        protected String getYouTubeBaseUrl() {
-            return "http://gdata.youtube.com";
-        }
-
-        public boolean upload() {
-            String uploadUrl = "http://uploads.gdata.youtube.com"
-                               + "/feeds/users/"
-                               + mYouTubeUsername
-                               + "/uploads?client=ytapi-google-android";
-
-            String title = mImageObj.getTitle();
-            String isPrivate = "";
-            String keywords = "";
-            String category = "";
-            if (mImageObj instanceof ImageManager.VideoObject) {
-                ImageManager.VideoObject video = (ImageManager.VideoObject)mImageObj;
-                if (mImageObj.getIsPrivate()) {
-                    isPrivate = "<yt:private/>";
-                }
-                keywords = video.getTags();
-                if (keywords == null || keywords.trim().length() == 0) {
-                    // there must be a keyword or YouTube will reject the video
-                    keywords = getResources().getString(R.string.upload_default_tags_text);
-                }
-                // TODO: use the real category when we have the category spinner in details
-//                category = video.getCategory();
-                category = "";
-                if (category == null || category.trim().length() == 0) {
-                    // there must be a description or YouTube will get an internal error and return 500
-                    category = getResources().getString(R.string.upload_default_category_text);
-                }
-            }
-            String description = mImageObj.getDescription();
-            if (description == null || description.trim().length() == 0) {
-                // there must be a description or YouTube will get an internal error and return 500
-                description = getResources().getString(R.string.upload_default_description_text);
-            }
-            String data = "<?xml version='1.0'?>\n"
-                        + "<entry xmlns='http://www.w3.org/2005/Atom'\n"
-                        + "  xmlns:media='http://search.yahoo.com/mrss/'\n"
-                        + "  xmlns:yt='http://gdata.youtube.com/schemas/2007'>\n"
-                        + "  <media:group>\n"
-                        + "    <media:title type='plain'>" + title + "</media:title>\n"   // TODO: need user entered title
-                        + "    <media:description type='plain'>" + description + "</media:description>\n"
-                        +      isPrivate
-                        + "    <media:category scheme='http://gdata.youtube.com/schemas/2007/categories.cat'>\n"
-                        +         category
-                        + "    </media:category>\n"
-                        + "    <media:keywords>" + keywords + "</media:keywords>\n"
-                        + "  </media:group>\n"
-                        + "</entry>";
-
-            if (LOCAL_LOGV) Log.v("youtube", "uploadUrl: " + uploadUrl);
-            if (LOCAL_LOGV) Log.v("youtube", "GData: " + data);
-
-            UploadResponse result = doUpload(uploadUrl,
-                    "video/3gpp2",
-                    data,
-                    null,
-                    mYouTubeAuthToken,
-                    title,
-                    mImageObj.fullSizeImageUri().getLastPathSegment(),
-                    true);
-
-            boolean success = false;
-            if (result != null) {
-                switch (result.getStatus()) {
-                case 401:
-                    if (result.getResponse().contains("Token expired")) {
-                        // When we tried to upload a video to YouTube, the youtube server told us
-                        // our auth token was expired. Get a new one and try again.
-                        try {
-                            mGls.invalidateAuthToken(mYouTubeAuthToken);
-                        } catch (GoogleLoginServiceNotFoundException e) {
-                            Log.e(TAG, "Could not invalidate youtube auth token", e);
-                        }
-                        mYouTubeAuthToken = null;   // Forces computeYouTubeAuthToken to get a new token.
-                        computeYouTubeAuthToken();
-                    }
-                    break;
-
-                case 200:
-                case 201:
-                case 202:
-                case 203:
-                case 204:
-                case 205:
-                case 206:
-                    success = true;
-                    break;
-
-                }
-            }
-            return success;
-        }
-    }
-
-    private class ImageUploadTask extends UploadTask {
-        public ImageUploadTask(IImage image) {
-            super(image);
-        }
-
-        public boolean upload(Album album) {
-            String uploadUrl = getServiceBaseUrl()
-                               + mPicasaUsername
-                               + "/album/"
-                               + album.getAlbumId();
-
-            String name = mImageObj.getTitle();
-            String description = mImageObj.getDescription();
-            String data = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:georss='http://www.georss.org/georss' xmlns:gml='http://www.opengis.net/gml'><title>"
-                    + name
-                    + "</title>"
-                    + "<summary>"
-                    + (description != null ? description : "")
-                    + "</summary>"
-                    + getLatLongString(mImageObj)
-                    + "<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/photos/2007#photo'/></entry>\n";
-
-            if (LOCAL_LOGV)
-                Log.v(TAG, "xml for image is " + data);
-            UploadResponse response = doUpload(uploadUrl,
-                            "image/jpeg",
-                            data,
-                            mImageObj,
-                            mPicasaAuthToken,
-                            name,
-                            name,
-                            false);
-
-            if (response != null) {
-                int status = response.getStatus();
-                if (status == HttpStatus.SC_UNAUTHORIZED ||
-                    status == HttpStatus.SC_FORBIDDEN ||
-                    status == HttpStatus.SC_INTERNAL_SERVER_ERROR) {
-                    try {
-                        mGls.invalidateAuthToken(mPicasaAuthToken);
-                    } catch (GoogleLoginServiceNotFoundException e) {
-                        Log.e(TAG, "Could not invalidate picasa auth token", e);
-                    }
-                    mPicasaAuthToken = null;
-                } else {
-                    ResponseHandler h = new ResponseHandler();
-                    try {
-                        Xml.parse(response.getResponse(), h.getContentHandler());
-                        String id = h.getId();
-                        if (id != null && mImageObj != null) {
-                            mImageObj.setPicasaId(id);
-                            mAndroidUploadAlbumPhotos.add(id);
-                            return true;
-                        }
-                    } catch (org.xml.sax.SAXException ex) {
-                        Log.e(TAG, "SAXException in doUpload " + ex.toString());
-                    }
-                }
-            }
-            return false;
-        }
-    }
-
-    private Album createAlbum(String name, String summary) {
-        String authToken = mPicasaAuthToken;
-        if (authToken == null)
-            return null;
-
-        try {
-            String url = getServiceBaseUrl() + mPicasaUsername;
-            HttpPost post = new HttpPost(url);
-            String entryString = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/' xmlns:gphoto='http://schemas.google.com/photos/2007'>"
-                + "<title type='text'>"
-                + name
-                + "</title>"
-                + "<summary>"
-                + summary
-                + "</summary>"
-                + "<gphoto:access>private</gphoto:access>"
-                + "<gphoto:commentingEnabled>true</gphoto:commentingEnabled>"
-                + "<gphoto:timestamp>"
-                + String.valueOf(System.currentTimeMillis())
-                + "</gphoto:timestamp>"
-                + "<category scheme=\"http://schemas.google.com/g/2005#kind\" term=\"http://schemas.google.com/photos/2007#album\"/>"
-                + "</entry>\n";
-
-            StringEntity entity = new StringEntity(entryString);
-            entity.setContentType(new BasicHeader("Content-Type", "application/atom+xml"));
-            post.setEntity(entity);
-            post.addHeader(new BasicHeader("Authorization", "GoogleLogin auth=" + authToken));
-            HttpResponse status = mClient.execute(post);
-            if (LOCAL_LOGV)
-                Log.v(TAG, "status is " + status.getStatusLine());
-            if (status.getStatusLine().getStatusCode() < 200 || status.getStatusLine().getStatusCode() >= 300) {
-                return null;
-            }
-            Album album = new Album();
-            Xml.parse(stringFromResponse(status), new PicasaAlbumHandler(album).getContentHandler());
-            return album;
-        } catch (java.io.UnsupportedEncodingException ex) {
-            Log.e(TAG, "gak, UnsupportedEncodingException " + ex.toString());
-        } catch (java.io.IOException ex) {
-            Log.e(TAG, "IOException " + ex.toString());
-        } catch (org.xml.sax.SAXException ex) {
-            Log.e(TAG, "XmlPullParserException " + ex.toString());
-        }
-        return null;
-    }
-
-    public static String streamToString(InputStream stream, int maxChars, boolean reset)
-            throws IOException {
-        BufferedReader reader = new BufferedReader(new InputStreamReader(stream), 8192);
-        StringBuilder sb = new StringBuilder();
-        String line = null;
-
-        while ((line = reader.readLine()) != null
-                && (maxChars == -1 || sb.length() < maxChars)) {
-            sb.append(line);
-        }
-        reader.close();
-        if (reset) stream.reset();
-        return sb.toString();
-    }
-
-    InputStream get(String url) {
-        try {
-            if (LOCAL_LOGV) Log.v(TAG, "url is  " + url);
-
-            for (int i = 0; i < 2; ++i) {
-                HttpGet get = new HttpGet(url);
-                get.setHeader(new BasicHeader("Authorization",
-                                              "GoogleLogin auth=" + mPicasaAuthToken));
-
-                HttpResponse response = mClient.execute(get);
-                if (LOCAL_LOGV) Log.v(TAG, "response is " + response.getStatusLine());
-                switch (response.getStatusLine().getStatusCode()) {
-                    case HttpStatus.SC_UNAUTHORIZED:
-                    case HttpStatus.SC_FORBIDDEN:
-                    case HttpStatus.SC_INTERNAL_SERVER_ERROR:   // http://b/1151576
-                        try {
-                            mGls.invalidateAuthToken(mPicasaAuthToken);
-                        } catch (GoogleLoginServiceNotFoundException e) {
-                            Log.e(TAG, "Could not invalidate picasa auth token", e);
-                        }
-                        mPicasaAuthToken = null;
-                        computeAuthToken();
-                        if (mPicasaAuthToken != null) {
-                            // retry fetch after getting new token
-                            continue;
-                        }
-                        break;
-                }
-
-                InputStream inputStream = response.getEntity().getContent();
-                return inputStream;
-            }
-            return null;
-        } catch (java.io.IOException ex) {
-            Log.e(TAG, "IOException");
-        }
-        return null;
-    }
-
-    private HashMap<String, Album> getAlbums() {
-        if (LOCAL_LOGV)
-            Log.v(TAG, "getAlbums");
-
-        PicasaAlbumHandler h = new PicasaAlbumHandler();
-        try {
-            String url = getServiceBaseUrl() + mPicasaUsername + "?kind=album";
-            InputStream inputStream = get(url);
-            if (inputStream == null) {
-                if (Config.LOGV)
-                    Log.v(TAG, "can't get " + url + "; bail from getAlbums()");
-                mPicasaAuthToken = null;
-                return null;
-            }
-
-            Xml.parse(inputStream, Xml.findEncodingByName("UTF-8"), h.getContentHandler());
-            if (LOCAL_LOGV)
-                Log.v(TAG, "done getting albums");
-            inputStream.close();
-        } catch (IOException e) {
-            Log.e(TAG, "got exception " + e.toString());
-            e.printStackTrace();
-        } catch (SAXException e) {
-            Log.e(TAG, "got exception " + e.toString());
-            e.printStackTrace();
-        }
-        if (LOCAL_LOGV) {
-            java.util.Iterator it = h.getAlbums().keySet().iterator();
-            while (it.hasNext()) {
-                if (Config.LOGV)
-                    Log.v(TAG, "album: " + (String) it.next());
-            }
-        }
-        return h.getAlbums();
-    }
-
-    ArrayList<String> getAlbumContents(String albumName) {
-        String url = getServiceBaseUrl() + mPicasaUsername + "/album/" + albumName + "?kind=photo&max-results=10000";
-        try {
-            InputStream inputStream = get(url);
-            if (inputStream == null)
-                return null;
-
-            AlbumContentsHandler ah = new AlbumContentsHandler();
-            Xml.parse(inputStream, Xml.findEncodingByName("UTF-8"), ah.getContentHandler());
-            ArrayList<String> photos = ah.getPhotos();
-            inputStream.close();
-            return photos;
-        } catch (IOException e) {
-            Log.e(TAG, "got IOException " + e.toString());
-            e.printStackTrace();
-        } catch (SAXException e) {
-            Log.e(TAG, "got SAXException " + e.toString());
-            e.printStackTrace();
-        }
-        return null;
-    }
-
-    class AlbumContentsHandler implements ElementListener {
-        private static final String ATOM_NAMESPACE
-                = "http://www.w3.org/2005/Atom";
-        private static final String PICASA_NAMESPACE
-                = "http://schemas.google.com/photos/2007";
-
-        private ContentHandler mHandler = null;
-        private ArrayList<String> mPhotos = new ArrayList<String>();
-
-        public AlbumContentsHandler() {
-            RootElement root = new RootElement(ATOM_NAMESPACE, "feed");
-            Element entry = root.getChild(ATOM_NAMESPACE, "entry");
-
-            entry.setElementListener(this);
-
-            entry.getChild(PICASA_NAMESPACE, "id")
-            .setEndTextElementListener(new EndTextElementListener() {
-                public void end(String body) {
-                    mPhotos.add(body);
-                }
-            });
-
-            mHandler = root.getContentHandler();
-        }
-
-        public void start(Attributes attributes) {
-        }
-
-        public void end() {
-        }
-
-        ContentHandler getContentHandler() {
-            return mHandler;
-        }
-
-        public ArrayList<String> getPhotos() {
-            return mPhotos;
-        }
-    }
-
-    private String getServiceBaseUrl() {
-        return "http://picasaweb.google.com/data/feed/api/user/";
-    }
-
-
-    class PicasaAlbumHandler implements ElementListener {
-        private Album mAlbum;
-        private HashMap<String, Album> mAlbums = new HashMap<String, Album>();
-        private boolean mJustOne;
-        private static final String ATOM_NAMESPACE
-                = "http://www.w3.org/2005/Atom";
-        private static final String PICASSA_NAMESPACE
-                = "http://schemas.google.com/photos/2007";
-        private ContentHandler handler = null;
-
-        public PicasaAlbumHandler() {
-            mJustOne = false;
-            init();
-        }
-
-        public HashMap<String, Album> getAlbums() {
-            return mAlbums;
-        }
-
-        public PicasaAlbumHandler(Album album) {
-            mJustOne = true;
-            mAlbum = album;
-            init();
-        }
-
-        private void init() {
-            Element entry;
-            RootElement root;
-            if (mJustOne) {
-                root = new RootElement(ATOM_NAMESPACE, "entry");
-                entry = root;
-            } else {
-                root = new RootElement(ATOM_NAMESPACE, "feed");
-                entry = root.getChild(ATOM_NAMESPACE, "entry");
-            }
-            entry.setElementListener(this);
-
-            entry.getChild(ATOM_NAMESPACE, "title")
-                .setEndTextElementListener(new EndTextElementListener() {
-                    public void end(String body) {
-                        mAlbum.setAlbumName(body);
-                    }
-                });
-
-            entry.getChild(PICASSA_NAMESPACE, "name")
-                .setEndTextElementListener(new EndTextElementListener() {
-                    public void end(String body) {
-                        mAlbum.setAlbumId(body);
-                    }
-                });
-
-            this.handler = root.getContentHandler();
-        }
-
-        public void start(Attributes attributes) {
-            if (!mJustOne) {
-                mAlbum = new Album();
-            }
-        }
-
-        public void end() {
-            if (!mJustOne) {
-                mAlbums.put(mAlbum.getAlbumName(), mAlbum);
-                mAlbum = null;
-            }
-        }
-
-        ContentHandler getContentHandler() {
-            return handler;
-        }
-    }
-}
diff --git a/src/com/android/camera/VideoCamera.java b/src/com/android/camera/VideoCamera.java
new file mode 100644
index 0000000..7fcab9a
--- /dev/null
+++ b/src/com/android/camera/VideoCamera.java
@@ -0,0 +1,810 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.io.IOException;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.ColorDrawable;
+import android.location.LocationManager;
+import android.media.MediaMetadataRetriever;
+import android.media.MediaRecorder;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.os.StatFs;
+import android.os.SystemClock;
+import android.preference.PreferenceManager;
+import android.provider.MediaStore.Images;
+import android.provider.MediaStore.Video;
+import android.text.format.DateFormat;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.MenuItem.OnMenuItemClickListener;
+import android.view.animation.Animation;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.RelativeLayout.LayoutParams;
+
+public class VideoCamera extends Activity implements View.OnClickListener, SurfaceHolder.Callback {
+
+    private static final String TAG = "videocamera";
+
+    private static final boolean DEBUG = true;
+    private static final boolean DEBUG_SUPPRESS_AUDIO_RECORDING = DEBUG && true;
+    private static final boolean DEBUG_DO_NOT_REUSE_MEDIA_RECORDER = DEBUG && true;
+
+    private static final int KEEP = 2;
+    private static final int CLEAR_SCREEN_DELAY = 4;
+    private static final int UPDATE_RECORD_TIME = 5;
+    private static final int RESTART_PREVIEW = 6;
+
+    private static final int SCREEN_DELAY = 2 * 60 * 1000;
+    private static final int POST_PICTURE_ALERT_TIMEOUT = 6 * 1000;
+
+    private static final int NO_STORAGE_ERROR = -1;
+    private static final int CANNOT_STAT_ERROR = -2;
+
+    public static final int MENU_SWITCH_TO_VIDEO = 0;
+    public static final int MENU_SWITCH_TO_CAMERA = 1;
+    public static final int MENU_SETTINGS = 6;
+    public static final int MENU_GALLERY_PHOTOS = 7;
+    public static final int MENU_GALLERY_VIDEOS = 8;
+    public static final int MENU_SAVE_SELECT_PHOTOS = 30;
+    public static final int MENU_SAVE_NEW_PHOTO = 31;
+    public static final int MENU_SAVE_SELECTVIDEO = 32;
+    public static final int MENU_SAVE_TAKE_NEW_VIDEO = 33;
+    public static final int MENU_SAVE_GALLERY_PHOTO = 34;
+    public static final int MENU_SAVE_GALLERY_VIDEO_PHOTO = 35;
+    public static final int MENU_SAVE_CAMERA_DONE = 36;
+    public static final int MENU_SAVE_CAMERA_VIDEO_DONE = 37;
+
+    Toast mToast;
+    SharedPreferences mPreferences;
+
+    private static final float VIDEO_ASPECT_RATIO = 176.0f / 144.0f;
+    VideoPreview mVideoPreview;
+    SurfaceHolder mSurfaceHolder = null;
+    ImageView mBlackout = null;
+    ImageView mVideoFrame;
+    Bitmap mVideoFrameBitmap;
+
+    private MediaRecorder mMediaRecorder;
+    private boolean mMediaRecorderRecording = false;
+    private long mRecordingStartTime;
+    private String mCurrentVideoFilename;
+    private Uri mCurrentVideoUri;
+
+    boolean mPausing = false;
+
+    static ContentResolver mContentResolver;
+    boolean mDidRegister = false;
+
+    int mCurrentZoomIndex = 0;
+
+    private ImageView mModeIndicatorView;
+    private ImageView mRecordingIndicatorView;
+    private TextView mRecordingTimeView;
+
+    ArrayList<MenuItem> mGalleryItems = new ArrayList<MenuItem>();
+
+    View mPostPictureAlert;
+    LocationManager mLocationManager = null;
+
+    private int mPicturesRemaining;
+
+    private Handler mHandler = new MainHandler();
+
+    private void cancelSavingNotification() {
+        if (mToast != null) {
+            mToast.cancel();
+            mToast = null;
+        }
+    }
+
+    /** This Handler is used to post message back onto the main thread of the application */
+    private class MainHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case KEEP: {
+                    keep();
+
+                    if (msg.obj != null) {
+                        mHandler.post((Runnable)msg.obj);
+                    }
+                    break;
+                }
+
+                case CLEAR_SCREEN_DELAY: {
+                    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+                    break;
+                }
+
+                case UPDATE_RECORD_TIME: {
+                    if (mMediaRecorderRecording) {
+                        long now = SystemClock.uptimeMillis();
+                        long delta = now - mRecordingStartTime;
+                        long seconds = delta / 1000;
+                        long minutes = seconds / 60;
+                        long remainderSeconds = seconds - (minutes * 60);
+
+                        String secondsString = Long.toString(remainderSeconds);
+                        if (secondsString.length() < 2) {
+                            secondsString = "0" + secondsString;
+                        }
+                        String minutesString = Long.toString(minutes);
+                        if (minutesString.length() < 2) {
+                            minutesString = "0" + minutesString;
+                        }
+                        String text = minutesString + ":" + secondsString;
+                        mRecordingTimeView.setText(text);
+                        mHandler.sendEmptyMessageDelayed(UPDATE_RECORD_TIME, 1000);
+                    }
+                    break;
+                }
+
+                case RESTART_PREVIEW:
+                    hideVideoFrameAndStartPreview();
+                    break;
+
+                default:
+                    Log.v(TAG, "Unhandled message: " + msg.what);
+                  break;
+            }
+        }
+    };
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
+                // SD card available
+                // TODO put up a "please wait" message
+                // TODO also listen for the media scanner finished message
+                showStorageToast();
+            } else if (action.equals(Intent.ACTION_MEDIA_UNMOUNTED)) {
+                // SD card unavailable
+                showStorageToast();
+            } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_STARTED)) {
+                Toast.makeText(VideoCamera.this, getResources().getString(R.string.wait), 5000);
+            } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
+                showStorageToast();
+            }
+        }
+    };
+
+    static private String createName(long dateTaken) {
+        return DateFormat.format("yyyy-MM-dd kk.mm.ss", dateTaken).toString();
+    }
+
+    private void postAfterKeep(final Runnable r) {
+        Message msg = mHandler.obtainMessage(KEEP);
+        msg.obj = r;
+        msg.sendToTarget();
+    }
+
+    /** Called with the activity is first created. */
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+
+        mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+        mContentResolver = getContentResolver();
+
+        //setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
+        requestWindowFeature(Window.FEATURE_PROGRESS);
+
+        Window win = getWindow();
+        win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        setContentView(R.layout.video_camera);
+
+        mVideoPreview = (VideoPreview) findViewById(R.id.camera_preview);
+        mVideoPreview.setAspectRatio(VIDEO_ASPECT_RATIO);
+
+        // don't set mSurfaceHolder here. We have it set ONLY within
+        // surfaceCreated / surfaceDestroyed, other parts of the code
+        // assume that when it is set, the surface is also set.
+        SurfaceHolder holder = mVideoPreview.getHolder();
+        holder.addCallback(this);
+        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+
+        mBlackout = (ImageView) findViewById(R.id.blackout);
+        mBlackout.setBackgroundDrawable(new ColorDrawable(0xFF000000));
+
+        mPostPictureAlert = findViewById(R.id.post_picture_panel);
+        View b;
+
+        b = findViewById(R.id.play);
+        b.setOnClickListener(this);
+
+        b = findViewById(R.id.share);
+        b.setOnClickListener(this);
+
+        b = findViewById(R.id.discard);
+        b.setOnClickListener(this);
+
+        mModeIndicatorView = (ImageView) findViewById(R.id.mode_indicator);
+        mRecordingIndicatorView = (ImageView) findViewById(R.id.recording_indicator);
+        mRecordingTimeView = (TextView) findViewById(R.id.recording_time);
+        mVideoFrame = (ImageView) findViewById(R.id.video_frame);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+
+        final View hintView = findViewById(R.id.hint_toast);
+        if (hintView != null)
+            hintView.setVisibility(View.GONE);
+
+        Thread t = new Thread(new Runnable() {
+            public void run() {
+                final boolean storageOK = calculatePicturesRemaining() > 0;
+                if (hintView == null)
+                    return;
+
+                if (storageOK) {
+                    mHandler.post(new Runnable() {
+                        public void run() {
+                            hintView.setVisibility(View.VISIBLE);
+                        }
+                    });
+                    mHandler.postDelayed(new Runnable() {
+                        public void run() {
+                            Animation a = new android.view.animation.AlphaAnimation(1F, 0F);
+                            a.setDuration(500);
+                            a.startNow();
+                            hintView.setAnimation(a);
+                            hintView.setVisibility(View.GONE);
+                        }
+                    }, 3000);
+                } else {
+                    mHandler.post(new Runnable() {
+                        public void run() {
+                            hintView.setVisibility(View.GONE);
+                            showStorageToast();
+                        }
+                    });
+                }
+            }
+        });
+        t.start();
+    }
+
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.discard: {
+                File f = new File(mCurrentVideoFilename);
+                f.delete();
+                mContentResolver.delete(mCurrentVideoUri, null, null);
+
+                hideVideoFrameAndStartPreview();
+                break;
+            }
+
+            case R.id.share: {
+                Intent intent = new Intent();
+                intent.setAction(Intent.ACTION_SEND);
+                intent.setType("video/3gpp");
+                intent.putExtra(Intent.EXTRA_STREAM, mCurrentVideoUri);
+                try {
+                    startActivity(Intent.createChooser(intent, getText(R.string.sendVideo)));
+                } catch (android.content.ActivityNotFoundException ex) {
+                    Toast.makeText(VideoCamera.this, R.string.no_way_to_share_video, Toast.LENGTH_SHORT).show();
+                }
+
+                break;
+            }
+
+            case R.id.play: {
+                Intent intent = new Intent(Intent.ACTION_VIEW, mCurrentVideoUri);
+                try {
+                    startActivity(intent);
+                } catch (android.content.ActivityNotFoundException ex) {
+                    Log.e(TAG, "Couldn't view video " + mCurrentVideoUri, ex);
+                }
+                break;
+            }
+        }
+    }
+
+    private void showStorageToast() {
+        String noStorageText = null;
+        int remaining = calculatePicturesRemaining();
+
+        if (remaining == NO_STORAGE_ERROR) {
+            noStorageText = getString(R.string.no_storage);
+        } else if (remaining < 1) {
+            noStorageText = getString(R.string.not_enough_space);
+        }
+
+        if (noStorageText != null) {
+            Toast.makeText(this, noStorageText, 5000).show();
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
+
+        mPausing = false;
+
+        // install an intent filter to receive SD card related events.
+        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
+        intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
+        intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_STARTED);
+        intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
+        intentFilter.addDataScheme("file");
+        registerReceiver(mReceiver, intentFilter);
+        mDidRegister = true;
+
+        mBlackout.setVisibility(View.INVISIBLE);
+        if (mVideoFrameBitmap == null) {
+            initializeVideo();
+        } else {
+            showPostRecordingAlert();
+        }
+    }
+
+    @Override
+    public void onStop() {
+        Log.v(TAG, "onStop");
+        stopVideoRecording();
+        keep();
+        mHandler.removeMessages(CLEAR_SCREEN_DELAY);
+        super.onStop();
+    }
+
+    @Override
+    protected void onPause() {
+        Log.v(TAG, "onPause");
+        stopVideoRecording();
+        keep();
+        hidePostPictureAlert();
+
+        mPausing = true;
+
+        if (mDidRegister) {
+            unregisterReceiver(mReceiver);
+            mDidRegister = false;
+        }
+
+        super.onPause();
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_BACK:
+                if (mMediaRecorderRecording) {
+                    Log.v(TAG, "onKeyBack");
+                    stopVideoRecordingAndDisplayDialog();
+                    return true;
+                } else if(isPostRecordingAlertVisible()) {
+                    hideVideoFrameAndStartPreview();
+                    return true;
+                }
+                break;
+            case KeyEvent.KEYCODE_FOCUS:
+                return true;
+            case KeyEvent.KEYCODE_CAMERA:
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+                if (event.getRepeatCount() == 0) {
+                    if (!mMediaRecorderRecording) {
+                        startVideoRecording();
+                    } else {
+                        stopVideoRecordingAndDisplayDialog();
+                    }
+                    return true;
+                }
+                return true;
+            case KeyEvent.KEYCODE_MENU:
+                if (mMediaRecorderRecording) {
+                    stopVideoRecordingAndDisplayDialog();
+                    return true;
+                }
+                hideVideoFrameAndStartPreview();
+                break;
+        }
+
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onTrackballEvent(MotionEvent event) {
+        cancelRestartPreviewTimeout();
+        return super.onTrackballEvent(event);
+    }
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+        stopVideoRecording();
+        initializeVideo();
+    }
+
+    public void surfaceCreated(SurfaceHolder holder) {
+        mSurfaceHolder = holder;
+    }
+
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        mSurfaceHolder = null;
+    }
+
+    void gotoGallery() {
+        Uri target = Video.Media.INTERNAL_CONTENT_URI;
+        Intent intent = new Intent(Intent.ACTION_VIEW, target);
+        try {
+            startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Could not start gallery activity", e);
+        }
+    }
+
+    void keep() {
+        cancelSavingNotification();
+    };
+
+    void toss() {
+        cancelSavingNotification();
+    };
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+
+        for (int i = 1; i <= MenuHelper.MENU_ITEM_MAX; i++) {
+            if (i != MenuHelper.GENERIC_ITEM) {
+                menu.setGroupVisible(i, false);
+            }
+        }
+
+        menu.setGroupVisible(MenuHelper.VIDEO_MODE_ITEM, true);
+
+        return true;
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        super.onCreateOptionsMenu(menu);
+
+        addBaseMenuItems(menu);
+        MenuHelper.addImageMenuItems(
+                menu,
+                MenuHelper.INCLUDE_ALL & ~MenuHelper.INCLUDE_ROTATE_MENU,
+                false,
+                VideoCamera.this,
+                mHandler,
+
+                // Handler for deletion
+                new Runnable() {
+                    public void run() {
+                        // What do we do here?
+                        // mContentResolver.delete(uri, null, null);
+                    }
+                },
+                new MenuHelper.MenuInvoker() {
+                    public void run(final MenuHelper.MenuCallback cb) {
+                    }
+                });
+
+        MenuItem gallery = menu.add(MenuHelper.IMAGE_SAVING_ITEM, MENU_SAVE_GALLERY_PHOTO, 0,
+                R.string.camera_gallery_photos_text).setOnMenuItemClickListener(
+                        new MenuItem.OnMenuItemClickListener() {
+            public boolean onMenuItemClick(MenuItem item) {
+                postAfterKeep(new Runnable() {
+                    public void run() {
+                        gotoGallery();
+                    }
+                });
+                return true;
+            }
+        });
+        gallery.setIcon(android.R.drawable.ic_menu_gallery);
+        return true;
+    }
+
+    private int calculatePicturesRemaining() {
+        try {
+            if (!ImageManager.hasStorage()) {
+                mPicturesRemaining = NO_STORAGE_ERROR;
+            } else {
+                String storageDirectory = Environment.getExternalStorageDirectory().toString();
+                StatFs stat = new StatFs(storageDirectory);
+                float remaining = ((float)stat.getAvailableBlocks() * (float)stat.getBlockSize()) / 400000F;
+                mPicturesRemaining = (int)remaining;
+            }
+        } catch (Exception ex) {
+            // if we can't stat the filesystem then we don't know how many
+            // pictures are remaining.  it might be zero but just leave it
+            // blank since we really don't know.
+            mPicturesRemaining = CANNOT_STAT_ERROR;
+        }
+        return mPicturesRemaining;
+    }
+
+    private void initializeVideo() {
+        Log.v(TAG, "initializeVideo");
+        releaseMediaRecorder();
+
+        if (mSurfaceHolder == null) {
+            Log.v(TAG, "SurfaceHolder is null");
+            return;
+        }
+
+        mMediaRecorder = new MediaRecorder();
+
+        if (DEBUG_SUPPRESS_AUDIO_RECORDING) {
+            Log.v(TAG, "DEBUG_SUPPRESS_AUDIO_RECORDING is true.");
+        } else {
+            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+        }
+        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+        Log.v(TAG, "before setOutputFile");
+        createVideoPath();
+        mMediaRecorder.setOutputFile(mCurrentVideoFilename);
+        Boolean videoQualityLow = getIntPreference("pref_camera_videoquality_key") == 0;
+
+        // Use the same frame rate for both, since internally
+        // if the frame rate is too large, it can cause camera to become
+        // unstable. We need to fix the MediaRecorder to disable the support
+        // of setting frame rate for now.
+        mMediaRecorder.setVideoFrameRate(20);
+        if (videoQualityLow) {
+            mMediaRecorder.setVideoSize(176,144);
+        } else {
+            mMediaRecorder.setVideoSize(352,288);
+        }
+        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263);
+        if (!DEBUG_SUPPRESS_AUDIO_RECORDING) {
+            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+        }
+        Log.v(TAG, "before setPreviewDisplay");
+        mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
+        try {
+            mMediaRecorder.prepare();
+        } catch (IOException exception) {
+            Log.e(TAG, "prepare failed for " + mCurrentVideoFilename);
+            releaseMediaRecorder();
+            // TODO: add more exception handling logic here
+            return;
+        }
+        mMediaRecorderRecording = false;
+    }
+
+    private void releaseMediaRecorder() {
+        Log.v(TAG, "Releasing media recorder.");
+        if (mMediaRecorder != null) {
+            mMediaRecorder.reset();
+            mMediaRecorder.release();
+            mMediaRecorder = null;
+        }
+    }
+
+    private void restartPreview() {
+        if (DEBUG_DO_NOT_REUSE_MEDIA_RECORDER) {
+            Log.v(TAG, "DEBUG_DO_NOT_REUSE_MEDIA_RECORDER recreating mMediaRecorder.");
+            initializeVideo();
+        } else {
+            try {
+                mMediaRecorder.prepare();
+            } catch (IOException exception) {
+                Log.e(TAG, "prepare failed for " + mCurrentVideoFilename);
+                releaseMediaRecorder();
+                // TODO: add more exception handling logic here
+            }
+        }
+    }
+
+    private int getIntPreference(String key) {
+        String s = mPreferences.getString(key, "0");
+        return Integer.parseInt(s);
+    }
+
+    private void createVideoPath() {
+        long dateTaken = System.currentTimeMillis();
+        String title = createName(dateTaken);
+        String displayName = title + ".3gp"; // Used when emailing.
+        String filename = ImageManager.CAMERA_IMAGE_BUCKET_NAME + "/"
+            + Long.toString(dateTaken) + ".3gp";
+        ContentValues values = new ContentValues(7);
+        values.put(Video.Media.TITLE, title);
+        values.put(Video.Media.DISPLAY_NAME, displayName);
+        values.put(Video.Media.DESCRIPTION, "");
+        values.put(Video.Media.DATE_TAKEN, dateTaken);
+        values.put(Video.Media.MIME_TYPE, "video/3gpp");
+        values.put(Video.Media.DATA, filename);
+        Uri videoTable = Uri.parse("content://media/external/video/media");
+        Uri item = mContentResolver.insert(videoTable, values);
+        mCurrentVideoFilename = filename;
+        mCurrentVideoUri = item;
+    }
+
+    private void addBaseMenuItems(Menu menu) {
+        MenuHelper.addSwitchModeMenuItem(menu, this, false);
+        {
+            MenuItem gallery = menu.add(MenuHelper.IMAGE_MODE_ITEM, MENU_GALLERY_PHOTOS, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new OnMenuItemClickListener() {
+                public boolean onMenuItemClick(MenuItem item) {
+                    gotoGallery();
+                    return true;
+                }
+            });
+            gallery.setIcon(android.R.drawable.ic_menu_gallery);
+            mGalleryItems.add(gallery);
+        }
+        {
+            MenuItem gallery = menu.add(MenuHelper.VIDEO_MODE_ITEM, MENU_GALLERY_VIDEOS, 0, R.string.camera_gallery_photos_text).setOnMenuItemClickListener(new OnMenuItemClickListener() {
+                public boolean onMenuItemClick(MenuItem item) {
+                    gotoGallery();
+                    return true;
+                }
+            });
+            gallery.setIcon(android.R.drawable.ic_menu_gallery);
+            mGalleryItems.add(gallery);
+        }
+
+        MenuItem item = menu.add(MenuHelper.GENERIC_ITEM, MENU_SETTINGS, 0, R.string.settings).setOnMenuItemClickListener(new OnMenuItemClickListener() {
+            public boolean onMenuItemClick(MenuItem item) {
+                Intent intent = new Intent();
+                intent.setClass(VideoCamera.this, CameraSettings.class);
+                startActivity(intent);
+                return true;
+            }
+        });
+        item.setIcon(android.R.drawable.ic_menu_preferences);
+    }
+
+    private void startVideoRecording() {
+        Log.v(TAG, "startVideoRecording");
+        if (!mMediaRecorderRecording) {
+
+            // Check mMediaRecorder to see whether it is initialized or not.
+            if (mMediaRecorder == null) {
+                initializeVideo();
+            }
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+            try {
+                mMediaRecorder.start();   // Recording is now started
+            } catch (RuntimeException e) {
+                Log.e(TAG, "Could not start media recorder. ", e);
+                return;
+            }
+            mMediaRecorderRecording = true;
+            mRecordingStartTime = SystemClock.uptimeMillis();
+            mModeIndicatorView.setVisibility(View.GONE);
+            mRecordingIndicatorView.setVisibility(View.VISIBLE);
+            mRecordingTimeView.setText("");
+            mRecordingTimeView.setVisibility(View.VISIBLE);
+            mHandler.sendEmptyMessage(UPDATE_RECORD_TIME);
+        }
+    }
+
+    private void stopVideoRecordingAndDisplayDialog() {
+        Log.v(TAG, "stopVideoRecordingAndDisplayDialog");
+        if (mMediaRecorderRecording) {
+            stopVideoRecording();
+            acquireAndShowVideoFrame();
+            showPostRecordingAlert();
+        }
+    }
+
+    private void showPostRecordingAlert() {
+        cancelRestartPreviewTimeout();
+        mPostPictureAlert.setVisibility(View.VISIBLE);
+        mHandler.sendEmptyMessageDelayed(RESTART_PREVIEW, POST_PICTURE_ALERT_TIMEOUT);
+    }
+
+    private void hidePostPictureAlert() {
+        cancelRestartPreviewTimeout();
+        mPostPictureAlert.setVisibility(View.INVISIBLE);
+    }
+
+    private void cancelRestartPreviewTimeout() {
+        mHandler.removeMessages(RESTART_PREVIEW);
+    }
+
+    private boolean isPostRecordingAlertVisible() {
+        return mPostPictureAlert.getVisibility() == View.VISIBLE;
+    }
+
+    private void stopVideoRecording() {
+        Log.v(TAG, "stopVideoRecording");
+        if (mMediaRecorderRecording || mMediaRecorder != null) {
+            if (mMediaRecorderRecording) {
+                mMediaRecorder.stop();
+            }
+            releaseMediaRecorder();
+            mMediaRecorderRecording = false;
+            mModeIndicatorView.setVisibility(View.VISIBLE);
+            mRecordingIndicatorView.setVisibility(View.GONE);
+            mRecordingTimeView.setVisibility(View.GONE);
+            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        }
+    }
+
+    private void hideVideoFrameAndStartPreview() {
+        hidePostPictureAlert();
+        hideVideoFrame();
+        restartPreview();
+    }
+
+    private void acquireAndShowVideoFrame() {
+        recycleVideoFrameBitmap();
+        mVideoFrameBitmap = createVideoThumbnail(mCurrentVideoFilename);
+        mVideoFrame.setImageBitmap(mVideoFrameBitmap);
+        mVideoFrame.setVisibility(View.VISIBLE);
+    }
+
+    private void hideVideoFrame() {
+        recycleVideoFrameBitmap();
+        mVideoFrame.setVisibility(View.GONE);
+    }
+
+    private void recycleVideoFrameBitmap() {
+        if (mVideoFrameBitmap != null) {
+            mVideoFrame.setImageDrawable(null);
+            mVideoFrameBitmap.recycle();
+            mVideoFrameBitmap = null;
+        }
+    }
+
+    private Bitmap createVideoThumbnail(String filePath) {
+        Bitmap bitmap = null;
+        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+        try {
+            retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
+            retriever.setDataSource(filePath);
+            bitmap = retriever.captureFrame();
+        } finally {
+            retriever.release();
+        }
+        return bitmap;
+    }
+
+}
+
diff --git a/src/com/android/camera/VideoPreview.java b/src/com/android/camera/VideoPreview.java
new file mode 100644
index 0000000..aed1e89
--- /dev/null
+++ b/src/com/android/camera/VideoPreview.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.camera;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.SurfaceView;
+import android.view.View.MeasureSpec;
+
+class VideoPreview extends SurfaceView {
+    private float mAspectRatio;
+    private int mHorizontalTileSize = 1;
+    private int mVerticalTileSize = 1;
+
+    /**
+     * Setting the aspect ratio to this value means to not enforce an aspect ratio.
+     */
+    public static float DONT_CARE = 0.0f;
+
+    public VideoPreview(Context context) {
+        super(context);
+    }
+
+    public VideoPreview(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public VideoPreview(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public void setTileSize(int horizontalTileSize, int verticalTileSize) {
+        if ((mHorizontalTileSize != horizontalTileSize)
+                || (mVerticalTileSize != verticalTileSize)) {
+            mHorizontalTileSize = horizontalTileSize;
+            mVerticalTileSize = verticalTileSize;
+            requestLayout();
+            invalidate();
+        }
+    }
+
+    public void setAspectRatio(int width, int height) {
+        setAspectRatio(((float) width) / ((float) height));
+    }
+
+    public void setAspectRatio(float aspectRatio) {
+        if (mAspectRatio != aspectRatio) {
+            mAspectRatio = aspectRatio;
+            requestLayout();
+            invalidate();
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mAspectRatio != DONT_CARE) {
+            int widthSpecSize =  MeasureSpec.getSize(widthMeasureSpec);
+            int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
+
+            int width = widthSpecSize;
+            int height = heightSpecSize;
+
+            if (width > 0 && height > 0) {
+                float defaultRatio = ((float) width) / ((float) height);
+                if (defaultRatio < mAspectRatio) {
+                    // Need to reduce height
+                    height = (int) (width / mAspectRatio);
+                } else if (defaultRatio > mAspectRatio) {
+                    width = (int) (height * mAspectRatio);
+                }
+                width = roundUpToTile(width, mHorizontalTileSize, widthSpecSize);
+                height = roundUpToTile(height, mVerticalTileSize, heightSpecSize);
+                Log.i("VideoPreview", "ar " + mAspectRatio + " setting size: " + width + 'x' + height);
+                setMeasuredDimension(width, height);
+                return;
+            }
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    private int roundUpToTile(int dimension, int tileSize, int maxDimension) {
+        return Math.min(((dimension + tileSize - 1) / tileSize) * tileSize, maxDimension);
+    }
+}
diff --git a/src/com/android/camera/ViewImage.java b/src/com/android/camera/ViewImage.java
index 4b9eb58..9579f19 100644
--- a/src/com/android/camera/ViewImage.java
+++ b/src/com/android/camera/ViewImage.java
@@ -102,8 +102,8 @@
     private Animation mHidePrevImageViewAnimation = new AlphaAnimation(1F, 0F);
     private Animation mShowNextImageViewAnimation = new AlphaAnimation(0F, 1F);
     private Animation mShowPrevImageViewAnimation = new AlphaAnimation(0F, 1F);
-    
-    
+
+
     static final int sPadding = 20;
     static final int sHysteresis = sPadding * 2;
     static final int sBaseScrollDuration = 1000; // ms
@@ -127,17 +127,17 @@
 
     Runnable mDismissOnScreenControlsRunnable;
     ZoomControls mZoomControls;
-    
+
     public ViewImage() {
     }
-    
+
     private void updateNextPrevControls() {
         boolean showPrev =  mCurrentPosition > 0;
         boolean showNext = mCurrentPosition < mAllImages.getCount() - 1;
-        
+
         boolean prevIsVisible = mPrevImageView.getVisibility() == View.VISIBLE;
         boolean nextIsVisible = mNextImageView.getVisibility() == View.VISIBLE;
-        
+
         if (showPrev && !prevIsVisible) {
             Animation a = mShowPrevImageViewAnimation;
             a.setDuration(500);
@@ -205,7 +205,7 @@
             mDismissOnScreenControlsRunnable = new Runnable() {
                 public void run() {
                     mZoomControls.hide();
-                    
+
                     if (mNextImageView.getVisibility() == View.VISIBLE) {
                         Animation a = mHideNextImageViewAnimation;
                         a.setDuration(500);
@@ -213,7 +213,7 @@
                         mNextImageView.setAnimation(a);
                         mNextImageView.setVisibility(View.INVISIBLE);
                     }
-                    
+
                     if (mPrevImageView.getVisibility() == View.VISIBLE) {
                         Animation a = mHidePrevImageViewAnimation;
                         a.setDuration(500);
@@ -252,14 +252,14 @@
 
     static public class ImageViewTouch extends ImageViewTouchBase {
         private ViewImage mViewImage;
-        
+
         private static int TOUCH_STATE_REST = 0;
         private static int TOUCH_STATE_LEFT_PRESS = 1;
-        private static int TOUCH_STATE_RIGHT_PRESS = 2;  
-        private static int TOUCH_STATE_PANNING = 3;  
-        
+        private static int TOUCH_STATE_RIGHT_PRESS = 2;
+        private static int TOUCH_STATE_PANNING = 3;
+
         private static int TOUCH_AREA_WIDTH = 60;
-        
+
         private int mTouchState = TOUCH_STATE_REST;
 
         public ImageViewTouch(Context context) {
@@ -300,7 +300,7 @@
                     mLastYTouchPos = y;
                     mTouchState = TOUCH_STATE_REST;
                     break;
-                case MotionEvent.ACTION_MOVE: 
+                case MotionEvent.ACTION_MOVE:
                     if (x < TOUCH_AREA_WIDTH) {
                         if (mTouchState == TOUCH_STATE_REST) {
                             mTouchState = TOUCH_STATE_LEFT_PRESS;
@@ -325,10 +325,10 @@
                         mTouchState = TOUCH_STATE_PANNING;
                         viewImage.mPrevImageView.setPressed(false);
                         viewImage.mNextImageView.setPressed(false);
-                        
+
                         int deltaX;
                         int deltaY;
-                        
+
                         if (mLastXTouchPos == -1) {
                             deltaX = 0;
                             deltaY = 0;
@@ -342,7 +342,7 @@
 
                         if (mBitmapDisplayed == null)
                             return true;
-                        
+
                         if (deltaX != 0) {
                             // Second.  Pan to whatever degree is possible.
                             if (getScale() > 1F) {
@@ -357,7 +357,7 @@
                     int nextImagePos = -1;
                     if (mTouchState == TOUCH_STATE_LEFT_PRESS && x < TOUCH_AREA_WIDTH) {
                         nextImagePos = viewImage.mCurrentPosition - 1;
-                    } else if (mTouchState == TOUCH_STATE_RIGHT_PRESS && 
+                    } else if (mTouchState == TOUCH_STATE_RIGHT_PRESS &&
                            x > viewWidth - TOUCH_AREA_WIDTH) {
                         nextImagePos = viewImage.mCurrentPosition + 1;
                     }
@@ -546,7 +546,7 @@
     private void animateScrollTo(int xNew, int yNew) {
         mScroller.startScrollTo(xNew, yNew);
     }
-    
+
     @Override
     public boolean onCreateOptionsMenu(Menu menu)
     {
@@ -564,9 +564,8 @@
             });
             item.setIcon(android.R.drawable.ic_menu_slideshow);
         }
-        
+
         mFlipItem = MenuHelper.addFlipOrientation(menu, ViewImage.this, mPrefs);
-        mFlipItem.setIcon(android.R.drawable.ic_menu_always_landscape_portrait);
 
         final SelectedImageGetter selectedImageGetter = new SelectedImageGetter() {
             public ImageManager.IImage getCurrentImage() {
@@ -581,6 +580,7 @@
         mImageMenuRunnable = MenuHelper.addImageMenuItems(
                 menu,
                 MenuHelper.INCLUDE_ALL,
+                true,
                 ViewImage.this,
                 mHandler,
                 mDeletePhotoRunnable,
@@ -641,7 +641,7 @@
             setImage(mCurrentPosition);
         }
     };
-    
+
     @Override
     public boolean onPrepareOptionsMenu(Menu menu)
     {
@@ -663,7 +663,7 @@
     private boolean isCurrentImageShareable() {
         IImage image = mAllImages.getImageAt(mCurrentPosition);
         if (image != null){
-        	Uri uri = image.fullSizeImageUri();
+            Uri uri = image.fullSizeImageUri();
             String fullUri = uri.toString();
             return fullUri.startsWith(MediaStore.Images.Media.INTERNAL_CONTENT_URI.toString()) ||
                     fullUri.startsWith(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString());
@@ -674,10 +674,12 @@
     @Override
     public void onConfigurationChanged(android.content.res.Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        for (ImageViewTouchBase iv: mImageViews) {
-            iv.setImageBitmapResetBase(null, false, true);
+        if (newConfig.orientation != getResources().getConfiguration().orientation) {
+            for (ImageViewTouchBase iv: mImageViews) {
+                iv.setImageBitmapResetBase(null, false, true);
+            }
+            MenuHelper.requestOrientation(this, mPrefs);
         }
-        MenuHelper.requestOrientation(this, mPrefs);
     }
 
     @Override
@@ -839,7 +841,7 @@
                                         if (mLoad != null) {
                                             long t1;
                                             if (Config.LOGV) t1 = System.currentTimeMillis();
-                                            
+
                                             Bitmap b = null;
                                             try {
                                                 b = mLoad.get();
@@ -941,7 +943,7 @@
             for (ImageViewTouchBase ivtb : mImageViews)
                 ivtb.dump();
         }
-        
+
         if (!mFirst) {
             if (left) {
                 mImageViews[2].copyFrom(mImageViews[1]);
@@ -977,7 +979,7 @@
         } else {
             mScroller.scrollTo(to, 0);
         }
-        
+
         ImageGetterCallback cb = new ImageGetterCallback() {
             public void completed(boolean wasCanceled) {
                 mImageViews[1].setFocusableInTouchMode(true);
@@ -1064,7 +1066,7 @@
             mSlideShowImageViews[i].setImageBitmapResetBase(null, true, true);
             mSlideShowImageViews[i].setVisibility(View.INVISIBLE);
         }
-        
+
         Uri uri = getIntent().getData();
 
         if (Config.LOGV)
@@ -1132,7 +1134,7 @@
             for (ImageViewTouchBase ivt: mImageViews) {
                 ivt.clear();
             }
-            
+
             if (false) {
                 Log.v(TAG, "current is " + this.mSlideShowImageCurrent);
                 this.mSlideShowImageViews[0].dump();
@@ -1161,7 +1163,7 @@
                 Log.v(TAG, "read prefs...  animidx: " + mAnimationIndex);
                 Log.v(TAG, "read prefs... interval: " + mSlideShowInterval);
             }
-            
+
             if (mUseShuffleOrder) {
                 generateShuffleOrder();
             }
@@ -1345,16 +1347,16 @@
     public void onSaveInstanceState(Bundle b) {
         super.onSaveInstanceState(b);
         ImageManager.IImage image = mAllImages.getImageAt(mCurrentPosition);
-          
+
         if (image != null){
-        	Uri uri = image.fullSizeImageUri();
-        	String bucket = null;
-        	if(getIntent()!= null && getIntent().getData()!=null)
-        		bucket = getIntent().getData().getQueryParameter("bucketId");
-        	
-        	if(bucket!=null)
-        		uri = uri.buildUpon().appendQueryParameter("bucketId", bucket).build();
-        	
+            Uri uri = image.fullSizeImageUri();
+            String bucket = null;
+            if(getIntent()!= null && getIntent().getData()!=null)
+                bucket = getIntent().getData().getQueryParameter("bucketId");
+
+            if(bucket!=null)
+                uri = uri.buildUpon().appendQueryParameter("bucketId", bucket).build();
+
             b.putString("uri", uri.toString());
         }
         if (mMode == MODE_SLIDESHOW)
@@ -1364,8 +1366,15 @@
     @Override
     public void onResume()
     {
-    	super.onResume();
-        
+        super.onResume();
+
+        // normally this will never be zero but if one "backs" into this
+        // activity after removing the sdcard it could be zero.  in that
+        // case just "finish" since there's nothing useful that can happen.
+        if (mAllImages.getCount() == 0) {
+            finish();
+        }
+
         ImageManager.IImage image = mAllImages.getImageAt(mCurrentPosition);
 
         String sortOrder = mPrefs.getString("pref_gallery_sort_key", null);
@@ -1376,7 +1385,7 @@
         if (sortAscending != mSortAscending) {
             init(image.fullSizeImageUri());
         }
-        
+
         if (mGetter == null) {
             makeGetter();
         }
@@ -1394,20 +1403,13 @@
          });
         setImage(mCurrentPosition);
 
-        // normally this will never be zero but if one "backs" into this
-        // activity after removing the sdcard it could be zero.  in that
-        // case just "finish" since there's nothing useful that can happen.
-        if (mAllImages.getCount() == 0) {
-            finish();
-        } else {
-            MenuHelper.requestOrientation(this, mPrefs);
-        }
+        MenuHelper.requestOrientation(this, mPrefs);
     }
 
     @Override
     public void onPause()
     {
-    	super.onPause();
+        super.onPause();
 
         mGetter.cancelCurrent();
         mGetter.stop();
@@ -1415,18 +1417,18 @@
         setMode(MODE_NORMAL);
 
         mAllImages.deactivate();
-        
+
         for (ImageViewTouchBase iv: mImageViews) {
             iv.recycleBitmaps();
             iv.setImageBitmap(null, true);
         }
-        
+
         for (ImageViewTouchBase iv: mSlideShowImageViews) {
             iv.recycleBitmaps();
             iv.setImageBitmap(null, true);
         }
     }
-    
+
     @Override
     public void onStop() {
         super.onStop();
diff --git a/src/com/android/camera/ViewVideo.java b/src/com/android/camera/ViewVideo.java
deleted file mode 100644
index 527f0bb..0000000
--- a/src/com/android/camera/ViewVideo.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera;
-
-import android.media.MediaPlayer;
-import android.app.Activity;
-import android.os.Bundle;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.net.Uri;
-import android.os.PowerManager;
-import android.util.Log;
-import android.view.Menu;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.MediaController;
-import android.view.Window;
-import android.widget.VideoView;
-import android.util.Config;
-
-class ViewVideo extends Activity
-{
-    static final String TAG = "ViewVideo";
-
-    private ImageManager.IImageList mAllVideos;
-    private PowerManager.WakeLock   mWakeLock;
-    private ContentResolver         mContentResolver;
-    private VideoView               mVideoView;
-    private ImageManager.IImage     mVideo;
-    private int                     mCurrentPosition = -1;
-    private MediaController         mMediaController;
-
-    // if the activity gets paused the stash the current position here
-    int mPausedPlaybackPosition = 0;
-
-
-    public ViewVideo()
-    {
-    }
-
-    @Override
-    public void onCreate(Bundle icicle)
-    {
-        super.onCreate(icicle);
-        if (Config.LOGV)
-            Log.v(TAG, "onCreate");
-        //getWindow().setFormat(android.graphics.PixelFormat.TRANSLUCENT);
-
-        PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
-
-        mContentResolver = getContentResolver();
-
-        setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
-        setContentView(R.layout.viewvideo);
-
-        mMediaController = new MediaController(this);
-        mVideoView = (VideoView) findViewById(R.id.video);
-        mVideoView.setMediaController(mMediaController);
-        mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
-            public void onCompletion(MediaPlayer mp) {
-                // TODO what do we really want to do at the end of playback?
-                finish();
-            }
-        });
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle b) {
-        if (Config.LOGV)
-            Log.v(TAG, "onSaveInstanceState");
-        b.putInt("playback_position", mPausedPlaybackPosition);
-    }
-
-    @Override
-    public void onRestoreInstanceState(Bundle b) {
-        if (Config.LOGV)
-            Log.v(TAG, "onRestoreInstanceState");
-        mPausedPlaybackPosition = b.getInt("playback_position", 0);
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        if (Config.LOGV)
-            Log.v(TAG, "onPause");
-        mAllVideos.deactivate();
-
-        mVideoView.pause();
-        mPausedPlaybackPosition = mVideoView.getCurrentPosition();
-        mVideoView.setVideoURI(null);
-    }
-
-    @Override
-    public void onStop() {
-        super.onStop();
-        if (Config.LOGV)
-            Log.v(TAG, "onStop");
-    }
-
-    @Override
-    public void onResume()
-    {
-    	super.onResume();
-        if (Config.LOGV)
-            Log.v(TAG, "onResume");
-
-        mAllVideos = ImageManager.instance().allImages(
-                ViewVideo.this,
-                mContentResolver,
-                ImageManager.DataLocation.ALL,
-                ImageManager.INCLUDE_VIDEOS,
-                ImageManager.SORT_DESCENDING);
-
-        // TODO smarter/faster here please
-        Uri uri = getIntent().getData();
-        if (mVideo == null) {
-            for (int i = 0; i < mAllVideos.getCount(); i++) {
-                ImageManager.IImage video = mAllVideos.getImageAt(i);
-                if (video.fullSizeImageUri().equals(uri)) {
-                    mCurrentPosition = i;
-                    mVideo = video;
-                    break;
-                }
-            }
-        }
-
-        if (mCurrentPosition != -1) {
-            mMediaController.setPrevNextListeners(
-                new android.view.View.OnClickListener() {
-                    public void onClick(View v) {
-                        if (++mCurrentPosition == mAllVideos.getCount())
-                            mCurrentPosition = 0;
-                        ImageManager.IImage video = mAllVideos.getImageAt(mCurrentPosition);
-                        mVideo = video;
-                        mVideoView.setVideoURI(video.fullSizeImageUri());
-                        mVideoView.start();
-                    }
-                },
-                new android.view.View.OnClickListener() {
-                    public void onClick(View v) {
-                        if (--mCurrentPosition == -1)
-                            mCurrentPosition = mAllVideos.getCount() - 1;
-                        ImageManager.IImage video = mAllVideos.getImageAt(mCurrentPosition);
-                        mVideo = video;
-                        mVideoView.setVideoURI(video.fullSizeImageUri());
-                        mVideoView.start();
-                    }
-                });
-        }
-        if (Config.LOGV)
-            android.util.Log.v("camera", "seekTo " + mPausedPlaybackPosition);
-        mVideoView.setVideoURI(uri);
-        mVideoView.seekTo(mPausedPlaybackPosition);
-        mVideoView.start();
-    }
-
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu)
-    {
-        super.onCreateOptionsMenu(menu);
-        MenuHelper.addVideoMenuItems(
-                menu,
-                MenuHelper.INCLUDE_ALL & ~MenuHelper.INCLUDE_VIEWPLAY_MENU,
-                ViewVideo.this,       // activity
-                null,       // handler
-                new SelectedImageGetter() {
-                    public ImageManager.IImage getCurrentImage() {
-                        return mVideo;
-                    }
-                    public Uri getCurrentImageUri() {
-                        return mVideo.fullSizeImageUri();
-                    }
-                },
-
-                // deletion case
-                new Runnable() {
-                    public void run() {
-                        mAllVideos.removeImage(mVideo);
-                        finish();
-                    }
-                },
-
-                // pre-work
-                null,
-
-                // post-work
-                null);
-        return true;
-    }
-
-    @Override
-    public boolean onPrepareOptionsMenu(Menu menu)
-    {
-        return super.onPrepareOptionsMenu(menu);
-    }
-}
diff --git a/src/com/android/camera/YouTubeUpload.java b/src/com/android/camera/YouTubeUpload.java
deleted file mode 100644
index 0963d09..0000000
--- a/src/com/android/camera/YouTubeUpload.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.camera;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.app.Activity;
-import android.app.ProgressDialog;
-import android.net.http.AndroidHttpClient;
-import android.os.Bundle;
-import android.widget.TextView;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-
-import android.util.Log;
-import android.util.Xml;
-
-
-public class YouTubeUpload extends Activity
-{
-    private static final String TAG = "YouTubeUpload";
-    
-    private static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
-    private static final boolean mDevServer = false;
-    
-    private ArrayList<String> mCategoriesShort = new ArrayList<String>();
-    private ArrayList<String> mCategoriesLong  = new ArrayList<String>();
-
-    @Override 
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        TextView tv = new TextView(this);
-        tv.setText("");        
-        setContentView(tv);
-    }
-    
-    @Override 
-    public void onResume() {
-        super.onResume();
-        
-        final ProgressDialog pd = ProgressDialog.show(this, "please wait", "");
-        
-        final ImageManager.IImageList all = ImageManager.instance().allImages(
-                YouTubeUpload.this, 
-                getContentResolver(), 
-                ImageManager.DataLocation.ALL, 
-                ImageManager.INCLUDE_VIDEOS, 
-                ImageManager.SORT_ASCENDING);
-
-        android.net.Uri uri = getIntent().getData();
-        if (uri == null) {
-            uri = (Uri) getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
-        }
-        if (uri != null) {
-            final ImageManager.VideoObject vid = (ImageManager.VideoObject) all.getImageForUri(uri);
-            if (vid != null) {
-                new Thread(new Runnable() {
-                    public void run() {
-                        getCategories();
-                        runOnUiThread(new Runnable() {
-                            public void run() {
-                                pd.cancel();
-                                MenuHelper.YouTubeUploadInfoDialog infoDialog = new MenuHelper.YouTubeUploadInfoDialog(
-                                        YouTubeUpload.this, 
-                                        mCategoriesShort,
-                                        mCategoriesLong,
-                                        vid, 
-                                        new Runnable() {
-                                            public void run() {
-                                                finish();
-                                            }
-                                        });
-                                infoDialog.show();
-                            }
-                        });
-                    }
-                }).start();
-            }
-        }
-    }
-    
-    protected String getYouTubeBaseUrl() {
-        if (mDevServer) {
-            return "http://dev.gdata.youtube.com";
-        } else {
-            return "http://gdata.youtube.com";
-        }
-    }
-
-    public void getCategories() {
-        String uri = getYouTubeBaseUrl() + "/schemas/2007/categories.cat";
-        AndroidHttpClient mClient = AndroidHttpClient.newInstance("Android-Camera/0.1");
-
-        try {
-            org.apache.http.HttpResponse r = mClient.execute(new org.apache.http.client.methods.HttpGet(uri));
-            processReturnedData(r.getEntity().getContent());
-        } catch (Exception ex) {
-            Log.e(TAG, "got exception getting categories... " + ex.toString());
-        }
-    }
-    
-    public void processReturnedData(InputStream s) throws IOException, SAXException, XmlPullParserException {
-        try {
-            Xml.parse(s, Xml.findEncodingByName(null), new DefaultHandler() {
-                    @Override
-                    public void startElement(String uri, String localName, String qName,
-                            Attributes attributes) throws SAXException {
-                        if (ATOM_NAMESPACE.equals(uri)) {
-                            if ("category".equals(localName)) {
-                                String catShortName = attributes.getValue("", "term");
-                                String catLongName = attributes.getValue("", "label");
-                                mCategoriesLong .add(catLongName);
-                                mCategoriesShort.add(catShortName);
-                                return;
-                            }
-                        }
-                    }
-                });
-        } catch (SAXException e) {
-            e.printStackTrace();
-        }
-    }
-}