OSDN Git Service

Merge "Update processing API to support pause/resume." into gb-ub-photos-denali
[android-x86/packages-apps-Camera2.git] / src / com / android / camera / settings / SettingsManager.java
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.camera.settings;
18
19 import android.content.Context;
20 import android.content.SharedPreferences;
21 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
22 import android.preference.PreferenceManager;
23 import android.util.Log;
24 import android.util.SparseArray;
25
26 import com.android.camera.ListPreference;
27 import com.android.camera.app.AppController;
28 import com.android.camera.app.LocationManager;
29 import com.android.camera.util.CameraUtil;
30 import com.android.camera.util.SettingsHelper;
31 import com.android.camera2.R;
32
33 import java.util.ArrayList;
34 import java.util.List;
35
36 /**
37  * SettingsManager class provides an api for getting and setting both global and
38  * local SharedPreferences.
39  */
40 public class SettingsManager {
41     private static final String TAG = "SettingsManager";
42
43     private final Context mContext;
44     private final SharedPreferences mDefaultSettings;
45     private final SettingsCache mSettingsCache;
46     private SharedPreferences mGlobalSettings;
47     private SharedPreferences mCameraSettings;
48     private final SparseArray<SharedPreferences> mModuleSettings = new SparseArray<SharedPreferences>();
49     private SettingsCapabilities mCapabilities;
50
51     private int mCameraId = -1;
52     private final AppController mAppController;
53
54     /**
55      * General settings upgrade model:
56      *
57      *  On app upgrade, there are three main ways Settings can be stale/incorrect:
58      *  (1) if the type of a setting has changed.
59      *  (2) if a set value is no longer a member of the set of possible values.
60      *  (3) if the SharedPreferences backing file has changed for a setting.
61      *
62      *  Recovery strategies:
63      *  (1) catch the ClassCastException or NumberFormatException and try to do a
64      *      type conversion, or reset the setting to whatever default is valid.
65      *  (2) sanitize sets, and reset to default if set value is no longer valid.
66      *  (3) use the default value by virtue of the setting not yet being in the
67      *      new file.
68      *
69      * Special cases:
70      *
71      *  There are some settings which shouldn't be reset to default on upgrade if
72      *  possible.  We provide a callback which is executed only on strict upgrade.
73      *  This callback does special case upgrades to a subset of the settings.  This
74      *  contrasts  with the general upgrade strategies, which happen lazily, once a
75      *  setting is used.
76      *
77      * Removing obsolete key/value pairs:
78      *
79      *  This can be done in the strict upgrade callback.  The strict upgrade callback
80      *  should be idempotent, so it is important to leave removal code in the upgrade
81      *  callback so the key/value pairs are removed even if a user skips a version.
82      */
83     public interface StrictUpgradeCallback {
84         /**
85          * Will be executed in the SettingsManager constructor if the strict
86          * upgrade version counter has changed.
87          */
88         public void upgrade(SettingsManager settingsManager, int version);
89     }
90
91     /**
92      * Increment this value whenever a new StrictUpgradeCallback needs to
93      * be executed.  This defines upgrade behavior that should be executed
94      * strictly on app upgrades, when the upgrade behavior differs from the general,
95      * lazy upgrade strategies.
96      */
97     private static final int STRICT_UPGRADE_VERSION = 2;
98
99     /**
100      * A List of OnSettingChangedListener's, maintained to compare to new
101      * listeners and prevent duplicate registering.
102      */
103     private final List<OnSettingChangedListener> mListeners = new ArrayList<OnSettingChangedListener>();
104
105     /**
106      * A List of OnSharedPreferenceChangeListener's, maintained to hold pointers
107      * to actually registered listeners, so they can be unregistered.
108      */
109     private final List<OnSharedPreferenceChangeListener> mSharedPreferenceListeners = new ArrayList<OnSharedPreferenceChangeListener>();
110
111     public SettingsManager(Context context, AppController app, int nCameras,
112                            StrictUpgradeCallback upgradeCallback) {
113         mContext = context;
114         mAppController = app;
115
116         SettingsCache.ExtraSettings extraSettings = new SettingsHelper();
117         mSettingsCache = new SettingsCache(mContext, extraSettings);
118
119         mDefaultSettings = PreferenceManager.getDefaultSharedPreferences(context);
120         initGlobal();
121
122         if (upgradeCallback != null) {
123             // Check for a strict version upgrade.
124             int version = getInt(SETTING_STRICT_UPGRADE_VERSION);
125             if (STRICT_UPGRADE_VERSION != version) {
126                 upgradeCallback.upgrade(this, STRICT_UPGRADE_VERSION);
127             }
128             setInt(SETTING_STRICT_UPGRADE_VERSION, STRICT_UPGRADE_VERSION);
129         }
130     }
131
132     /**
133      * Initialize global SharedPreferences.
134      */
135     private void initGlobal() {
136         String globalKey = mContext.getPackageName() + "_preferences_camera";
137         mGlobalSettings = mContext.getSharedPreferences(globalKey, Context.MODE_PRIVATE);
138     }
139
140     /**
141      * Load and cache a module specific SharedPreferences.
142      */
143     public SharedPreferences getModulePreferences(int modeIndex) {
144         SharedPreferences sharedPreferences = mModuleSettings.get(modeIndex);
145         if (sharedPreferences == null) {
146             String moduleKey = mContext.getPackageName() + "_preferences_module_" + modeIndex;
147             sharedPreferences = mContext.getSharedPreferences(moduleKey, Context.MODE_PRIVATE);
148             mModuleSettings.put(modeIndex, sharedPreferences);
149         }
150         return sharedPreferences;
151     }
152
153     /**
154      * Initialize SharedPreferences for other cameras.
155      */
156     public void changeCamera(int cameraId, SettingsCapabilities capabilities) {
157         mCapabilities = capabilities;
158         mSettingsCache.setCapabilities(mCapabilities);
159
160         if (cameraId == mCameraId) {
161             return;
162         }
163
164         // We've changed camera ids, that means we need to flush the
165         // settings cache of settings dependent on SettingsCapabilities.
166         mSettingsCache.flush();
167
168         // Cache the camera id so we don't need to reload preferences
169         // if we're using the same camera.
170         mCameraId = cameraId;
171
172         String cameraKey = mContext.getPackageName() + "_preferences_" + cameraId;
173         mCameraSettings = mContext.getSharedPreferences(
174                 cameraKey, Context.MODE_PRIVATE);
175
176         for (OnSharedPreferenceChangeListener listener : mSharedPreferenceListeners) {
177             mCameraSettings.registerOnSharedPreferenceChangeListener(listener);
178         }
179     }
180
181     /**
182      * Interface with Camera Parameters and Modules.
183      */
184     public interface OnSettingChangedListener {
185         /**
186          * Called every time a SharedPreference has been changed.
187          */
188         public void onSettingChanged(SettingsManager settingsManager, int setting);
189     }
190
191     private OnSharedPreferenceChangeListener getSharedPreferenceListener(
192             final OnSettingChangedListener listener) {
193         return new OnSharedPreferenceChangeListener() {
194             @Override
195             public void onSharedPreferenceChanged(
196                     SharedPreferences sharedPreferences, String key) {
197                 Integer settingId = mSettingsCache.getId(key);
198                 if (settingId != null) {
199                     listener.onSettingChanged(SettingsManager.this, settingId);
200                 } else {
201                     Log.w(TAG, "Setting id from key=" + key + " is null");
202                 }
203             }
204         };
205     }
206
207     /**
208      * Add an OnSettingChangedListener to the SettingsManager, which will
209      * execute onSettingsChanged when any SharedPreference has been updated.
210      */
211     public void addListener(final OnSettingChangedListener listener) {
212         if (listener == null) {
213             throw new IllegalArgumentException("OnSettingChangedListener cannot be null.");
214         }
215
216         if (mListeners.contains(listener)) {
217             return;
218         }
219
220         mListeners.add(listener);
221         OnSharedPreferenceChangeListener sharedPreferenceListener =
222                 getSharedPreferenceListener(listener);
223         mSharedPreferenceListeners.add(sharedPreferenceListener);
224
225         if (mGlobalSettings != null) {
226             mGlobalSettings.registerOnSharedPreferenceChangeListener(sharedPreferenceListener);
227         }
228
229         if (mCameraSettings != null) {
230             mCameraSettings.registerOnSharedPreferenceChangeListener(sharedPreferenceListener);
231         }
232
233         if (mDefaultSettings != null) {
234             mDefaultSettings.registerOnSharedPreferenceChangeListener(sharedPreferenceListener);
235         }
236     }
237
238     /**
239      * Remove a specific SettingsListener. This should be done in onPause if a
240      * listener has been set.
241      */
242     public void removeListener(OnSettingChangedListener listener) {
243         if (listener == null) {
244             throw new IllegalArgumentException();
245         }
246
247         if (!mListeners.contains(listener)) {
248             return;
249         }
250
251         int index = mListeners.indexOf(listener);
252         mListeners.remove(listener);
253
254         // Get the reference to the actual OnSharedPreferenceChangeListener
255         // that was registered.
256         OnSharedPreferenceChangeListener sharedPreferenceListener =
257                 mSharedPreferenceListeners.get(index);
258         mSharedPreferenceListeners.remove(index);
259
260         if (mGlobalSettings != null) {
261             mGlobalSettings.unregisterOnSharedPreferenceChangeListener(
262                     sharedPreferenceListener);
263         }
264
265         if (mCameraSettings != null) {
266             mCameraSettings.unregisterOnSharedPreferenceChangeListener(
267                     sharedPreferenceListener);
268         }
269
270         if (mDefaultSettings != null) {
271             mDefaultSettings.unregisterOnSharedPreferenceChangeListener(
272                     sharedPreferenceListener);
273         }
274     }
275
276     /**
277      * Remove all OnSharedPreferenceChangedListener's. This should be done in
278      * onDestroy.
279      */
280     public void removeAllListeners() {
281         for (OnSharedPreferenceChangeListener listener : mSharedPreferenceListeners) {
282             if (mGlobalSettings != null) {
283                 mGlobalSettings.unregisterOnSharedPreferenceChangeListener(listener);
284             }
285
286             if (mCameraSettings != null) {
287                 mCameraSettings.unregisterOnSharedPreferenceChangeListener(listener);
288             }
289
290             if (mDefaultSettings != null) {
291                 mDefaultSettings.unregisterOnSharedPreferenceChangeListener(listener);
292             }
293         }
294         mSharedPreferenceListeners.clear();
295         mListeners.clear();
296     }
297
298     /**
299      * SettingsCapabilities defines constraints around settings that need to be
300      * queried from external sources, like the camera parameters. This interface
301      * is camera api agnostic.
302      */
303     public interface SettingsCapabilities {
304         /**
305          * Returns a dynamically calculated list of exposure values, based on
306          * the min and max exposure compensation supported by the camera device.
307          */
308         public String[] getSupportedExposureValues();
309
310         /**
311          * Returns a list of camera ids based on the number of cameras available
312          * on the device.
313          */
314         public String[] getSupportedCameraIds();
315     }
316
317     /**
318      * Get the camera id for which the SettingsManager has loaded camera
319      * specific preferences.
320      */
321     public int getRegisteredCameraId() {
322         return mCameraId;
323     }
324
325     // Manage individual settings.
326     public static final String VALUE_NONE = "none";
327     public static final String VALUE_ON = "on";
328     public static final String VALUE_OFF = "off";
329
330     public static final String TYPE_STRING = "string";
331     public static final String TYPE_BOOLEAN = "boolean";
332     public static final String TYPE_INTEGER = "integer";
333
334     public static final String SOURCE_DEFAULT = "default";
335     public static final String SOURCE_GLOBAL = "global";
336     public static final String SOURCE_CAMERA = "camera";
337     public static final String SOURCE_MODULE = "module";
338
339     public static final boolean FLUSH_ON = true;
340     public static final boolean FLUSH_OFF = false;
341
342     // For quick lookup from id to Setting.
343     public static final int SETTING_RECORD_LOCATION = 0;
344     public static final int SETTING_VIDEO_QUALITY_BACK = 1;
345     public static final int SETTING_VIDEO_QUALITY_FRONT = 2;
346     public static final int SETTING_VIDEO_TIME_LAPSE_FRAME_INTERVAL = 3;
347     public static final int SETTING_PICTURE_SIZE_BACK = 4;
348     public static final int SETTING_PICTURE_SIZE_FRONT = 5;
349     public static final int SETTING_JPEG_QUALITY = 6;
350     public static final int SETTING_FOCUS_MODE = 7;
351     public static final int SETTING_FLASH_MODE = 8;
352     public static final int SETTING_VIDEOCAMERA_FLASH_MODE = 9;
353     public static final int SETTING_WHITE_BALANCE = 10;
354     public static final int SETTING_SCENE_MODE = 11;
355     public static final int SETTING_EXPOSURE = 12;
356     public static final int SETTING_TIMER = 13;
357     public static final int SETTING_TIMER_SOUND_EFFECTS = 14;
358     public static final int SETTING_VIDEO_EFFECT = 15;
359     public static final int SETTING_CAMERA_ID = 16;
360     public static final int SETTING_CAMERA_HDR = 17;
361     public static final int SETTING_CAMERA_HDR_PLUS = 18;
362     public static final int SETTING_CAMERA_FIRST_USE_HINT_SHOWN = 19;
363     public static final int SETTING_VIDEO_FIRST_USE_HINT_SHOWN = 20;
364     public static final int SETTING_STARTUP_MODULE_INDEX = 21;
365     public static final int SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX = 22;
366     public static final int SETTING_KEY_CAMERA_MODULE_LAST_USED_INDEX = 23;
367     public static final int SETTING_CAMERA_PANO_ORIENTATION = 24;
368     public static final int SETTING_CAMERA_GRID_LINES = 25;
369     public static final int SETTING_RELEASE_DIALOG_LAST_SHOWN_VERSION = 26;
370     public static final int SETTING_FLASH_SUPPORTED_BACK_CAMERA = 27;
371     public static final int SETTING_STRICT_UPGRADE_VERSION = 28;
372     public static final int SETTING_FILMSTRIP_PEEK_ANIM_REMAINING_PLAY_TIMES_INDEX = 29;
373     // A boolean for requesting to return to HDR plus
374     // as soon as possible, if a user requests a setting/mode option
375     // that forces them to leave HDR plus.
376     public static final int SETTING_REQUEST_RETURN_HDR_PLUS = 30;
377
378     // Shared preference keys.
379     public static final String KEY_RECORD_LOCATION = "pref_camera_recordlocation_key";
380     public static final String KEY_VIDEO_QUALITY_BACK = "pref_video_quality_back_key";
381     public static final String KEY_VIDEO_QUALITY_FRONT = "pref_video_quality_front_key";
382     public static final String KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL =
383             "pref_video_time_lapse_frame_interval_key";
384     public static final String KEY_PICTURE_SIZE_BACK = "pref_camera_picturesize_back_key";
385     public static final String KEY_PICTURE_SIZE_FRONT = "pref_camera_picturesize_front_key";
386     public static final String KEY_JPEG_QUALITY = "pref_camera_jpegquality_key";
387     public static final String KEY_FOCUS_MODE = "pref_camera_focusmode_key";
388     public static final String KEY_FLASH_MODE = "pref_camera_flashmode_key";
389     public static final String KEY_VIDEOCAMERA_FLASH_MODE = "pref_camera_video_flashmode_key";
390     public static final String KEY_WHITE_BALANCE = "pref_camera_whitebalance_key";
391     public static final String KEY_SCENE_MODE = "pref_camera_scenemode_key";
392     public static final String KEY_EXPOSURE = "pref_camera_exposure_key";
393     public static final String KEY_TIMER = "pref_camera_timer_key";
394     public static final String KEY_TIMER_SOUND_EFFECTS = "pref_camera_timer_sound_key";
395     public static final String KEY_VIDEO_EFFECT = "pref_video_effect_key";
396     public static final String KEY_CAMERA_ID = "pref_camera_id_key";
397     public static final String KEY_CAMERA_HDR = "pref_camera_hdr_key";
398     public static final String KEY_CAMERA_HDR_PLUS = "pref_camera_hdr_plus_key";
399     public static final String KEY_CAMERA_FIRST_USE_HINT_SHOWN =
400             "pref_camera_first_use_hint_shown_key";
401     public static final String KEY_VIDEO_FIRST_USE_HINT_SHOWN =
402             "pref_video_first_use_hint_shown_key";
403     public static final String KEY_STARTUP_MODULE_INDEX = "camera.startup_module";
404     public static final String KEY_SHIMMY_REMAINING_PLAY_TIMES =
405             "pref_shimmy_remaining_play_times";
406     public static final String KEY_CAMERA_MODULE_LAST_USED =
407             "pref_camera_module_last_used_index";
408     public static final String KEY_CAMERA_PANO_ORIENTATION = "pref_camera_pano_orientation";
409     public static final String KEY_CAMERA_GRID_LINES = "pref_camera_grid_lines";
410     public static final String KEY_RELEASE_DIALOG_LAST_SHOWN_VERSION =
411             "pref_release_dialog_last_shown_version";
412     public static final String KEY_FLASH_SUPPORTED_BACK_CAMERA =
413             "pref_flash_supported_back_camera";
414     public static final String KEY_STRICT_UPGRADE_VERSION = "pref_strict_upgrade_version";
415     public static final String KEY_FILMSTRIP_PEEK_ANIM_REMAINING_PLAY_TIMES =
416             "pref_filmstrip_peek_anim_remaining_play_times";
417     public static final String KEY_REQUEST_RETURN_HDR_PLUS = "pref_request_return_hdr_plus";
418
419     public static final int WHITE_BALANCE_DEFAULT_INDEX = 2;
420
421     /**
422      * Defines a simple class for holding a the spec of a setting. This spec is
423      * used by the generic api methods to query and update a setting.
424      */
425     public static class Setting {
426         private final String mSource;
427         private final String mType;
428         private final String mDefault;
429         private final String mKey;
430         private final String[] mValues;
431         private final boolean mFlushOnCameraChange;
432
433         /**
434          * A constructor used to store a setting's profile.
435          */
436         public Setting(String source, String type, String defaultValue, String key,
437                 String[] values, boolean flushOnCameraChange) {
438             mSource = source;
439             mType = type;
440             mDefault = defaultValue;
441             mKey = key;
442             mValues = values;
443             mFlushOnCameraChange = flushOnCameraChange;
444         }
445
446         /**
447          * Returns the id of a SharedPreferences instance from which this
448          * Setting may be found. Possible values are {@link #SOURCE_DEFAULT},
449          * {@link #SOURCE_GLOBAL}, {@link #SOURCE_CAMERA}.
450          */
451         public String getSource() {
452             return mSource;
453         }
454
455         /**
456          * Returns the type of the setting stored in SharedPreferences. Possible
457          * values are {@link #TYPE_STRING}, {@link #TYPE_INTEGER},
458          * {@link #TYPE_BOOLEAN}.
459          */
460         public String getType() {
461             return mType;
462         }
463
464         /**
465          * Returns the default value of this setting.
466          */
467         public String getDefault() {
468             return mDefault;
469         }
470
471         /**
472          * Returns the SharedPreferences key for this setting.
473          */
474         public String getKey() {
475             return mKey;
476         }
477
478         /**
479          * Returns an array of possible String values for this setting. If this
480          * setting is not of type {@link #TYPE_STRING}, or it's not possible to
481          * generate the string values, this should return null;
482          */
483         public String[] getStringValues() {
484             return mValues;
485         }
486
487         /**
488          * Returns whether the setting should be flushed from the cache when the
489          * camera device has changed.
490          */
491         public boolean isFlushedOnCameraChanged() {
492             return mFlushOnCameraChange;
493         }
494     }
495
496     /**
497      * Get the SharedPreferences needed to query/update the setting.
498      */
499     public SharedPreferences getSettingSource(Setting setting) {
500         return getSettingSource(setting.getSource());
501     }
502
503     private SharedPreferences getSettingSource(String source) {
504         if (source.equals(SOURCE_DEFAULT)) {
505             return mDefaultSettings;
506         }
507         if (source.equals(SOURCE_GLOBAL)) {
508             return mGlobalSettings;
509         }
510         if (source.equals(SOURCE_CAMERA)) {
511             return mCameraSettings;
512         }
513         if (source.equals(SOURCE_MODULE)) {
514             int modeIndex = CameraUtil.getCameraModeParentModeId(
515                 mAppController.getCurrentModuleIndex(), mAppController.getAndroidContext());
516             return getModulePreferences(modeIndex);
517         }
518         return null;
519     }
520
521     /**
522      * Based on Setting id, finds the index of a Setting's String value in an
523      * array of possible String values. If the Setting is not of type String,
524      * this returns -1.
525      * <p>
526      * TODO: make this a supported api call for all types.
527      * </p>
528      */
529     public int getStringValueIndex(int id) {
530         Setting setting = mSettingsCache.get(id);
531         if (setting == null || !TYPE_STRING.equals(setting.getType())) {
532             return -1;
533         }
534         return getStringValueIndex(setting.getStringValues(), get(id));
535     }
536
537     private int getStringValueIndex(String[] possibleValues, String value) {
538         if (value != null) {
539             if (possibleValues != null) {
540                 for (int i = 0; i < possibleValues.length; i++) {
541                     if (value.equals(possibleValues[i])) {
542                         return i;
543                     }
544                 }
545             }
546         }
547         return -1;
548     }
549
550     /**
551      * Based on Setting id, sets a Setting's String value using the index into
552      * an array of possible String values. Fails to set a value if the index is
553      * out of bounds or the Setting is not of type String.
554      *
555      * @return Whether the value was set.
556      */
557     public boolean setStringValueIndex(int id, int index) {
558         Setting setting = mSettingsCache.get(id);
559         if (setting == null || setting.getType() != TYPE_STRING) {
560             return false;
561         }
562
563         String[] possibleValues = setting.getStringValues();
564         if (possibleValues != null) {
565             if (index >= 0 && index < possibleValues.length) {
566                 set(id, possibleValues[index]);
567                 return true;
568             }
569         }
570         return false;
571     }
572
573     /**
574      * Returns whether this Setting was last set as a String.
575      */
576     private boolean isString(int id, String source) {
577         Setting setting = mSettingsCache.get(id);
578         SharedPreferences preferences = getSettingSource(source);
579         try {
580             preferences.getString(setting.getKey(), null);
581             return true;
582         } catch (ClassCastException e) {
583             return false;
584         }
585     }
586
587     /**
588      * Returns whether this Setting was last set as a boolean.
589      */
590     private boolean isBoolean(int id, String source) {
591         Setting setting = mSettingsCache.get(id);
592         SharedPreferences preferences = getSettingSource(source);
593         try {
594             preferences.getBoolean(setting.getKey(), false);
595             return true;
596         } catch (ClassCastException e) {
597             return false;
598         }
599     }
600
601     /**
602      * Returns whether this Setting was last set as an Integer.
603      */
604     private boolean isInteger(int id, String source) {
605         Setting setting = mSettingsCache.get(id);
606         SharedPreferences preferences = getSettingSource(source);
607         try {
608             preferences.getInt(setting.getKey(), 0);
609             return true;
610         } catch (NumberFormatException e) {
611             return false;
612         }
613     }
614
615     /**
616      * Recover a Setting by converting it to a String if the type
617      * is known and the type conversion is successful, otherwise
618      * reset to the default.
619      */
620     private String recoverToString(int id, String source) {
621         String value;
622         try {
623             if (isBoolean(id, source)) {
624                 value = (getBoolean(id, source) ? VALUE_ON : VALUE_OFF);
625             } else if (isInteger(id, source)) {
626                 value = Integer.toString(getInt(id, source));
627             } else {
628                 throw new Exception();
629             }
630         } catch (Exception e) {
631             value = mSettingsCache.get(id).getDefault();
632         }
633         set(id, source, value);
634         return value;
635     }
636
637     /**
638      * Recover a Setting by converting it to a boolean if the type
639      * is known and the type conversion is successful, otherwise
640      * reset to the default.
641      */
642     private boolean recoverToBoolean(int id, String source) {
643         boolean value;
644         try {
645             if (isString(id, source)) {
646                 value = VALUE_ON.equals(get(id, source));
647             } else if (isInteger(id, source)) {
648                 value = getInt(id, source) != 0;
649             } else {
650                 throw new Exception();
651             }
652         } catch (Exception e) {
653             value = VALUE_ON.equals(mSettingsCache.get(id).getDefault());
654         }
655         setBoolean(id, source, value);
656         return value;
657     }
658
659     /**
660      * Recover a Setting by converting it to an Integer if the type
661      * is known and the type conversion is successful, otherwise
662      * reset to the default.
663      */
664     private int recoverToInteger(int id, String source) {
665         int value;
666         try {
667             if (isString(id, source)) {
668                 value = Integer.parseInt(get(id, source));
669             } else if (isBoolean(id, source)) {
670                 value = getBoolean(id, source) ? 1 : 0;
671             } else {
672                 throw new Exception();
673             }
674         } catch (Exception e) {
675             value = Integer.parseInt(mSettingsCache.get(id).getDefault());
676         }
677         setInt(id, value);
678         return value;
679     }
680
681     /**
682      * Check if a String value is in the set of possible values for a Setting.
683      * We only keep track of possible values for String types for now.
684      */
685     private String sanitize(Setting setting, String value) {
686         if (setting.getStringValues() != null &&
687                 getStringValueIndex(setting.getStringValues(), value) < 0) {
688             // If the set of possible values is not empty, and the value
689             // is not in the set of possible values, use the default, because
690             // the set of possible values probably changed.
691             return setting.getDefault();
692         }
693         return value;
694     }
695
696     /**
697      * Get a Setting's String value based on Setting id.
698      */
699     // TODO: rename to something more descriptive like getString.
700     public String get(int id) {
701         Setting setting = mSettingsCache.get(id);
702         return get(id, setting.getSource());
703     }
704
705     /**
706      * Get a Setting's String value based on Setting id and a source file id.
707      */
708     public String get(int id, String source) {
709         Setting setting = mSettingsCache.get(id);
710         SharedPreferences preferences = getSettingSource(source);
711         if (preferences != null) {
712             try {
713                 String value = preferences.getString(setting.getKey(), setting.getDefault());
714                 return sanitize(setting, value);
715             } catch (ClassCastException e) {
716                 // If the api defines this Setting as a String, but the
717                 // last set saved it as a different type, try to recover
718                 // the value, but if impossible reset to default.
719                 return recoverToString(id, source);
720             }
721         } else {
722             throw new IllegalStateException(
723                 "Setting source=" + source + " is unitialized.");
724         }
725     }
726
727     /**
728      * Get a Setting's boolean value based on Setting id.
729      */
730     public boolean getBoolean(int id) {
731         Setting setting = mSettingsCache.get(id);
732         return getBoolean(id, setting.getSource());
733     }
734
735     /**
736      * Get a Setting's boolean value based on a Setting id and a source file id.
737      */
738     public boolean getBoolean(int id, String source) {
739         Setting setting = mSettingsCache.get(id);
740         SharedPreferences preferences = getSettingSource(source);
741         boolean defaultValue = VALUE_ON.equals(setting.getDefault());
742         if (preferences != null) {
743             try {
744                 return preferences.getBoolean(setting.getKey(), defaultValue);
745             } catch (ClassCastException e) {
746                 // If the api defines this Setting as a boolean, but the
747                 // last set saved it as a different type, try to recover
748                 // the value, but if impossible reset to default.
749                 return recoverToBoolean(id, source);
750             }
751         } else {
752             throw new IllegalStateException(
753                 "Setting source=" + source + " is unitialized.");
754         }
755     }
756
757     /**
758      * Get a Setting's int value based on Setting id.
759      */
760     public int getInt(int id) {
761         Setting setting = mSettingsCache.get(id);
762         return getInt(id, setting.getSource());
763     }
764
765     /**
766      * Get a Setting's int value based on Setting id and a source file id.
767      */
768     public int getInt(int id, String source) {
769         Setting setting = mSettingsCache.get(id);
770         SharedPreferences preferences = getSettingSource(source);
771         int defaultValue = Integer.parseInt(setting.getDefault());
772         if (preferences != null) {
773             try {
774                 return preferences.getInt(setting.getKey(), defaultValue);
775             } catch (NumberFormatException e) {
776                 // If the api defines this Setting as an Integer, but the
777                 // last set saved it as a different type, try to recover
778                 // the value, but if impossible reset to default.
779                 return recoverToInteger(id, source);
780             }
781         } else {
782             throw new IllegalStateException(
783                 "Setting source=" + source + " is unitialized.");
784         }
785     }
786
787     /**
788      * Set a Setting with a String value based on Setting id.
789      */
790     // TODO: rename to something more descriptive.
791     public void set(int id, String value) {
792         Setting setting = mSettingsCache.get(id);
793         set(id, setting.getSource(), value);
794     }
795
796     /**
797      * Set a Setting with a String value based on Setting id and a source file id.
798      */
799     public void set(int id, String source, String value) {
800         Setting setting = mSettingsCache.get(id);
801         value = sanitize(setting, value);
802         SharedPreferences preferences = getSettingSource(source);
803         if (preferences != null) {
804             preferences.edit().putString(setting.getKey(), value).apply();
805         } else {
806             throw new IllegalStateException(
807                 "Setting source=" + source + " is unitialized.");
808         }
809     }
810
811     /**
812      * Set a Setting with a boolean value based on Setting id.
813      */
814     public void setBoolean(int id, boolean value) {
815         Setting setting = mSettingsCache.get(id);
816         setBoolean(id, setting.getSource(), value);
817     }
818     /**
819      * Set a Setting with a boolean value based on Setting id and a source file id.
820      */
821     public void setBoolean(int id, String source, boolean value) {
822         Setting setting = mSettingsCache.get(id);
823         SharedPreferences preferences = getSettingSource(source);
824         if (preferences != null) {
825             preferences.edit().putBoolean(setting.getKey(), value).apply();
826         } else {
827             throw new IllegalStateException(
828                 "Setting source=" + source + " is unitialized.");
829         }
830     }
831
832     /**
833      * Set a Setting with an int value based on Setting id.
834      */
835     public void setInt(int id, int value) {
836         Setting setting = mSettingsCache.get(id);
837         setInt(id, setting.getSource(), value);
838     }
839
840     /**
841      * Set a Setting with an int value based on Setting id and a source file id.
842      */
843     public void setInt(int id, String source, int value) {
844         Setting setting = mSettingsCache.get(id);
845         SharedPreferences preferences = getSettingSource(source);
846         if (preferences != null) {
847             preferences.edit().putInt(setting.getKey(), value).apply();
848         } else {
849             throw new IllegalStateException(
850                 "Setting source=" + source + " is unitialized.");
851         }
852     }
853
854     /**
855      * Check if a Setting has ever been set based on Setting id.
856      */
857     public boolean isSet(int id) {
858         Setting setting = mSettingsCache.get(id);
859         return isSet(id, setting.getSource());
860     }
861
862     /**
863      * Check if a Setting has ever been set based on Setting id and a source file id.
864      */
865     public boolean isSet(int id, String source) {
866         Setting setting = mSettingsCache.get(id);
867         SharedPreferences preferences = getSettingSource(source);
868         if (preferences != null) {
869             return preferences.contains(setting.getKey());
870         } else {
871             throw new IllegalStateException(
872                 "Setting source=" + setting.getSource() + " is unitialized.");
873         }
874     }
875
876     /**
877      * Set a Setting to its default value based on Setting id.
878      */
879     public void setDefault(int id) {
880         Setting setting = mSettingsCache.get(id);
881         SharedPreferences preferences = getSettingSource(setting);
882         if (preferences != null) {
883             preferences.edit().putString(setting.getKey(), setting.getDefault());
884         } else {
885             throw new IllegalStateException(
886                 "Setting source=" + setting.getSource() + " is unitialized.");
887         }
888     }
889
890     /**
891      * Check if a Setting is set to its default value.
892      */
893     public boolean isDefault(int id) {
894         Setting setting = mSettingsCache.get(id);
895         SharedPreferences preferences = getSettingSource(setting);
896         if (preferences != null) {
897             String type = setting.getType();
898             if (TYPE_STRING.equals(type)) {
899                 String value = get(id);
900                 return (value.equals(setting.getDefault()));
901             } else if (TYPE_BOOLEAN.equals(type)) {
902                 boolean value = getBoolean(id);
903                 boolean defaultValue = VALUE_ON.equals(setting.getDefault());
904                 return (value == defaultValue);
905             } else if (TYPE_INTEGER.equals(type)) {
906                 int value = getInt(id);
907                 int defaultValue = Integer.parseInt(setting.getDefault());
908                 return (value == defaultValue);
909             } else {
910                 throw new IllegalArgumentException("Type " + type + " is not known.");
911             }
912         } else {
913             throw new IllegalStateException(
914                 "Setting source=" + setting.getSource() + " is unitialized.");
915         }
916     }
917
918     /**
919      * Remove a Setting from SharedPreferences.
920      */
921     public void remove(int id) {
922         Setting setting = mSettingsCache.get(id);
923         SharedPreferences preferences = getSettingSource(setting);
924         if (preferences != null) {
925             preferences.edit().remove(setting.getKey()).apply();
926         } else {
927             throw new IllegalStateException(
928                 "Setting source=" + setting.getSource() + " is unitialized.");
929         }
930     }
931
932     public static Setting getLocationSetting(Context context) {
933         String defaultValue = context.getString(R.string.setting_none_value);
934         String[] values = null;
935         return new Setting(SOURCE_DEFAULT, TYPE_BOOLEAN, defaultValue, KEY_RECORD_LOCATION,
936                 values, FLUSH_OFF);
937     }
938
939     public static Setting getPictureSizeBackSetting(Context context) {
940         String defaultValue = null;
941         String[] values = context.getResources().getStringArray(
942                 R.array.pref_camera_picturesize_entryvalues);
943         return new Setting(SOURCE_DEFAULT, TYPE_STRING, defaultValue, KEY_PICTURE_SIZE_BACK,
944                 values, FLUSH_OFF);
945     }
946
947     public static Setting getPictureSizeFrontSetting(Context context) {
948         String defaultValue = null;
949         String[] values = context.getResources().getStringArray(
950                 R.array.pref_camera_picturesize_entryvalues);
951         return new Setting(SOURCE_DEFAULT, TYPE_STRING, defaultValue, KEY_PICTURE_SIZE_FRONT,
952                 values, FLUSH_OFF);
953     }
954
955     public static Setting getDefaultCameraIdSetting(Context context,
956             SettingsCapabilities capabilities) {
957         String defaultValue = context.getString(R.string.pref_camera_id_default);
958         String[] values = null;
959         if (capabilities != null) {
960             values = capabilities.getSupportedCameraIds();
961         }
962         return new Setting(SOURCE_MODULE, TYPE_STRING, defaultValue, KEY_CAMERA_ID,
963                 values, FLUSH_ON);
964     }
965
966     public static Setting getWhiteBalanceSetting(Context context) {
967         String defaultValue = context.getString(R.string.pref_camera_whitebalance_default);
968         String[] values = context.getResources().getStringArray(
969                 R.array.pref_camera_whitebalance_entryvalues);
970         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue, KEY_WHITE_BALANCE,
971                 values, FLUSH_OFF);
972     }
973
974     public static Setting getHdrSetting(Context context) {
975         String defaultValue = context.getString(R.string.pref_camera_hdr_default);
976         String[] values = context.getResources().getStringArray(
977                 R.array.pref_camera_hdr_entryvalues);
978         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_CAMERA_HDR,
979                 values, FLUSH_OFF);
980     }
981
982     public static Setting getHdrPlusSetting(Context context) {
983         String defaultValue = context.getString(R.string.pref_camera_hdr_plus_default);
984         String[] values = context.getResources().getStringArray(
985                 R.array.pref_camera_hdr_plus_entryvalues);
986         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_CAMERA_HDR_PLUS,
987                 values, FLUSH_OFF);
988     }
989
990     public static Setting getSceneModeSetting(Context context) {
991         String defaultValue = context.getString(R.string.pref_camera_scenemode_default);
992         String[] values = context.getResources().getStringArray(
993                 R.array.pref_camera_scenemode_entryvalues);
994         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue, KEY_SCENE_MODE,
995                 values, FLUSH_OFF);
996     }
997
998     public static Setting getFlashSetting(Context context) {
999         String defaultValue = context.getString(R.string.pref_camera_flashmode_default);
1000         String[] values = context.getResources().getStringArray(
1001                 R.array.pref_camera_flashmode_entryvalues);
1002         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue, KEY_FLASH_MODE,
1003                 values, FLUSH_OFF);
1004     }
1005
1006     public static Setting getExposureSetting(Context context,
1007             SettingsCapabilities capabilities) {
1008         String defaultValue = context.getString(R.string.pref_exposure_default);
1009         String[] values = null;
1010         if (capabilities != null) {
1011             values = capabilities.getSupportedExposureValues();
1012         }
1013         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue, KEY_EXPOSURE,
1014                 values, FLUSH_ON);
1015     }
1016
1017     public static Setting getHintSetting(Context context) {
1018         String defaultValue = context.getString(R.string.setting_on_value);
1019         String[] values = null;
1020         return new Setting(SOURCE_GLOBAL, TYPE_BOOLEAN, defaultValue,
1021                 KEY_CAMERA_FIRST_USE_HINT_SHOWN, values, FLUSH_OFF);
1022     }
1023
1024     public static Setting getFocusModeSetting(Context context) {
1025         String defaultValue = null;
1026         String[] values = context.getResources().getStringArray(
1027                 R.array.pref_camera_focusmode_entryvalues);
1028         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue, KEY_FOCUS_MODE,
1029                 values, FLUSH_OFF);
1030     }
1031
1032     public static Setting getTimerSetting(Context context) {
1033         String defaultValue = context.getString(R.string.pref_camera_timer_default);
1034         String[] values = null; // TODO: get the values dynamically.
1035         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_TIMER,
1036                 values, FLUSH_OFF);
1037     }
1038
1039     public static Setting getTimerSoundSetting(Context context) {
1040         String defaultValue = context.getString(R.string.pref_camera_timer_sound_default);
1041         String[] values = context.getResources().getStringArray(
1042                 R.array.pref_camera_timer_sound_entryvalues);
1043         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_TIMER_SOUND_EFFECTS,
1044                 values, FLUSH_OFF);
1045     }
1046
1047     public static Setting getVideoQualityBackSetting(Context context) {
1048         String defaultValue = context.getString(R.string.pref_video_quality_default);
1049         String[] values = context.getResources().getStringArray(
1050                 R.array.pref_video_quality_entryvalues);
1051         return new Setting(SOURCE_DEFAULT, TYPE_STRING, defaultValue, KEY_VIDEO_QUALITY_BACK,
1052                 values, FLUSH_OFF);
1053     }
1054
1055     public static Setting getVideoQualityFrontSetting(Context context) {
1056         String defaultValue = context.getString(R.string.pref_video_quality_default);
1057         String[] values = context.getResources().getStringArray(
1058                 R.array.pref_video_quality_entryvalues);
1059         return new Setting(SOURCE_DEFAULT, TYPE_STRING, defaultValue, KEY_VIDEO_QUALITY_FRONT,
1060                 values, FLUSH_OFF);
1061     }
1062
1063     public static Setting getTimeLapseFrameIntervalSetting(Context context) {
1064         String defaultValue = context.getString(
1065                 R.string.pref_video_time_lapse_frame_interval_default);
1066         String[] values = context.getResources().getStringArray(
1067                 R.array.pref_video_time_lapse_frame_interval_entryvalues);
1068         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue,
1069                 KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL, values, FLUSH_OFF);
1070     }
1071
1072     public static Setting getJpegQualitySetting(Context context) {
1073         String defaultValue = context.getString(
1074                 R.string.pref_camera_jpeg_quality_normal);
1075         String[] values = context.getResources().getStringArray(
1076                 R.array.pref_camera_jpeg_quality_entryvalues);
1077         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue, KEY_JPEG_QUALITY,
1078                 values, FLUSH_OFF);
1079     }
1080
1081     public static Setting getVideoFlashSetting(Context context) {
1082         String defaultValue = context.getString(R.string.pref_camera_video_flashmode_default);
1083         String[] values = context.getResources().getStringArray(
1084                 R.array.pref_camera_video_flashmode_entryvalues);
1085         return new Setting(SOURCE_CAMERA, TYPE_STRING, defaultValue,
1086                 KEY_VIDEOCAMERA_FLASH_MODE, values, FLUSH_OFF);
1087     }
1088
1089     public static Setting getVideoEffectSetting(Context context) {
1090         String defaultValue = context.getString(R.string.pref_video_effect_default);
1091         String[] values = context.getResources().getStringArray(
1092                 R.array.pref_video_effect_entryvalues);
1093         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue, KEY_VIDEO_EFFECT,
1094                 values, FLUSH_OFF);
1095     }
1096
1097     public static Setting getHintVideoSetting(Context context) {
1098         String defaultValue = context.getString(R.string.setting_on_value);
1099         String[] values = null;
1100         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue,
1101                 KEY_VIDEO_FIRST_USE_HINT_SHOWN, values, FLUSH_OFF);
1102     }
1103
1104     public static Setting getStartupModuleSetting(Context context) {
1105         String defaultValue = context.getString(R.string.pref_camera_startup_index_default);
1106         String[] values = null;
1107         return new Setting(SOURCE_DEFAULT, TYPE_INTEGER, defaultValue,
1108                 KEY_STARTUP_MODULE_INDEX, values, FLUSH_OFF);
1109     }
1110
1111     public static Setting getShimmyRemainingTimesSetting(Context context) {
1112         String defaultValue = context.getString(R.string.pref_shimmy_play_times);
1113         return new Setting(SOURCE_DEFAULT, TYPE_INTEGER, defaultValue,
1114                 KEY_SHIMMY_REMAINING_PLAY_TIMES, null, FLUSH_OFF);
1115     }
1116
1117     public static Setting getLastUsedCameraModule(Context context) {
1118         String defaultValue = Integer.toString(context.getResources()
1119                 .getInteger(R.integer.camera_mode_photo));
1120         return new Setting(SOURCE_DEFAULT, TYPE_INTEGER, defaultValue,
1121                 KEY_CAMERA_MODULE_LAST_USED, null, FLUSH_OFF);
1122     }
1123
1124     public static Setting getPanoOrientationSetting(Context context) {
1125         String defaultValue = context.getString(R.string.pano_orientation_horizontal);
1126         String[] values = context.getResources().getStringArray(
1127                 R.array.pref_camera_pano_orientation_entryvalues);
1128         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue,
1129                 KEY_CAMERA_PANO_ORIENTATION, values, FLUSH_OFF);
1130     }
1131
1132     public static Setting getGridLinesSetting(Context context) {
1133         String defaultValue = context.getString(R.string.setting_off_value);
1134         String[] values = context.getResources().getStringArray(
1135                 R.array.pref_camera_gridlines_entryvalues);
1136         return new Setting(SOURCE_GLOBAL, TYPE_STRING, defaultValue,
1137                 KEY_CAMERA_GRID_LINES, values, FLUSH_OFF);
1138     }
1139
1140     public static Setting getReleaseDialogLastShownVersionSetting(Context context) {
1141         return new Setting(SOURCE_DEFAULT, TYPE_STRING, null,
1142                 KEY_RELEASE_DIALOG_LAST_SHOWN_VERSION, null, FLUSH_OFF);
1143     }
1144
1145     public static Setting getFlashSupportedBackCameraSetting(Context context) {
1146         String defaultValue = context.getString(R.string.setting_none_value);
1147         return new Setting(SOURCE_GLOBAL, TYPE_BOOLEAN, defaultValue,
1148                 KEY_FLASH_SUPPORTED_BACK_CAMERA, null, FLUSH_OFF);
1149     }
1150
1151     public static Setting getStrictUpgradeVersionSetting(Context context) {
1152         String defaultValue = "0";
1153         return new Setting(SOURCE_DEFAULT, TYPE_INTEGER, defaultValue,
1154                 KEY_STRICT_UPGRADE_VERSION, null, FLUSH_OFF);
1155     }
1156
1157     public static Setting getPeekAnimRemainingTimesSetting(Context context) {
1158         String defaultValue = context.getString(R.string.pref_filmstrip_peek_anim_play_times);
1159         return new Setting(SOURCE_DEFAULT, TYPE_INTEGER, defaultValue,
1160                 KEY_FILMSTRIP_PEEK_ANIM_REMAINING_PLAY_TIMES, null, FLUSH_OFF);
1161     }
1162
1163     public static Setting getRequestReturnHdrPlusSetting(Context context) {
1164         String defaultValue = context.getString(R.string.setting_none_value);
1165         return new Setting(SOURCE_MODULE, TYPE_BOOLEAN, VALUE_OFF,
1166                 KEY_REQUEST_RETURN_HDR_PLUS, null, FLUSH_OFF);
1167     }
1168
1169     // Utilities.
1170
1171     /**
1172      * Returns whether the camera has been set to back facing in settings.
1173      */
1174     public boolean isCameraBackFacing() {
1175         String cameraFacing = get(SETTING_CAMERA_ID);
1176         String backFacing = mContext.getString(R.string.pref_camera_id_default);
1177         return (Integer.parseInt(cameraFacing) == Integer.parseInt(backFacing));
1178     }
1179
1180     /**
1181      * Returns whether hdr plus mode is set on.
1182      */
1183     public boolean isHdrPlusOn() {
1184         String hdrOn = get(SettingsManager.SETTING_CAMERA_HDR);
1185         return hdrOn.equals(SettingsManager.VALUE_ON);
1186     }
1187
1188     /**
1189      * Returns whether the app should return to hdr plus mode if possible.
1190      */
1191     public boolean requestsReturnToHdrPlus() {
1192         return getBoolean(SettingsManager.SETTING_REQUEST_RETURN_HDR_PLUS);
1193     }
1194
1195     /**
1196      * Returns whether grid lines are set on.
1197      */
1198     public boolean areGridLinesOn() {
1199         String gridLinesOn = get(SettingsManager.SETTING_CAMERA_GRID_LINES);
1200         return gridLinesOn.equals(SettingsManager.VALUE_ON);
1201     }
1202
1203     /**
1204      * Returns whether pano orientation is horizontal.
1205      */
1206     public boolean isPanoOrientationHorizontal() {
1207         String orientation = get(SettingsManager.SETTING_CAMERA_PANO_ORIENTATION);
1208         String horizontal = mContext.getString(R.string.pano_orientation_horizontal);
1209         return orientation.equals(horizontal);
1210     }
1211
1212     // TODO: refactor this into a separate utils module.
1213
1214     /**
1215      * Get a String value from first the ListPreference, and if not found from
1216      * the SettingsManager. This is a wrapper that adds backwards compatibility
1217      * to views that rely on PreferenceGroups.
1218      */
1219     public String getValueFromPreference(ListPreference pref) {
1220         String value = pref.getValue();
1221         if (value == null) {
1222             Integer id = mSettingsCache.getId(pref.getKey());
1223             if (id == null) {
1224                 return null;
1225             }
1226             value = get(id);
1227         }
1228         return value;
1229     }
1230
1231     /**
1232      * Set a String value first from the ListPreference, and if unable from the
1233      * SettingsManager. This is a wrapper that adds backwards compatibility to
1234      * views that rely on PreferenceGroups.
1235      */
1236     public void setValueFromPreference(ListPreference pref, String value) {
1237         boolean set = pref.setValue(value);
1238         if (!set) {
1239             Integer id = mSettingsCache.getId(pref.getKey());
1240             if (id != null) {
1241                 set(id, value);
1242             }
1243         }
1244     }
1245
1246     /**
1247      * Set a String value first from the ListPreference based on a
1248      * ListPreference index, and if unable use the ListPreference key to set the
1249      * value using the SettingsManager. This is a wrapper that adds backwards
1250      * compatibility to views that rely on PreferenceGroups.
1251      */
1252     public void setValueIndexFromPreference(ListPreference pref, int index) {
1253         boolean set = pref.setValueIndex(index);
1254         if (!set) {
1255             Integer id = mSettingsCache.getId(pref.getKey());
1256             if (id != null) {
1257                 String value = pref.getValueAtIndex(index);
1258                 set(id, value);
1259             }
1260         }
1261     }
1262
1263     /**
1264      * Sets the settings for whether location recording should be enabled or
1265      * not. Also makes sure to pass on the change to the location manager.
1266      */
1267     public void setLocation(boolean on, LocationManager locationManager) {
1268         setBoolean(SettingsManager.SETTING_RECORD_LOCATION, on);
1269         locationManager.recordLocation(on);
1270     }
1271
1272     /**
1273      * Reads the current location recording settings and passes it on to the
1274      * given location manager.
1275      */
1276     public void syncLocationManager(LocationManager locationManager) {
1277         boolean value = getBoolean(SettingsManager.SETTING_RECORD_LOCATION);
1278         locationManager.recordLocation(value);
1279     }
1280 }