Storage location configuration options

Based on d050f80 and 2bb0437 in the former Camera package

PS2: Whitespace cleanup

PS3: Fixed filmstrip preview from camera.

PS4: Check volume availability

PS5: Load the settings when either the camera opens
  or when the gallery opens

PS6: Remove unused import

PS7: Rebase

PS8: Change icon to stat_notify_sdcard, add credit for
  original sources

PS10: Code style cleanup, include icon, and add setting
  to the camcorder settings

PS11: Rebase, reword commit message

Change-Id: Ic484149f1f051aea5c5c8440bbc322e9924683a7

merge this
diff --git a/res/drawable-hdpi/stat_notify_sdcard.png b/res/drawable-hdpi/stat_notify_sdcard.png
new file mode 100644
index 0000000..5892d38
--- /dev/null
+++ b/res/drawable-hdpi/stat_notify_sdcard.png
Binary files differ
diff --git a/res/drawable-mdpi/stat_notify_sdcard.png b/res/drawable-mdpi/stat_notify_sdcard.png
new file mode 100644
index 0000000..5eae7a2
--- /dev/null
+++ b/res/drawable-mdpi/stat_notify_sdcard.png
Binary files differ
diff --git a/res/drawable-xhdpi/stat_notify_sdcard.png b/res/drawable-xhdpi/stat_notify_sdcard.png
new file mode 100644
index 0000000..7201213
--- /dev/null
+++ b/res/drawable-xhdpi/stat_notify_sdcard.png
Binary files differ
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
deleted file mode 100644
index c0a3cf2..0000000
--- a/res/values/cm_strings.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The CyanogenMod Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!--  Text for undo toast message. Only modify if the string is too long to be displayed -->
-    <string name="filtershow_undo_toast">@string/filtershow_undo</string>
-    <!--  Text for redo toast message. Only modify if the string is too long to be displayed -->
-    <string name="filtershow_redo_toast">@string/filtershow_redo</string>
-
-    <!-- Settings screen, dialog choice for 12.8 megapixels picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_12_8mp">12.8M pixels</string>
-    <!-- Settings screen, dialog choice for 11.5 megapixels picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_11_5mp">11.5M pixels</string>
-    <!-- Settings screen, dialog choice for 9 megapixels picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_9mp">9M pixels</string>
-    <!-- Settings screen, dialog choice for 6.4 megapixels picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_6_4mp">6.4M pixels</string>
-    <!-- Settings screen, dialog choice for 6 megapixels picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_6mp">6M pixels</string>
-    <!-- Settings screen, dialog choice for 4.5 megapixels picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_4_5mp">4.5M pixels</string>
-    <!-- Settings screen, dialog choice for 3.7 megapixels picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_3_7mp">3.7M pixels</string>
-    <!-- Settings screen, dialog choice for 2.5 megapixels picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_2_5mp">2.5M pixels</string>
-    <!-- Settings screen, dialog choice for 2.1 megapixels picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_2_1mp">2.1M pixels</string>
-    <!-- Settings screen, dialog choice for 1.9 megapixels picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_1_9mp">1.9M pixels</string>
-    <!-- Settings screen, dialog choice for 1.5 megapixels picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_1_5mp">1.5M pixels</string>
-    <!-- Settings screen, dialog choice for WXGA picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_wxga">WXGA</string>
-    <!-- Settings screen, dialog choice for XGA picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_xga">XGA</string>
-    <!-- Settings screen, dialog choice for SVGA picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_svga">SVGA</string>
-    <!-- Settings screen, dialog choice for WVGA picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_wvga">WVGA</string>
-    <!-- Settings screen, dialog choice for WQVGA picture size [CHAR LIMIT=15] -->
-    <string name="pref_camera_picturesize_entry_wqvga">WQVGA</string>
-
-</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8a2f997..a9dda01 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -666,6 +666,9 @@
     <!-- Settings screen, Camera setting category title -->
     <string name="pref_camera_settings_category">Camera settings</string>
 
