OSDN Git Service

Remove white balance and non-HDR scene mode settings
[android-x86/packages-apps-Camera2.git] / src / com / android / camera / settings / SettingsManager.java
index e1a3664..187bfd8 100644 (file)
@@ -19,39 +19,99 @@ package com.android.camera.settings;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.hardware.Camera.Size;
 import android.preference.PreferenceManager;
-import android.util.Log;
+import android.util.SparseArray;
 
 import com.android.camera.ListPreference;
+import com.android.camera.app.AppController;
+import com.android.camera.app.LocationManager;
+import com.android.camera.debug.Log;
+import com.android.camera.util.CameraUtil;
 import com.android.camera.util.SettingsHelper;
 import com.android.camera2.R;
 
-import java.util.List;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
- * SettingsManager class provides an api for getting and setting both
- * global and local SharedPreferences.
+ * SettingsManager class provides an api for getting and setting both global and
+ * local SharedPreferences.
  */
 public class SettingsManager {
-    private static final String TAG = "SettingsManager";
+    private static final Log.Tag TAG = new Log.Tag("SettingsManager");
 
     private final Context mContext;
     private final SharedPreferences mDefaultSettings;
     private final SettingsCache mSettingsCache;
     private SharedPreferences mGlobalSettings;
     private SharedPreferences mCameraSettings;
+    private final SparseArray<SharedPreferences> mModuleSettings = new SparseArray<SharedPreferences>();
     private SettingsCapabilities mCapabilities;
 
     private int mCameraId = -1;
+    private final AppController mAppController;
+
+    /**
+     * General settings upgrade model:
+     *
+     *  On app upgrade, there are three main ways Settings can be stale/incorrect:
+     *  (1) if the type of a setting has changed.
+     *  (2) if a set value is no longer a member of the set of possible values.
+     *  (3) if the SharedPreferences backing file has changed for a setting.
+     *
+     *  Recovery strategies:
+     *  (1) catch the ClassCastException or NumberFormatException and try to do a
+     *      type conversion, or reset the setting to whatever default is valid.
+     *  (2) sanitize sets, and reset to default if set value is no longer valid.
+     *  (3) use the default value by virtue of the setting not yet being in the
+     *      new file.
+     *
+     * Special cases:
+     *
+     *  There are some settings which shouldn't be reset to default on upgrade if
+     *  possible.  We provide a callback which is executed only on strict upgrade.
+     *  This callback does special case upgrades to a subset of the settings.  This
+     *  contrasts  with the general upgrade strategies, which happen lazily, once a
+     *  setting is used.
+     *
+     * Removing obsolete key/value pairs:
+     *
+     *  This can be done in the strict upgrade callback.  The strict upgrade callback
+     *  should be idempotent, so it is important to leave removal code in the upgrade
+     *  callback so the key/value pairs are removed even if a user skips a version.
+     */
+    public interface StrictUpgradeCallback {
+        /**
+         * Will be executed in the SettingsManager constructor if the strict
+         * upgrade version counter has changed.
+         */
+        public void upgrade(SettingsManager settingsManager, int version);
+    }
+
+    /**
+     * Increment this value whenever a new StrictUpgradeCallback needs to
+     * be executed.  This defines upgrade behavior that should be executed
+     * strictly on app upgrades, when the upgrade behavior differs from the general,
+     * lazy upgrade strategies.
+     */
+    private static final int STRICT_UPGRADE_VERSION = 2;
+
+    /**
+     * A List of OnSettingChangedListener's, maintained to compare to new
+     * listeners and prevent duplicate registering.
+     */
+    private final List<OnSettingChangedListener> mListeners = new ArrayList<OnSettingChangedListener>();
 
-    private final List<OnSharedPreferenceChangeListener>
-        mSharedPreferenceListeners =
-        new ArrayList<OnSharedPreferenceChangeListener>();
+    /**
+     * A List of OnSharedPreferenceChangeListener's, maintained to hold pointers
+     * to actually registered listeners, so they can be unregistered.
+     */
+    private final List<OnSharedPreferenceChangeListener> mSharedPreferenceListeners = new ArrayList<OnSharedPreferenceChangeListener>();
 
-    public SettingsManager(Context context, int nCameras) {
+    public SettingsManager(Context context, AppController app, int nCameras,
+                           StrictUpgradeCallback upgradeCallback) {
         mContext = context;
+        mAppController = app;
 
         SettingsCache.ExtraSettings extraSettings = new SettingsHelper();
         mSettingsCache = new SettingsCache(mContext, extraSettings);
@@ -59,9 +119,13 @@ public class SettingsManager {
         mDefaultSettings = PreferenceManager.getDefaultSharedPreferences(context);
         initGlobal();
 
-        int cameraId = Integer.parseInt(get(SETTING_CAMERA_ID));
-        if (cameraId < 0 || cameraId >= nCameras) {
-            setDefault(SETTING_CAMERA_ID);
+        if (upgradeCallback != null) {
+            // Check for a strict version upgrade.
+            int version = getInt(SETTING_STRICT_UPGRADE_VERSION);
+            if (STRICT_UPGRADE_VERSION != version) {
+                upgradeCallback.upgrade(this, STRICT_UPGRADE_VERSION);
+            }
+            setInt(SETTING_STRICT_UPGRADE_VERSION, STRICT_UPGRADE_VERSION);
         }
     }
 
@@ -74,6 +138,19 @@ public class SettingsManager {
     }
 
     /**
+     * Load and cache a module specific SharedPreferences.
+     */
+    public SharedPreferences getModulePreferences(int modeIndex) {
+        SharedPreferences sharedPreferences = mModuleSettings.get(modeIndex);
+        if (sharedPreferences == null) {
+            String moduleKey = mContext.getPackageName() + "_preferences_module_" + modeIndex;
+            sharedPreferences = mContext.getSharedPreferences(moduleKey, Context.MODE_PRIVATE);
+            mModuleSettings.put(modeIndex, sharedPreferences);
+        }
+        return sharedPreferences;
+    }
+
+    /**
      * Initialize SharedPreferences for other cameras.
      */
     public void changeCamera(int cameraId, SettingsCapabilities capabilities) {
@@ -94,7 +171,8 @@ public class SettingsManager {
 
         String cameraKey = mContext.getPackageName() + "_preferences_" + cameraId;
         mCameraSettings = mContext.getSharedPreferences(
-            cameraKey, Context.MODE_PRIVATE);
+                cameraKey, Context.MODE_PRIVATE);
+
         for (OnSharedPreferenceChangeListener listener : mSharedPreferenceListeners) {
             mCameraSettings.registerOnSharedPreferenceChangeListener(listener);
         }
@@ -113,81 +191,91 @@ public class SettingsManager {
     private OnSharedPreferenceChangeListener getSharedPreferenceListener(
             final OnSettingChangedListener listener) {
         return new OnSharedPreferenceChangeListener() {
-                @Override
-                public void onSharedPreferenceChanged(
-                        SharedPreferences sharedPreferences, String key) {
-                    Integer settingId = mSettingsCache.getId(key);
-                    if (settingId != null) {
-                        listener.onSettingChanged(SettingsManager.this, settingId);
-                    }
+            @Override
+            public void onSharedPreferenceChanged(
+                    SharedPreferences sharedPreferences, String key) {
+                Integer settingId = mSettingsCache.getId(key);
+                if (settingId != null) {
+                    listener.onSettingChanged(SettingsManager.this, settingId);
+                } else {
+                    Log.w(TAG, "Setting id from key=" + key + " is null");
                 }
-            };
+            }
+        };
     }
 
     /**
-     * Add an OnSettingChangedListener to the SettingsManager, which will execute
-     * onSettingsChanged when any SharedPreference has been updated.
+     * Add an OnSettingChangedListener to the SettingsManager, which will
+     * execute onSettingsChanged when any SharedPreference has been updated.
      */
     public void addListener(final OnSettingChangedListener listener) {
         if (listener == null) {
             throw new IllegalArgumentException("OnSettingChangedListener cannot be null.");
         }
 
-        OnSharedPreferenceChangeListener sharedPreferenceListener =
-            getSharedPreferenceListener(listener);
+        if (mListeners.contains(listener)) {
+            return;
+        }
 
-        if (!mSharedPreferenceListeners.contains(sharedPreferenceListener)) {
-            mSharedPreferenceListeners.add(sharedPreferenceListener);
+        mListeners.add(listener);
+        OnSharedPreferenceChangeListener sharedPreferenceListener =
+                getSharedPreferenceListener(listener);
+        mSharedPreferenceListeners.add(sharedPreferenceListener);
 
-            if (mGlobalSettings != null) {
-                mGlobalSettings.registerOnSharedPreferenceChangeListener(sharedPreferenceListener);
-            }
+        if (mGlobalSettings != null) {
+            mGlobalSettings.registerOnSharedPreferenceChangeListener(sharedPreferenceListener);
+        }
 
-            if (mCameraSettings != null) {
-                mCameraSettings.registerOnSharedPreferenceChangeListener(sharedPreferenceListener);
-            }
+        if (mCameraSettings != null) {
+            mCameraSettings.registerOnSharedPreferenceChangeListener(sharedPreferenceListener);
+        }
 
-            if (mDefaultSettings != null) {
-                mDefaultSettings.registerOnSharedPreferenceChangeListener(sharedPreferenceListener);
-            }
+        if (mDefaultSettings != null) {
+            mDefaultSettings.registerOnSharedPreferenceChangeListener(sharedPreferenceListener);
         }
     }
 
     /**
-     * Remove a specific SettingsListener.
-     * This should be done in onPause if a listener has been set.
+     * Remove a specific SettingsListener. This should be done in onPause if a
+     * listener has been set.
      */
     public void removeListener(OnSettingChangedListener listener) {
         if (listener == null) {
             throw new IllegalArgumentException();
         }
 
-        OnSharedPreferenceChangeListener sharedPreferenceListener =
-            getSharedPreferenceListener(listener);
+        if (!mListeners.contains(listener)) {
+            return;
+        }
 
-        if (mSharedPreferenceListeners.contains(sharedPreferenceListener)) {
-            mSharedPreferenceListeners.remove(sharedPreferenceListener);
+        int index = mListeners.indexOf(listener);
+        mListeners.remove(listener);
 
-            if (mGlobalSettings != null) {
-                mGlobalSettings.unregisterOnSharedPreferenceChangeListener(
+        // Get the reference to the actual OnSharedPreferenceChangeListener
+        // that was registered.
+        OnSharedPreferenceChangeListener sharedPreferenceListener =
+                mSharedPreferenceListeners.get(index);
+        mSharedPreferenceListeners.remove(index);
+
+        if (mGlobalSettings != null) {
+            mGlobalSettings.unregisterOnSharedPreferenceChangeListener(
                     sharedPreferenceListener);
-            }
+        }
 
-            if (mCameraSettings != null) {
-                mCameraSettings.unregisterOnSharedPreferenceChangeListener(
+        if (mCameraSettings != null) {
+            mCameraSettings.unregisterOnSharedPreferenceChangeListener(
                     sharedPreferenceListener);
-            }
+        }
 
-            if (mDefaultSettings != null) {
-                mDefaultSettings.unregisterOnSharedPreferenceChangeListener(
+        if (mDefaultSettings != null) {
+            mDefaultSettings.unregisterOnSharedPreferenceChangeListener(
                     sharedPreferenceListener);
-            }
         }
     }
 
     /**
-     * Remove all OnSharedPreferenceChangedListener's.
-     * This should be done in onDestroy.
+     * Remove all OnSharedPreferenceChangedListener's. This should be done in
+     * onDestroy.
      */
     public void removeAllListeners() {
         for (OnSharedPreferenceChangeListener listener : mSharedPreferenceListeners) {
@@ -204,47 +292,29 @@ public class SettingsManager {
             }
         }
         mSharedPreferenceListeners.clear();
+        mListeners.clear();
     }
 
     /**
      * SettingsCapabilities defines constraints around settings that need to be
-     * queried from external sources, like the camera parameters.
-     *
-     * This interface is camera api agnostic.
+     * queried from external sources, like the camera parameters. This interface
+     * is camera api agnostic.
      */
     public interface SettingsCapabilities {
         /**
-         * Returns a list of the picture sizes currently
-         * supported by the camera device.
-         */
-        public List<Size> getSupportedPictureSizes();
-
-        /**
-         * Returns a dynamically calculated list of
-         * exposure values, based on the min and max
-         * exposure compensation supported by the camera device.
+         * Returns a dynamically calculated list of exposure values, based on
+         * the min and max exposure compensation supported by the camera device.
          */
         public String[] getSupportedExposureValues();
 
         /**
-         * Returns a list of camera ids based on the number
-         * of cameras available on the device.
+         * Returns a list of camera ids based on the number of cameras available
+         * on the device.
          */
         public String[] getSupportedCameraIds();
     }
 
     /**
-     * Exposes SettingsCapabilities functionality.
-     */
-    public List<Size> getSupportedPictureSizes() {
-        if (mCapabilities != null) {
-            return mCapabilities.getSupportedPictureSizes();
-        } else {
-            return null;
-        }
-    }
-
-    /**
      * Get the camera id for which the SettingsManager has loaded camera
      * specific preferences.
      */
@@ -264,69 +334,81 @@ public class SettingsManager {
     public static final String SOURCE_DEFAULT = "default";
     public static final String SOURCE_GLOBAL = "global";
     public static final String SOURCE_CAMERA = "camera";
+    public static final String SOURCE_MODULE = "module";
 
     public static final boolean FLUSH_ON = true;
     public static final boolean FLUSH_OFF = false;
 
     // For quick lookup from id to Setting.
     public static final int SETTING_RECORD_LOCATION = 0;
-    public static final int SETTING_VIDEO_QUALITY = 1;
-    public static final int SETTING_VIDEO_TIME_LAPSE_FRAME_INTERVAL = 2;
-    public static final int SETTING_PICTURE_SIZE = 3;
-    public static final int SETTING_JPEG_QUALITY = 4;
-    public static final int SETTING_FOCUS_MODE = 5;
-    public static final int SETTING_FLASH_MODE = 6;
-    public static final int SETTING_VIDEOCAMERA_FLASH_MODE = 7;
-    public static final int SETTING_WHITE_BALANCE = 8;
-    public static final int SETTING_SCENE_MODE = 9;
-    public static final int SETTING_EXPOSURE = 10;
-    public static final int SETTING_TIMER = 11;
-    public static final int SETTING_TIMER_SOUND_EFFECTS = 12;
-    public static final int SETTING_VIDEO_EFFECT = 13;
-    public static final int SETTING_CAMERA_ID = 14;
-    public static final int SETTING_CAMERA_HDR = 15;
-    public static final int SETTING_CAMERA_HDR_PLUS = 16;
-    public static final int SETTING_CAMERA_FIRST_USE_HINT_SHOWN = 17;
-    public static final int SETTING_VIDEO_FIRST_USE_HINT_SHOWN = 18;
-    public static final int SETTING_STARTUP_MODULE_INDEX = 19;
-    public static final int SETTING_CAMERA_REFOCUS = 20;
-    public static final int SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX = 21;
+    public static final int SETTING_VIDEO_QUALITY_BACK = 1;
+    public static final int SETTING_VIDEO_QUALITY_FRONT = 2;
+    public static final int SETTING_VIDEO_TIME_LAPSE_FRAME_INTERVAL = 3;
+    public static final int SETTING_PICTURE_SIZE_BACK = 4;
+    public static final int SETTING_PICTURE_SIZE_FRONT = 5;
+    public static final int SETTING_JPEG_QUALITY = 6;
+    public static final int SETTING_FOCUS_MODE = 7;
+    public static final int SETTING_FLASH_MODE = 8;
+    public static final int SETTING_VIDEOCAMERA_FLASH_MODE = 9;
+    public static final int SETTING_SCENE_MODE = 11;
+    public static final int SETTING_EXPOSURE = 12;
+    public static final int SETTING_VIDEO_EFFECT = 15;
+    public static final int SETTING_CAMERA_ID = 16;
+    public static final int SETTING_CAMERA_HDR = 17;
+    public static final int SETTING_CAMERA_HDR_PLUS = 18;
+    public static final int SETTING_CAMERA_FIRST_USE_HINT_SHOWN = 19;
+    public static final int SETTING_VIDEO_FIRST_USE_HINT_SHOWN = 20;
+    public static final int SETTING_STARTUP_MODULE_INDEX = 21;
+    public static final int SETTING_KEY_CAMERA_MODULE_LAST_USED_INDEX = 23;
+    public static final int SETTING_CAMERA_PANO_ORIENTATION = 24;
+    public static final int SETTING_CAMERA_GRID_LINES = 25;
+    public static final int SETTING_RELEASE_DIALOG_LAST_SHOWN_VERSION = 26;
+    public static final int SETTING_FLASH_SUPPORTED_BACK_CAMERA = 27;
+    public static final int SETTING_STRICT_UPGRADE_VERSION = 28;
+    // A boolean for requesting to return to HDR plus
+    // as soon as possible, if a user requests a setting/mode option
+    // that forces them to leave HDR plus.
+    public static final int SETTING_REQUEST_RETURN_HDR_PLUS = 30;
 
     // Shared preference keys.
     public static final String KEY_RECORD_LOCATION = "pref_camera_recordlocation_key";
-    public static final String KEY_VIDEO_QUALITY = "pref_video_quality_key";
+    public static final String KEY_VIDEO_QUALITY_BACK = "pref_video_quality_back_key";
+    public static final String KEY_VIDEO_QUALITY_FRONT = "pref_video_quality_front_key";
     public static final String KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL =
-        "pref_video_time_lapse_frame_interval_key";
-    public static final String KEY_PICTURE_SIZE = "pref_camera_picturesize_key";
+            "pref_video_time_lapse_frame_interval_key";
+    public static final String KEY_PICTURE_SIZE_BACK = "pref_camera_picturesize_back_key";
+    public static final String KEY_PICTURE_SIZE_FRONT = "pref_camera_picturesize_front_key";
     public static final String KEY_JPEG_QUALITY = "pref_camera_jpegquality_key";
     public static final String KEY_FOCUS_MODE = "pref_camera_focusmode_key";
     public static final String KEY_FLASH_MODE = "pref_camera_flashmode_key";
     public static final String KEY_VIDEOCAMERA_FLASH_MODE = "pref_camera_video_flashmode_key";
-    public static final String KEY_WHITE_BALANCE = "pref_camera_whitebalance_key";
     public static final String KEY_SCENE_MODE = "pref_camera_scenemode_key";
     public static final String KEY_EXPOSURE = "pref_camera_exposure_key";
-    public static final String KEY_TIMER = "pref_camera_timer_key";
-    public static final String KEY_TIMER_SOUND_EFFECTS = "pref_camera_timer_sound_key";
     public static final String KEY_VIDEO_EFFECT = "pref_video_effect_key";
     public static final String KEY_CAMERA_ID = "pref_camera_id_key";
     public static final String KEY_CAMERA_HDR = "pref_camera_hdr_key";
     public static final String KEY_CAMERA_HDR_PLUS = "pref_camera_hdr_plus_key";
     public static final String KEY_CAMERA_FIRST_USE_HINT_SHOWN =
-        "pref_camera_first_use_hint_shown_key";
+            "pref_camera_first_use_hint_shown_key";
     public static final String KEY_VIDEO_FIRST_USE_HINT_SHOWN =
-        "pref_video_first_use_hint_shown_key";
+            "pref_video_first_use_hint_shown_key";
     public static final String KEY_STARTUP_MODULE_INDEX = "camera.startup_module";
-    public static final String KEY_CAMERA_REFOCUS = "pref_camera_refocus";
-    public static final String KEY_SHIMMY_REMAINING_PLAY_TIMES =
-            "pref_shimmy_remaining_play_times";
+    public static final String KEY_CAMERA_MODULE_LAST_USED =
+            "pref_camera_module_last_used_index";
+    public static final String KEY_CAMERA_PANO_ORIENTATION = "pref_camera_pano_orientation";
+    public static final String KEY_CAMERA_GRID_LINES = "pref_camera_grid_lines";
+    public static final String KEY_RELEASE_DIALOG_LAST_SHOWN_VERSION =
+            "pref_release_dialog_last_shown_version";
+    public static final String KEY_FLASH_SUPPORTED_BACK_CAMERA =
+            "pref_flash_supported_back_camera";
+    public static final String KEY_STRICT_UPGRADE_VERSION = "pref_strict_upgrade_version";
+    public static final String KEY_REQUEST_RETURN_HDR_PLUS = "pref_request_return_hdr_plus";
 
     public static final int WHITE_BALANCE_DEFAULT_INDEX = 2;
 
-
     /**
-     * Defines a simple class for holding a the spec of a setting.
-     * This spec is used by the generic api methods to query and
-     * update a setting.
+     * Defines a simple class for holding a the spec of a setting. This spec is
+     * used by the generic api methods to query and update a setting.
      */
     public static class Setting {
         private final String mSource;
@@ -339,7 +421,7 @@ public class SettingsManager {
         /**
          * A constructor used to store a setting's profile.
          */
-        Setting(String source, String type, String defaultValue, String key,
+        public Setting(String source, String type, String defaultValue, String key,
                 String[] values, boolean flushOnCameraChange) {
             mSource = source;
             mType = type;
@@ -350,18 +432,17 @@ public class SettingsManager {
         }
 
         /**
-         * Returns the id of a SharedPreferences instance from which
-         * this Setting may be found.
-         * Possible values are {@link #SOURCE_DEFAULT}, {@link #SOURCE_GLOBAL},
-         * {@link #SOURCE_CAMERA}.
+         * Returns the id of a SharedPreferences instance from which this
+         * Setting may be found. Possible values are {@link #SOURCE_DEFAULT},
+         * {@link #SOURCE_GLOBAL}, {@link #SOURCE_CAMERA}.
          */
         public String getSource() {
             return mSource;
         }
 
         /**
-         * Returns the type of the setting stored in SharedPreferences.
-         * Possible values are {@link #TYPE_STRING}, {@link #TYPE_INTEGER},
+         * Returns the type of the setting stored in SharedPreferences. Possible
+         * values are {@link #TYPE_STRING}, {@link #TYPE_INTEGER},
          * {@link #TYPE_BOOLEAN}.
          */
         public String getType() {
@@ -383,18 +464,17 @@ public class SettingsManager {
         }
 
         /**
-         * Returns an array of possible String values for this setting.
-         * If this setting is not of type {@link #TYPE_STRING}, or
-         * it's not possible to generate the string values, this should
-         * return null;
+         * Returns an array of possible String values for this setting. If this
+         * setting is not of type {@link #TYPE_STRING}, or it's not possible to
+         * generate the string values, this should return null;
          */
         public String[] getStringValues() {
             return mValues;
         }
 
         /**
-         * Returns whether the setting should be flushed from the cache
-         * when the camera device has changed.
+         * Returns whether the setting should be flushed from the cache when the
+         * camera device has changed.
          */
         public boolean isFlushedOnCameraChanged() {
             return mFlushOnCameraChange;
@@ -405,7 +485,10 @@ public class SettingsManager {
      * Get the SharedPreferences needed to query/update the setting.
      */
     public SharedPreferences getSettingSource(Setting setting) {
-        String source = setting.getSource();
+        return getSettingSource(setting.getSource());
+    }
+
+    private SharedPreferences getSettingSource(String source) {
         if (source.equals(SOURCE_DEFAULT)) {
             return mDefaultSettings;
         }
@@ -415,26 +498,32 @@ public class SettingsManager {
         if (source.equals(SOURCE_CAMERA)) {
             return mCameraSettings;
         }
+        if (source.equals(SOURCE_MODULE)) {
+            int modeIndex = CameraUtil.getCameraModeParentModeId(
+                mAppController.getCurrentModuleIndex(), mAppController.getAndroidContext());
+            return getModulePreferences(modeIndex);
+        }
         return null;
     }
 
     /**
-     * Based on Setting id, finds the index of a Setting's
-     * String value in an array of possible String values.
-     *
-     * If the Setting is not of type String, this returns -1.
-     *
-     * <p>TODO: make this a supported api call for all types.</p>
+     * Based on Setting id, finds the index of a Setting's String value in an
+     * array of possible String values. If the Setting is not of type String,
+     * this returns -1.
+     * <p>
+     * TODO: make this a supported api call for all types.
+     * </p>
      */
     public int getStringValueIndex(int id) {
         Setting setting = mSettingsCache.get(id);
         if (setting == null || !TYPE_STRING.equals(setting.getType())) {
             return -1;
         }
+        return getStringValueIndex(setting.getStringValues(), get(id));
+    }
 
-        String value = get(id);
+    private int getStringValueIndex(String[] possibleValues, String value) {
         if (value != null) {
-            String[] possibleValues = setting.getStringValues();
             if (possibleValues != null) {
                 for (int i = 0; i < possibleValues.length; i++) {
                     if (value.equals(possibleValues[i])) {
@@ -447,11 +536,9 @@ public class SettingsManager {
     }
 
     /**
-     * Based on Setting id, sets a Setting's String value using the
-     * index into an array of possible String values.
-     *
-     * Fails to set a value if the index is out of bounds or the Setting
-     * is not of type String.
+     * Based on Setting id, sets a Setting's String value using the index into
+     * an array of possible String values. Fails to set a value if the index is
+     * out of bounds or the Setting is not of type String.
      *
      * @return Whether the value was set.
      */
@@ -472,16 +559,156 @@ public class SettingsManager {
     }
 
     /**
+     * Returns whether this Setting was last set as a String.
+     */
+    private boolean isString(int id, String source) {
+        Setting setting = mSettingsCache.get(id);
+        SharedPreferences preferences = getSettingSource(source);
+        try {
+            preferences.getString(setting.getKey(), null);
+            return true;
+        } catch (ClassCastException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns whether this Setting was last set as a boolean.
+     */
+    private boolean isBoolean(int id, String source) {
+        Setting setting = mSettingsCache.get(id);
+        SharedPreferences preferences = getSettingSource(source);
+        try {
+            preferences.getBoolean(setting.getKey(), false);
+            return true;
+        } catch (ClassCastException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns whether this Setting was last set as an Integer.
+     */
+    private boolean isInteger(int id, String source) {
+        Setting setting = mSettingsCache.get(id);
+        SharedPreferences preferences = getSettingSource(source);
+        try {
+            preferences.getInt(setting.getKey(), 0);
+            return true;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Recover a Setting by converting it to a String if the type
+     * is known and the type conversion is successful, otherwise
+     * reset to the default.
+     */
+    private String recoverToString(int id, String source) {
+        String value;
+        try {
+            if (isBoolean(id, source)) {
+                value = (getBoolean(id, source) ? VALUE_ON : VALUE_OFF);
+            } else if (isInteger(id, source)) {
+                value = Integer.toString(getInt(id, source));
+            } else {
+                throw new Exception();
+            }
+        } catch (Exception e) {
+            value = mSettingsCache.get(id).getDefault();
+        }
+        set(id, source, value);
+        return value;
+    }
+
+    /**
+     * Recover a Setting by converting it to a boolean if the type
+     * is known and the type conversion is successful, otherwise
+     * reset to the default.
+     */
+    private boolean recoverToBoolean(int id, String source) {
+        boolean value;
+        try {
+            if (isString(id, source)) {
+                value = VALUE_ON.equals(get(id, source));
+            } else if (isInteger(id, source)) {
+                value = getInt(id, source) != 0;
+            } else {
+                throw new Exception();
+            }
+        } catch (Exception e) {
+            value = VALUE_ON.equals(mSettingsCache.get(id).getDefault());
+        }
+        setBoolean(id, source, value);
+        return value;
+    }
+
+    /**
+     * Recover a Setting by converting it to an Integer if the type
+     * is known and the type conversion is successful, otherwise
+     * reset to the default.
+     */
+    private int recoverToInteger(int id, String source) {
+        int value;
+        try {
+            if (isString(id, source)) {
+                value = Integer.parseInt(get(id, source));
+            } else if (isBoolean(id, source)) {
+                value = getBoolean(id, source) ? 1 : 0;
+            } else {
+                throw new Exception();
+            }
+        } catch (Exception e) {
+            value = Integer.parseInt(mSettingsCache.get(id).getDefault());
+        }
+        setInt(id, value);
+        return value;
+    }
+
+    /**
+     * Check if a String value is in the set of possible values for a Setting.
+     * We only keep track of possible values for String types for now.
+     */
+    private String sanitize(Setting setting, String value) {
+        if (setting.getStringValues() != null &&
+                getStringValueIndex(setting.getStringValues(), value) < 0) {
+            // If the set of possible values is not empty, and the value
+            // is not in the set of possible values, use the default, because
+            // the set of possible values probably changed.
+            return setting.getDefault();
+        }
+        return value;
+    }
+
+    /**
      * Get a Setting's String value based on Setting id.
      */
-    // TODO: rename to something more descriptive.
+    // TODO: rename to something more descriptive like getString.
     public String get(int id) {
         Setting setting = mSettingsCache.get(id);
-        SharedPreferences preferences = getSettingSource(setting);
+        return get(id, setting.getSource());
+    }
+
+    /**
+     * Get a Setting's String value based on Setting id and a source file id.
+     */
+    public String get(int id, String source) {
+        Setting setting = mSettingsCache.get(id);
+        SharedPreferences preferences = getSettingSource(source);
         if (preferences != null) {
-            return preferences.getString(setting.getKey(), setting.getDefault());
+            try {
+                String value = preferences.getString(setting.getKey(), setting.getDefault());
+                return sanitize(setting, value);
+            } catch (ClassCastException e) {
+                // If the api defines this Setting as a String, but the
+                // last set saved it as a different type, try to recover
+                // the value, but if impossible reset to default.
+                return recoverToString(id, source);
+            }
         } else {
-            return null;
+            throw new IllegalStateException(
+                "Setting source=" + source + " is unitialized.");
         }
     }
 
@@ -490,12 +717,28 @@ public class SettingsManager {
      */
     public boolean getBoolean(int id) {
         Setting setting = mSettingsCache.get(id);
-        SharedPreferences preferences = getSettingSource(setting);
-        boolean defaultValue = setting.getDefault().equals(VALUE_ON);
+        return getBoolean(id, setting.getSource());
+    }
+
+    /**
+     * Get a Setting's boolean value based on a Setting id and a source file id.
+     */
+    public boolean getBoolean(int id, String source) {
+        Setting setting = mSettingsCache.get(id);
+        SharedPreferences preferences = getSettingSource(source);
+        boolean defaultValue = VALUE_ON.equals(setting.getDefault());
         if (preferences != null) {
-            return preferences.getBoolean(setting.getKey(), defaultValue);
+            try {
+                return preferences.getBoolean(setting.getKey(), defaultValue);
+            } catch (ClassCastException e) {
+                // If the api defines this Setting as a boolean, but the
+                // last set saved it as a different type, try to recover
+                // the value, but if impossible reset to default.
+                return recoverToBoolean(id, source);
+            }
         } else {
-            return defaultValue;
+            throw new IllegalStateException(
+                "Setting source=" + source + " is unitialized.");
         }
     }
 
@@ -504,12 +747,28 @@ public class SettingsManager {
      */
     public int getInt(int id) {
         Setting setting = mSettingsCache.get(id);
-        SharedPreferences preferences = getSettingSource(setting);
+        return getInt(id, setting.getSource());
+    }
+
+    /**
+     * Get a Setting's int value based on Setting id and a source file id.
+     */
+    public int getInt(int id, String source) {
+        Setting setting = mSettingsCache.get(id);
+        SharedPreferences preferences = getSettingSource(source);
         int defaultValue = Integer.parseInt(setting.getDefault());
         if (preferences != null) {
-            return preferences.getInt(setting.getKey(), defaultValue);
+            try {
+                return preferences.getInt(setting.getKey(), defaultValue);
+            } catch (NumberFormatException e) {
+                // If the api defines this Setting as an Integer, but the
+                // last set saved it as a different type, try to recover
+                // the value, but if impossible reset to default.
+                return recoverToInteger(id, source);
+            }
         } else {
-            return defaultValue;
+            throw new IllegalStateException(
+                "Setting source=" + source + " is unitialized.");
         }
     }
 
@@ -519,9 +778,21 @@ public class SettingsManager {
     // TODO: rename to something more descriptive.
     public void set(int id, String value) {
         Setting setting = mSettingsCache.get(id);
-        SharedPreferences preferences = getSettingSource(setting);
+        set(id, setting.getSource(), value);
+    }
+
+    /**
+     * Set a Setting with a String value based on Setting id and a source file id.
+     */
+    public void set(int id, String source, String value) {
+        Setting setting = mSettingsCache.get(id);
+        value = sanitize(setting, value);
+        SharedPreferences preferences = getSettingSource(source);
         if (preferences != null) {
             preferences.edit().putString(setting.getKey(), value).apply();
+        } else {
+            throw new IllegalStateException(
+                "Setting source=" + source + " is unitialized.");
         }
     }
 
@@ -530,9 +801,19 @@ public class SettingsManager {
      */
     public void setBoolean(int id, boolean value) {
         Setting setting = mSettingsCache.get(id);
-        SharedPreferences preferences = getSettingSource(setting);
+        setBoolean(id, setting.getSource(), value);
+    }
+    /**
+     * Set a Setting with a boolean value based on Setting id and a source file id.
+     */
+    public void setBoolean(int id, String source, boolean value) {
+        Setting setting = mSettingsCache.get(id);
+        SharedPreferences preferences = getSettingSource(source);
         if (preferences != null) {
             preferences.edit().putBoolean(setting.getKey(), value).apply();
+        } else {
+            throw new IllegalStateException(
+                "Setting source=" + source + " is unitialized.");
         }
     }
 
@@ -541,9 +822,20 @@ public class SettingsManager {
      */
     public void setInt(int id, int value) {
         Setting setting = mSettingsCache.get(id);
-        SharedPreferences preferences = getSettingSource(setting);
+        setInt(id, setting.getSource(), value);
+    }
+
+    /**
+     * Set a Setting with an int value based on Setting id and a source file id.
+     */
+    public void setInt(int id, String source, int value) {
+        Setting setting = mSettingsCache.get(id);
+        SharedPreferences preferences = getSettingSource(source);
         if (preferences != null) {
             preferences.edit().putInt(setting.getKey(), value).apply();
+        } else {
+            throw new IllegalStateException(
+                "Setting source=" + source + " is unitialized.");
         }
     }
 
@@ -552,11 +844,20 @@ public class SettingsManager {
      */
     public boolean isSet(int id) {
         Setting setting = mSettingsCache.get(id);
-        SharedPreferences preferences = getSettingSource(setting);
+        return isSet(id, setting.getSource());
+    }
+
+    /**
+     * Check if a Setting has ever been set based on Setting id and a source file id.
+     */
+    public boolean isSet(int id, String source) {
+        Setting setting = mSettingsCache.get(id);
+        SharedPreferences preferences = getSettingSource(source);
         if (preferences != null) {
-            return (preferences.getString(setting.getKey(), null) != null);
+            return preferences.contains(setting.getKey());
         } else {
-            return false;
+            throw new IllegalStateException(
+                "Setting source=" + setting.getSource() + " is unitialized.");
         }
     }
 
@@ -568,6 +869,9 @@ public class SettingsManager {
         SharedPreferences preferences = getSettingSource(setting);
         if (preferences != null) {
             preferences.edit().putString(setting.getKey(), setting.getDefault());
+        } else {
+            throw new IllegalStateException(
+                "Setting source=" + setting.getSource() + " is unitialized.");
         }
     }
 
@@ -594,24 +898,46 @@ public class SettingsManager {
                 throw new IllegalArgumentException("Type " + type + " is not known.");
             }
         } else {
-            return false;
+            throw new IllegalStateException(
+                "Setting source=" + setting.getSource() + " is unitialized.");
+        }
+    }
+
+    /**
+     * Remove a Setting from SharedPreferences.
+     */
+    public void remove(int id) {
+        Setting setting = mSettingsCache.get(id);
+        SharedPreferences preferences = getSettingSource(setting);
+        if (preferences != null) {
+            preferences.edit().remove(setting.getKey()).apply();
+        } else {
+            throw new IllegalStateException(
+                "Setting source=" + setting.getSource() + " is unitialized.");
         }
     }
 
     public static Setting getLocationSetting(Context context) {
         String defaultValue = context.getString(R.string.setting_none_value);
+        String[] values = null;
+        return new Setting(SOURCE_DEFAULT, TYPE_BOOLEAN, defaultValue, KEY_RECORD_LOCATION,
+                values, FLUSH_OFF);
+    }
+
+    public static Setting getPictureSizeBackSetting(Context context) {
+        String defaultValue = null;
         String[] values = context.getResources().getStringArray(
-            R.array.pref_camera_recordlocation_entryvalues);
-        return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_RECORD_LOCATION,
-            values, FLUSH_OFF);
+                R.array.pref_camera_picturesize_entryvalues);
+        return new Setting(SOURCE_DEFAULT, TYPE_STRING, defaultValue, KEY_PICTURE_SIZE_BACK,
+                values, FLUSH_OFF);
     }
 
-    public static Setting getPictureSizeSetting(Context context) {
+    public static Setting getPictureSizeFrontSetting(Context context) {
         String defaultValue = null;
         String[] values = context.getResources().getStringArray(
-            R.array.pref_camera_picturesize_entryvalues);
-        return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_PICTURE_SIZE,
-            values, FLUSH_OFF);
+                R.array.pref_camera_picturesize_entryvalues);
+        return new Setting(SOURCE_DEFAULT, TYPE_STRING, defaultValue, KEY_PICTURE_SIZE_FRONT,
+                values, FLUSH_OFF);
     }
 
     public static Setting getDefaultCameraIdSetting(Context context,
@@ -621,48 +947,40 @@ public class SettingsManager {
         if (capabilities != null) {
             values = capabilities.getSupportedCameraIds();
         }
-        return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_CAMERA_ID,
-            values, FLUSH_ON);
-    }
-
-    public static Setting getWhiteBalanceSetting(Context context) {
-        String defaultValue = context.getString(R.string.pref_camera_whitebalance_default);
-        String[] values = context.getResources().getStringArray(
-            R.array.pref_camera_whitebalance_entryvalues);
-        return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue, KEY_WHITE_BALANCE,
-            values, FLUSH_OFF);
+        return new Setting(SOURCE_MODULE, TYPE_STRING, defaultValue, KEY_CAMERA_ID,
+                values, FLUSH_ON);
     }
 
     public static Setting getHdrSetting(Context context) {
         String defaultValue = context.getString(R.string.pref_camera_hdr_default);
         String[] values = context.getResources().getStringArray(
-            R.array.pref_camera_hdr_entryvalues);
+                R.array.pref_camera_hdr_entryvalues);
         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_CAMERA_HDR,
-            values, FLUSH_OFF);
+                values, FLUSH_OFF);
     }
 
     public static Setting getHdrPlusSetting(Context context) {
         String defaultValue = context.getString(R.string.pref_camera_hdr_plus_default);
         String[] values = context.getResources().getStringArray(
-            R.array.pref_camera_hdr_plus_entryvalues);
+                R.array.pref_camera_hdr_plus_entryvalues);
         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_CAMERA_HDR_PLUS,
-            values, FLUSH_OFF);
+                values, FLUSH_OFF);
     }
 
     public static Setting getSceneModeSetting(Context context) {
         String defaultValue = context.getString(R.string.pref_camera_scenemode_default);
         String[] values = context.getResources().getStringArray(
-            R.array.pref_camera_scenemode_entryvalues);
+                R.array.pref_camera_scenemode_entryvalues);
         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue, KEY_SCENE_MODE,
-            values, FLUSH_OFF);
+                values, FLUSH_OFF);
     }
 
     public static Setting getFlashSetting(Context context) {
         String defaultValue = context.getString(R.string.pref_camera_flashmode_default);
         String[] values = context.getResources().getStringArray(
-            R.array.pref_camera_flashmode_entryvalues);
+                R.array.pref_camera_flashmode_entryvalues);
         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue, KEY_FLASH_MODE,
-            values, FLUSH_OFF);
+                values, FLUSH_OFF);
     }
 
     public static Setting getExposureSetting(Context context,
@@ -673,145 +991,191 @@ public class SettingsManager {
             values = capabilities.getSupportedExposureValues();
         }
         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue, KEY_EXPOSURE,
-            values, FLUSH_ON);
+                values, FLUSH_ON);
     }
 
     public static Setting getHintSetting(Context context) {
         String defaultValue = context.getString(R.string.setting_on_value);
         String[] values = null;
         return new Setting(SOURCE_GLOBAL, TYPE_BOOLEAN, defaultValue,
-            KEY_CAMERA_FIRST_USE_HINT_SHOWN, values, FLUSH_OFF);
+                KEY_CAMERA_FIRST_USE_HINT_SHOWN, values, FLUSH_OFF);
     }
 
     public static Setting getFocusModeSetting(Context context) {
         String defaultValue = null;
         String[] values = context.getResources().getStringArray(
-            R.array.pref_camera_focusmode_entryvalues);
+                R.array.pref_camera_focusmode_entryvalues);
         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue, KEY_FOCUS_MODE,
-            values, FLUSH_OFF);
+                values, FLUSH_OFF);
     }
 
-    public static Setting getTimerSetting(Context context) {
-        String defaultValue = context.getString(R.string.pref_camera_timer_default);
-        String[] values = null; // TODO: get the values dynamically.
-        return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_TIMER,
-            values, FLUSH_OFF);
-    }
-
-    public static Setting getTimerSoundSetting(Context context) {
-        String defaultValue = context.getString(R.string.pref_camera_timer_sound_default);
+    public static Setting getVideoQualityBackSetting(Context context) {
+        String defaultValue = context.getString(R.string.pref_video_quality_default);
         String[] values = context.getResources().getStringArray(
-            R.array.pref_camera_timer_sound_entryvalues);
-        return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_TIMER_SOUND_EFFECTS,
-            values, FLUSH_OFF);
+                R.array.pref_video_quality_entryvalues);
+        return new Setting(SOURCE_DEFAULT, TYPE_STRING, defaultValue, KEY_VIDEO_QUALITY_BACK,
+                values, FLUSH_OFF);
     }
 
-    public static Setting getVideoQualitySetting(Context context) {
+    public static Setting getVideoQualityFrontSetting(Context context) {
         String defaultValue = context.getString(R.string.pref_video_quality_default);
         String[] values = context.getResources().getStringArray(
-            R.array.pref_video_quality_entryvalues);
-        return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_VIDEO_QUALITY,
-            values, FLUSH_OFF);
+                R.array.pref_video_quality_entryvalues);
+        return new Setting(SOURCE_DEFAULT, TYPE_STRING, defaultValue, KEY_VIDEO_QUALITY_FRONT,
+                values, FLUSH_OFF);
     }
 
     public static Setting getTimeLapseFrameIntervalSetting(Context context) {
         String defaultValue = context.getString(
-            R.string.pref_video_time_lapse_frame_interval_default);
+                R.string.pref_video_time_lapse_frame_interval_default);
         String[] values = context.getResources().getStringArray(
-            R.array.pref_video_time_lapse_frame_interval_entryvalues);
+                R.array.pref_video_time_lapse_frame_interval_entryvalues);
         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue,
-            KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL, values, FLUSH_OFF);
+                KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL, values, FLUSH_OFF);
     }
 
     public static Setting getJpegQualitySetting(Context context) {
         String defaultValue = context.getString(
-            R.string.pref_camera_jpeg_quality_normal);
+                R.string.pref_camera_jpeg_quality_normal);
         String[] values = context.getResources().getStringArray(
-            R.array.pref_camera_jpeg_quality_entryvalues);
+                R.array.pref_camera_jpeg_quality_entryvalues);
         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue, KEY_JPEG_QUALITY,
-            values, FLUSH_OFF);
+                values, FLUSH_OFF);
     }
 
     public static Setting getVideoFlashSetting(Context context) {
         String defaultValue = context.getString(R.string.pref_camera_video_flashmode_default);
         String[] values = context.getResources().getStringArray(
-            R.array.pref_camera_video_flashmode_entryvalues);
+                R.array.pref_camera_video_flashmode_entryvalues);
         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue,
-            KEY_VIDEOCAMERA_FLASH_MODE, values, FLUSH_OFF);
+                KEY_VIDEOCAMERA_FLASH_MODE, values, FLUSH_OFF);
     }
 
     public static Setting getVideoEffectSetting(Context context) {
         String defaultValue = context.getString(R.string.pref_video_effect_default);
         String[] values = context.getResources().getStringArray(
-            R.array.pref_video_effect_entryvalues);
+                R.array.pref_video_effect_entryvalues);
         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_VIDEO_EFFECT,
