1/*
2 * Copyright (C) 2015 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.ContentResolver;
20import android.graphics.ImageFormat;
21
22import com.android.camera.debug.Log;
23import com.android.camera.device.CameraId;
24import com.android.camera.exif.Rational;
25import com.android.camera.one.OneCamera;
26import com.android.camera.one.OneCamera.Facing;
27import com.android.camera.one.OneCameraAccessException;
28import com.android.camera.one.OneCameraCharacteristics;
29import com.android.camera.one.OneCameraManager;
30import com.android.camera.util.GservicesHelper;
31import com.android.camera.util.Size;
32
33import com.google.common.base.Preconditions;
34
35import java.util.List;
36
37/**
38 * Handles the picture resolution setting stored in SharedPreferences keyed by
39 * Keys.KEY_PICTURE_SIZE_BACK and Keys.KEY_PICTURE_SIZE_FRONT.
40 */
41public class ResolutionSetting {
42    private static final Log.Tag TAG = new Log.Tag("ResolutionSettings");
43
44    private final SettingsManager mSettingsManager;
45    private final OneCameraManager mOneCameraManager;
46    private final String mResolutionBlackListBack;
47    private final String mResolutionBlackListFront;
48
49    public ResolutionSetting(SettingsManager settingsManager,
50            OneCameraManager oneCameraManager,
51            ContentResolver contentResolver) {
52        mSettingsManager = settingsManager;
53        mOneCameraManager = oneCameraManager;
54
55        mResolutionBlackListBack = GservicesHelper.getBlacklistedResolutionsBack(contentResolver);
56        mResolutionBlackListFront = GservicesHelper.getBlacklistedResolutionsFront(contentResolver);
57    }
58
59    /**
60     * Changes the picture size settings for the cameras with specified facing.
61     * Pick the largest picture size with the specified aspect ratio.
62     *
63     * @param cameraId The specific camera device.
64     * @param aspectRatio The chosen aspect ratio.
65     */
66    public void setPictureAspectRatio(CameraId cameraId, Rational aspectRatio)
67            throws OneCameraAccessException {
68        OneCameraCharacteristics cameraCharacteristics =
69                mOneCameraManager.getOneCameraCharacteristics(cameraId);
70
71        Facing cameraFacing = cameraCharacteristics.getCameraDirection();
72
73        // Pick the largest picture size with the selected aspect ratio and save
74        // the choice for front camera.
75        final String pictureSizeSettingKey = cameraFacing == OneCamera.Facing.FRONT ?
76                Keys.KEY_PICTURE_SIZE_FRONT : Keys.KEY_PICTURE_SIZE_BACK;
77        final String blacklist = cameraFacing == OneCamera.Facing.FRONT ? mResolutionBlackListFront
78                : mResolutionBlackListBack;
79
80        // All resolutions supported by the camera.
81        List<Size> supportedPictureSizes = cameraCharacteristics
82                .getSupportedPictureSizes(ImageFormat.JPEG);
83
84        // Filter sizes which we are showing to the user in settings.
85        // This might also add some new resolution we support on some devices
86        // non-natively.
87        supportedPictureSizes = ResolutionUtil.getDisplayableSizesFromSupported(
88                supportedPictureSizes, cameraFacing == OneCamera.Facing.BACK);
89
90        // Filter the remaining sizes through our backlist.
91        supportedPictureSizes = ResolutionUtil.filterBlackListedSizes(supportedPictureSizes,
92                blacklist);
93
94        final Size chosenPictureSize =
95                ResolutionUtil.getLargestPictureSize(aspectRatio, supportedPictureSizes);
96        mSettingsManager.set(
97                SettingsManager.SCOPE_GLOBAL,
98                pictureSizeSettingKey,
99                SettingsUtil.sizeToSettingString(chosenPictureSize));
100    }
101
102    /**
103     * Reads the picture size setting for the cameras with specified facing.
104     * This specifically avoids reading camera characteristics unless the size
105     * is blacklisted or is not cached to prevent a crash.
106     */
107    public Size getPictureSize(CameraId cameraId, Facing cameraFacing)
108            throws OneCameraAccessException {
109        final String pictureSizeSettingKey = cameraFacing == OneCamera.Facing.FRONT ?
110                Keys.KEY_PICTURE_SIZE_FRONT : Keys.KEY_PICTURE_SIZE_BACK;
111
112        Size pictureSize = null;
113
114        String blacklist = "";
115        if (cameraFacing == OneCamera.Facing.BACK) {
116            blacklist = mResolutionBlackListBack;
117        } else if (cameraFacing == OneCamera.Facing.FRONT) {
118            blacklist = mResolutionBlackListFront;
119        }
120
121        // If there is no saved picture size preference or the saved on is
122        // blacklisted., pick a largest size with 4:3 aspect
123        boolean isPictureSizeSettingSet =
124                mSettingsManager.isSet(SettingsManager.SCOPE_GLOBAL, pictureSizeSettingKey);
125        boolean isPictureSizeBlacklisted = false;
126
127        // If a picture size is set, check whether it's blacklisted.
128        if (isPictureSizeSettingSet) {
129            pictureSize = SettingsUtil.sizeFromSettingString(
130                    mSettingsManager.getString(SettingsManager.SCOPE_GLOBAL,
131                            pictureSizeSettingKey));
132            isPictureSizeBlacklisted = pictureSize == null ||
133                    ResolutionUtil.isBlackListed(pictureSize, blacklist);
134        }
135
136        // Due to b/21758681, it is possible that an invalid picture size has
137        // been saved to the settings. Therefore, picture size is set AND is not
138        // blacklisted, but completely invalid. In these cases, need to take the
139        // fallback, instead of the saved value. This logic should now save a
140        // valid picture size to the settings and self-correct the state of the
141        // settings.
142        final boolean isPictureSizeFromSettingsValid = pictureSize != null &&
143                pictureSize.width() > 0 && pictureSize.height() > 0;
144
145        if (!isPictureSizeSettingSet || isPictureSizeBlacklisted || !isPictureSizeFromSettingsValid) {
146            final Rational aspectRatio = ResolutionUtil.ASPECT_RATIO_4x3;
147
148            OneCameraCharacteristics cameraCharacteristics =
149                    mOneCameraManager.getOneCameraCharacteristics(cameraId);
150
151            final List<Size> supportedPictureSizes =
152                    ResolutionUtil.filterBlackListedSizes(
153                            cameraCharacteristics.getSupportedPictureSizes(ImageFormat.JPEG),
154                            blacklist);
155            final Size fallbackPictureSize =
156                    ResolutionUtil.getLargestPictureSize(aspectRatio, supportedPictureSizes);
157            mSettingsManager.set(
158                    SettingsManager.SCOPE_GLOBAL,
159                    pictureSizeSettingKey,
160                    SettingsUtil.sizeToSettingString(fallbackPictureSize));
161            pictureSize = fallbackPictureSize;
162            Log.e(TAG, "Picture size setting is not set. Choose " + fallbackPictureSize);
163            // Crash here if invariants are violated
164            Preconditions.checkNotNull(fallbackPictureSize);
165            Preconditions.checkState(fallbackPictureSize.width() > 0
166                    && fallbackPictureSize.height() > 0);
167        }
168        return pictureSize;
169    }
170
171    /**
172     * Obtains the preferred picture aspect ratio in terms of the picture size
173     * setting.
174     *
175     * @param cameraId The specific camera device.
176     * @return The preferred picture aspect ratio.
177     * @throws OneCameraAccessException
178     */
179    public Rational getPictureAspectRatio(CameraId cameraId, Facing facing)
180            throws OneCameraAccessException {
181        Size pictureSize = getPictureSize(cameraId, facing);
182        return new Rational(pictureSize.getWidth(), pictureSize.getHeight());
183    }
184}
185