package com.android.camera.settings;
|
|
/*
|
* Copyright (C) 2014 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
import android.app.ActionBar;
|
import android.app.Activity;
|
import android.content.Context;
|
import android.content.Intent;
|
import android.content.SharedPreferences;
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
import android.os.Bundle;
|
import android.preference.ListPreference;
|
import android.preference.Preference;
|
import android.preference.Preference.OnPreferenceClickListener;
|
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceGroup;
|
import android.preference.PreferenceScreen;
|
import androidx.fragment.app.FragmentActivity;
|
import android.Manifest;
|
import android.content.pm.PackageManager;
|
import android.preference.SwitchPreference;
|
import android.view.MenuItem;
|
|
import com.android.camera.FatalErrorHandler;
|
import com.android.camera.FatalErrorHandlerImpl;
|
import com.android.camera.debug.Log;
|
import com.android.camera.device.CameraId;
|
import com.android.camera.one.OneCamera.Facing;
|
import com.android.camera.one.OneCameraAccessException;
|
import com.android.camera.one.OneCameraCharacteristics;
|
import com.android.camera.one.OneCameraException;
|
import com.android.camera.one.OneCameraManager;
|
import com.android.camera.one.OneCameraModule;
|
import com.android.camera.settings.PictureSizeLoader.PictureSizes;
|
import com.android.camera.settings.SettingsUtil.SelectedVideoQualities;
|
import com.android.camera.util.CameraSettingsActivityHelper;
|
import com.android.camera.util.GoogleHelpHelper;
|
import com.android.camera.util.Size;
|
import com.android.camera2.R;
|
import com.android.ex.camera2.portability.CameraAgentFactory;
|
import com.android.ex.camera2.portability.CameraDeviceInfo;
|
|
import java.text.DecimalFormat;
|
import java.util.ArrayList;
|
import java.util.List;
|
|
/**
|
* Provides the settings UI for the Camera app.
|
*/
|
public class CameraSettingsActivity extends FragmentActivity {
|
|
/**
|
* Used to denote a subsection of the preference tree to display in the
|
* Fragment. For instance, if 'Advanced' key is provided, the advanced
|
* preference section will be treated as the root for display. This is used
|
* to enable activity transitions between preference sections, and allows
|
* back/up stack to operate correctly.
|
*/
|
public static final String PREF_SCREEN_EXTRA = "pref_screen_extra";
|
public static final String HIDE_ADVANCED_SCREEN = "hide_advanced";
|
private OneCameraManager mOneCameraManager;
|
|
|
|
@Override
|
public void onCreate(Bundle savedInstanceState) {
|
super.onCreate(savedInstanceState);
|
|
FatalErrorHandler fatalErrorHandler = new FatalErrorHandlerImpl(this);
|
boolean hideAdvancedScreen = false;
|
|
try {
|
mOneCameraManager = OneCameraModule.provideOneCameraManager();
|
} catch (OneCameraException e) {
|
// Log error and continue. Modules requiring OneCamera should check
|
// and handle if null by showing error dialog or other treatment.
|
fatalErrorHandler.onGenericCameraAccessFailure();
|
}
|
|
// Check if manual exposure is available, so we can decide whether to
|
// display Advanced screen.
|
try {
|
CameraId frontCameraId = mOneCameraManager.findFirstCameraFacing(Facing.FRONT);
|
CameraId backCameraId = mOneCameraManager.findFirstCameraFacing(Facing.BACK);
|
|
// The exposure compensation is supported when both of the following conditions meet
|
// - we have the valid camera, and
|
// - the valid camera supports the exposure compensation
|
boolean isExposureCompensationSupportedByFrontCamera = (frontCameraId != null) &&
|
(mOneCameraManager.getOneCameraCharacteristics(frontCameraId)
|
.isExposureCompensationSupported());
|
boolean isExposureCompensationSupportedByBackCamera = (backCameraId != null) &&
|
(mOneCameraManager.getOneCameraCharacteristics(backCameraId)
|
.isExposureCompensationSupported());
|
|
// Hides the option if neither front and back camera support exposure compensation.
|
if (!isExposureCompensationSupportedByFrontCamera &&
|
!isExposureCompensationSupportedByBackCamera) {
|
hideAdvancedScreen = true;
|
}
|
} catch (OneCameraAccessException e) {
|
fatalErrorHandler.onGenericCameraAccessFailure();
|
}
|
|
ActionBar actionBar = getActionBar();
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
actionBar.setTitle(R.string.mode_settings);
|
|
String prefKey = getIntent().getStringExtra(PREF_SCREEN_EXTRA);
|
CameraSettingsFragment dialog = new CameraSettingsFragment();
|
|
Bundle bundle = new Bundle(1);
|
bundle.putString(PREF_SCREEN_EXTRA, prefKey);
|
bundle.putBoolean(HIDE_ADVANCED_SCREEN, hideAdvancedScreen);
|
dialog.setArguments(bundle);
|
getFragmentManager().beginTransaction().replace(android.R.id.content, dialog).commit();
|
}
|
|
@Override
|
public boolean onMenuItemSelected(int featureId, MenuItem item) {
|
int itemId = item.getItemId();
|
if (itemId == android.R.id.home) {
|
finish();
|
return true;
|
}
|
return true;
|
}
|
|
public static class CameraSettingsFragment extends PreferenceFragment implements
|
OnSharedPreferenceChangeListener,Preference.OnPreferenceChangeListener {
|
|
public static final String PREF_CATEGORY_RESOLUTION = "pref_category_resolution";
|
public static final String PREF_CATEGORY_ADVANCED = "pref_category_advanced";
|
public static final String PREF_LAUNCH_HELP = "pref_launch_help";
|
private static final Log.Tag TAG = new Log.Tag("SettingsFragment");
|
private static DecimalFormat sMegaPixelFormat = new DecimalFormat("##0.0");
|
private String[] mCamcorderProfileNames;
|
private CameraDeviceInfo mInfos;
|
private String mPrefKey;
|
private boolean mHideAdvancedScreen;
|
private boolean mGetSubPrefAsRoot = true;
|
private Context mContext;
|
private SwitchPreference mLocationPreference;
|
private SettingsManager mSettingsManager;
|
|
|
// Selected resolutions for the different cameras and sizes.
|
private PictureSizes mPictureSizes;
|
|
@Override
|
public void onCreate(Bundle savedInstanceState) {
|
super.onCreate(savedInstanceState);
|
Bundle arguments = getArguments();
|
if (arguments != null) {
|
mPrefKey = arguments.getString(PREF_SCREEN_EXTRA);
|
mHideAdvancedScreen = arguments.getBoolean(HIDE_ADVANCED_SCREEN);
|
}
|
mContext = this.getActivity().getApplicationContext();
|
mSettingsManager = new SettingsManager(mContext);
|
addPreferencesFromResource(R.xml.camera_preferences);
|
PreferenceScreen advancedScreen =
|
(PreferenceScreen) findPreference(PREF_CATEGORY_ADVANCED);
|
mLocationPreference = (SwitchPreference) findPreference(Keys.KEY_RECORD_LOCATION);
|
mLocationPreference.setOnPreferenceChangeListener(this);
|
// If manual exposure not enabled, hide the Advanced screen.
|
if (mHideAdvancedScreen) {
|
PreferenceScreen root = (PreferenceScreen) findPreference("prefscreen_top");
|
root.removePreference(advancedScreen);
|
}
|
|
// Allow the Helper to edit the full preference hierarchy, not the
|
// sub tree we may show as root. See {@link #getPreferenceScreen()}.
|
mGetSubPrefAsRoot = false;
|
CameraSettingsActivityHelper.addAdditionalPreferences(this, mContext);
|
mGetSubPrefAsRoot = true;
|
|
mCamcorderProfileNames = getResources().getStringArray(R.array.camcorder_profile_names);
|
mInfos = CameraAgentFactory
|
.getAndroidCameraAgent(mContext, CameraAgentFactory.CameraApi.API_1)
|
.getCameraDeviceInfo();
|
CameraAgentFactory.recycle(CameraAgentFactory.CameraApi.API_1);
|
}
|
|
@Override
|
public void onResume() {
|
super.onResume();
|
final Activity activity = this.getActivity();
|
|
// Load the camera sizes.
|
loadSizes();
|
|
// Send loaded sizes to additional preferences.
|
if (!mPictureSizes.backCameraSizes.isEmpty()) {
|
CameraSettingsActivityHelper.onSizesLoaded(this, mPictureSizes.backCameraSizes,
|
new ListPreferenceFiller() {
|
@Override
|
public void fill(List<Size> sizes, ListPreference preference) {
|
setEntriesForSelection(sizes, preference);
|
}
|
});
|
} else if (!mPictureSizes.frontCameraSizes.isEmpty()) {
|
CameraSettingsActivityHelper.onSizesLoaded(this, mPictureSizes.frontCameraSizes,
|
new ListPreferenceFiller() {
|
@Override
|
public void fill(List<Size> sizes, ListPreference preference) {
|
setEntriesForSelection(sizes, preference);
|
}
|
});
|
} else {
|
CameraSettingsActivityHelper.onSizesLoaded(this, mPictureSizes.externalCameraSizes,
|
new ListPreferenceFiller() {
|
@Override
|
public void fill(List<Size> sizes, ListPreference preference) {
|
setEntriesForSelection(sizes, preference);
|
}
|
});
|
}
|
|
// Make sure to hide settings for cameras that don't exist on this
|
// device.
|
setVisibilities();
|
|
// Put in the summaries for the currently set values.
|
final PreferenceScreen resolutionScreen =
|
(PreferenceScreen) findPreference(PREF_CATEGORY_RESOLUTION);
|
fillEntriesAndSummaries(resolutionScreen);
|
setPreferenceScreenIntent(resolutionScreen);
|
|
final PreferenceScreen advancedScreen =
|
(PreferenceScreen) findPreference(PREF_CATEGORY_ADVANCED);
|
|
if (!mHideAdvancedScreen) {
|
setPreferenceScreenIntent(advancedScreen);
|
}
|
|
Preference helpPref = findPreference(PREF_LAUNCH_HELP);
|
helpPref.setOnPreferenceClickListener(
|
new OnPreferenceClickListener() {
|
@Override
|
public boolean onPreferenceClick(Preference preference) {
|
new GoogleHelpHelper(activity).launchGoogleHelp();
|
return true;
|
}
|
});
|
getPreferenceScreen().getSharedPreferences()
|
.registerOnSharedPreferenceChangeListener(this);
|
}
|
|
/**
|
* Configure home-as-up for sub-screens.
|
*/
|
private void setPreferenceScreenIntent(final PreferenceScreen preferenceScreen) {
|
Intent intent = new Intent(getActivity(), CameraSettingsActivity.class);
|
intent.putExtra(PREF_SCREEN_EXTRA, preferenceScreen.getKey());
|
preferenceScreen.setIntent(intent);
|
}
|
|
/**
|
* This override allows the CameraSettingsFragment to be reused for
|
* different nested PreferenceScreens within the single camera
|
* preferences XML resource. If the fragment is constructed with a
|
* desired preference key (delivered via an extra in the creation
|
* intent), it is used to look up the nested PreferenceScreen and
|
* returned here.
|
*/
|
@Override
|
public PreferenceScreen getPreferenceScreen() {
|
PreferenceScreen root = super.getPreferenceScreen();
|
if (!mGetSubPrefAsRoot || mPrefKey == null || root == null) {
|
return root;
|
} else {
|
PreferenceScreen match = findByKey(root, mPrefKey);
|
if (match != null) {
|
return match;
|
} else {
|
throw new RuntimeException("key " + mPrefKey + " not found");
|
}
|
}
|
}
|
|
private PreferenceScreen findByKey(PreferenceScreen parent, String key) {
|
if (key.equals(parent.getKey())) {
|
return parent;
|
} else {
|
for (int i = 0; i < parent.getPreferenceCount(); i++) {
|
Preference child = parent.getPreference(i);
|
if (child instanceof PreferenceScreen) {
|
PreferenceScreen match = findByKey((PreferenceScreen) child, key);
|
if (match != null) {
|
return match;
|
}
|
}
|
}
|
return null;
|
}
|
}
|
|
/**
|
* Depending on camera availability on the device, this removes settings
|
* for cameras the device doesn't have.
|
*/
|
private void setVisibilities() {
|
PreferenceGroup resolutions =
|
(PreferenceGroup) findPreference(PREF_CATEGORY_RESOLUTION);
|
if (mPictureSizes.backCameraSizes.isEmpty()) {
|
recursiveDelete(resolutions,
|
findPreference(Keys.KEY_PICTURE_SIZE_BACK));
|
recursiveDelete(resolutions,
|
findPreference(Keys.KEY_VIDEO_QUALITY_BACK));
|
}
|
if (mPictureSizes.frontCameraSizes.isEmpty()) {
|
recursiveDelete(resolutions,
|
findPreference(Keys.KEY_PICTURE_SIZE_FRONT));
|
recursiveDelete(resolutions,
|
findPreference(Keys.KEY_VIDEO_QUALITY_FRONT));
|
}
|
if (mPictureSizes.externalCameraSizes.isEmpty()) {
|
recursiveDelete(resolutions,
|
findPreference(Keys.KEY_PICTURE_SIZE_EXTERNAL));
|
recursiveDelete(resolutions,
|
findPreference(Keys.KEY_VIDEO_QUALITY_EXTERNAL));
|
}
|
}
|
|
/**
|
* Recursively go through settings and fill entries and summaries of our
|
* preferences.
|
*/
|
private void fillEntriesAndSummaries(PreferenceGroup group) {
|
for (int i = 0; i < group.getPreferenceCount(); ++i) {
|
Preference pref = group.getPreference(i);
|
if (pref instanceof PreferenceGroup) {
|
fillEntriesAndSummaries((PreferenceGroup) pref);
|
}
|
setSummary(pref);
|
setEntries(pref);
|
}
|
}
|
|
/**
|
* Recursively traverses the tree from the given group as the route and
|
* tries to delete the preference. Traversal stops once the preference
|
* was found and removed.
|
*/
|
private boolean recursiveDelete(PreferenceGroup group, Preference preference) {
|
if (group == null) {
|
Log.d(TAG, "attempting to delete from null preference group");
|
return false;
|
}
|
if (preference == null) {
|
Log.d(TAG, "attempting to delete null preference");
|
return false;
|
}
|
if (group.removePreference(preference)) {
|
// Removal was successful.
|
return true;
|
}
|
|
for (int i = 0; i < group.getPreferenceCount(); ++i) {
|
Preference pref = group.getPreference(i);
|
if (pref instanceof PreferenceGroup) {
|
if (recursiveDelete((PreferenceGroup) pref, preference)) {
|
return true;
|
}
|
}
|
}
|
return false;
|
}
|
|
@Override
|
public void onPause() {
|
super.onPause();
|
getPreferenceScreen().getSharedPreferences()
|
.unregisterOnSharedPreferenceChangeListener(this);
|
}
|
|
@Override
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
setSummary(findPreference(key));
|
}
|
|
/**
|
* Set the entries for the given preference. The given preference needs
|
* to be a {@link ListPreference}
|
*/
|
private void setEntries(Preference preference) {
|
if (!(preference instanceof ListPreference)) {
|
return;
|
}
|
|
ListPreference listPreference = (ListPreference) preference;
|
if (listPreference.getKey().equals(Keys.KEY_PICTURE_SIZE_BACK)) {
|
setEntriesForSelection(mPictureSizes.backCameraSizes, listPreference);
|
} else if (listPreference.getKey().equals(Keys.KEY_PICTURE_SIZE_FRONT)) {
|
setEntriesForSelection(mPictureSizes.frontCameraSizes, listPreference);
|
} else if (listPreference.getKey().equals(Keys.KEY_PICTURE_SIZE_EXTERNAL)) {
|
setEntriesForSelection(mPictureSizes.externalCameraSizes, listPreference);
|
} else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_BACK)) {
|
setEntriesForSelection(mPictureSizes.videoQualitiesBack.orNull(), listPreference);
|
} else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_FRONT)) {
|
setEntriesForSelection(mPictureSizes.videoQualitiesFront.orNull(), listPreference);
|
} else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_EXTERNAL)) {
|
setEntriesForSelection(mPictureSizes.videoQualitiesExternal.orNull(), listPreference);
|
}
|
}
|
|
/**
|
* Set the summary for the given preference. The given preference needs
|
* to be a {@link ListPreference}.
|
*/
|
private void setSummary(Preference preference) {
|
if (!(preference instanceof ListPreference)) {
|
return;
|
}
|
|
ListPreference listPreference = (ListPreference) preference;
|
if (listPreference.getKey().equals(Keys.KEY_PICTURE_SIZE_BACK)) {
|
setSummaryForSelection(mPictureSizes.backCameraSizes,
|
listPreference);
|
} else if (listPreference.getKey().equals(Keys.KEY_PICTURE_SIZE_FRONT)) {
|
setSummaryForSelection(mPictureSizes.frontCameraSizes,
|
listPreference);
|
} else if (listPreference.getKey().equals(Keys.KEY_PICTURE_SIZE_EXTERNAL)) {
|
setSummaryForSelection(mPictureSizes.externalCameraSizes,
|
listPreference);
|
} else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_BACK)) {
|
setSummaryForSelection(mPictureSizes.videoQualitiesBack.orNull(), listPreference);
|
} else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_FRONT)) {
|
setSummaryForSelection(mPictureSizes.videoQualitiesFront.orNull(), listPreference);
|
} else if (listPreference.getKey().equals(Keys.KEY_VIDEO_QUALITY_EXTERNAL)) {
|
setSummaryForSelection(mPictureSizes.videoQualitiesExternal.orNull(), listPreference);
|
} else {
|
listPreference.setSummary(listPreference.getEntry());
|
}
|
}
|
|
/**
|
* Sets the entries for the given list preference.
|
*
|
* @param selectedSizes The possible S,M,L entries the user can choose
|
* from.
|
* @param preference The preference to set the entries for.
|
*/
|
private void setEntriesForSelection(List<Size> selectedSizes,
|
ListPreference preference) {
|
if (selectedSizes == null) {
|
return;
|
}
|
|
String[] entries = new String[selectedSizes.size()];
|
String[] entryValues = new String[selectedSizes.size()];
|
for (int i = 0; i < selectedSizes.size(); i++) {
|
Size size = selectedSizes.get(i);
|
entries[i] = getSizeSummaryString(size);
|
entryValues[i] = SettingsUtil.sizeToSettingString(size);
|
}
|
preference.setEntries(entries);
|
preference.setEntryValues(entryValues);
|
}
|
|
/**
|
* Sets the entries for the given list preference.
|
*
|
* @param selectedQualities The possible S,M,L entries the user can
|
* choose from.
|
* @param preference The preference to set the entries for.
|
*/
|
private void setEntriesForSelection(SelectedVideoQualities selectedQualities,
|
ListPreference preference) {
|
if (selectedQualities == null) {
|
return;
|
}
|
|
// Avoid adding double entries at the bottom of the list which
|
// indicates that not at least 3 qualities are supported.
|
ArrayList<String> entries = new ArrayList<String>();
|
entries.add(mCamcorderProfileNames[selectedQualities.large]);
|
if (selectedQualities.medium != selectedQualities.large) {
|
entries.add(mCamcorderProfileNames[selectedQualities.medium]);
|
}
|
if (selectedQualities.small != selectedQualities.medium) {
|
entries.add(mCamcorderProfileNames[selectedQualities.small]);
|
}
|
preference.setEntries(entries.toArray(new String[0]));
|
}
|
|
/**
|
* Sets the summary for the given list preference.
|
*
|
* @param displayableSizes The human readable preferred sizes
|
* @param preference The preference for which to set the summary.
|
*/
|
private void setSummaryForSelection(List<Size> displayableSizes,
|
ListPreference preference) {
|
String setting = preference.getValue();
|
if (setting == null || !setting.contains("x")) {
|
return;
|
}
|
Size settingSize = SettingsUtil.sizeFromSettingString(setting);
|
if (settingSize == null || settingSize.area() == 0) {
|
return;
|
}
|
preference.setSummary(getSizeSummaryString(settingSize));
|
}
|
|
/**
|
* Sets the summary for the given list preference.
|
*
|
* @param selectedQualities The selected video qualities.
|
* @param preference The preference for which to set the summary.
|
*/
|
private void setSummaryForSelection(SelectedVideoQualities selectedQualities,
|
ListPreference preference) {
|
if (selectedQualities == null) {
|
return;
|
}
|
|
int selectedQuality = selectedQualities.getFromSetting(preference.getValue());
|
preference.setSummary(mCamcorderProfileNames[selectedQuality]);
|
}
|
|
/**
|
* This method gets the selected picture sizes for S,M,L and populates
|
* {@link #mPictureSizes} accordingly.
|
*/
|
private void loadSizes() {
|
if (mInfos == null) {
|
Log.w(TAG, "null deviceInfo, cannot display resolution sizes");
|
return;
|
}
|
PictureSizeLoader loader = new PictureSizeLoader(getActivity().getApplicationContext());
|
mPictureSizes = loader.computePictureSizes();
|
loader.release();
|
}
|
|
/**
|
* @param size The photo resolution.
|
* @return A human readable and translated string for labeling the
|
* picture size in megapixels.
|
*/
|
private String getSizeSummaryString(Size size) {
|
Size approximateSize = ResolutionUtil.getApproximateSize(size);
|
String megaPixels = sMegaPixelFormat.format((size.width() * size.height()) / 1e6);
|
int numerator = ResolutionUtil.aspectRatioNumerator(approximateSize);
|
int denominator = ResolutionUtil.aspectRatioDenominator(approximateSize);
|
String result = getResources().getString(
|
R.string.setting_summary_aspect_ratio_and_megapixels, numerator, denominator,
|
megaPixels);
|
return result;
|
}
|
|
@Override
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
String key=preference.getKey();
|
if(key.equals(Keys.KEY_RECORD_LOCATION)) {
|
if(mContext.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED ||
|
mContext.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION)!= PackageManager.PERMISSION_GRANTED){
|
requestPermissions( new String[] {Manifest.permission.ACCESS_FINE_LOCATION,
|
Manifest.permission.ACCESS_COARSE_LOCATION},
|
0);
|
}
|
}
|
return true;
|
}
|
|
@Override
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
switch (requestCode) {
|
case 0:
|
//如果用户取消,permissions可能为null.
|
if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults.length > 0) { //有权限
|
// 获取到权限,作相应处理
|
mLocationPreference.setChecked(true);
|
mSettingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_RECORD_LOCATION, true);
|
} else {
|
mLocationPreference.setChecked(false);
|
mSettingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_RECORD_LOCATION, false);
|
}
|
break;
|
default:
|
break;
|
}
|
}
|
}
|
|
}
|