-            values, FLUSH_OFF);
+                values, FLUSH_OFF);
     }
 
     public static Setting getHintVideoSetting(Context context) {
         String defaultValue = context.getString(R.string.setting_on_value);
         String[] values = null;
         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue,
-            KEY_VIDEO_FIRST_USE_HINT_SHOWN, values, FLUSH_OFF);
+                KEY_VIDEO_FIRST_USE_HINT_SHOWN, values, FLUSH_OFF);
     }
 
     public static Setting getStartupModuleSetting(Context context) {
         String defaultValue = context.getString(R.string.pref_camera_startup_index_default);
         String[] values = null;
         return new Setting(SOURCE_DEFAULT, TYPE_INTEGER, defaultValue,
-            KEY_STARTUP_MODULE_INDEX, values, FLUSH_OFF);
+                KEY_STARTUP_MODULE_INDEX, values, FLUSH_OFF);
     }
 
-    public static Setting getShimmyRemainingTimesSetting(Context context) {
-        String defaultValue = context.getString(R.string.pref_shimmy_play_times);
+    public static Setting getLastUsedCameraModule(Context context) {
+        String defaultValue = Integer.toString(context.getResources()
+                .getInteger(R.integer.camera_mode_photo));
         return new Setting(SOURCE_DEFAULT, TYPE_INTEGER, defaultValue,
-                KEY_SHIMMY_REMAINING_PLAY_TIMES, null, FLUSH_OFF);
+                KEY_CAMERA_MODULE_LAST_USED, null, FLUSH_OFF);
     }
 