+   <!-- More Settings screen, storage title -->
+    <string name="pref_camera_storage_title">Storage</string>
+
     <!-- Settings screen, Camcorder setting category title -->
     <string name="pref_camcorder_settings_category">Camcorder settings</string>
 
diff --git a/res/xml/camera_preferences.xml b/res/xml/camera_preferences.xml
index 8c13a34..d6f8f51 100644
--- a/res/xml/camera_preferences.xml
+++ b/res/xml/camera_preferences.xml
@@ -65,6 +65,9 @@
             camera:entries="@array/pref_camera_picturesize_entries"
             camera:entryValues="@array/pref_camera_picturesize_entryvalues" />
     <ListPreference
+            camera:key="pref_camera_storage_key"
+            camera:title="@string/pref_camera_storage_title" />
+    <ListPreference
             camera:key="pref_camera_focusmode_key"
             camera:defaultValue="@array/pref_camera_focusmode_default_array"
             camera:title="@string/pref_camera_focusmode_title"
diff --git a/res/xml/video_preferences.xml b/res/xml/video_preferences.xml
index faf5f65..86de6d0 100644
--- a/res/xml/video_preferences.xml
+++ b/res/xml/video_preferences.xml
@@ -23,6 +23,9 @@
             camera:title="@string/pref_video_quality_title"
             camera:entries="@array/pref_video_quality_entries"
             camera:entryValues="@array/pref_video_quality_entryvalues"/>
+    <ListPreference
+            camera:key="pref_camera_storage_key"
+            camera:title="@string/pref_camera_storage_title" />
     <IconListPreference
             camera:key="pref_video_time_lapse_frame_interval_key"
             camera:defaultValue="@string/pref_video_time_lapse_frame_interval_default"
diff --git a/src/com/android/camera/ActivityBase.java b/src/com/android/camera/ActivityBase.java
index 59bd82c..057d5a0 100644
--- a/src/com/android/camera/ActivityBase.java
+++ b/src/com/android/camera/ActivityBase.java
@@ -20,12 +20,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.SharedPreferences;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Rect;
 import android.hardware.Camera.Parameters;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.Message;
 import android.view.KeyEvent;
@@ -104,6 +106,7 @@
     protected boolean mSecureCamera;
     private static boolean sFirstStartAfterScreenOn = true;
 
+    private String mStoragePath;
     private long mStorageSpace = Storage.LOW_STORAGE_THRESHOLD;
     private static final int UPDATE_STORAGE_HINT = 0;
     private final Handler mHandler = new Handler() {
@@ -212,6 +215,17 @@
         return false;
     }
 
