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 
17 package com.android.camera.settings;
18 
19 import android.content.ContentResolver;
20 import android.graphics.ImageFormat;
21 
22 import com.android.camera.debug.Log;
23 import com.android.camera.device.CameraId;
24 import com.android.camera.exif.Rational;
25 import com.android.camera.one.OneCamera;
26 import com.android.camera.one.OneCamera.Facing;
27 import com.android.camera.one.OneCameraAccessException;
28 import com.android.camera.one.OneCameraCharacteristics;
29 import com.android.camera.one.OneCameraManager;
30 import com.android.camera.util.GservicesHelper;
31 import com.android.camera.util.Size;
32 
33 import com.google.common.base.Preconditions;
34 
35 import 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  */
41 public 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 
ResolutionSetting(SettingsManager settingsManager, OneCameraManager oneCameraManager, ContentResolver contentResolver)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      */
setPictureAspectRatio(CameraId cameraId, Rational aspectRatio)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      */
getPictureSize(CameraId cameraId, Facing cameraFacing)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      */
getPictureAspectRatio(CameraId cameraId, Facing facing)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