summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml2
-rw-r--r--res/values/cm_strings.xml2
-rw-r--r--res/xml/camera_preferences.xml5
-rw-r--r--src/com/android/camera/CameraActivity.java103
-rw-r--r--src/com/android/camera/Storage.java31
-rw-r--r--src/com/android/camera/VideoModule.java2
-rw-r--r--src/com/android/camera/app/CameraServicesImpl.java2
-rw-r--r--src/com/android/camera/data/FilmstripContentQueries.java2
-rw-r--r--src/com/android/camera/data/FilmstripItemBase.java2
-rw-r--r--src/com/android/camera/one/v2/OneCameraImpl.java2
-rw-r--r--src/com/android/camera/settings/CameraSettingsActivity.java49
-rw-r--r--src/com/android/camera/settings/Keys.java9
-rw-r--r--src/com/android/camera/settings/SettingsUtil.java11
13 files changed, 201 insertions, 21 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2ec81141d..1b6e4acf8 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -27,9 +27,11 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.PREVENT_POWER_KEY" />
+ <uses-permission android:name="com.android.gallery3d.permission.STORAGE_CHANGE" />
<supports-screens
android:anyDensity="true"
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
index a42f411ee..be628f8f7 100644
--- a/res/values/cm_strings.xml
+++ b/res/values/cm_strings.xml
@@ -19,4 +19,6 @@
<string name="pref_camera_power_shutter_title">Power shutter</string>
<!-- More Settings screen, max brightness title -->
<string name="pref_camera_max_brightness_title">Bright screen</string>
+ <!-- More Settings screen, storage title -->
+ <string name="pref_camera_storage_title">Storage location</string>
</resources>
diff --git a/res/xml/camera_preferences.xml b/res/xml/camera_preferences.xml
index ff97e2cf3..73ff62a43 100644
--- a/res/xml/camera_preferences.xml
+++ b/res/xml/camera_preferences.xml
@@ -60,6 +60,11 @@
android:key="pref_camera_recordlocation_key"
android:title="@string/pref_camera_save_location_title" />
+ <!-- Storage -->
+ <ListPreference
+ android:key="pref_camera_storage_key"
+ android:title="@string/pref_camera_storage_title" />
+
<!-- Advanced -->
<PreferenceScreen
android:key="pref_category_advanced"
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index 18c4237db..cf5b0cffc 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -44,9 +44,13 @@ import android.nfc.NfcEvent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
import android.provider.MediaStore;
import android.provider.Settings;
import android.text.TextUtils;
@@ -125,6 +129,7 @@ import com.android.camera.settings.Keys;
import com.android.camera.settings.PictureSizeLoader;
import com.android.camera.settings.ResolutionSetting;
import com.android.camera.settings.ResolutionUtil;
+import com.android.camera.settings.SettingsUtil;
import com.android.camera.settings.SettingsManager;
import com.android.camera.stats.UsageStatistics;
import com.android.camera.stats.profiler.Profile;
@@ -183,6 +188,8 @@ public class CameraActivity extends QuickActivity
"android.media.action.STILL_IMAGE_CAMERA_SECURE";
public static final String ACTION_IMAGE_CAPTURE_SECURE =
"android.media.action.IMAGE_CAPTURE_SECURE";
+ public static final String INTENT_GALLERY3D_STORAGE_CHANGE =
+ "com.android.gallery3d.STORAGE_CHANGE";
// The intent extra for camera from secure lock screen. True if the gallery
// should only show newly captured pictures. sSecureAlbumId does not
@@ -240,6 +247,8 @@ public class CameraActivity extends QuickActivity
private Intent mResultDataForTesting;
private OnScreenHint mStorageHint;
private final Object mStorageSpaceLock = new Object();
+ private String mStoragePath;
+ private StorageManager mStorageManager;
private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES;
private boolean mAutoRotateScreen;
private boolean mSecureCamera;
@@ -580,6 +589,10 @@ public class CameraActivity extends QuickActivity
initPowerShutter();
} else if (key.equals(Keys.KEY_MAX_BRIGHTNESS)) {
initMaxBrightness();
+ } else if (key.equals(Keys.KEY_STORAGE)) {
+ if (setStoragePath()) {
+ updateStorageSpaceAndHint(null);
+ }
}
}
@@ -1679,6 +1692,8 @@ public class CameraActivity extends QuickActivity
mMotionManager = getServices().getMotionManager();
+ syncStorageSettings();
+
mFirstRunDialog = new FirstRunDialog(this,
getAndroidContext(),
mResolutionSetting,
@@ -1882,6 +1897,8 @@ public class CameraActivity extends QuickActivity
@Override
public void onResumeTasks() {
mPaused = false;
+
+ setStoragePath();
checkPermissions();
if (!mHasCriticalPermissions) {
Log.v(TAG, "onResume: Missing critical permissions.");
@@ -1946,14 +1963,6 @@ public class CameraActivity extends QuickActivity
mDataAdapter);
if (!mSecureCamera) {
mFilmstripController.setDataAdapter(mDataAdapter);
- if (!isCaptureIntent()) {
- mDataAdapter.requestLoad(new Callback<Void>() {
- @Override
- public void onCallback(Void result) {
- fillTemporarySessions();
- }
- });
- }
} else {
// Put a lock placeholder as the last image by setting its date to
// 0.
@@ -2186,6 +2195,9 @@ public class CameraActivity extends QuickActivity
if (mSecureCamera) {
unregisterReceiver(mShutdownReceiver);
}
+ if (mStorageManager != null) {
+ mStorageManager.unregisterListener(mStorageEventListener);
+ }
// Ensure anything that checks for "isPaused" returns true.
mPaused = true;
@@ -3063,4 +3075,79 @@ public class CameraActivity extends QuickActivity
boolean showDetails = data.getAttributes().hasDetailedCaptureInfo();
detailsMenuItem.setVisible(showDetails);
}
+
+ protected boolean setStoragePath() {
+ String storagePath = mSettingsManager.getString(SettingsManager.SCOPE_GLOBAL,
+ Keys.KEY_STORAGE);
+ Storage.setRoot(storagePath);
+ if (storagePath.equals(mStoragePath)) {
+ return false;
+ }
+ mStoragePath = storagePath;
+
+ // Sync the swipe preview with the right path
+ if (mDataAdapter != null) {
+ if (!mSecureCamera) {
+ mDataAdapter.requestLoad(new Callback<Void>() {
+ @Override
+ public void onCallback(Void result) {
+ fillTemporarySessions();
+ }
+ });
+ }
+ }
+
+ // Update the gallery app
+ Intent intent = new Intent(INTENT_GALLERY3D_STORAGE_CHANGE);
+ intent.putExtra(Keys.KEY_STORAGE, mStoragePath);
+ sendBroadcast(intent);
+ return true;
+ }
+
+ protected void syncStorageSettings() {
+ if (mStorageManager == null) {
+ mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
+ mStorageManager.registerListener(mStorageEventListener);
+ }
+ StorageVolume[] volumes = mStorageManager.getVolumeList();
+ List<String> values = new ArrayList<String>(volumes.length);
+ List<StorageVolume> mountedVolumes = new ArrayList<StorageVolume>(volumes.length);
+
+ // Find all mounted volumes
+ String defaultValue = Environment.getExternalStorageDirectory().toString();
+ for (int i = 0; i < volumes.length; i++) {
+ StorageVolume v = volumes[i];
+ if (mStorageManager.getVolumeState(v.getPath()).equals(Environment.MEDIA_MOUNTED)) {
+ values.add(v.getPath());
+ mountedVolumes.add(v);
+ if (v.isPrimary()) {
+ defaultValue = v.getPath();
+ }
+ }
+ }
+ SettingsUtil.setMountedStorageVolumes(mountedVolumes);
+
+ mSettingsManager.setDefaults(Keys.KEY_STORAGE, defaultValue,
+ values.toArray(new String[values.size()]));
+
+ // Check if current volume is mounted. If not, restore the default storage path.
+ try {
+ mSettingsManager.getIndexOfCurrentValue(SettingsManager.SCOPE_GLOBAL,
+ Keys.KEY_STORAGE);
+ } catch (IllegalStateException e) {
+ mSettingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL, Keys.KEY_STORAGE);
+ }
+
+ if (setStoragePath()) {
+ updateStorageSpaceAndHint(null);
+ }
+ }
+
+ private StorageEventListener mStorageEventListener = new StorageEventListener () {
+ @Override
+ public void onStorageStateChanged(String path, String oldState, String newState) {
+ Log.v(TAG, "onStorageStateChanged: " + path + "(" + oldState + "->" + newState + ")");
+ syncStorageSettings();
+ }
+ };
}
diff --git a/src/com/android/camera/Storage.java b/src/com/android/camera/Storage.java
index 842f9a4f6..67e56ab71 100644
--- a/src/com/android/camera/Storage.java
+++ b/src/com/android/camera/Storage.java
@@ -46,10 +46,6 @@ import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
public class Storage {
- public static final String DCIM =
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).toString();
- public static final String DIRECTORY = DCIM + "/Camera";
- public static final File DIRECTORY_FILE = new File(DIRECTORY);
public static final String JPEG_POSTFIX = ".jpg";
public static final String GIF_POSTFIX = ".gif";
public static final long UNAVAILABLE = -1L;
@@ -72,6 +68,17 @@ public class Storage {
};
private static HashMap<Uri, Point> sSessionsToSizes = new HashMap<>();
private static HashMap<Uri, Integer> sSessionsToPlaceholderVersions = new HashMap<>();
+ private static String sRoot = Environment.getExternalStorageDirectory().toString();
+
+ public static void setRoot(String root) {
+ if (!root.equals(sRoot)) {
+ sSessionsToContentUris.clear();
+ sContentUrisToSessions.clear();
+ sSessionsToSizes.clear();
+ sSessionsToPlaceholderVersions.clear();
+ }
+ sRoot = root;
+ }
/**
* Save the image with default JPEG MIME type and add it to the MediaStore.
@@ -416,7 +423,7 @@ public class Storage {
}
private static String generateFilepath(String title, String mimeType) {
- return generateFilepath(DIRECTORY, title, mimeType);
+ return generateFilepath(generateDirectory(), title, mimeType);
}
public static String generateFilepath(String directory, String title, String mimeType) {
@@ -431,6 +438,14 @@ public class Storage {
return (new File(directory, title + extension)).getAbsolutePath();
}
+ private static String generateDCIM() {
+ return new File(sRoot, Environment.DIRECTORY_DCIM).toString();
+ }
+
+ public static String generateDirectory() {
+ return generateDCIM() + "/Camera";
+ }
+
/**
* Returns the jpeg bytes for a placeholder session
*
@@ -499,14 +514,14 @@ public class Storage {
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);
@@ -519,7 +534,7 @@ public class Storage {
* imported. This is a temporary fix for bug#1655552.
*/
public static void ensureOSXCompatible() {
- File nnnAAAAA = new File(DCIM, "100ANDRO");
+ 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/VideoModule.java b/src/com/android/camera/VideoModule.java
index 48f8b6c8d..59ff4babf 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -1222,7 +1222,7 @@ public class VideoModule extends CameraModule
// Used when emailing.
String filename = title + convertOutputFormatToFileExt(outputFileFormat);
String mime = convertOutputFormatToMimeType(outputFileFormat);
- String path = Storage.DIRECTORY + '/' + filename;
+ String path = Storage.generateDirectory() + '/' + filename;
String tmpPath = path + ".tmp";
mCurrentVideoValues = new ContentValues(9);
mCurrentVideoValues.put(Video.Media.TITLE, title);
diff --git a/src/com/android/camera/app/CameraServicesImpl.java b/src/com/android/camera/app/CameraServicesImpl.java
index a1a2e861e..8c02d5caf 100644
--- a/src/com/android/camera/app/CameraServicesImpl.java
+++ b/src/com/android/camera/app/CameraServicesImpl.java
@@ -65,7 +65,7 @@ public class CameraServicesImpl implements CameraServices {
PlaceholderManager mPlaceHolderManager = new PlaceholderManager(context);
SessionStorageManager mSessionStorageManager = SessionStorageManagerImpl.create(context);
- StackSaverFactory mStackSaverFactory = new StackSaverFactory(Storage.DIRECTORY,
+ StackSaverFactory mStackSaverFactory = new StackSaverFactory(Storage.generateDirectory(),
context.getContentResolver());
CaptureSessionFactory captureSessionFactory = new CaptureSessionFactoryImpl(
mMediaSaver, mPlaceHolderManager, mSessionStorageManager, mStackSaverFactory);
diff --git a/src/com/android/camera/data/FilmstripContentQueries.java b/src/com/android/camera/data/FilmstripContentQueries.java
index a3c273ca9..5f8c60053 100644
--- a/src/com/android/camera/data/FilmstripContentQueries.java
+++ b/src/com/android/camera/data/FilmstripContentQueries.java
@@ -32,7 +32,7 @@ import java.util.List;
*/
public class FilmstripContentQueries {
private static final Log.Tag TAG = new Log.Tag("LocalDataQuery");
- private static final String CAMERA_PATH = Storage.DIRECTORY + "%";
+ private static final String CAMERA_PATH = Storage.generateDirectory() + "%";
private static final String SELECT_BY_PATH = MediaStore.MediaColumns.DATA + " LIKE ?";
public interface CursorToFilmstripItemFactory<I extends FilmstripItem> {
diff --git a/src/com/android/camera/data/FilmstripItemBase.java b/src/com/android/camera/data/FilmstripItemBase.java
index f225a0701..cc7ff54ac 100644
--- a/src/com/android/camera/data/FilmstripItemBase.java
+++ b/src/com/android/camera/data/FilmstripItemBase.java
@@ -146,7 +146,7 @@ public abstract class FilmstripItemBase<T extends FilmstripItemData> implements
}
// Check if this is a 'Camera' sub-directory.
- String cameraPathStr = Storage.DIRECTORY_FILE.getAbsolutePath();
+ String cameraPathStr = new File(Storage.generateDirectory()).getAbsolutePath();
String fileParentPathStr = directory.getParentFile().getAbsolutePath();
Log.d(TAG, "CameraPathStr: " + cameraPathStr + " fileParentPathStr: " + fileParentPathStr);
diff --git a/src/com/android/camera/one/v2/OneCameraImpl.java b/src/com/android/camera/one/v2/OneCameraImpl.java
index d4c7cba01..1e1fbc33d 100644
--- a/src/com/android/camera/one/v2/OneCameraImpl.java
+++ b/src/com/android/camera/one/v2/OneCameraImpl.java
@@ -161,7 +161,7 @@ public class OneCameraImpl extends AbstractOneCamera {
}
/** Directory to store raw DNG files in. */
- private static final File RAW_DIRECTORY = new File(Storage.DIRECTORY, "DNG");
+ private static final File RAW_DIRECTORY = new File(Storage.generateDirectory(), "DNG");
/** Current CONTROL_AF_MODE request to Camera2 API. */
private int mControlAFMode = CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
diff --git a/src/com/android/camera/settings/CameraSettingsActivity.java b/src/com/android/camera/settings/CameraSettingsActivity.java
index f890bf11f..4e05add96 100644
--- a/src/com/android/camera/settings/CameraSettingsActivity.java
+++ b/src/com/android/camera/settings/CameraSettingsActivity.java
@@ -23,6 +23,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
+import android.os.storage.StorageVolume;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
@@ -150,6 +151,8 @@ public class CameraSettingsActivity extends FragmentActivity {
// Selected resolutions for the different cameras and sizes.
private PictureSizes mPictureSizes;
+ private List<StorageVolume> mStorageVolumes;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -189,6 +192,9 @@ public class CameraSettingsActivity extends FragmentActivity {
// Load the camera sizes.
loadSizes();
+ // Load storage volumes
+ loadStorageVolumeList();
+
// Send loaded sizes to additional preferences.
CameraSettingsActivityHelper.onSizesLoaded(this, mPictureSizes.backCameraSizes,
new ListPreferenceFiller() {
@@ -215,6 +221,17 @@ public class CameraSettingsActivity extends FragmentActivity {
setPreferenceScreenIntent(advancedScreen);
}
+ // Fill Storage preference
+ final Preference storagePreference = findPreference(Keys.KEY_STORAGE);
+ if (storagePreference != null) {
+ if (mStorageVolumes == null) {
+ getPreferenceScreen().removePreference(storagePreference);
+ } else {
+ setEntries(storagePreference);
+ setSummary(storagePreference);
+ }
+ }
+
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
@@ -365,6 +382,8 @@ public class CameraSettingsActivity extends FragmentActivity {
setEntriesForSelection(mPictureSizes.videoQualitiesBack.orNull(), listPreference);
} else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_FRONT)) {
setEntriesForSelection(mPictureSizes.videoQualitiesFront.orNull(), listPreference);
+ } else if (listPreference.getKey().equals(Keys.KEY_STORAGE)) {
+ setStorageEntriesForSelection(mStorageVolumes, listPreference);
}
}
@@ -444,6 +463,28 @@ public class CameraSettingsActivity extends FragmentActivity {
}
/**
+ * Sets the entries for the storage list preference.
+ *
+ * @param storageVolumes The storage volumes.
+ * @param preference The preference to set the entries for.
+ */
+ private void setStorageEntriesForSelection(List<StorageVolume> storageVolumes,
+ ListPreference preference) {
+ if (storageVolumes == null) {
+ return;
+ }
+ String[] entries = new String[storageVolumes.size()];
+ String[] entryValues = new String[storageVolumes.size()];
+ for (int i = 0; i < storageVolumes.size(); i++) {
+ StorageVolume v = storageVolumes.get(i);
+ entries[i] = v.getDescription(getActivity());
+ entryValues[i] = v.getPath();
+ }
+ preference.setEntries(entries);
+ preference.setEntryValues(entryValues);
+ }
+
+ /**
* Sets the summary for the given list preference.
*
* @param displayableSizes The human readable preferred sizes
@@ -491,6 +532,14 @@ public class CameraSettingsActivity extends FragmentActivity {
mPictureSizes = loader.computePictureSizes();
}
+ private void loadStorageVolumeList() {
+ mStorageVolumes = SettingsUtil.getMountedStorageVolumes();
+ if (mStorageVolumes.size() < 2) {
+ // Remove storage preference
+ mStorageVolumes = null;
+ }
+ }
+
/**
* @param size The photo resolution.
* @return A human readable and translated string for labeling the
diff --git a/src/com/android/camera/settings/Keys.java b/src/com/android/camera/settings/Keys.java
index f59591f84..8cc643b2f 100644
--- a/src/com/android/camera/settings/Keys.java
+++ b/src/com/android/camera/settings/Keys.java
@@ -18,6 +18,8 @@ package com.android.camera.settings;
import android.content.Context;
+import android.os.Environment;
+
import com.android.camera.app.LocationManager;
import com.android.camera.util.ApiHelper;
import com.android.camera2.R;
@@ -83,6 +85,7 @@ public class Keys {
public static final String KEY_HAS_SEEN_PERMISSIONS_DIALOGS = "pref_has_seen_permissions_dialogs";
public static final String KEY_POWER_SHUTTER = "pref_power_shutter";
public static final String KEY_MAX_BRIGHTNESS = "pref_max_brightness";
+ public static final String KEY_STORAGE = "pref_camera_storage_key";
/**
* Set some number of defaults for the defined keys.
@@ -182,6 +185,12 @@ public class Keys {
settingsManager.setDefaults(KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, true);
+ settingsManager.setDefaults(KEY_STORAGE,
+ Environment.getExternalStorageDirectory().toString(), null);
+ if (!settingsManager.isSet(SettingsManager.SCOPE_GLOBAL, KEY_STORAGE)) {
+ settingsManager.setToDefault(SettingsManager.SCOPE_GLOBAL, KEY_STORAGE);
+ }
+
}
/** Helper functions for some defined keys. */
diff --git a/src/com/android/camera/settings/SettingsUtil.java b/src/com/android/camera/settings/SettingsUtil.java
index f83fb8bcd..64fd8435d 100644
--- a/src/com/android/camera/settings/SettingsUtil.java
+++ b/src/com/android/camera/settings/SettingsUtil.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.media.CamcorderProfile;
+import android.os.storage.StorageVolume;
import android.util.SparseArray;
import com.android.camera.debug.Log;
@@ -491,6 +492,16 @@ public class SettingsUtil {
return -1;
}
+ private static List<StorageVolume> sMountedStorageVolumes;
+
+ public static void setMountedStorageVolumes(List<StorageVolume> volumes) {
+ sMountedStorageVolumes = volumes;
+ }
+
+ public static List<StorageVolume> getMountedStorageVolumes() {
+ return sMountedStorageVolumes;
+ }
+
public static interface CameraDeviceSelector {
/**
* Given the static characteristics of a specific camera device, decide whether it is the