1 /* 2 * Copyright (C) 2014 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.app.ActionBar; 20 import android.app.Activity; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.SharedPreferences; 24 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 25 import android.os.Bundle; 26 import android.preference.ListPreference; 27 import android.preference.Preference; 28 import android.preference.Preference.OnPreferenceClickListener; 29 import android.preference.PreferenceFragment; 30 import android.preference.PreferenceGroup; 31 import android.preference.PreferenceScreen; 32 import android.support.v4.app.FragmentActivity; 33 import android.view.MenuItem; 34 35 import com.android.camera.debug.Log; 36 import com.android.camera.settings.SettingsUtil.SelectedPictureSizes; 37 import com.android.camera.settings.SettingsUtil.SelectedVideoQualities; 38 import com.android.camera.util.CameraSettingsActivityHelper; 39 import com.android.camera.util.GoogleHelpHelper; 40 import com.android.camera2.R; 41 import com.android.ex.camera2.portability.CameraAgentFactory; 42 import com.android.ex.camera2.portability.CameraDeviceInfo; 43 import com.android.ex.camera2.portability.Size; 44 45 import java.text.DecimalFormat; 46 import java.util.ArrayList; 47 import java.util.List; 48 49 /** 50 * Provides the settings UI for the Camera app. 51 */ 52 public class CameraSettingsActivity extends FragmentActivity { 53 /** 54 * Used to denote a subsection of the preference tree to display in the 55 * Fragment. For instance, if 'Advanced' key is provided, the advanced 56 * preference section will be treated as the root for display. This is used 57 * to enable activity transitions between preference sections, and allows 58 * back/up stack to operate correctly. 59 */ 60 public static final String PREF_SCREEN_EXTRA = "pref_screen_extra"; 61 62 @Override 63 public void onCreate(Bundle savedInstanceState) { 64 super.onCreate(savedInstanceState); 65 66 ActionBar actionBar = getActionBar(); 67 actionBar.setDisplayHomeAsUpEnabled(true); 68 actionBar.setTitle(R.string.mode_settings); 69 70 String prefKey = getIntent().getStringExtra(PREF_SCREEN_EXTRA); 71 CameraSettingsFragment dialog = new CameraSettingsFragment(); 72 Bundle bundle = new Bundle(1); 73 bundle.putString(PREF_SCREEN_EXTRA, prefKey); 74 dialog.setArguments(bundle); 75 getFragmentManager().beginTransaction().replace(android.R.id.content, dialog).commit(); 76 } 77 78 @Override 79 public boolean onMenuItemSelected(int featureId, MenuItem item) { 80 int itemId = item.getItemId(); 81 if (itemId == android.R.id.home) { 82 finish(); 83 return true; 84 } 85 return true; 86 } 87 88 public static class CameraSettingsFragment extends PreferenceFragment implements 89 OnSharedPreferenceChangeListener { 90 91 public static final String PREF_CATEGORY_RESOLUTION = "pref_category_resolution"; 92 public static final String PREF_CATEGORY_ADVANCED = "pref_category_advanced"; 93 public static final String PREF_LAUNCH_HELP = "pref_launch_help"; 94 private static final Log.Tag TAG = new Log.Tag("SettingsFragment"); 95 private static DecimalFormat sMegaPixelFormat = new DecimalFormat("##0.0"); 96 private String[] mCamcorderProfileNames; 97 private CameraDeviceInfo mInfos; 98 private String mPrefKey; 99 private boolean mGetSubPrefAsRoot = true; 100 101 // Selected resolutions for the different cameras and sizes. 102 private SelectedPictureSizes mOldPictureSizesBack; 103 private SelectedPictureSizes mOldPictureSizesFront; 104 private List<Size> mPictureSizesBack; 105 private List<Size> mPictureSizesFront; 106 private SelectedVideoQualities mVideoQualitiesBack; 107 private SelectedVideoQualities mVideoQualitiesFront; 108 109 @Override 110 public void onCreate(Bundle savedInstanceState) { 111 super.onCreate(savedInstanceState); 112 Bundle arguments = getArguments(); 113 if (arguments != null) { 114 mPrefKey = arguments.getString(PREF_SCREEN_EXTRA); 115 } 116 Context context = this.getActivity().getApplicationContext(); 117 addPreferencesFromResource(R.xml.camera_preferences); 118 119 // Allow the Helper to edit the full preference hierarchy, not the sub 120 // tree we may show as root. See {@link #getPreferenceScreen()}. 121 mGetSubPrefAsRoot = false; 122 CameraSettingsActivityHelper.addAdditionalPreferences(this, context); 123 mGetSubPrefAsRoot = true; 124 125 mCamcorderProfileNames = getResources().getStringArray(R.array.camcorder_profile_names); 126 mInfos = CameraAgentFactory 127 .getAndroidCameraAgent(context, CameraAgentFactory.CameraApi.API_1) 128 .getCameraDeviceInfo(); 129 } 130 131 @Override 132 public void onResume() { 133 super.onResume(); 134 final Activity activity = this.getActivity(); 135 136 // Load the camera sizes. 137 loadSizes(); 138 139 // Make sure to hide settings for cameras that don't exist on this 140 // device. 141 setVisibilities(); 142 143 // Put in the summaries for the currently set values. 144 final PreferenceScreen resolutionScreen = 145 (PreferenceScreen) findPreference(PREF_CATEGORY_RESOLUTION); 146 fillEntriesAndSummaries(resolutionScreen); 147 setPreferenceScreenIntent(resolutionScreen); 148 149 final PreferenceScreen advancedScreen = 150 (PreferenceScreen) findPreference(PREF_CATEGORY_ADVANCED); 151 setPreferenceScreenIntent(advancedScreen); 152 153 Preference helpPref = findPreference(PREF_LAUNCH_HELP); 154 helpPref.setOnPreferenceClickListener( 155 new OnPreferenceClickListener() { 156 @Override 157 public boolean onPreferenceClick(Preference preference) { 158 GoogleHelpHelper.launchGoogleHelp(activity); 159 return true; 160 } 161 }); 162 getPreferenceScreen().getSharedPreferences() 163 .registerOnSharedPreferenceChangeListener(this); 164 } 165 166 /** 167 * Configure home-as-up for sub-screens. 168 */ 169 private void setPreferenceScreenIntent(final PreferenceScreen preferenceScreen) { 170 Intent intent = new Intent(getActivity(), CameraSettingsActivity.class); 171 intent.putExtra(PREF_SCREEN_EXTRA, preferenceScreen.getKey()); 172 preferenceScreen.setIntent(intent); 173 } 174 175 /** 176 * This override allows the CameraSettingsFragment to be reused for 177 * different nested PreferenceScreens within the single camera 178 * preferences XML resource. If the fragment is constructed with a 179 * desired preference key (delivered via an extra in the creation 180 * intent), it is used to look up the nested PreferenceScreen and 181 * returned here. 182 */ 183 @Override 184 public PreferenceScreen getPreferenceScreen() { 185 PreferenceScreen root = super.getPreferenceScreen(); 186 if (!mGetSubPrefAsRoot || mPrefKey == null || root == null) { 187 return root; 188 } else { 189 PreferenceScreen match = findByKey(root, mPrefKey); 190 if (match != null) { 191 return match; 192 } else { 193 throw new RuntimeException("key " + mPrefKey + " not found"); 194 } 195 } 196 } 197 198 private PreferenceScreen findByKey(PreferenceScreen parent, String key) { 199 if (key.equals(parent.getKey())) { 200 return parent; 201 } else { 202 for (int i = 0; i < parent.getPreferenceCount(); i++) { 203 Preference child = parent.getPreference(i); 204 if (child instanceof PreferenceScreen) { 205 PreferenceScreen match = findByKey((PreferenceScreen) child, key); 206 if (match != null) { 207 return match; 208 } 209 } 210 } 211 return null; 212 } 213 } 214 215 /** 216 * Depending on camera availability on the device, this removes settings 217 * for cameras the device doesn't have. 218 */ 219 private void setVisibilities() { 220 PreferenceGroup resolutions = 221 (PreferenceGroup) findPreference(PREF_CATEGORY_RESOLUTION); 222 if (mPictureSizesBack == null) { 223 recursiveDelete(resolutions, 224 findPreference(Keys.KEY_PICTURE_SIZE_BACK)); 225 recursiveDelete(resolutions, 226 findPreference(Keys.KEY_VIDEO_QUALITY_BACK)); 227 } 228 if (mPictureSizesFront == null) { 229 recursiveDelete(resolutions, 230 findPreference(Keys.KEY_PICTURE_SIZE_FRONT)); 231 recursiveDelete(resolutions, 232 findPreference(Keys.KEY_VIDEO_QUALITY_FRONT)); 233 } 234 } 235 236 /** 237 * Recursively go through settings and fill entries and summaries of our 238 * preferences. 239 */ 240 private void fillEntriesAndSummaries(PreferenceGroup group) { 241 for (int i = 0; i < group.getPreferenceCount(); ++i) { 242 Preference pref = group.getPreference(i); 243 if (pref instanceof PreferenceGroup) { 244 fillEntriesAndSummaries((PreferenceGroup) pref); 245 } 246 setSummary(pref); 247 setEntries(pref); 248 } 249 } 250 251 /** 252 * Recursively traverses the tree from the given group as the route and 253 * tries to delete the preference. Traversal stops once the preference 254 * was found and removed. 255 */ 256 private boolean recursiveDelete(PreferenceGroup group, Preference preference) { 257 if (group == null) { 258 Log.d(TAG, "attempting to delete from null preference group"); 259 return false; 260 } 261 if (preference == null) { 262 Log.d(TAG, "attempting to delete null preference"); 263 return false; 264 } 265 if (group.removePreference(preference)) { 266 // Removal was successful. 267 return true; 268 } 269 270 for (int i = 0; i < group.getPreferenceCount(); ++i) { 271 Preference pref = group.getPreference(i); 272 if (pref instanceof PreferenceGroup) { 273 if (recursiveDelete((PreferenceGroup) pref, preference)) { 274 return true; 275 } 276 } 277 } 278 return false; 279 } 280 281 @Override 282 public void onPause() { 283 super.onPause(); 284 getPreferenceScreen().getSharedPreferences() 285 .unregisterOnSharedPreferenceChangeListener(this); 286 } 287 288 @Override 289 public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 290 setSummary(findPreference(key)); 291 } 292 293 /** 294 * Set the entries for the given preference. The given preference needs 295 * to be a {@link ListPreference} 296 */ 297 private void setEntries(Preference preference) { 298 if (!(preference instanceof ListPreference)) { 299 return; 300 } 301 302 ListPreference listPreference = (ListPreference) preference; 303 if (listPreference.getKey().equals(Keys.KEY_PICTURE_SIZE_BACK)) { 304 setEntriesForSelection(mPictureSizesBack, listPreference); 305 } else if (listPreference.getKey().equals(Keys.KEY_PICTURE_SIZE_FRONT)) { 306 setEntriesForSelection(mPictureSizesFront, listPreference); 307 } else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_BACK)) { 308 setEntriesForSelection(mVideoQualitiesBack, listPreference); 309 } else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_FRONT)) { 310 setEntriesForSelection(mVideoQualitiesFront, listPreference); 311 } 312 } 313 314 /** 315 * Set the summary for the given preference. The given preference needs 316 * to be a {@link ListPreference}. 317 */ 318 private void setSummary(Preference preference) { 319 if (!(preference instanceof ListPreference)) { 320 return; 321 } 322 323 ListPreference listPreference = (ListPreference) preference; 324 if (listPreference.getKey().equals(Keys.KEY_PICTURE_SIZE_BACK)) { 325 setSummaryForSelection(mOldPictureSizesBack, mPictureSizesBack, listPreference); 326 } else if (listPreference.getKey().equals(Keys.KEY_PICTURE_SIZE_FRONT)) { 327 setSummaryForSelection(mOldPictureSizesFront, mPictureSizesFront, listPreference); 328 } else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_BACK)) { 329 setSummaryForSelection(mVideoQualitiesBack, listPreference); 330 } else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_FRONT)) { 331 setSummaryForSelection(mVideoQualitiesFront, listPreference); 332 } else { 333 listPreference.setSummary(listPreference.getEntry()); 334 } 335 } 336 337 /** 338 * Sets the entries for the given list preference. 339 * 340 * @param selectedSizes The possible S,M,L entries the user can 341 * choose from. 342 * @param preference The preference to set the entries for. 343 */ 344 private void setEntriesForSelection(List<Size> selectedSizes, 345 ListPreference preference) { 346 if (selectedSizes == null) { 347 return; 348 } 349 350 String[] entries = new String[selectedSizes.size()]; 351 String[] entryValues = new String[selectedSizes.size()]; 352 for (int i = 0; i < selectedSizes.size(); i++) { 353 Size size = selectedSizes.get(i); 354 entries[i] = getSizeSummaryString(size); 355 entryValues[i] = SettingsUtil.sizeToSetting(size); 356 } 357 preference.setEntries(entries); 358 preference.setEntryValues(entryValues); 359 } 360 361 /** 362 * Sets the entries for the given list preference. 363 * 364 * @param selectedQualities The possible S,M,L entries the user can 365 * choose from. 366 * @param preference The preference to set the entries for. 367 */ 368 private void setEntriesForSelection(SelectedVideoQualities selectedQualities, 369 ListPreference preference) { 370 if (selectedQualities == null) { 371 return; 372 } 373 374 // Avoid adding double entries at the bottom of the list which 375 // indicates that not at least 3 qualities are supported. 376 ArrayList<String> entries = new ArrayList<String>(); 377 entries.add(mCamcorderProfileNames[selectedQualities.large]); 378 if (selectedQualities.medium != selectedQualities.large) { 379 entries.add(mCamcorderProfileNames[selectedQualities.medium]); 380 } 381 if (selectedQualities.small != selectedQualities.medium) { 382 entries.add(mCamcorderProfileNames[selectedQualities.small]); 383 } 384 preference.setEntries(entries.toArray(new String[0])); 385 } 386 387 /** 388 * Sets the summary for the given list preference. 389 * 390 * @param oldPictureSizes The old selected picture sizes for small medium and large 391 * @param displayableSizes The human readable preferred sizes 392 * @param preference The preference for which to set the summary. 393 */ 394 private void setSummaryForSelection(SelectedPictureSizes oldPictureSizes, 395 List<Size> displayableSizes, ListPreference preference) { 396 if (oldPictureSizes == null) { 397 return; 398 } 399 400 String setting = preference.getValue(); 401 Size selectedSize = oldPictureSizes.getFromSetting(setting, displayableSizes); 402 403 preference.setSummary(getSizeSummaryString(selectedSize)); 404 } 405 406 /** 407 * Sets the summary for the given list preference. 408 * 409 * @param selectedQualities The selected video qualities. 410 * @param preference The preference for which to set the summary. 411 */ 412 private void setSummaryForSelection(SelectedVideoQualities selectedQualities, 413 ListPreference preference) { 414 if (selectedQualities == null) { 415 return; 416 } 417 418 int selectedQuality = selectedQualities.getFromSetting(preference.getValue()); 419 preference.setSummary(mCamcorderProfileNames[selectedQuality]); 420 } 421 422 /** 423 * This method gets the selected picture sizes for S,M,L and populates 424 * {@link #mPictureSizesBack}, {@link #mPictureSizesFront}, 425 * {@link #mVideoQualitiesBack} and {@link #mVideoQualitiesFront} 426 * accordingly. 427 */ 428 private void loadSizes() { 429 if (mInfos == null) { 430 Log.w(TAG, "null deviceInfo, cannot display resolution sizes"); 431 return; 432 } 433 // Back camera. 434 int backCameraId = SettingsUtil.getCameraId(mInfos, SettingsUtil.CAMERA_FACING_BACK); 435 if (backCameraId >= 0) { 436 List<Size> sizes = CameraPictureSizesCacher.getSizesForCamera(backCameraId, 437 this.getActivity().getApplicationContext()); 438 if (sizes != null) { 439 mOldPictureSizesBack = SettingsUtil.getSelectedCameraPictureSizes(sizes, 440 backCameraId); 441 mPictureSizesBack = ResolutionUtil 442 .getDisplayableSizesFromSupported(sizes, true); 443 } 444 mVideoQualitiesBack = SettingsUtil.getSelectedVideoQualities(backCameraId); 445 } else { 446 mPictureSizesBack = null; 447 mVideoQualitiesBack = null; 448 } 449 450 // Front camera. 451 int frontCameraId = SettingsUtil.getCameraId(mInfos, SettingsUtil.CAMERA_FACING_FRONT); 452 if (frontCameraId >= 0) { 453 List<Size> sizes = CameraPictureSizesCacher.getSizesForCamera(frontCameraId, 454 this.getActivity().getApplicationContext()); 455 if (sizes != null) { 456 mOldPictureSizesFront= SettingsUtil.getSelectedCameraPictureSizes(sizes, 457 frontCameraId); 458 mPictureSizesFront = 459 ResolutionUtil.getDisplayableSizesFromSupported(sizes, false); 460 } 461 mVideoQualitiesFront = SettingsUtil.getSelectedVideoQualities(frontCameraId); 462 } else { 463 mPictureSizesFront = null; 464 mVideoQualitiesFront = null; 465 } 466 } 467 468 /** 469 * @param size The photo resolution. 470 * @return A human readable and translated string for labeling the 471 * picture size in megapixels. 472 */ 473 private String getSizeSummaryString(Size size) { 474 Size approximateSize = ResolutionUtil.getApproximateSize(size); 475 String megaPixels = sMegaPixelFormat.format((size.width() * size.height()) / 1e6); 476 int numerator = ResolutionUtil.aspectRatioNumerator(approximateSize); 477 int denominator = ResolutionUtil.aspectRatioDenominator(approximateSize); 478 String result = getResources().getString( 479 R.string.setting_summary_aspect_ratio_and_megapixels, numerator, denominator, 480 megaPixels); 481 return result; 482 } 483 } 484 } 485