-    public static Setting getRefocusSetting(Context context) {
+    public static Setting getPanoOrientationSetting(Context context) {
+        String defaultValue = context.getString(R.string.pano_orientation_horizontal);
+        String[] values = context.getResources().getStringArray(
+                R.array.pref_camera_pano_orientation_entryvalues);
+        return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue,
+                KEY_CAMERA_PANO_ORIENTATION, values, FLUSH_OFF);
+    }
+
+    public static Setting getGridLinesSetting(Context context) {
         String defaultValue = context.getString(R.string.setting_off_value);
         String[] values = context.getResources().getStringArray(
-            R.array.pref_camera_refocus_entryvalues);
+                R.array.pref_camera_gridlines_entryvalues);
         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue,
-            KEY_CAMERA_REFOCUS, values, FLUSH_OFF);
+                KEY_CAMERA_GRID_LINES, values, FLUSH_OFF);
+    }
+
+    public static Setting getReleaseDialogLastShownVersionSetting(Context context) {
+        return new Setting(SOURCE_DEFAULT, TYPE_STRING, null,
+                KEY_RELEASE_DIALOG_LAST_SHOWN_VERSION, null, FLUSH_OFF);
+    }
+
+    public static Setting getFlashSupportedBackCameraSetting(Context context) {
+        String defaultValue = context.getString(R.string.setting_none_value);
+        return new Setting(SOURCE_GLOBAL, TYPE_BOOLEAN, defaultValue,
+                KEY_FLASH_SUPPORTED_BACK_CAMERA, null, FLUSH_OFF);
+    }
+
+    public static Setting getStrictUpgradeVersionSetting(Context context) {
+        String defaultValue = "0";
+        return new Setting(SOURCE_DEFAULT, TYPE_INTEGER, defaultValue,
+                KEY_STRICT_UPGRADE_VERSION, null, FLUSH_OFF);
+    }
+
+    public static Setting getRequestReturnHdrPlusSetting(Context context) {
+        String defaultValue = context.getString(R.string.setting_none_value);
+        return new Setting(SOURCE_MODULE, TYPE_BOOLEAN, VALUE_OFF,
+                KEY_REQUEST_RETURN_HDR_PLUS, null, FLUSH_OFF);
     }
 
     // Utilities.
 
     /**
-     * Returns whether the camera has been set to back facing
-     * in settings.
+     * Returns whether the camera has been set to back facing in settings.
      */
     public boolean isCameraBackFacing() {
-        int cameraFacingIndex = getStringValueIndex(SETTING_CAMERA_ID);
-        String backFacingIndex = mContext.getString(R.string.pref_camera_id_index_back);
-        return (cameraFacingIndex == Integer.parseInt(backFacingIndex));
+        String cameraFacing = get(SETTING_CAMERA_ID);
+        String backFacing = mContext.getString(R.string.pref_camera_id_default);
+        return (Integer.parseInt(cameraFacing) == Integer.parseInt(backFacing));
     }
 
     /**
-     * Returns whether refocus mode is set on.
+     * Returns whether hdr plus mode is set on.
      */
