1 /* 2 * Copyright (C) 2014 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 22 import com.android.camera.CameraActivity; 23 import com.android.camera.app.AppController; 24 import com.android.camera.app.ModuleManagerImpl; 25 import com.android.camera.debug.Log; 26 import com.android.camera.module.ModuleController; 27 import com.android.camera2.R; 28 import com.android.ex.camera2.portability.CameraAgentFactory; 29 import com.android.ex.camera2.portability.CameraDeviceInfo; 30 import com.android.ex.camera2.portability.Size; 31 32 import java.util.List; 33 import java.util.Map; 34 35 /** 36 * Defines the general upgrade path for the app. Modules may define specific 37 * upgrade logic, but upgrading for preferences across modules, CameraActivity 38 * or application-wide can be added here. 39 */ 40 public class AppUpgrader extends SettingsUpgrader { 41 private static final Log.Tag TAG = new Log.Tag("AppUpgrader"); 42 43 private static final String OLD_CAMERA_PREFERENCES_PREFIX = "_preferences_"; 44 private static final String OLD_MODULE_PREFERENCES_PREFIX = "_preferences_module_"; 45 private static final String OLD_GLOBAL_PREFERENCES_FILENAME = "_preferences_camera"; 46 private static final String OLD_KEY_UPGRADE_VERSION = "pref_strict_upgrade_version"; 47 48 /** 49 * With this version everyone was forced to choose their location settings 50 * again. 51 */ 52 private static final int FORCE_LOCATION_CHOICE_VERSION = 2; 53 54 /** 55 * With this version, the camera size setting changed from a "small", 56 * "medium" and "default" to strings representing the actual resolutions, 57 * i.e. "1080x1776". 58 */ 59 private static final int CAMERA_SIZE_SETTING_UPGRADE_VERSION = 3; 60 61 /** 62 * With this version, the names of the files storing camera specific and 63 * module specific settings changed. 64 * <p> 65 * NOTE: changed this from 4 to 6 to re-run on latest Glacier upgrade. 66 * Initial upgraders to Glacier will run conversion once as of the change. 67 * When re-run for early dogfooders, values will get overwritten but will 68 * all work. 69 */ 70 private static final int CAMERA_MODULE_SETTINGS_FILES_RENAMED_VERSION = 6; 71 72 /** 73 * With this version, timelapse mode was removed and mode indices need to be 74 * resequenced. 75 */ 76 private static final int CAMERA_SETTINGS_SELECTED_MODULE_INDEX = 5; 77 78 /** 79 * With this version internal storage is changed to use only Strings, and 80 * a type conversion process should execute. 81 */ 82 private static final int CAMERA_SETTINGS_STRINGS_UPGRADE = 5; 83 84 /** 85 * Increment this value whenever new AOSP UpgradeSteps need to be executed. 86 */ 87 public static final int APP_UPGRADE_VERSION = 6; 88 89 private final AppController mAppController; 90 AppUpgrader(final AppController appController)91 public AppUpgrader(final AppController appController) { 92 super(Keys.KEY_UPGRADE_VERSION, APP_UPGRADE_VERSION); 93 mAppController = appController; 94 } 95 96 @Override getLastVersion(SettingsManager settingsManager)97 protected int getLastVersion(SettingsManager settingsManager) { 98 // Prior upgrade versions were stored in the default preferences as int 99 // and String. We create a new version location for migration to String. 100 // If we don't have a version persisted in the new location, check for 101 // the prior value from the old location. We expect the old value to be 102 // processed during {@link #upgradeTypesToStrings}. 103 SharedPreferences defaultPreferences = settingsManager.getDefaultPreferences(); 104 if (defaultPreferences.contains(OLD_KEY_UPGRADE_VERSION)) { 105 Map<String, ?> allPrefs = defaultPreferences.getAll(); 106 Object oldVersion = allPrefs.get(OLD_KEY_UPGRADE_VERSION); 107 defaultPreferences.edit().remove(OLD_KEY_UPGRADE_VERSION).apply(); 108 if (oldVersion instanceof Integer) { 109 return (Integer) oldVersion; 110 } else if (oldVersion instanceof String) { 111 return SettingsManager.convertToInt((String) oldVersion); 112 } 113 } 114 return super.getLastVersion(settingsManager); 115 } 116 117 @Override upgrade(SettingsManager settingsManager, int lastVersion, int currentVersion)118 public void upgrade(SettingsManager settingsManager, int lastVersion, int currentVersion) { 119 Context context = mAppController.getAndroidContext(); 120 121 // Do strings upgrade first before 'earlier' upgrades, since they assume 122 // valid storage of values. 123 if (lastVersion < CAMERA_SETTINGS_STRINGS_UPGRADE) { 124 upgradeTypesToStrings(settingsManager); 125 } 126 127 if (lastVersion < FORCE_LOCATION_CHOICE_VERSION) { 128 forceLocationChoice(settingsManager); 129 } 130 131 if (lastVersion < CAMERA_SIZE_SETTING_UPGRADE_VERSION) { 132 CameraDeviceInfo infos = CameraAgentFactory 133 .getAndroidCameraAgent(context, CameraAgentFactory.CameraApi.API_1) 134 .getCameraDeviceInfo(); 135 upgradeCameraSizeSetting(settingsManager, context, infos, 136 SettingsUtil.CAMERA_FACING_FRONT); 137 upgradeCameraSizeSetting(settingsManager, context, infos, 138 SettingsUtil.CAMERA_FACING_BACK); 139 // We changed size handling and aspect ratio placement, put user 140 // back into Camera mode this time to ensure they see the ratio 141 // chooser if applicable. 142 settingsManager.remove(SettingsManager.SCOPE_GLOBAL, 143 Keys.KEY_STARTUP_MODULE_INDEX); 144 } 145 146 if (lastVersion < CAMERA_MODULE_SETTINGS_FILES_RENAMED_VERSION) { 147 upgradeCameraSettingsFiles(settingsManager, context); 148 upgradeModuleSettingsFiles(settingsManager, context, 149 mAppController); 150 } 151 152 if (lastVersion < CAMERA_SETTINGS_SELECTED_MODULE_INDEX) { 153 upgradeSelectedModeIndex(settingsManager, context); 154 } 155 } 156 157 /** 158 * Converts settings that were stored in SharedPreferences as non-Strings, 159 * to Strings. This is necessary due to a SettingsManager API refactoring. 160 * Should only be executed if we detected a change in 161 * Keys.KEY_UPGRADE_VERSION type from int to string; rerunning this on 162 * string values will result in ClassCastExceptions when trying to retrieve 163 * an int or boolean as a String. 164 */ upgradeTypesToStrings(SettingsManager settingsManager)165 private void upgradeTypesToStrings(SettingsManager settingsManager) { 166 SharedPreferences defaultPreferences = settingsManager.getDefaultPreferences(); 167 SharedPreferences oldGlobalPreferences = 168 settingsManager.openPreferences(OLD_GLOBAL_PREFERENCES_FILENAME); 169 170 // Location: boolean -> String, from default. 171 if (defaultPreferences.contains(Keys.KEY_RECORD_LOCATION)) { 172 boolean location = removeBoolean(defaultPreferences, Keys.KEY_RECORD_LOCATION); 173 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_RECORD_LOCATION, location); 174 } 175 176 // User selected aspect ratio: boolean -> String, from default. 177 if (defaultPreferences.contains(Keys.KEY_USER_SELECTED_ASPECT_RATIO)) { 178 boolean userSelectedAspectRatio = removeBoolean(defaultPreferences, 179 Keys.KEY_USER_SELECTED_ASPECT_RATIO); 180 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_USER_SELECTED_ASPECT_RATIO, 181 userSelectedAspectRatio); 182 } 183 184 // Manual exposure compensation: boolean -> String, from default. 185 if (defaultPreferences.contains(Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) { 186 boolean manualExposureCompensationEnabled = removeBoolean(defaultPreferences, 187 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED); 188 settingsManager.set(SettingsManager.SCOPE_GLOBAL, 189 Keys.KEY_EXPOSURE_COMPENSATION_ENABLED, manualExposureCompensationEnabled); 190 } 191 192 // Hint: boolean -> String, from default. 193 if (defaultPreferences.contains(Keys.KEY_CAMERA_FIRST_USE_HINT_SHOWN)) { 194 boolean hint = removeBoolean(defaultPreferences, Keys.KEY_CAMERA_FIRST_USE_HINT_SHOWN); 195 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_FIRST_USE_HINT_SHOWN, 196 hint); 197 } 198 199 // Startup module index: Integer -> String, from default. 200 if (defaultPreferences.contains(Keys.KEY_STARTUP_MODULE_INDEX)) { 201 int startupModuleIndex = removeInteger(defaultPreferences, 202 Keys.KEY_STARTUP_MODULE_INDEX); 203 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_STARTUP_MODULE_INDEX, 204 startupModuleIndex); 205 } 206 207 // Last camera used module index: Integer -> String, from default. 208 if (defaultPreferences.contains(Keys.KEY_CAMERA_MODULE_LAST_USED)) { 209 int lastCameraUsedModuleIndex = removeInteger(defaultPreferences, 210 Keys.KEY_CAMERA_MODULE_LAST_USED); 211 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_MODULE_LAST_USED, 212 lastCameraUsedModuleIndex); 213 } 214 215 // Flash supported back camera setting: boolean -> String, from old 216 // global. 217 if (oldGlobalPreferences.contains(Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA)) { 218 boolean flashSupportedBackCamera = removeBoolean(oldGlobalPreferences, 219 Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA); 220 if (flashSupportedBackCamera) { 221 settingsManager.set(SettingsManager.SCOPE_GLOBAL, 222 Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA, flashSupportedBackCamera); 223 } 224 } 225 226 // Should show refocus viewer cling: boolean -> String, from default. 227 if (defaultPreferences.contains(Keys.KEY_SHOULD_SHOW_REFOCUS_VIEWER_CLING)) { 228 boolean shouldShowRefocusViewer = removeBoolean(defaultPreferences, 229 Keys.KEY_SHOULD_SHOW_REFOCUS_VIEWER_CLING); 230 settingsManager.set(SettingsManager.SCOPE_GLOBAL, 231 Keys.KEY_SHOULD_SHOW_REFOCUS_VIEWER_CLING, shouldShowRefocusViewer); 232 } 233 234 // Should show settings button cling: boolean -> String, from default. 235 if (defaultPreferences.contains(Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING)) { 236 boolean shouldShowSettingsButtonCling = removeBoolean(defaultPreferences, 237 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING); 238 settingsManager.set(SettingsManager.SCOPE_GLOBAL, 239 Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, shouldShowSettingsButtonCling); 240 } 241 242 // HDR plus on setting: String on/off -> String, from old global. 243 if (oldGlobalPreferences.contains(Keys.KEY_CAMERA_HDR_PLUS)) { 244 String hdrPlus = removeString(oldGlobalPreferences, Keys.KEY_CAMERA_HDR_PLUS); 245 if (OLD_SETTINGS_VALUE_ON.equals(hdrPlus)) { 246 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, true); 247 } 248 } 249 250 // HDR on setting: String on/off -> String, from old global. 251 if (oldGlobalPreferences.contains(Keys.KEY_CAMERA_HDR)) { 252 String hdrPlus = removeString(oldGlobalPreferences, Keys.KEY_CAMERA_HDR); 253 if (OLD_SETTINGS_VALUE_ON.equals(hdrPlus)) { 254 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, true); 255 } 256 } 257 258 // Grid on setting: String on/off -> String, from old global. 259 if (oldGlobalPreferences.contains(Keys.KEY_CAMERA_GRID_LINES)) { 260 String hdrPlus = removeString(oldGlobalPreferences, Keys.KEY_CAMERA_GRID_LINES); 261 if (OLD_SETTINGS_VALUE_ON.equals(hdrPlus)) { 262 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_GRID_LINES, 263 true); 264 } 265 } 266 } 267 268 /** 269 * Part of the AOSP upgrade path, forces the user to choose their location 270 * again if it was originally set to false. 271 */ forceLocationChoice(SettingsManager settingsManager)272 private void forceLocationChoice(SettingsManager settingsManager) { 273 SharedPreferences oldGlobalPreferences = 274 settingsManager.openPreferences(OLD_GLOBAL_PREFERENCES_FILENAME); 275 // Show the location dialog on upgrade if 276 // (a) the user has never set this option (status quo). 277 // (b) the user opt'ed out previously. 278 if (settingsManager.isSet(SettingsManager.SCOPE_GLOBAL, 279 Keys.KEY_RECORD_LOCATION)) { 280 // Location is set in the source file defined for this setting. 281 // Remove the setting if the value is false to launch the dialog. 282 if (!settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL, 283 Keys.KEY_RECORD_LOCATION)) { 284 settingsManager.remove(SettingsManager.SCOPE_GLOBAL, Keys.KEY_RECORD_LOCATION); 285 } 286 } else if (oldGlobalPreferences.contains(Keys.KEY_RECORD_LOCATION)) { 287 // Location is not set, check to see if we're upgrading from 288 // a different source file. 289 String location = removeString(oldGlobalPreferences, Keys.KEY_RECORD_LOCATION); 290 if (OLD_SETTINGS_VALUE_ON.equals(location)) { 291 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_RECORD_LOCATION, 292 true); 293 } 294 } 295 } 296 297 /** 298 * Part of the AOSP upgrade path, sets front and back picture sizes. 299 */ upgradeCameraSizeSetting(SettingsManager settingsManager, Context context, CameraDeviceInfo infos, SettingsUtil.CameraDeviceSelector facing)300 private void upgradeCameraSizeSetting(SettingsManager settingsManager, 301 Context context, CameraDeviceInfo infos, 302 SettingsUtil.CameraDeviceSelector facing) { 303 String key; 304 if (facing == SettingsUtil.CAMERA_FACING_FRONT) { 305 key = Keys.KEY_PICTURE_SIZE_FRONT; 306 } else if (facing == SettingsUtil.CAMERA_FACING_BACK) { 307 key = Keys.KEY_PICTURE_SIZE_BACK; 308 } else { 309 Log.w(TAG, "Ignoring attempt to upgrade size of unhandled camera facing direction"); 310 return; 311 } 312 313 // infos might be null if the underlying camera device is broken. In 314 // that case, just delete the old settings and force the user to 315 // reselect, it's the least evil solution given we want to only upgrade 316 // settings once. 317 if (infos == null) { 318 settingsManager.remove(SettingsManager.SCOPE_GLOBAL, key); 319 return; 320 } 321 322 String pictureSize = settingsManager.getString(SettingsManager.SCOPE_GLOBAL, key); 323 int camera = SettingsUtil.getCameraId(infos, facing); 324 if (camera != -1) { 325 List<Size> supported = CameraPictureSizesCacher.getSizesForCamera(camera, context); 326 if (supported != null) { 327 Size size = SettingsUtil.getPhotoSize(pictureSize, supported, camera); 328 settingsManager.set(SettingsManager.SCOPE_GLOBAL, key, 329 SettingsUtil.sizeToSetting(size)); 330 } 331 } 332 } 333 334 /** 335 * Part of the AOSP upgrade path, copies all of the keys and values in a 336 * SharedPreferences file to another SharedPreferences file, as Strings. 337 * Settings that are not a known supported format (int/boolean/String) 338 * are dropped with warning. 339 * 340 * This will normally be run only once but was used both for upgrade version 341 * 4 and 6 -- in 6 we repair issues with previous runs of the upgrader. So 342 * we make sure to remove entries from destination if the source isn't valid 343 * like a null or unsupported type. 344 */ copyPreferences(SharedPreferences oldPrefs, SharedPreferences newPrefs)345 private void copyPreferences(SharedPreferences oldPrefs, 346 SharedPreferences newPrefs) { 347 Map<String, ?> entries = oldPrefs.getAll(); 348 for (Map.Entry<String, ?> entry : entries.entrySet()) { 349 String key = entry.getKey(); 350 Object value = entry.getValue(); 351 if (value == null) { 352 Log.w(TAG, "skipped upgrade and removing entry for null key " + key); 353 newPrefs.edit().remove(key).apply(); 354 } else if (value instanceof Boolean) { 355 String boolValue = SettingsManager.convert((Boolean) value); 356 newPrefs.edit().putString(key, boolValue).apply(); 357 } else if (value instanceof Integer) { 358 String intValue = SettingsManager.convert((Integer) value); 359 newPrefs.edit().putString(key, intValue).apply(); 360 } else if (value instanceof Long){ 361 // New SettingsManager only supports int values. Attempt to 362 // recover any longs which happen to be present if they are 363 // within int range. 364 long longValue = (Long) value; 365 if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) { 366 String intValue = SettingsManager.convert((int) longValue); 367 newPrefs.edit().putString(key, intValue).apply(); 368 } else { 369 Log.w(TAG, "skipped upgrade for out of bounds long key " + 370 key + " : " + longValue); 371 } 372 } else if (value instanceof String){ 373 newPrefs.edit().putString(key, (String) value).apply(); 374 } else { 375 Log.w(TAG,"skipped upgrade and removing entry for unrecognized " 376 + "key type " + key + " : " + value.getClass()); 377 newPrefs.edit().remove(key).apply(); 378 } 379 } 380 } 381 382 /** 383 * Part of the AOSP upgrade path, copies all of the key and values in the 384 * old camera SharedPreferences files to new files. 385 */ upgradeCameraSettingsFiles(SettingsManager settingsManager, Context context)386 private void upgradeCameraSettingsFiles(SettingsManager settingsManager, 387 Context context) { 388 String[] cameraIds = 389 context.getResources().getStringArray(R.array.camera_id_entryvalues); 390 391 for (int i = 0; i < cameraIds.length; i++) { 392 SharedPreferences oldCameraPreferences = 393 settingsManager.openPreferences( 394 OLD_CAMERA_PREFERENCES_PREFIX + cameraIds[i]); 395 SharedPreferences newCameraPreferences = 396 settingsManager.openPreferences(CameraActivity.CAMERA_SCOPE_PREFIX 397 + cameraIds[i]); 398 399 copyPreferences(oldCameraPreferences, newCameraPreferences); 400 } 401 } 402 upgradeModuleSettingsFiles(SettingsManager settingsManager, Context context, AppController app)403 private void upgradeModuleSettingsFiles(SettingsManager settingsManager, 404 Context context, AppController app) { 405 int[] moduleIds = context.getResources().getIntArray(R.array.camera_modes); 406 407 for (int i = 0; i < moduleIds.length; i++) { 408 String moduleId = Integer.toString(moduleIds[i]); 409 SharedPreferences oldModulePreferences = 410 settingsManager.openPreferences( 411 OLD_MODULE_PREFERENCES_PREFIX + moduleId); 412 413 ModuleManagerImpl.ModuleAgent agent = 414 app.getModuleManager().getModuleAgent(moduleIds[i]); 415 if (agent == null) { 416 continue; 417 } 418 ModuleController module = agent.createModule(app); 419 SharedPreferences newModulePreferences = 420 settingsManager.openPreferences(CameraActivity.MODULE_SCOPE_PREFIX 421 + module.getModuleStringIdentifier()); 422 423 copyPreferences(oldModulePreferences, newModulePreferences); 424 } 425 } 426 427 /** 428 * The R.integer.camera_mode_* indices were cleaned up, resulting in 429 * removals and renaming of certain values. In particular camera_mode_gcam 430 * is now 5, not 6. We modify any persisted user settings that may refer to 431 * the old value. 432 */ upgradeSelectedModeIndex(SettingsManager settingsManager, Context context)433 private void upgradeSelectedModeIndex(SettingsManager settingsManager, Context context) { 434 int oldGcamIndex = 6; // from hardcoded previous mode index resource 435 int gcamIndex = context.getResources().getInteger(R.integer.camera_mode_gcam); 436 437 int lastUsedCameraIndex = settingsManager.getInteger(SettingsManager.SCOPE_GLOBAL, 438 Keys.KEY_CAMERA_MODULE_LAST_USED); 439 if (lastUsedCameraIndex == oldGcamIndex) { 440 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_MODULE_LAST_USED, 441 gcamIndex); 442 } 443 444 int startupModuleIndex = settingsManager.getInteger(SettingsManager.SCOPE_GLOBAL, 445 Keys.KEY_STARTUP_MODULE_INDEX); 446 if (startupModuleIndex == oldGcamIndex) { 447 settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_STARTUP_MODULE_INDEX, 448 gcamIndex); 449 } 450 } 451 } 452