+    protected boolean setStoragePath(SharedPreferences prefs) {
+        String storagePath = prefs.getString(CameraSettings.KEY_STORAGE,
+                Environment.getExternalStorageDirectory().toString());
+        Storage.getInstance().setRoot(storagePath);
+        if (storagePath.equals(mStoragePath)) {
+            return false;
+        }
+        mStoragePath = storagePath;
+        return true;
+    }
+
     @Override
     protected void onResume() {
         super.onResume();
@@ -308,7 +322,7 @@
     }
 
     protected void updateStorageSpace() {
-        mStorageSpace = Storage.getAvailableSpace();
+        mStorageSpace = Storage.getInstance().getAvailableSpace();
     }
 
     protected long getStorageSpace() {
@@ -367,7 +381,7 @@
             if (mSecureCamera) {
                 path = "/secure/all/" + sSecureAlbumId;
             } else {
-                path = "/local/all/" + MediaSetUtils.CAMERA_BUCKET_ID;
+                path = "/local/all/" + Storage.getInstance().generateBucketId();
             }
         } else {
             path = "/local/all/0"; // Use 0 so gallery does not show anything.
@@ -401,7 +415,7 @@
             if (mSecureCamera) {
                 path = "/secure/all/" + sSecureAlbumId;
             } else {
-                path = "/local/all/" + MediaSetUtils.CAMERA_BUCKET_ID;
+                path = "/local/all/" + Storage.getInstance().generateBucketId();
             }
         } else {
             path = "/local/all/0"; // Use 0 so gallery does not show anything.
diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java
index 15ec1e3..f7f8b12 100644
--- a/src/com/android/camera/CameraSettings.java
+++ b/src/com/android/camera/CameraSettings.java
@@ -27,6 +27,9 @@
 import android.hardware.Camera.Parameters;
 import android.hardware.Camera.Size;
 import android.media.CamcorderProfile;
+import android.os.Environment;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
 import android.text.TextUtils;
 import android.util.FloatMath;
 import android.util.Log;
@@ -65,6 +68,7 @@
     public static final String KEY_CAMERA_FIRST_USE_HINT_SHOWN = "pref_camera_first_use_hint_shown_key";
     public static final String KEY_VIDEO_FIRST_USE_HINT_SHOWN = "pref_video_first_use_hint_shown_key";
     public static final String KEY_PHOTOSPHERE_PICTURESIZE = "pref_photosphere_picturesize_key";
+    public static final String KEY_STORAGE = "pref_camera_storage_key";
 
     public static final String EXPOSURE_DEFAULT_VALUE = "0";
 
@@ -174,6 +178,7 @@
                 group.findPreference(KEY_VIDEOCAMERA_FLASH_MODE);
         ListPreference videoEffect = group.findPreference(KEY_VIDEO_EFFECT);
         ListPreference cameraHdr = group.findPreference(KEY_CAMERA_HDR);
+        ListPreference storage = group.findPreference(KEY_STORAGE);
 
         // Since the screen could be loaded from different resources, we need
         // to check if the preference is available here
@@ -233,6 +238,44 @@
                     || !Util.isCameraHdrSupported(mParameters))) {
             removePreference(group, cameraHdr.getKey());
         }
+        if (storage != null) {
+            buildStorage(group, storage);
+        }
+    }
+
+    private void buildStorage(PreferenceGroup group, ListPreference storage) {
+        StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
+        StorageVolume[] volumes = sm.getVolumeList();
+        List<String> entries = new ArrayList<String>(volumes.length);
+        List<String> entryValues = new ArrayList<String>(volumes.length);
+        int primary = 0;
+
+        for (int i = 0; i < volumes.length; i++) {
+            StorageVolume v = volumes[i];
+            //Hide unavailable volumes
+            if (sm.getVolumeState(v.getPath()).equals(Environment.MEDIA_MOUNTED)) {
+                entries.add(v.getDescription(mContext));
+                entryValues.add(v.getPath());
+                if (v.isPrimary()) {
+                    primary = i;
+                }
+            }
+        }
+
+        if (entries.size() < 2) {
+            // No need for storage setting
+            removePreference(group, storage.getKey());
+            return;
+        }
+
+        storage.setEntries(entries.toArray(new String[entries.size()]));
+        storage.setEntryValues(entryValues.toArray(new String[entryValues.size()]));
+
+        // Filter saved invalid value
+        if (storage.findIndexOfValue(storage.getValue()) < 0) {
+            // Default to the primary storage
+            storage.setValueIndex(primary);
+        }
     }
 
     private void buildExposureCompensation(
diff --git a/src/com/android/camera/ComboPreferences.java b/src/com/android/camera/ComboPreferences.java
index e17e47a..aa39fcc 100644
--- a/src/com/android/camera/ComboPreferences.java
+++ b/src/com/android/camera/ComboPreferences.java
@@ -151,7 +151,8 @@
                 || key.equals(CameraSettings.KEY_VIDEO_EFFECT)
                 || key.equals(CameraSettings.KEY_TIMER)
                 || key.equals(CameraSettings.KEY_TIMER_SOUND_EFFECTS)
-                || key.equals(CameraSettings.KEY_PHOTOSPHERE_PICTURESIZE);
+                || key.equals(CameraSettings.KEY_PHOTOSPHERE_PICTURESIZE)
+                || key.equals(CameraSettings.KEY_STORAGE);
     }
 
     @Override
