AppUpgrader.java revision 8d0178ea8ea318e52dd8bd3ed5f3c8e697383304
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
17package com.android.camera.settings;
18
19import android.content.Context;
20import android.content.SharedPreferences;
21
22import com.android.camera.CameraActivity;
23import com.android.camera.app.AppController;
24import com.android.camera.app.ModuleManagerImpl;
25import com.android.camera.debug.Log;
26import com.android.camera.module.ModuleController;
27import com.android.camera.util.Size;
28import com.android.camera2.R;
29import com.android.ex.camera2.portability.CameraAgentFactory;
30import com.android.ex.camera2.portability.CameraDeviceInfo;
31
32import java.util.List;
33import 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 */
40public 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
91    public AppUpgrader(final AppController appController) {
92        super(Keys.KEY_UPGRADE_VERSION, APP_UPGRADE_VERSION);
93        mAppController = appController;
94    }
95
96    @Override
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
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     */
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     */
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     */
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.sizeToSettingString(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     */
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     */
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
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            if (oldModulePreferences != null && oldModulePreferences.getAll().size() > 0) {
414                ModuleManagerImpl.ModuleAgent agent =
415                        app.getModuleManager().getModuleAgent(moduleIds[i]);
416                if (agent == null) {
417                    continue;
418                }
419                ModuleController module = agent.createModule(app, null);
420                SharedPreferences newModulePreferences =
421                        settingsManager.openPreferences(CameraActivity.MODULE_SCOPE_PREFIX
422                                + module.getModuleStringIdentifier());
423
424                copyPreferences(oldModulePreferences, newModulePreferences);
425            }
426        }
427    }
428
429    /**
430     * The R.integer.camera_mode_* indices were cleaned up, resulting in
431     * removals and renaming of certain values. In particular camera_mode_gcam
432     * is now 5, not 6. We modify any persisted user settings that may refer to
433     * the old value.
434     */
435    private void upgradeSelectedModeIndex(SettingsManager settingsManager, Context context) {
436        int oldGcamIndex = 6; // from hardcoded previous mode index resource
437        int gcamIndex = context.getResources().getInteger(R.integer.camera_mode_gcam);
438
439        int lastUsedCameraIndex = settingsManager.getInteger(SettingsManager.SCOPE_GLOBAL,
440                Keys.KEY_CAMERA_MODULE_LAST_USED);
441        if (lastUsedCameraIndex == oldGcamIndex) {
442            settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_CAMERA_MODULE_LAST_USED,
443                    gcamIndex);
444        }
445
446        int startupModuleIndex = settingsManager.getInteger(SettingsManager.SCOPE_GLOBAL,
447                Keys.KEY_STARTUP_MODULE_INDEX);
448        if (startupModuleIndex == oldGcamIndex) {
449            settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_STARTUP_MODULE_INDEX,
450                    gcamIndex);
451        }
452    }
453}
454