1 /* 2 * Copyright (C) 2010 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; 18 19 import android.app.backup.BackupManager; 20 import android.content.Context; 21 import android.content.SharedPreferences; 22 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 23 import android.preference.PreferenceManager; 24 25 import java.util.Map; 26 import java.util.Set; 27 import java.util.WeakHashMap; 28 import java.util.concurrent.CopyOnWriteArrayList; 29 30 public class ComboPreferences implements 31 SharedPreferences, 32 OnSharedPreferenceChangeListener { 33 private SharedPreferences mPrefGlobal; // global preferences 34 private SharedPreferences mPrefLocal; // per-camera preferences 35 private BackupManager mBackupManager; 36 private CopyOnWriteArrayList<OnSharedPreferenceChangeListener> mListeners; 37 private static WeakHashMap<Context, ComboPreferences> sMap = 38 new WeakHashMap<Context, ComboPreferences>(); 39 ComboPreferences(Context context)40 public ComboPreferences(Context context) { 41 mPrefGlobal = context.getSharedPreferences( 42 getGlobalSharedPreferencesName(context), Context.MODE_PRIVATE); 43 mPrefGlobal.registerOnSharedPreferenceChangeListener(this); 44 45 synchronized (sMap) { 46 sMap.put(context, this); 47 } 48 mBackupManager = new BackupManager(context); 49 mListeners = new CopyOnWriteArrayList<OnSharedPreferenceChangeListener>(); 50 51 // The global preferences was previously stored in the default 52 // shared preferences file. They should be stored in the camera-specific 53 // shared preferences file so we can backup them solely. 54 SharedPreferences oldprefs = 55 PreferenceManager.getDefaultSharedPreferences(context); 56 if (!mPrefGlobal.contains(CameraSettings.KEY_VERSION) 57 && oldprefs.contains(CameraSettings.KEY_VERSION)) { 58 moveGlobalPrefsFrom(oldprefs); 59 } 60 } 61 get(Context context)62 public static ComboPreferences get(Context context) { 63 synchronized (sMap) { 64 return sMap.get(context); 65 } 66 } 67 getLocalSharedPreferencesName( Context context, int cameraId)68 private static String getLocalSharedPreferencesName( 69 Context context, int cameraId) { 70 return context.getPackageName() + "_preferences_" + cameraId; 71 } 72 getGlobalSharedPreferencesName(Context context)73 private static String getGlobalSharedPreferencesName(Context context) { 74 return context.getPackageName() + "_preferences_camera"; 75 } 76 movePrefFrom( Map<String, ?> m, String key, SharedPreferences src)77 private void movePrefFrom( 78 Map<String, ?> m, String key, SharedPreferences src) { 79 if (m.containsKey(key)) { 80 Object v = m.get(key); 81 if (v instanceof String) { 82 mPrefGlobal.edit().putString(key, (String) v).apply(); 83 } else if (v instanceof Integer) { 84 mPrefGlobal.edit().putInt(key, (Integer) v).apply(); 85 } else if (v instanceof Long) { 86 mPrefGlobal.edit().putLong(key, (Long) v).apply(); 87 } else if (v instanceof Float) { 88 mPrefGlobal.edit().putFloat(key, (Float) v).apply(); 89 } else if (v instanceof Boolean) { 90 mPrefGlobal.edit().putBoolean(key, (Boolean) v).apply(); 91 } 92 src.edit().remove(key).apply(); 93 } 94 } 95 moveGlobalPrefsFrom(SharedPreferences src)96 private void moveGlobalPrefsFrom(SharedPreferences src) { 97 Map<String, ?> prefMap = src.getAll(); 98 movePrefFrom(prefMap, CameraSettings.KEY_VERSION, src); 99 movePrefFrom(prefMap, CameraSettings.KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL, src); 100 movePrefFrom(prefMap, CameraSettings.KEY_CAMERA_ID, src); 101 movePrefFrom(prefMap, CameraSettings.KEY_RECORD_LOCATION, src); 102 movePrefFrom(prefMap, CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, src); 103 movePrefFrom(prefMap, CameraSettings.KEY_VIDEO_FIRST_USE_HINT_SHOWN, src); 104 movePrefFrom(prefMap, CameraSettings.KEY_VIDEO_EFFECT, src); 105 } 106 getSharedPreferencesNames(Context context)107 public static String[] getSharedPreferencesNames(Context context) { 108 int numOfCameras = CameraHolder.instance().getNumberOfCameras(); 109 String prefNames[] = new String[numOfCameras + 1]; 110 prefNames[0] = getGlobalSharedPreferencesName(context); 111 for (int i = 0; i < numOfCameras; i++) { 112 prefNames[i + 1] = getLocalSharedPreferencesName(context, i); 113 } 114 return prefNames; 115 } 116 117 // Sets the camera id and reads its preferences. Each camera has its own 118 // preferences. setLocalId(Context context, int cameraId)119 public void setLocalId(Context context, int cameraId) { 120 String prefName = getLocalSharedPreferencesName(context, cameraId); 121 if (mPrefLocal != null) { 122 mPrefLocal.unregisterOnSharedPreferenceChangeListener(this); 123 } 124 mPrefLocal = context.getSharedPreferences( 125 prefName, Context.MODE_PRIVATE); 126 mPrefLocal.registerOnSharedPreferenceChangeListener(this); 127 } 128 getGlobal()129 public SharedPreferences getGlobal() { 130 return mPrefGlobal; 131 } 132 getLocal()133 public SharedPreferences getLocal() { 134 return mPrefLocal; 135 } 136 137 @Override getAll()138 public Map<String, ?> getAll() { 139 throw new UnsupportedOperationException(); // Can be implemented if needed. 140 } 141 isGlobal(String key)142 private static boolean isGlobal(String key) { 143 return key.equals(CameraSettings.KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL) 144 || key.equals(CameraSettings.KEY_CAMERA_ID) 145 || key.equals(CameraSettings.KEY_RECORD_LOCATION) 146 || key.equals(CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN) 147 || key.equals(CameraSettings.KEY_VIDEO_FIRST_USE_HINT_SHOWN) 148 || key.equals(CameraSettings.KEY_VIDEO_EFFECT) 149 || key.equals(CameraSettings.KEY_TIMER) 150 || key.equals(CameraSettings.KEY_TIMER_SOUND_EFFECTS); 151 } 152 153 @Override getString(String key, String defValue)154 public String getString(String key, String defValue) { 155 if (isGlobal(key) || !mPrefLocal.contains(key)) { 156 return mPrefGlobal.getString(key, defValue); 157 } else { 158 return mPrefLocal.getString(key, defValue); 159 } 160 } 161 162 @Override getInt(String key, int defValue)163 public int getInt(String key, int defValue) { 164 if (isGlobal(key) || !mPrefLocal.contains(key)) { 165 return mPrefGlobal.getInt(key, defValue); 166 } else { 167 return mPrefLocal.getInt(key, defValue); 168 } 169 } 170 171 @Override getLong(String key, long defValue)172 public long getLong(String key, long defValue) { 173 if (isGlobal(key) || !mPrefLocal.contains(key)) { 174 return mPrefGlobal.getLong(key, defValue); 175 } else { 176 return mPrefLocal.getLong(key, defValue); 177 } 178 } 179 180 @Override getFloat(String key, float defValue)181 public float getFloat(String key, float defValue) { 182 if (isGlobal(key) || !mPrefLocal.contains(key)) { 183 return mPrefGlobal.getFloat(key, defValue); 184 } else { 185 return mPrefLocal.getFloat(key, defValue); 186 } 187 } 188 189 @Override getBoolean(String key, boolean defValue)190 public boolean getBoolean(String key, boolean defValue) { 191 if (isGlobal(key) || !mPrefLocal.contains(key)) { 192 return mPrefGlobal.getBoolean(key, defValue); 193 } else { 194 return mPrefLocal.getBoolean(key, defValue); 195 } 196 } 197 198 // This method is not used. 199 @Override getStringSet(String key, Set<String> defValues)200 public Set<String> getStringSet(String key, Set<String> defValues) { 201 throw new UnsupportedOperationException(); 202 } 203 204 @Override contains(String key)205 public boolean contains(String key) { 206 if (mPrefLocal.contains(key)) return true; 207 if (mPrefGlobal.contains(key)) return true; 208 return false; 209 } 210 211 private class MyEditor implements Editor { 212 private Editor mEditorGlobal; 213 private Editor mEditorLocal; 214 MyEditor()215 MyEditor() { 216 mEditorGlobal = mPrefGlobal.edit(); 217 mEditorLocal = mPrefLocal.edit(); 218 } 219 220 @Override commit()221 public boolean commit() { 222 boolean result1 = mEditorGlobal.commit(); 223 boolean result2 = mEditorLocal.commit(); 224 return result1 && result2; 225 } 226 227 @Override apply()228 public void apply() { 229 mEditorGlobal.apply(); 230 mEditorLocal.apply(); 231 } 232 233 // Note: clear() and remove() affects both local and global preferences. 234 @Override clear()235 public Editor clear() { 236 mEditorGlobal.clear(); 237 mEditorLocal.clear(); 238 return this; 239 } 240 241 @Override remove(String key)242 public Editor remove(String key) { 243 mEditorGlobal.remove(key); 244 mEditorLocal.remove(key); 245 return this; 246 } 247 248 @Override putString(String key, String value)249 public Editor putString(String key, String value) { 250 if (isGlobal(key)) { 251 mEditorGlobal.putString(key, value); 252 } else { 253 mEditorLocal.putString(key, value); 254 } 255 return this; 256 } 257 258 @Override putInt(String key, int value)259 public Editor putInt(String key, int value) { 260 if (isGlobal(key)) { 261 mEditorGlobal.putInt(key, value); 262 } else { 263 mEditorLocal.putInt(key, value); 264 } 265 return this; 266 } 267 268 @Override putLong(String key, long value)269 public Editor putLong(String key, long value) { 270 if (isGlobal(key)) { 271 mEditorGlobal.putLong(key, value); 272 } else { 273 mEditorLocal.putLong(key, value); 274 } 275 return this; 276 } 277 278 @Override putFloat(String key, float value)279 public Editor putFloat(String key, float value) { 280 if (isGlobal(key)) { 281 mEditorGlobal.putFloat(key, value); 282 } else { 283 mEditorLocal.putFloat(key, value); 284 } 285 return this; 286 } 287 288 @Override putBoolean(String key, boolean value)289 public Editor putBoolean(String key, boolean value) { 290 if (isGlobal(key)) { 291 mEditorGlobal.putBoolean(key, value); 292 } else { 293 mEditorLocal.putBoolean(key, value); 294 } 295 return this; 296 } 297 298 // This method is not used. 299 @Override putStringSet(String key, Set<String> values)300 public Editor putStringSet(String key, Set<String> values) { 301 throw new UnsupportedOperationException(); 302 } 303 } 304 305 // Note the remove() and clear() of the returned Editor may not work as 306 // expected because it doesn't touch the global preferences at all. 307 @Override edit()308 public Editor edit() { 309 return new MyEditor(); 310 } 311 312 @Override registerOnSharedPreferenceChangeListener( OnSharedPreferenceChangeListener listener)313 public void registerOnSharedPreferenceChangeListener( 314 OnSharedPreferenceChangeListener listener) { 315 mListeners.add(listener); 316 } 317 318 @Override unregisterOnSharedPreferenceChangeListener( OnSharedPreferenceChangeListener listener)319 public void unregisterOnSharedPreferenceChangeListener( 320 OnSharedPreferenceChangeListener listener) { 321 mListeners.remove(listener); 322 } 323 324 @Override onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)325 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, 326 String key) { 327 for (OnSharedPreferenceChangeListener listener : mListeners) { 328 listener.onSharedPreferenceChanged(this, key); 329 } 330 mBackupManager.dataChanged(); 331 } 332 } 333