diff --git a/src/com/android/camera/MediaSaveService.java b/src/com/android/camera/MediaSaveService.java
index e37b45c..c9e61b0 100644
--- a/src/com/android/camera/MediaSaveService.java
+++ b/src/com/android/camera/MediaSaveService.java
@@ -154,7 +154,7 @@
 
         @Override
         protected Uri doInBackground(Void... v) {
-            return Storage.addImage(
+            return Storage.getInstance().addImage(
                     resolver, title, date, loc, orientation, exif, data, width, height);
         }
 
diff --git a/src/com/android/camera/PanoramaModule.java b/src/com/android/camera/PanoramaModule.java
index 007ea7a..57405ec 100644
--- a/src/com/android/camera/PanoramaModule.java
+++ b/src/com/android/camera/PanoramaModule.java
@@ -908,7 +908,7 @@
         if (jpegData != null) {
             String filename = PanoUtil.createName(
                     mActivity.getResources().getString(R.string.pano_file_name_format), mTimeTaken);
-            String filepath = Storage.generateFilepath(filename);
+            String filepath = Storage.getInstance().generateFilepath(filename);
 
             Location loc = mLocationManager.getCurrentLocation();
             ExifInterface exif = new ExifInterface();
@@ -923,10 +923,10 @@
                 exif.writeExif(jpegData, filepath);
             } catch (IOException e) {
                 Log.e(TAG, "Cannot set exif for " + filepath, e);
-                Storage.writeFile(filepath, jpegData);
+                Storage.getInstance().writeFile(filepath, jpegData);
             }
             int jpegLength = (int) (new File(filepath).length());
-            return Storage.addImage(mContentResolver, filename, mTimeTaken,
+            return Storage.getInstance().addImage(mContentResolver, filename, mTimeTaken,
                     loc, orientation, jpegLength, filepath, width, height);
         }
         return null;
diff --git a/src/com/android/camera/PhotoMenu.java b/src/com/android/camera/PhotoMenu.java
index d0f21ed..d9583a6 100644
--- a/src/com/android/camera/PhotoMenu.java
+++ b/src/com/android/camera/PhotoMenu.java
@@ -18,6 +18,7 @@
 
 import android.content.res.Resources;
 import android.hardware.Camera.Parameters;
+import android.view.LayoutInflater;
 
 import com.android.camera.ui.AbstractSettingPopup;
 import com.android.camera.ui.CountdownTimerPopup;
@@ -142,6 +143,26 @@
             }
         });
         more.addItem(item);
+        // Storage location
+        if (group.findPreference(CameraSettings.KEY_STORAGE) != null) {
+            item = makeItem(R.drawable.stat_notify_sdcard);
+            final ListPreference storagePref = group.findPreference(CameraSettings.KEY_STORAGE);
+            item.setLabel(res.getString(R.string.pref_camera_storage_title).toUpperCase(locale));
+            item.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(PieItem item) {
+                    LayoutInflater inflater =  mActivity.getLayoutInflater();
+                    ListPrefSettingPopup popup = (ListPrefSettingPopup) inflater.inflate(
+                            R.layout.list_pref_setting_popup, null, false);
+                    popup.initialize(storagePref);
+                    popup.setSettingChangedListener(PhotoMenu.this);
+                    mUI.dismissPopup();
+                    mPopup = popup;
+                    mUI.showPopup(mPopup);
+                }
+            });
+            more.addItem(item);
+        }
         // white balance
         if (group.findPreference(CameraSettings.KEY_WHITE_BALANCE) != null) {
             item = makeItem(CameraSettings.KEY_WHITE_BALANCE);
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index 10aed9b..add7973 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -440,6 +440,7 @@
 
         mPreferences.setLocalId(mActivity, mCameraId);
         CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
+        mActivity.setStoragePath(mPreferences);
         // we need to reset exposure for the preview
         resetExposureCompensation();
         // Starting the preview needs preferences, camera screen nail, and
@@ -697,7 +698,7 @@
         queue.addIdleHandler(new MessageQueue.IdleHandler() {
             @Override
             public boolean queueIdle() {
-                Storage.ensureOSXCompatible();
+                Storage.getInstance().ensureOSXCompatible();
                 return false;
             }
         });
@@ -2048,6 +2049,11 @@
                 mPreferences, mContentResolver);
         mLocationManager.recordLocation(recordLocation);
 
