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