| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.camera.settings; |
| |
| import android.content.Context; |
| import android.content.SharedPreferences; |
| |
| import com.android.camera.app.AppController; |
| import com.android.camera.app.ModuleManagerImpl; |
| import com.android.camera.debug.Log; |
| import com.android.camera.util.ApiHelper; |
| import com.android.camera.util.Size; |
| import com.android.camera2.R; |
| import com.android.ex.camera2.portability.CameraAgentFactory; |
| import com.android.ex.camera2.portability.CameraDeviceInfo; |
| |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Defines the general upgrade path for the app. Modules may define specific |
| * upgrade logic, but upgrading for preferences across modules, CameraActivity |
| * or application-wide can be added here. |
| */ |
| public class AppUpgrader extends SettingsUpgrader { |
| private static final Log.Tag TAG = new Log.Tag("AppUpgrader"); |
| |
| private static final String OLD_CAMERA_PREFERENCES_PREFIX = "_preferences_"; |
| private static final String OLD_MODULE_PREFERENCES_PREFIX = "_preferences_module_"; |
| private static final String OLD_GLOBAL_PREFERENCES_FILENAME = "_preferences_camera"; |
| private static final String OLD_KEY_UPGRADE_VERSION = "pref_strict_upgrade_version"; |
| |
| /** |
| * With this version everyone was forced to choose their location settings |
| * again. |
| */ |
| private static final int FORCE_LOCATION_CHOICE_VERSION = 2; |
| |
| /** |
| * With this version, the camera size setting changed from a "small", |
| * "medium" and "default" to strings representing the actual resolutions, |
| * i.e. "1080x1776". |
| */ |
| private static final int CAMERA_SIZE_SETTING_UPGRADE_VERSION = 3; |
| |
| /** |
| * With this version, the names of the files storing camera specific and |
| * module specific settings changed. |
| * <p> |
| * NOTE: changed this from 4 to 6 to re-run on latest Glacier upgrade. |
| * Initial upgraders to Glacier will run conversion once as of the change. |
| * When re-run for early dogfooders, values will get overwritten but will |
| * all work. |
| */ |
| private static final int CAMERA_MODULE_SETTINGS_FILES_RENAMED_VERSION = 6; |
| |
| /** |
| * With this version, timelapse mode was removed and mode indices need to be |
| * resequenced. |
| */ |
| private static final int CAMERA_SETTINGS_SELECTED_MODULE_INDEX = 5; |
| |
| /** |
| * With this version internal storage is changed to use only Strings, and |
| * a type conversion process should execute. |
| */ |
| private static final int CAMERA_SETTINGS_STRINGS_UPGRADE = 5; |
| |
| /** |
| * With this version we needed to convert the artificial 16:9 high |
| * resolution size on the N5 since we stored it with a swapped width/height. |
| */ |
| public static final int NEEDS_N5_16by9_RESOLUTION_SWAP = 7; |
| /** |
| * Increment this value whenever new AOSP UpgradeSteps need to be executed. |
| */ |
| public static final int APP_UPGRADE_VERSION = 7; |
| |
| private final AppController mAppController; |
| |
| public AppUpgrader(final AppController appController) { |
| super(Keys.KEY_UPGRADE_VERSION, APP_UPGRADE_VERSION); |
| mAppController = appController; |
| } |
| |
| @Override |
| protected int getLastVersion(SettingsManager settingsManager) { |
| // Prior upgrade versions were stored in the default preferences as int |
| // and String. We create a new version location for migration to String. |
| // If we don't have a version persisted in the new location, check for |
| // the prior value from the old location. We expect the old value to be |
| // processed during {@link #upgradeTypesToStrings}. |
| SharedPreferences defaultPreferences = settingsManager.getDefaultPreferences(); |
| if (defaultPreferences.contains(OLD_KEY_UPGRADE_VERSION)) { |
| Map<String, ?> allPrefs = defaultPreferences.getAll(); |
| Object oldVersion = allPrefs.get(OLD_KEY_UPGRADE_VERSION); |
| defaultPreferences.edit().remove(OLD_KEY_UPGRADE_VERSION).apply(); |
| if (oldVersion instanceof Integer) { |
| return (Integer) oldVersion; |
| } else if (oldVersion instanceof String) { |
| return SettingsManager.convertToInt((String) oldVersion); |
| } |
| } |
| return super.getLastVersion(settingsManager); |
| } |
| |
| @Override |
| public void upgrade(SettingsManager settingsManager, int lastVersion, int currentVersion) { |
| Context context = mAppController.getAndroidContext(); |
| |
| // Do strings upgrade first before 'earlier' upgrades, since they assume |
| // valid storage of values. |
| if (lastVersion < CAMERA_SETTINGS_STRINGS_UPGRADE) { |
| upgradeTypesToStrings(settingsManager); |
| } |
| |
| if (lastVersion < FORCE_LOCATION_CHOICE_VERSION) { |
| forceLocationChoice(settingsManager); |
| } |
| |
| if (lastVersion < CAMERA_SIZE_SETTING_UPGRADE_VERSION) { |
| CameraDeviceInfo infos = CameraAgentFactory |
| .getAndroidCameraAgent(context, CameraAgentFactory.CameraApi.API_1) |
| .getCameraDeviceInfo(); |
| upgradeCameraSizeSetting(settingsManager, context, infos, |
| SettingsUtil.CAMERA_FACING_FRONT); |
| upgradeCameraSizeSetting(settingsManager, context, infos, |
| SettingsUtil.CAMERA_FACING_BACK); |
| // We changed size handling and aspect ratio placement, put user |
| // back into Camera mode this time to ensure they see the ratio |
| // chooser if applicable. |
| settingsManager.remove(SettingsManager.SCOPE_GLOBAL, |
| Keys.KEY_STARTUP_MODULE_INDEX); |
| CameraAgentFactory.recycle(CameraAgentFactory.CameraApi.API_1); |
| } |
| |
| if (lastVersion < CAMERA_MODULE_SETTINGS_FILES_RENAMED_VERSION) { |
| upgradeCameraSettingsFiles(settingsManager, context); |
| upgradeModuleSettingsFiles(settingsManager, context, |
| mAppController); |
| } |
| |
| if (lastVersion < CAMERA_SETTINGS_SELECTED_MODULE_INDEX) { |
| upgradeSelectedModeIndex(settingsManager, context); |
| } |
| |
| if (lastVersion < NEEDS_N5_16by9_RESOLUTION_SWAP) { |
| updateN516by9ResolutionIfNeeded(settingsManager); |
| } |
| } |
| |
| /** |
| * Converts settings that were stored in SharedPreferences as non-Strings, |
| * to Strings. This is necessary due to a SettingsManager API refactoring. |
| * Should only be executed if we detected a change in |
| * Keys.KEY_UPGRADE_VERSION type from int to string; rerunning this on |
| * string values will result in ClassCastExceptions when trying to retrieve |
| * an int or boolean as a String. |
| */ |
| private void upgradeTypesToStrings(SettingsManager settingsManager) { |
| SharedPreferences defaultPreferences = settingsManager.getDefaultPreferences(); |
| SharedPreferences oldGlobalPreferences = |
| settingsManager.openPreferences(OLD_GLOBAL_PREFERENCES_FILENAME); |
| |
| // Location: boolean -> String, from default. |
| if (defaultPreferences.contains(Keys.KEY_RECORD_LOCATION)) { |
| boolean location = removeBoolean(defaultPreferences, Keys.KEY_RECORD_LOCATION); |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_RECORD_LOCATION, location); |
| } |
| |
| // User selected aspect ratio: boolean -> String, from default. |
| if (defaultPreferences.contains(Keys.KEY_USER_SELECTED_ASPECT_RATIO)) { |
| boolean userSelectedAspectRatio = removeBoolean(defaultPreferences, |
| Keys.KEY_USER_SELECTED_ASPECT_RATIO); |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_USER_SELECTED_ASPECT_RATIO, |
| userSelectedAspectRatio); |
| } |
| |
| // Manual exposure compensation: boolean -> String, from default. |
| if (defaultPreferences.contains(Keys.KEY_EXPOSURE_COMPENSATION_ENABLED)) { |
| boolean manualExposureCompensationEnabled = removeBoolean(defaultPreferences, |
| Keys.KEY_EXPOSURE_COMPENSATION_ENABLED); |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, |
| Keys.KEY_EXPOSURE_COMPENSATION_ENABLED, manualExposureCompensationEnabled); |
| } |
| |
| // Hint: boolean -> String, from default. |
| if (defaultPreferences.contains(Keys.KEY_CAMERA_FIRST_USE_HINT_SHOWN)) { |
| boolean hint = removeBoolean(defaultPreferences, Keys.KEY_CAMERA_FIRST_USE_HINT_SHOWN); |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_FIRST_USE_HINT_SHOWN, |
| hint); |
| } |
| |
| // Startup module index: Integer -> String, from default. |
| if (defaultPreferences.contains(Keys.KEY_STARTUP_MODULE_INDEX)) { |
| int startupModuleIndex = removeInteger(defaultPreferences, |
| Keys.KEY_STARTUP_MODULE_INDEX); |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_STARTUP_MODULE_INDEX, |
| startupModuleIndex); |
| } |
| |
| // Last camera used module index: Integer -> String, from default. |
| if (defaultPreferences.contains(Keys.KEY_CAMERA_MODULE_LAST_USED)) { |
| int lastCameraUsedModuleIndex = removeInteger(defaultPreferences, |
| Keys.KEY_CAMERA_MODULE_LAST_USED); |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_MODULE_LAST_USED, |
| lastCameraUsedModuleIndex); |
| } |
| |
| // Flash supported back camera setting: boolean -> String, from old |
| // global. |
| if (oldGlobalPreferences.contains(Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA)) { |
| boolean flashSupportedBackCamera = removeBoolean(oldGlobalPreferences, |
| Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA); |
| if (flashSupportedBackCamera) { |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, |
| Keys.KEY_FLASH_SUPPORTED_BACK_CAMERA, flashSupportedBackCamera); |
| } |
| } |
| |
| // Should show refocus viewer cling: boolean -> String, from default. |
| if (defaultPreferences.contains(Keys.KEY_SHOULD_SHOW_REFOCUS_VIEWER_CLING)) { |
| boolean shouldShowRefocusViewer = removeBoolean(defaultPreferences, |
| Keys.KEY_SHOULD_SHOW_REFOCUS_VIEWER_CLING); |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, |
| Keys.KEY_SHOULD_SHOW_REFOCUS_VIEWER_CLING, shouldShowRefocusViewer); |
| } |
| |
| // Should show settings button cling: boolean -> String, from default. |
| if (defaultPreferences.contains(Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING)) { |
| boolean shouldShowSettingsButtonCling = removeBoolean(defaultPreferences, |
| Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING); |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, |
| Keys.KEY_SHOULD_SHOW_SETTINGS_BUTTON_CLING, shouldShowSettingsButtonCling); |
| } |
| |
| // HDR plus on setting: String on/off -> String, from old global. |
| if (oldGlobalPreferences.contains(Keys.KEY_CAMERA_HDR_PLUS)) { |
| String hdrPlus = removeString(oldGlobalPreferences, Keys.KEY_CAMERA_HDR_PLUS); |
| if (OLD_SETTINGS_VALUE_ON.equals(hdrPlus)) { |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR_PLUS, true); |
| } |
| } |
| |
| // HDR on setting: String on/off -> String, from old global. |
| if (oldGlobalPreferences.contains(Keys.KEY_CAMERA_HDR)) { |
| String hdrPlus = removeString(oldGlobalPreferences, Keys.KEY_CAMERA_HDR); |
| if (OLD_SETTINGS_VALUE_ON.equals(hdrPlus)) { |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_HDR, true); |
| } |
| } |
| |
| // Grid on setting: String on/off -> String, from old global. |
| if (oldGlobalPreferences.contains(Keys.KEY_CAMERA_GRID_LINES)) { |
| String hdrPlus = removeString(oldGlobalPreferences, Keys.KEY_CAMERA_GRID_LINES); |
| if (OLD_SETTINGS_VALUE_ON.equals(hdrPlus)) { |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_GRID_LINES, |
| true); |
| } |
| } |
| } |
| |
| /** |
| * Part of the AOSP upgrade path, forces the user to choose their location |
| * again if it was originally set to false. |
| */ |
| private void forceLocationChoice(SettingsManager settingsManager) { |
| SharedPreferences oldGlobalPreferences = |
| settingsManager.openPreferences(OLD_GLOBAL_PREFERENCES_FILENAME); |
| // Show the location dialog on upgrade if |
| // (a) the user has never set this option (status quo). |
| // (b) the user opt'ed out previously. |
| if (settingsManager.isSet(SettingsManager.SCOPE_GLOBAL, |
| Keys.KEY_RECORD_LOCATION)) { |
| // Location is set in the source file defined for this setting. |
| // Remove the setting if the value is false to launch the dialog. |
| if (!settingsManager.getBoolean(SettingsManager.SCOPE_GLOBAL, |
| Keys.KEY_RECORD_LOCATION)) { |
| settingsManager.remove(SettingsManager.SCOPE_GLOBAL, Keys.KEY_RECORD_LOCATION); |
| } |
| } else if (oldGlobalPreferences.contains(Keys.KEY_RECORD_LOCATION)) { |
| // Location is not set, check to see if we're upgrading from |
| // a different source file. |
| String location = removeString(oldGlobalPreferences, Keys.KEY_RECORD_LOCATION); |
| if (OLD_SETTINGS_VALUE_ON.equals(location)) { |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_RECORD_LOCATION, |
| true); |
| } |
| } |
| } |
| |
| /** |
| * Part of the AOSP upgrade path, sets front and back picture sizes. |
| */ |
| private void upgradeCameraSizeSetting(SettingsManager settingsManager, |
| Context context, CameraDeviceInfo infos, |
| SettingsUtil.CameraDeviceSelector facing) { |
| String key; |
| if (facing == SettingsUtil.CAMERA_FACING_FRONT) { |
| key = Keys.KEY_PICTURE_SIZE_FRONT; |
| } else if (facing == SettingsUtil.CAMERA_FACING_BACK) { |
| key = Keys.KEY_PICTURE_SIZE_BACK; |
| } else { |
| Log.w(TAG, "Ignoring attempt to upgrade size of unhandled camera facing direction"); |
| return; |
| } |
| |
| // infos might be null if the underlying camera device is broken. In |
| // that case, just delete the old settings and force the user to |
| // reselect, it's the least evil solution given we want to only upgrade |
| // settings once. |
| if (infos == null) { |
| settingsManager.remove(SettingsManager.SCOPE_GLOBAL, key); |
| return; |
| } |
| |
| String pictureSize = settingsManager.getString(SettingsManager.SCOPE_GLOBAL, key); |
| int camera = SettingsUtil.getCameraId(infos, facing); |
| if (camera != -1) { |
| List<Size> supported = CameraPictureSizesCacher.getSizesForCamera(camera, context); |
| if (supported != null) { |
| Size size = SettingsUtil.getPhotoSize(pictureSize, supported, camera); |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, key, |
| SettingsUtil.sizeToSettingString(size)); |
| } |
| } |
| } |
| |
| /** |
| * Part of the AOSP upgrade path, copies all of the keys and values in a |
| * SharedPreferences file to another SharedPreferences file, as Strings. |
| * Settings that are not a known supported format (int/boolean/String) |
| * are dropped with warning. |
| * |
| * This will normally be run only once but was used both for upgrade version |
| * 4 and 6 -- in 6 we repair issues with previous runs of the upgrader. So |
| * we make sure to remove entries from destination if the source isn't valid |
| * like a null or unsupported type. |
| */ |
| private void copyPreferences(SharedPreferences oldPrefs, |
| SharedPreferences newPrefs) { |
| Map<String, ?> entries = oldPrefs.getAll(); |
| for (Map.Entry<String, ?> entry : entries.entrySet()) { |
| String key = entry.getKey(); |
| Object value = entry.getValue(); |
| if (value == null) { |
| Log.w(TAG, "skipped upgrade and removing entry for null key " + key); |
| newPrefs.edit().remove(key).apply(); |
| } else if (value instanceof Boolean) { |
| String boolValue = SettingsManager.convert((Boolean) value); |
| newPrefs.edit().putString(key, boolValue).apply(); |
| } else if (value instanceof Integer) { |
| String intValue = SettingsManager.convert((Integer) value); |
| newPrefs.edit().putString(key, intValue).apply(); |
| } else if (value instanceof Long){ |
| // New SettingsManager only supports int values. Attempt to |
| // recover any longs which happen to be present if they are |
| // within int range. |
| long longValue = (Long) value; |
| if (longValue <= Integer.MAX_VALUE && longValue >= Integer.MIN_VALUE) { |
| String intValue = SettingsManager.convert((int) longValue); |
| newPrefs.edit().putString(key, intValue).apply(); |
| } else { |
| Log.w(TAG, "skipped upgrade for out of bounds long key " + |
| key + " : " + longValue); |
| } |
| } else if (value instanceof String){ |
| newPrefs.edit().putString(key, (String) value).apply(); |
| } else { |
| Log.w(TAG,"skipped upgrade and removing entry for unrecognized " |
| + "key type " + key + " : " + value.getClass()); |
| newPrefs.edit().remove(key).apply(); |
| } |
| } |
| } |
| |
| /** |
| * Part of the AOSP upgrade path, copies all of the key and values in the |
| * old camera SharedPreferences files to new files. |
| */ |
| private void upgradeCameraSettingsFiles(SettingsManager settingsManager, |
| Context context) { |
| String[] cameraIds = |
| context.getResources().getStringArray(R.array.camera_id_entryvalues); |
| |
| for (int i = 0; i < cameraIds.length; i++) { |
| SharedPreferences oldCameraPreferences = |
| settingsManager.openPreferences( |
| OLD_CAMERA_PREFERENCES_PREFIX + cameraIds[i]); |
| SharedPreferences newCameraPreferences = |
| settingsManager.openPreferences( |
| SettingsManager.getCameraSettingScope(cameraIds[i])); |
| |
| copyPreferences(oldCameraPreferences, newCameraPreferences); |
| } |
| } |
| |
| private void upgradeModuleSettingsFiles(SettingsManager settingsManager, |
| Context context, AppController app) { |
| int[] moduleIds = context.getResources().getIntArray(R.array.camera_modes); |
| |
| for (int i = 0; i < moduleIds.length; i++) { |
| String moduleId = Integer.toString(moduleIds[i]); |
| SharedPreferences oldModulePreferences = |
| settingsManager.openPreferences( |
| OLD_MODULE_PREFERENCES_PREFIX + moduleId); |
| |
| if (oldModulePreferences != null && oldModulePreferences.getAll().size() > 0) { |
| ModuleManagerImpl.ModuleAgent agent = |
| app.getModuleManager().getModuleAgent(moduleIds[i]); |
| if (agent != null) { |
| SharedPreferences newModulePreferences = settingsManager.openPreferences( |
| SettingsManager.getModuleSettingScope(agent.getScopeNamespace())); |
| |
| copyPreferences(oldModulePreferences, newModulePreferences); |
| } |
| } |
| } |
| } |
| |
| /** |
| * The R.integer.camera_mode_* indices were cleaned up, resulting in |
| * removals and renaming of certain values. In particular camera_mode_gcam |
| * is now 5, not 6. We modify any persisted user settings that may refer to |
| * the old value. |
| */ |
| private void upgradeSelectedModeIndex(SettingsManager settingsManager, Context context) { |
| int oldGcamIndex = 6; // from hardcoded previous mode index resource |
| int gcamIndex = context.getResources().getInteger(R.integer.camera_mode_gcam); |
| |
| int lastUsedCameraIndex = settingsManager.getInteger(SettingsManager.SCOPE_GLOBAL, |
| Keys.KEY_CAMERA_MODULE_LAST_USED); |
| if (lastUsedCameraIndex == oldGcamIndex) { |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_MODULE_LAST_USED, |
| gcamIndex); |
| } |
| |
| int startupModuleIndex = settingsManager.getInteger(SettingsManager.SCOPE_GLOBAL, |
| Keys.KEY_STARTUP_MODULE_INDEX); |
| if (startupModuleIndex == oldGcamIndex) { |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_STARTUP_MODULE_INDEX, |
| gcamIndex); |
| } |
| } |
| |
| /** |
| * A targeted fix for b/19693226. |
| * <p> |
| * Since the N5 doesn't natively support a high resolution 16:9 size we need |
| * to artificially add it and then crop the result from the high-resolution |
| * 4:3 size. In version 2.4 we unfortunately swapped the dimensions of |
| * ResolutionUtil#NEXUS_5_LARGE_16_BY_9_SIZE, which now causes a few issues |
| * in 2.5. If we detect this case, we will swap the dimensions here to make |
| * sure they are the right way around going forward. |
| */ |
| private void updateN516by9ResolutionIfNeeded(SettingsManager settingsManager) { |
| if (!ApiHelper.IS_NEXUS_5) { |
| return; |
| } |
| |
| String pictureSize = settingsManager.getString(SettingsManager.SCOPE_GLOBAL, |
| Keys.KEY_PICTURE_SIZE_BACK); |
| if ("1836x3264".equals(pictureSize)) { |
| Log.i(TAG, "Swapped dimensions on N5 16:9 resolution."); |
| settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_PICTURE_SIZE_BACK, |
| "3264x1836"); |
| } |
| } |
| } |