+        if (mActivity.setStoragePath(mPreferences)) {
+            mActivity.updateStorageSpaceAndHint();
+            mActivity.reuseCameraScreenNail(!mIsImageCaptureIntent);
+        }
+
         setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
         mUI.updateOnScreenIndicators(mParameters, mPreferenceGroup, mPreferences);
     }
diff --git a/src/com/android/camera/Storage.java b/src/com/android/camera/Storage.java
index ba995ed..d342edf 100644
--- a/src/com/android/camera/Storage.java
+++ b/src/com/android/camera/Storage.java
@@ -31,6 +31,7 @@
 
 import com.android.gallery3d.common.ApiHelper;
 import com.android.gallery3d.exif.ExifInterface;
+import com.android.gallery3d.util.MediaSetUtils;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -38,20 +39,28 @@
 public class Storage {
     private static final String TAG = "CameraStorage";
 
-    public static final String DCIM =
-            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString();
-
-    public static final String DIRECTORY = DCIM + "/Camera";
-
-    // Match the code in MediaProvider.computeBucketValues().
-    public static final String BUCKET_ID =
-            String.valueOf(DIRECTORY.toLowerCase().hashCode());
-
     public static final long UNAVAILABLE = -1L;
     public static final long PREPARING = -2L;
     public static final long UNKNOWN_SIZE = -3L;
     public static final long LOW_STORAGE_THRESHOLD = 50000000;
 
+    private String mRoot = Environment.getExternalStorageDirectory().toString();
+    private static Storage sStorage;
+
+    // Singleton
+    private Storage() {}
+
+    public static Storage getInstance() {
+        if (sStorage == null) {
+            sStorage = new Storage();
+        }
+        return sStorage;
+    }
+
+    public void setRoot(String root) {
+        mRoot = root;
+    }
+
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
     private static void setImageSize(ContentValues values, int width, int height) {
         // The two fields are available since ICS but got published in JB
@@ -61,7 +70,7 @@
         }
     }
 
-    public static void writeFile(String path, byte[] data) {
+    public String writeFile(String path, byte[] data) {
         FileOutputStream out = null;
         try {
             out = new FileOutputStream(path);
@@ -74,10 +83,12 @@
             } catch (Exception e) {
             }
         }
+
+        return path;
     }
 
     // Save the image and add it to media store.
