• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.SharedPreferences;
22 import android.content.SharedPreferences.Editor;
23 import android.hardware.Camera.CameraInfo;
24 import android.hardware.Camera.Parameters;
25 import android.hardware.Camera.Size;
26 import android.media.CamcorderProfile;
27 import android.util.Log;
28 
29 import java.util.ArrayList;
30 import java.util.List;
31 
32 /**
33  *  Provides utilities and keys for Camera settings.
34  */
35 public class CameraSettings {
36     private static final int NOT_FOUND = -1;
37 
38     public static final String KEY_VERSION = "pref_version_key";
39     public static final String KEY_LOCAL_VERSION = "pref_local_version_key";
40     public static final String KEY_RECORD_LOCATION = RecordLocationPreference.KEY;
41     public static final String KEY_VIDEO_QUALITY = "pref_video_quality_key";
42     public static final String KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL = "pref_video_time_lapse_frame_interval_key";
43     public static final String KEY_PICTURE_SIZE = "pref_camera_picturesize_key";
44     public static final String KEY_JPEG_QUALITY = "pref_camera_jpegquality_key";
45     public static final String KEY_FOCUS_MODE = "pref_camera_focusmode_key";
46     public static final String KEY_FLASH_MODE = "pref_camera_flashmode_key";
47     public static final String KEY_VIDEOCAMERA_FLASH_MODE = "pref_camera_video_flashmode_key";
48     public static final String KEY_WHITE_BALANCE = "pref_camera_whitebalance_key";
49     public static final String KEY_SCENE_MODE = "pref_camera_scenemode_key";
50     public static final String KEY_EXPOSURE = "pref_camera_exposure_key";
51     public static final String KEY_VIDEO_EFFECT = "pref_video_effect_key";
52     public static final String KEY_CAMERA_ID = "pref_camera_id_key";
53     public static final String KEY_TAP_TO_FOCUS_PROMPT_SHOWN = "pref_tap_to_focus_prompt_shown_key";
54 
55     public static final String EXPOSURE_DEFAULT_VALUE = "0";
56 
57     public static final int CURRENT_VERSION = 5;
58     public static final int CURRENT_LOCAL_VERSION = 2;
59 
60     public static final int DEFAULT_VIDEO_DURATION = 0; // no limit
61 
62     private static final String TAG = "CameraSettings";
63 
64     private final Context mContext;
65     private final Parameters mParameters;
66     private final CameraInfo[] mCameraInfo;
67     private final int mCameraId;
68 
CameraSettings(Activity activity, Parameters parameters, int cameraId, CameraInfo[] cameraInfo)69     public CameraSettings(Activity activity, Parameters parameters,
70                           int cameraId, CameraInfo[] cameraInfo) {
71         mContext = activity;
72         mParameters = parameters;
73         mCameraId = cameraId;
74         mCameraInfo = cameraInfo;
75     }
76 
getPreferenceGroup(int preferenceRes)77     public PreferenceGroup getPreferenceGroup(int preferenceRes) {
78         PreferenceInflater inflater = new PreferenceInflater(mContext);
79         PreferenceGroup group =
80                 (PreferenceGroup) inflater.inflate(preferenceRes);
81         initPreference(group);
82         return group;
83     }
84 
getDefaultVideoQuality(int cameraId, String defaultQuality)85     public static String getDefaultVideoQuality(int cameraId,
86             String defaultQuality) {
87         int quality = Integer.valueOf(defaultQuality);
88         if (CamcorderProfile.hasProfile(cameraId, quality)) {
89             return defaultQuality;
90         }
91         return Integer.toString(CamcorderProfile.QUALITY_HIGH);
92     }
93 
initialCameraPictureSize( Context context, Parameters parameters)94     public static void initialCameraPictureSize(
95             Context context, Parameters parameters) {
96         // When launching the camera app first time, we will set the picture
97         // size to the first one in the list defined in "arrays.xml" and is also
98         // supported by the driver.
99         List<Size> supported = parameters.getSupportedPictureSizes();
100         if (supported == null) return;
101         for (String candidate : context.getResources().getStringArray(
102                 R.array.pref_camera_picturesize_entryvalues)) {
103             if (setCameraPictureSize(candidate, supported, parameters)) {
104                 SharedPreferences.Editor editor = ComboPreferences
105                         .get(context).edit();
106                 editor.putString(KEY_PICTURE_SIZE, candidate);
107                 editor.apply();
108                 return;
109             }
110         }
111         Log.e(TAG, "No supported picture size found");
112     }
113 
removePreferenceFromScreen( PreferenceGroup group, String key)114     public static void removePreferenceFromScreen(
115             PreferenceGroup group, String key) {
116         removePreference(group, key);
117     }
118 
setCameraPictureSize( String candidate, List<Size> supported, Parameters parameters)119     public static boolean setCameraPictureSize(
120             String candidate, List<Size> supported, Parameters parameters) {
121         int index = candidate.indexOf('x');
122         if (index == NOT_FOUND) return false;
123         int width = Integer.parseInt(candidate.substring(0, index));
124         int height = Integer.parseInt(candidate.substring(index + 1));
125         for (Size size : supported) {
126             if (size.width == width && size.height == height) {
127                 parameters.setPictureSize(width, height);
128                 return true;
129             }
130         }
131         return false;
132     }
133 
initPreference(PreferenceGroup group)134     private void initPreference(PreferenceGroup group) {
135         ListPreference videoQuality = group.findPreference(KEY_VIDEO_QUALITY);
136         ListPreference timeLapseInterval = group.findPreference(KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL);
137         ListPreference pictureSize = group.findPreference(KEY_PICTURE_SIZE);
138         ListPreference whiteBalance =  group.findPreference(KEY_WHITE_BALANCE);
139         ListPreference sceneMode = group.findPreference(KEY_SCENE_MODE);
140         ListPreference flashMode = group.findPreference(KEY_FLASH_MODE);
141         ListPreference focusMode = group.findPreference(KEY_FOCUS_MODE);
142         ListPreference exposure = group.findPreference(KEY_EXPOSURE);
143         IconListPreference cameraIdPref =
144                 (IconListPreference) group.findPreference(KEY_CAMERA_ID);
145         ListPreference videoFlashMode =
146                 group.findPreference(KEY_VIDEOCAMERA_FLASH_MODE);
147         ListPreference videoEffect = group.findPreference(KEY_VIDEO_EFFECT);
148 
149         // Since the screen could be loaded from different resources, we need
150         // to check if the preference is available here
151         if (videoQuality != null) {
152             filterUnsupportedOptions(group, videoQuality, getSupportedVideoQuality());
153         }
154 
155         if (pictureSize != null) {
156             filterUnsupportedOptions(group, pictureSize, sizeListToStringList(
157                     mParameters.getSupportedPictureSizes()));
158         }
159         if (whiteBalance != null) {
160             filterUnsupportedOptions(group,
161                     whiteBalance, mParameters.getSupportedWhiteBalance());
162         }
163         if (sceneMode != null) {
164             filterUnsupportedOptions(group,
165                     sceneMode, mParameters.getSupportedSceneModes());
166         }
167         if (flashMode != null) {
168             filterUnsupportedOptions(group,
169                     flashMode, mParameters.getSupportedFlashModes());
170         }
171         if (focusMode != null) {
172             if (mParameters.getMaxNumFocusAreas() == 0) {
173                 filterUnsupportedOptions(group,
174                         focusMode, mParameters.getSupportedFocusModes());
175             } else {
176                 // Remove the focus mode if we can use tap-to-focus.
177                 removePreference(group, focusMode.getKey());
178             }
179         }
180         if (videoFlashMode != null) {
181             filterUnsupportedOptions(group,
182                     videoFlashMode, mParameters.getSupportedFlashModes());
183         }
184         if (exposure != null) buildExposureCompensation(group, exposure);
185         if (cameraIdPref != null) buildCameraId(group, cameraIdPref);
186 
187         if (timeLapseInterval != null) resetIfInvalid(timeLapseInterval);
188         if (videoEffect != null) {
189             initVideoEffect(group, videoEffect);
190             resetIfInvalid(videoEffect);
191         }
192     }
193 
buildExposureCompensation( PreferenceGroup group, ListPreference exposure)194     private void buildExposureCompensation(
195             PreferenceGroup group, ListPreference exposure) {
196         int max = mParameters.getMaxExposureCompensation();
197         int min = mParameters.getMinExposureCompensation();
198         if (max == 0 && min == 0) {
199             removePreference(group, exposure.getKey());
200             return;
201         }
202         float step = mParameters.getExposureCompensationStep();
203 
204         // show only integer values for exposure compensation
205         int maxValue = (int) Math.floor(max * step);
206         int minValue = (int) Math.ceil(min * step);
207         CharSequence entries[] = new CharSequence[maxValue - minValue + 1];
208         CharSequence entryValues[] = new CharSequence[maxValue - minValue + 1];
209         for (int i = minValue; i <= maxValue; ++i) {
210             entryValues[maxValue - i] = Integer.toString(Math.round(i / step));
211             StringBuilder builder = new StringBuilder();
212             if (i > 0) builder.append('+');
213             entries[maxValue - i] = builder.append(i).toString();
214         }
215         exposure.setEntries(entries);
216         exposure.setEntryValues(entryValues);
217     }
218 
buildCameraId( PreferenceGroup group, IconListPreference preference)219     private void buildCameraId(
220             PreferenceGroup group, IconListPreference preference) {
221         int numOfCameras = mCameraInfo.length;
222         if (numOfCameras < 2) {
223             removePreference(group, preference.getKey());
224             return;
225         }
226 
227         CharSequence[] entryValues = new CharSequence[2];
228         for (int i = 0; i < mCameraInfo.length; ++i) {
229             int index =
230                     (mCameraInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT)
231                     ? CameraInfo.CAMERA_FACING_FRONT
232                     : CameraInfo.CAMERA_FACING_BACK;
233             if (entryValues[index] == null) {
234                 entryValues[index] = "" + i;
235                 if (entryValues[((index == 1) ? 0 : 1)] != null) break;
236             }
237         }
238         preference.setEntryValues(entryValues);
239     }
240 
removePreference(PreferenceGroup group, String key)241     private static boolean removePreference(PreferenceGroup group, String key) {
242         for (int i = 0, n = group.size(); i < n; i++) {
243             CameraPreference child = group.get(i);
244             if (child instanceof PreferenceGroup) {
245                 if (removePreference((PreferenceGroup) child, key)) {
246                     return true;
247                 }
248             }
249             if (child instanceof ListPreference &&
250                     ((ListPreference) child).getKey().equals(key)) {
251                 group.removePreference(i);
252                 return true;
253             }
254         }
255         return false;
256     }
257 
filterUnsupportedOptions(PreferenceGroup group, ListPreference pref, List<String> supported)258     private void filterUnsupportedOptions(PreferenceGroup group,
259             ListPreference pref, List<String> supported) {
260 
261         // Remove the preference if the parameter is not supported or there is
262         // only one options for the settings.
263         if (supported == null || supported.size() <= 1) {
264             removePreference(group, pref.getKey());
265             return;
266         }
267 
268         pref.filterUnsupported(supported);
269         if (pref.getEntries().length <= 1) {
270             removePreference(group, pref.getKey());
271             return;
272         }
273 
274         resetIfInvalid(pref);
275     }
276 
resetIfInvalid(ListPreference pref)277     private void resetIfInvalid(ListPreference pref) {
278         // Set the value to the first entry if it is invalid.
279         String value = pref.getValue();
280         if (pref.findIndexOfValue(value) == NOT_FOUND) {
281             pref.setValueIndex(0);
282         }
283     }
284 
sizeListToStringList(List<Size> sizes)285     private static List<String> sizeListToStringList(List<Size> sizes) {
286         ArrayList<String> list = new ArrayList<String>();
287         for (Size size : sizes) {
288             list.add(String.format("%dx%d", size.width, size.height));
289         }
290         return list;
291     }
292 
upgradeLocalPreferences(SharedPreferences pref)293     public static void upgradeLocalPreferences(SharedPreferences pref) {
294         int version;
295         try {
296             version = pref.getInt(KEY_LOCAL_VERSION, 0);
297         } catch (Exception ex) {
298             version = 0;
299         }
300         if (version == CURRENT_LOCAL_VERSION) return;
301 
302         SharedPreferences.Editor editor = pref.edit();
303         if (version == 1) {
304             // We use numbers to represent the quality now. The quality definition is identical to
305             // that of CamcorderProfile.java.
306             editor.remove("pref_video_quality_key");
307         }
308         editor.putInt(KEY_LOCAL_VERSION, CURRENT_LOCAL_VERSION);
309         editor.apply();
310     }
311 
upgradeGlobalPreferences(SharedPreferences pref)312     public static void upgradeGlobalPreferences(SharedPreferences pref) {
313         int version;
314         try {
315             version = pref.getInt(KEY_VERSION, 0);
316         } catch (Exception ex) {
317             version = 0;
318         }
319         if (version == CURRENT_VERSION) return;
320 
321         SharedPreferences.Editor editor = pref.edit();
322         if (version == 0) {
323             // We won't use the preference which change in version 1.
324             // So, just upgrade to version 1 directly
325             version = 1;
326         }
327         if (version == 1) {
328             // Change jpeg quality {65,75,85} to {normal,fine,superfine}
329             String quality = pref.getString(KEY_JPEG_QUALITY, "85");
330             if (quality.equals("65")) {
331                 quality = "normal";
332             } else if (quality.equals("75")) {
333                 quality = "fine";
334             } else {
335                 quality = "superfine";
336             }
337             editor.putString(KEY_JPEG_QUALITY, quality);
338             version = 2;
339         }
340         if (version == 2) {
341             editor.putString(KEY_RECORD_LOCATION,
342                     pref.getBoolean(KEY_RECORD_LOCATION, false)
343                     ? RecordLocationPreference.VALUE_ON
344                     : RecordLocationPreference.VALUE_NONE);
345             version = 3;
346         }
347         if (version == 3) {
348             // Just use video quality to replace it and
349             // ignore the current settings.
350             editor.remove("pref_camera_videoquality_key");
351             editor.remove("pref_camera_video_duration_key");
352         }
353 
354         editor.putInt(KEY_VERSION, CURRENT_VERSION);
355         editor.apply();
356     }
357 
readPreferredCameraId(SharedPreferences pref)358     public static int readPreferredCameraId(SharedPreferences pref) {
359         return Integer.parseInt(pref.getString(KEY_CAMERA_ID, "0"));
360     }
361 
writePreferredCameraId(SharedPreferences pref, int cameraId)362     public static void writePreferredCameraId(SharedPreferences pref,
363             int cameraId) {
364         Editor editor = pref.edit();
365         editor.putString(KEY_CAMERA_ID, Integer.toString(cameraId));
366         editor.apply();
367     }
368 
readExposure(ComboPreferences preferences)369     public static int readExposure(ComboPreferences preferences) {
370         String exposure = preferences.getString(
371                 CameraSettings.KEY_EXPOSURE,
372                 EXPOSURE_DEFAULT_VALUE);
373         try {
374             return Integer.parseInt(exposure);
375         } catch (Exception ex) {
376             Log.e(TAG, "Invalid exposure: " + exposure);
377         }
378         return 0;
379     }
380 
readEffectType(SharedPreferences pref)381     public static int readEffectType(SharedPreferences pref) {
382         String effectSelection = pref.getString(KEY_VIDEO_EFFECT, "none");
383         if (effectSelection.equals("none")) {
384             return EffectsRecorder.EFFECT_NONE;
385         } else if (effectSelection.startsWith("goofy_face")) {
386             return EffectsRecorder.EFFECT_GOOFY_FACE;
387         } else if (effectSelection.startsWith("backdropper")) {
388             return EffectsRecorder.EFFECT_BACKDROPPER;
389         }
390         Log.e(TAG, "Invalid effect selection: " + effectSelection);
391         return EffectsRecorder.EFFECT_NONE;
392     }
393 
readEffectParameter(SharedPreferences pref)394     public static Object readEffectParameter(SharedPreferences pref) {
395         String effectSelection = pref.getString(KEY_VIDEO_EFFECT, "none");
396         if (effectSelection.equals("none")) {
397             return null;
398         }
399         int separatorIndex = effectSelection.indexOf('/');
400         String effectParameter =
401                 effectSelection.substring(separatorIndex + 1);
402         if (effectSelection.startsWith("goofy_face")) {
403             if (effectParameter.equals("squeeze")) {
404                 return EffectsRecorder.EFFECT_GF_SQUEEZE;
405             } else if (effectParameter.equals("big_eyes")) {
406                 return EffectsRecorder.EFFECT_GF_BIG_EYES;
407             } else if (effectParameter.equals("big_mouth")) {
408                 return EffectsRecorder.EFFECT_GF_BIG_MOUTH;
409             } else if (effectParameter.equals("small_mouth")) {
410                 return EffectsRecorder.EFFECT_GF_SMALL_MOUTH;
411             } else if (effectParameter.equals("big_nose")) {
412                 return EffectsRecorder.EFFECT_GF_BIG_NOSE;
413             } else if (effectParameter.equals("small_eyes")) {
414                 return EffectsRecorder.EFFECT_GF_SMALL_EYES;
415             }
416         } else if (effectSelection.startsWith("backdropper")) {
417             // Parameter is a string that either encodes the URI to use,
418             // or specifies 'gallery'.
419             return effectParameter;
420         }
421 
422         Log.e(TAG, "Invalid effect selection: " + effectSelection);
423         return null;
424     }
425 
426 
restorePreferences(Context context, ComboPreferences preferences, Parameters parameters)427     public static void restorePreferences(Context context,
428             ComboPreferences preferences, Parameters parameters) {
429         int currentCameraId = readPreferredCameraId(preferences);
430 
431         // Clear the preferences of both cameras.
432         int backCameraId = CameraHolder.instance().getBackCameraId();
433         if (backCameraId != -1) {
434             preferences.setLocalId(context, backCameraId);
435             Editor editor = preferences.edit();
436             editor.clear();
437             editor.apply();
438         }
439         int frontCameraId = CameraHolder.instance().getFrontCameraId();
440         if (frontCameraId != -1) {
441             preferences.setLocalId(context, frontCameraId);
442             Editor editor = preferences.edit();
443             editor.clear();
444             editor.apply();
445         }
446 
447         // Switch back to the preferences of the current camera. Otherwise,
448         // we may write the preference to wrong camera later.
449         preferences.setLocalId(context, currentCameraId);
450 
451         upgradeGlobalPreferences(preferences.getGlobal());
452         upgradeLocalPreferences(preferences.getLocal());
453 
454         // Write back the current camera id because parameters are related to
455         // the camera. Otherwise, we may switch to the front camera but the
456         // initial picture size is that of the back camera.
457         initialCameraPictureSize(context, parameters);
458         writePreferredCameraId(preferences, currentCameraId);
459     }
460 
getSupportedVideoQuality()461     private ArrayList<String> getSupportedVideoQuality() {
462         ArrayList<String> supported = new ArrayList<String>();
463         // Check for supported quality
464         if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_1080P)) {
465             supported.add(Integer.toString(CamcorderProfile.QUALITY_1080P));
466         }
467         if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_720P)) {
468             supported.add(Integer.toString(CamcorderProfile.QUALITY_720P));
469         }
470         if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_480P)) {
471             supported.add(Integer.toString(CamcorderProfile.QUALITY_480P));
472         }
473 
474         return supported;
475     }
476 
initVideoEffect(PreferenceGroup group, ListPreference videoEffect)477     private void initVideoEffect(PreferenceGroup group, ListPreference videoEffect) {
478         CharSequence[] values = videoEffect.getEntryValues();
479 
480         boolean goofyFaceSupported =
481                 EffectsRecorder.isEffectSupported(EffectsRecorder.EFFECT_GOOFY_FACE);
482         boolean backdropperSupported =
483                 EffectsRecorder.isEffectSupported(EffectsRecorder.EFFECT_BACKDROPPER) &&
484                 mParameters.isAutoExposureLockSupported() &&
485                 mParameters.isAutoWhiteBalanceLockSupported();
486 
487         ArrayList<String> supported = new ArrayList<String>();
488         for (CharSequence value : values) {
489             String effectSelection = value.toString();
490             if (!goofyFaceSupported && effectSelection.startsWith("goofy_face")) continue;
491             if (!backdropperSupported && effectSelection.startsWith("backdropper")) continue;
492             supported.add(effectSelection);
493         }
494 
495         filterUnsupportedOptions(group, videoEffect, supported);
496     }
497 }
498