-    public boolean isRefocusOn() {
-        String refocusOn = get(SettingsManager.SETTING_CAMERA_REFOCUS);
-        return refocusOn.equals(SettingsManager.VALUE_ON);
+    public boolean isHdrPlusOn() {
+        String hdrOn = get(SettingsManager.SETTING_CAMERA_HDR_PLUS);
+        return hdrOn.equals(SettingsManager.VALUE_ON);
     }
 
     /**
-     * Returns whether hdr plus mode is set on.
+     * Returns whether hdr mode is set on.
      */
-    public boolean isHdrPlusOn() {
+    public boolean isHdrOn() {
         String hdrOn = get(SettingsManager.SETTING_CAMERA_HDR);
         return hdrOn.equals(SettingsManager.VALUE_ON);
     }
 
-    //TODO: refactor this into a separate utils module.
+    /**
+     * Returns whether the app should return to hdr plus mode if possible.
+     */
+    public boolean requestsReturnToHdrPlus() {
+        return getBoolean(SettingsManager.SETTING_REQUEST_RETURN_HDR_PLUS);
+    }
 
     /**
-     * Get a String value from first the ListPreference, and if not found
-     * from the SettingsManager.
-     *
-     * This is a wrapper that adds backwards compatibility to views that
-     * rely on PreferenceGroups.
+     * Returns whether grid lines are set on.
+     */
+    public boolean areGridLinesOn() {
+        String gridLinesOn = get(SettingsManager.SETTING_CAMERA_GRID_LINES);
+        return gridLinesOn.equals(SettingsManager.VALUE_ON);
+    }
+
+    /**
+     * Returns whether pano orientation is horizontal.
+     */
+    public boolean isPanoOrientationHorizontal() {
+        String orientation = get(SettingsManager.SETTING_CAMERA_PANO_ORIENTATION);
+        String horizontal = mContext.getString(R.string.pano_orientation_horizontal);
+        return orientation.equals(horizontal);
+    }
+
+    // TODO: refactor this into a separate utils module.
+
+    /**
+     * Get a String value from first the ListPreference, and if not found from
+     * the SettingsManager. This is a wrapper that adds backwards compatibility
+     * to views that rely on PreferenceGroups.
      */
     public String getValueFromPreference(ListPreference pref) {
         String value = pref.getValue();
@@ -826,11 +1190,9 @@ public class SettingsManager {
     }
 
     /**
-     * Set a String value first from the ListPreference, and if unable
-     * from the SettingsManager.
-     *
-     * This is a wrapper that adds backwards compatibility to views that
-     * rely on PreferenceGroups.
+     * Set a String value first from the ListPreference, and if unable from the
+     * SettingsManager. This is a wrapper that adds backwards compatibility to
+     * views that rely on PreferenceGroups.
      */
     public void setValueFromPreference(ListPreference pref, String value) {
         boolean set = pref.setValue(value);
@@ -844,11 +1206,9 @@ public class SettingsManager {
 
     /**
      * Set a String value first from the ListPreference based on a
-     * ListPreference index, and if unable use the ListPreference key
-     * to set the value using the SettingsManager.
-     *
-     * This is a wrapper that adds backwards compatibility to views that
-     * rely on PreferenceGroups.
+     * ListPreference index, and if unable use the ListPreference key to set the
+     * value using the SettingsManager. This is a wrapper that adds backwards
+     * compatibility to views that rely on PreferenceGroups.
      */
     public void setValueIndexFromPreference(ListPreference pref, int index) {
         boolean set = pref.setValueIndex(index);
@@ -860,4 +1220,22 @@ public class SettingsManager {
             }
         }
     }
+
+    /**
+     * Sets the settings for whether location recording should be enabled or
+     * not. Also makes sure to pass on the change to the location manager.
+     */
+    public void setLocation(boolean on, LocationManager locationManager) {
+        setBoolean(SettingsManager.SETTING_RECORD_LOCATION, on);
+        locationManager.recordLocation(on);
+    }
+
+    /**
+     * Reads the current location recording settings and passes it on to the
+     * given location manager.
+     */
+    public void syncLocationManager(LocationManager locationManager) {
+        boolean value = getBoolean(SettingsManager.SETTING_RECORD_LOCATION);
+        locationManager.recordLocation(value);
+    }
 }