-    public static Uri addImage(ContentResolver resolver, String title,
+    public Uri addImage(ContentResolver resolver, String title,
             long date, Location location, int orientation, ExifInterface exif,
             byte[] jpeg, int width, int height) {
         // Save the image.
@@ -96,7 +107,7 @@
     }
 
     // Add the image to media store.
-    public static Uri addImage(ContentResolver resolver, String title,
+    public Uri addImage(ContentResolver resolver, String title,
             long date, Location location, int orientation, int jpegLength,
             String path, int width, int height) {
         // Insert into MediaStore.
@@ -131,7 +142,7 @@
         return uri;
     }
 
-    public static void deleteImage(ContentResolver resolver, Uri uri) {
+    public void deleteImage(ContentResolver resolver, Uri uri) {
         try {
             resolver.delete(uri, null, null);
         } catch (Throwable th) {
@@ -139,11 +150,27 @@
         }
     }
 
-    public static String generateFilepath(String title) {
-        return DIRECTORY + '/' + title + ".jpg";
+    private String generateDCIM() {
+        return new File(mRoot, Environment.DIRECTORY_DCIM).toString();
     }
 
-    public static long getAvailableSpace() {
+    public String generateDirectory() {
+        return generateDCIM() + "/Camera";
+    }
+
+    public String generateFilepath(String title) {
+        return generateDirectory() + '/' + title + ".jpg";
+    }
+
+    public String generateBucketId() {
+        return String.valueOf(generateBucketIdInt());
+    }
+
+    public int generateBucketIdInt() {
+        return generateDirectory().toLowerCase().hashCode();
+    }
+
+    public long getAvailableSpace() {
         String state = Environment.getExternalStorageState();
         Log.d(TAG, "External storage state=" + state);
         if (Environment.MEDIA_CHECKING.equals(state)) {
@@ -153,14 +180,14 @@
             return UNAVAILABLE;
         }
 
-        File dir = new File(DIRECTORY);
+        File dir = new File(generateDirectory());
         dir.mkdirs();
         if (!dir.isDirectory() || !dir.canWrite()) {
             return UNAVAILABLE;
         }
 
         try {
-            StatFs stat = new StatFs(DIRECTORY);
+            StatFs stat = new StatFs(generateDirectory());
             return stat.getAvailableBlocks() * (long) stat.getBlockSize();
         } catch (Exception e) {
             Log.i(TAG, "Fail to access external storage", e);
@@ -172,8 +199,8 @@
      * OSX requires plugged-in USB storage to have path /DCIM/NNNAAAAA to be
      * imported. This is a temporary fix for bug#1655552.
      */
-    public static void ensureOSXCompatible() {
-        File nnnAAAAA = new File(DCIM, "100ANDRO");
+    public void ensureOSXCompatible() {
+        File nnnAAAAA = new File(generateDCIM(), "100ANDRO");
         if (!(nnnAAAAA.exists() || nnnAAAAA.mkdirs())) {
             Log.e(TAG, "Failed to create " + nnnAAAAA.getPath());
         }
diff --git a/src/com/android/camera/VideoMenu.java b/src/com/android/camera/VideoMenu.java
index 9bfcdea..467061a 100644
--- a/src/com/android/camera/VideoMenu.java
+++ b/src/com/android/camera/VideoMenu.java
@@ -66,7 +66,8 @@
                 CameraSettings.KEY_VIDEO_EFFECT,
                 CameraSettings.KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL,
                 CameraSettings.KEY_VIDEO_QUALITY,
-                CameraSettings.KEY_RECORD_LOCATION
+                CameraSettings.KEY_RECORD_LOCATION,
+                CameraSettings.KEY_STORAGE
         };
         item = makeItem(R.drawable.ic_settings_holo_light);
         item.setLabel(mActivity.getResources().getString(R.string.camera_menu_settings_label));
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index 6e07c29..531a05d 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -369,6 +369,8 @@
         mPreferences.setLocalId(mActivity, mCameraId);
         CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
 
+        mActivity.setStoragePath(mPreferences);
+
         mActivity.mNumberOfCameras = CameraHolder.instance().getNumberOfCameras();
         mPrefVideoEffectDefault = mActivity.getString(R.string.pref_video_effect_default);
         resetEffect();
@@ -1389,7 +1391,7 @@
         // Used when emailing.
         String filename = title + convertOutputFormatToFileExt(outputFileFormat);
         String mime = convertOutputFormatToMimeType(outputFileFormat);
-        String path = Storage.DIRECTORY + '/' + filename;
+        String path = Storage.getInstance().generateDirectory() + '/' + filename;
         String tmpPath = path + ".tmp";
         mCurrentVideoValues = new ContentValues(9);
         mCurrentVideoValues.put(Video.Media.TITLE, title);
@@ -2048,6 +2050,11 @@
             // Check if the current effects selection has changed
             if (updateEffectSelection()) return;
 
+            if (mActivity.setStoragePath(mPreferences)) {
+                mActivity.updateStorageSpaceAndHint();
+                mActivity.reuseCameraScreenNail(!mIsVideoCaptureIntent);
+            }
+
             readVideoPreferences();
             mUI.showTimeLapseUI(mCaptureTimeLapse);
             // We need to restart the preview if preview size is changed.
diff --git a/src/com/android/gallery3d/app/AbstractGalleryActivity.java b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
index ac39aa5..fccc0dc 100644
--- a/src/com/android/gallery3d/app/AbstractGalleryActivity.java
+++ b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
@@ -23,6 +23,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.SharedPreferences;
 import android.content.DialogInterface.OnCancelListener;
 import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
@@ -30,12 +31,16 @@
 import android.content.ServiceConnection;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.IBinder;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.Window;
 import android.view.WindowManager;
 
+import com.android.camera.CameraSettings;
+import com.android.camera.ComboPreferences;
+import com.android.camera.Storage;
 import com.android.gallery3d.R;
 import com.android.gallery3d.common.ApiHelper;
 import com.android.gallery3d.data.DataManager;
@@ -75,6 +80,16 @@
         mPanoramaViewHelper = new PanoramaViewHelper(this);
         mPanoramaViewHelper.onCreate();
         doBindBatchService();
+        ComboPreferences prefs = new ComboPreferences(this);
+        CameraSettings.upgradeGlobalPreferences(prefs.getGlobal());
+        setStoragePath(prefs);
+    }
+
+    protected boolean setStoragePath(SharedPreferences prefs) {
+        String storagePath = prefs.getString(CameraSettings.KEY_STORAGE,
+                Environment.getExternalStorageDirectory().toString());
+        Storage.getInstance().setRoot(storagePath);
+        return true;
     }
 
     @Override
diff --git a/src/com/android/gallery3d/app/PhotoDataAdapter.java b/src/com/android/gallery3d/app/PhotoDataAdapter.java
index bee0149..c1f525e 100644
--- a/src/com/android/gallery3d/app/PhotoDataAdapter.java
+++ b/src/com/android/gallery3d/app/PhotoDataAdapter.java
@@ -747,7 +747,7 @@
         // Must be an item in camera roll.
         if (!(mediaItem instanceof LocalMediaItem)) return false;
         LocalMediaItem item = (LocalMediaItem) mediaItem;
-        if (item.getBucketId() != MediaSetUtils.CAMERA_BUCKET_ID) return false;
+        if (item.getBucketId() != MediaSetUtils.getCameraBucketId()) return false;
         // Must have no size, but must have width and height information
         if (item.getSize() != 0) return false;
         if (item.getWidth() == 0) return false;
diff --git a/src/com/android/gallery3d/data/LocalAlbum.java b/src/com/android/gallery3d/data/LocalAlbum.java
index 7b7015a..0757d88 100644
--- a/src/com/android/gallery3d/data/LocalAlbum.java
+++ b/src/com/android/gallery3d/data/LocalAlbum.java
@@ -95,7 +95,7 @@
 
     @Override
     public boolean isCameraRoll() {
-        return mBucketId == MediaSetUtils.CAMERA_BUCKET_ID;
+        return mBucketId == MediaSetUtils.getCameraBucketId();
     }
 
     @Override
@@ -279,7 +279,7 @@
 
     public static String getLocalizedName(Resources res, int bucketId,
             String name) {
-        if (bucketId == MediaSetUtils.CAMERA_BUCKET_ID) {
+        if (bucketId == MediaSetUtils.getCameraBucketId()) {
             return res.getString(R.string.folder_camera);
         } else if (bucketId == MediaSetUtils.DOWNLOAD_BUCKET_ID) {
             return res.getString(R.string.folder_download);
@@ -297,7 +297,7 @@
     // Relative path is the absolute path minus external storage path
     public static String getRelativePath(int bucketId) {
         String relativePath = "/";
-        if (bucketId == MediaSetUtils.CAMERA_BUCKET_ID) {
+        if (bucketId == MediaSetUtils.getCameraBucketId()) {
             relativePath += BucketNames.CAMERA;
         } else if (bucketId == MediaSetUtils.DOWNLOAD_BUCKET_ID) {
             relativePath += BucketNames.DOWNLOAD;
diff --git a/src/com/android/gallery3d/data/LocalAlbumSet.java b/src/com/android/gallery3d/data/LocalAlbumSet.java
index b2b4b8c..877eaff 100644
--- a/src/com/android/gallery3d/data/LocalAlbumSet.java
+++ b/src/com/android/gallery3d/data/LocalAlbumSet.java
@@ -113,7 +113,7 @@
             int offset = 0;
             // Move camera and download bucket to the front, while keeping the
             // order of others.
-            int index = findBucket(entries, MediaSetUtils.CAMERA_BUCKET_ID);
+            int index = findBucket(entries, MediaSetUtils.getCameraBucketId());
             if (index != -1) {
                 circularShiftRight(entries, offset++, index);
             }
diff --git a/src/com/android/gallery3d/data/SecureAlbum.java b/src/com/android/gallery3d/data/SecureAlbum.java
index 204f848..bb505a5 100644
--- a/src/com/android/gallery3d/data/SecureAlbum.java
+++ b/src/com/android/gallery3d/data/SecureAlbum.java
@@ -151,7 +151,7 @@
     private boolean isCameraBucketEmpty(Uri baseUri) {
         Uri uri = baseUri.buildUpon()
                 .appendQueryParameter("limit", "1").build();
-        String[] selection = {String.valueOf(MediaSetUtils.CAMERA_BUCKET_ID)};
+        String[] selection = {String.valueOf(MediaSetUtils.getCameraBucketId())};
         Cursor cursor = mContext.getContentResolver().query(uri, PROJECTION,
                 "bucket_id = ?", selection, null);
         if (cursor == null) return true;
diff --git a/src/com/android/gallery3d/util/MediaSetUtils.java b/src/com/android/gallery3d/util/MediaSetUtils.java
index 0438005..75689f5 100644
--- a/src/com/android/gallery3d/util/MediaSetUtils.java
+++ b/src/com/android/gallery3d/util/MediaSetUtils.java
@@ -18,6 +18,7 @@
 
 import android.os.Environment;
 
+import com.android.camera.Storage;
 import com.android.gallery3d.data.LocalAlbum;
 import com.android.gallery3d.data.LocalMergeAlbum;
 import com.android.gallery3d.data.MediaSet;
@@ -28,9 +29,9 @@
 public class MediaSetUtils {
     public static final Comparator<MediaSet> NAME_COMPARATOR = new NameComparator();
 
-    public static final int CAMERA_BUCKET_ID = GalleryUtils.getBucketId(
-            Environment.getExternalStorageDirectory().toString() + "/"
-            + BucketNames.CAMERA);
+    public static int getCameraBucketId() {
+        return Storage.getInstance().generateBucketIdInt();
+    }
     public static final int DOWNLOAD_BUCKET_ID = GalleryUtils.getBucketId(
             Environment.getExternalStorageDirectory().toString() + "/"
             + BucketNames.DOWNLOAD);
@@ -44,14 +45,10 @@
             Environment.getExternalStorageDirectory().toString() +
             "/" + BucketNames.SCREENSHOTS);
 
-    private static final Path[] CAMERA_PATHS = {
-            Path.fromString("/local/all/" + CAMERA_BUCKET_ID),
-            Path.fromString("/local/image/" + CAMERA_BUCKET_ID),
-            Path.fromString("/local/video/" + CAMERA_BUCKET_ID)};
-
     public static boolean isCameraSource(Path path) {
-        return CAMERA_PATHS[0] == path || CAMERA_PATHS[1] == path
-                || CAMERA_PATHS[2] == path;
+        return path.equalsIgnoreCase("/local/all/" + getCameraBucketId())
+                || path.equalsIgnoreCase("/local/image/" + getCameraBucketId())
+                || path.equalsIgnoreCase("/local/video/" + getCameraBucketId());
     }
 
     // Sort MediaSets by name