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