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