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