Merge "Prevent NullPointerException of PhotoModule in quick resume-pause." into ub-camera-glacier
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index 12edaf5..d05248a 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -1321,6 +1321,7 @@
= new CameraExceptionHandler.CameraExceptionCallback() {
@Override
public void onCameraError(int errorCode) {
+ // Not a fatal error. only do Log.e().
Log.e(TAG, "Camera error callback. error=" + errorCode);
}
@Override
@@ -1334,14 +1335,19 @@
onFatalError();
}
private void onFatalError() {
+ if (mCameraFatalError) {
+ return;
+ }
mCameraFatalError = true;
+
// If the activity receives exception during onPause, just exit the app.
if (mPaused && !isFinishing()) {
- Log.v(TAG, "Fatal error during onPause, call Activity.finish()");
+ Log.e(TAG, "Fatal error during onPause, call Activity.finish()");
finish();
+ } else {
+ CameraUtil.showErrorAndFinish(CameraActivity.this,
+ R.string.cannot_connect_camera);
}
- CameraUtil.showErrorAndFinish(CameraActivity.this,
- R.string.cannot_connect_camera);
}
};
@@ -1456,7 +1462,7 @@
mPanoramaViewHelper = new PanoramaViewHelper(this);
mPanoramaViewHelper.onCreate();
// Set up the camera preview first so the preview shows up ASAP.
- mDataAdapter = new CameraDataAdapter(mAppContext, R.color.photo_placeholder);
+ mDataAdapter = new CameraDataAdapter(mAppContext, mMainHandler, R.color.photo_placeholder);
mDataAdapter.setLocalDataListener(mLocalDataListener);
mPreloader = new Preloader<Integer, AsyncTask>(FILMSTRIP_PRELOAD_AHEAD_ITEMS, mDataAdapter,
@@ -1692,9 +1698,10 @@
if (mCameraFatalError && !isFinishing()) {
Log.v(TAG, "onPause when camera is in fatal state, call Activity.finish()");
finish();
+ } else {
+ // Close the camera and wait for the operation done.
+ mCameraController.closeCamera(true);
}
- // Close the camera and wait for the operation done.
- mCameraController.closeCamera(true);
}
@Override
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java
index d410bc6..e8b05aa 100644
--- a/src/com/android/camera/CaptureModule.java
+++ b/src/com/android/camera/CaptureModule.java
@@ -1206,6 +1206,12 @@
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while waiting to acquire camera-open lock.", e);
}
+ if (mCamera != null) {
+ // If the camera is already open, do nothing.
+ Log.d(TAG, "Camera already open, not re-opening.");
+ mCameraOpenCloseLock.release();
+ return;
+ }
mCameraManager.open(mCameraFacing, useHdr, getPictureSizeFromSettings(),
new OpenCallback() {
@Override
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index ce53dc2..a99fcb4 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -596,6 +596,7 @@
mMeteringAreaSupported =
mCameraCapabilities.supports(CameraCapabilities.Feature.METERING_AREA);
readVideoPreferences();
+ updateDesiredPreviewSize();
resizeForPreviewAspectRatio();
initializeFocusManager();
// TODO: Having focus overlay manager caching the parameters is prone to error,
@@ -765,17 +766,28 @@
}
mProfile = CamcorderProfile.get(mCameraId, quality);
mPreferenceRead = true;
+ }
+
+ /**
+ * Calculates and sets local class variables for Desired Preview sizes.
+ * This function should be called after every change in preview camera
+ * resolution and/or before the preview starts. Note that these values still
+ * need to be pushed to the CameraSettings to actually change the preview
+ * resolution. Does nothing when camera pointer is null.
+ */
+ private void updateDesiredPreviewSize() {
if (mCameraDevice == null) {
return;
}
+
mCameraSettings = mCameraDevice.getSettings();
Point desiredPreviewSize = getDesiredPreviewSize(mAppController.getAndroidContext(),
mCameraSettings, mCameraCapabilities, mProfile, mUI.getPreviewScreenSize());
mDesiredPreviewWidth = desiredPreviewSize.x;
mDesiredPreviewHeight = desiredPreviewSize.y;
mUI.setPreviewSize(mDesiredPreviewWidth, mDesiredPreviewHeight);
- Log.v(TAG, "mDesiredPreviewWidth=" + mDesiredPreviewWidth +
- ". mDesiredPreviewHeight=" + mDesiredPreviewHeight);
+ Log.v(TAG, "Updated DesiredPreview=" + mDesiredPreviewWidth + "x"
+ + mDesiredPreviewHeight);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@@ -787,7 +799,9 @@
* com.android.camera.cameradevice.CameraCapabilities#getPreferredPreviewSizeForVideo()}
* but also considers the current preview area size on screen and make sure
* the final preview size will not be smaller than 1/2 of the current
- * on screen preview area in terms of their short sides.</p>
+ * on screen preview area in terms of their short sides. This function has
+ * highest priority of WYSIWYG, 1:1 matching as its best match, even if
+ * there's a larger preview that meets the condition above. </p>
*
* @return The preferred preview size or {@code null} if the camera is not
* opened yet.
@@ -817,6 +831,19 @@
it.remove();
}
}
+
+ // Take highest priority for WYSIWYG when the preview exactly matches
+ // video frame size. The variable sizes is assumed to be filtered
+ // for sizes beyond the UI size.
+ for (Size size : sizes) {
+ if (size.width() == profile.videoFrameWidth
+ && size.height() == profile.videoFrameHeight) {
+ Log.v(TAG, "Selected =" + size.width() + "x" + size.height()
+ + " on WYSIWYG Priority");
+ return new Point(profile.videoFrameWidth, profile.videoFrameHeight);
+ }
+ }
+
Size optimalSize = CameraUtil.getOptimalPreviewSize(context, sizes,
(double) profile.videoFrameWidth / profile.videoFrameHeight);
return new Point(optimalSize.width(), optimalSize.height());
@@ -1589,6 +1616,9 @@
private void setCameraParameters() {
SettingsManager settingsManager = mActivity.getSettingsManager();
+ // Update Desired Preview size in case video camera resolution has changed.
+ updateDesiredPreviewSize();
+
mCameraSettings.setPreviewSize(new Size(mDesiredPreviewWidth, mDesiredPreviewHeight));
// This is required for Samsung SGH-I337 and probably other Samsung S4 versions
if (Build.BRAND.toLowerCase().contains("samsung")) {
@@ -1623,7 +1653,7 @@
// here we determine the picture size based on the preview size.
List<Size> supported = mCameraCapabilities.getSupportedPhotoSizes();
Size optimalSize = CameraUtil.getOptimalVideoSnapshotPictureSize(supported,
- (double) mDesiredPreviewWidth / mDesiredPreviewHeight);
+ mDesiredPreviewWidth, mDesiredPreviewHeight);
Size original = new Size(mCameraSettings.getCurrentPhotoSize());
if (!original.equals(optimalSize)) {
mCameraSettings.setPhotoSize(optimalSize);
diff --git a/src/com/android/camera/data/CameraDataAdapter.java b/src/com/android/camera/data/CameraDataAdapter.java
index 721b213..19ae6f6 100644
--- a/src/com/android/camera/data/CameraDataAdapter.java
+++ b/src/com/android/camera/data/CameraDataAdapter.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Handler;
import android.view.View;
import com.android.camera.Storage;
@@ -40,6 +41,7 @@
private static final int DEFAULT_DECODE_SIZE = 1600;
private final Context mContext;
+ private final Handler mCallbackHandler;
private LocalDataList mImages;
@@ -53,8 +55,9 @@
private LocalData mLocalDataToDelete;
- public CameraDataAdapter(Context context, int placeholderResource) {
+ public CameraDataAdapter(Context context, Handler callbackHandler, int placeholderResource) {
mContext = context;
+ mCallbackHandler = callbackHandler;
mImages = new LocalDataList();
mPlaceHolderResourceId = placeholderResource;
}
@@ -144,10 +147,17 @@
}
@Override
- public void setListener(Listener listener) {
+ public void setListener(final Listener listener) {
mListener = listener;
if (mImages.size() != 0) {
- mListener.onDataLoaded();
+ mCallbackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (listener != null) {
+ listener.onDataLoaded();
+ }
+ }
+ });
}
}
@@ -160,8 +170,8 @@
}
@Override
- public void removeData(int dataID) {
- LocalData d = mImages.remove(dataID);
+ public void removeData(final int dataID) {
+ final LocalData d = mImages.remove(dataID);
if (d == null) {
return;
}
@@ -169,7 +179,14 @@
// Delete previously removed data first.
executeDeletion();
mLocalDataToDelete = d;
- mListener.onDataRemoved(dataID, d);
+ mCallbackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mListener != null) {
+ mListener.onDataRemoved(dataID, d);
+ }
+ }
+ });
}
@Override
@@ -231,12 +248,19 @@
return;
}
- LocalData data = mImages.get(pos);
+ final LocalData data = mImages.get(pos);
LocalData refreshedData = data.refresh(mContext);
// Refresh failed. Probably removed already.
- if (refreshedData == null && mListener != null) {
- mListener.onDataRemoved(pos, data);
+ if (refreshedData == null) {
+ mCallbackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mListener != null) {
+ mListener.onDataRemoved(pos, data);
+ }
+ }
+ });
return;
}
updateData(pos, refreshedData);
@@ -245,22 +269,27 @@
@Override
public void updateData(final int pos, LocalData data) {
mImages.set(pos, data);
- if (mListener != null) {
- mListener.onDataUpdated(new UpdateReporter() {
- @Override
- public boolean isDataRemoved(int dataID) {
- return false;
- }
+ mCallbackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mListener != null) {
+ mListener.onDataUpdated(new UpdateReporter() {
+ @Override
+ public boolean isDataRemoved(int dataID) {
+ return false;
+ }
- @Override
- public boolean isDataUpdated(int dataID) {
- return (dataID == pos);
+ @Override
+ public boolean isDataUpdated(int dataID) {
+ return (dataID == pos);
+ }
+ });
}
- });
- }
+ }
+ });
}
- private void insertData(LocalData data) {
+ private void insertData(final LocalData data) {
// Since this function is mostly for adding the newest data,
// a simple linear search should yield the best performance over a
// binary search.
@@ -271,9 +300,15 @@
;
}
mImages.add(pos, data);
- if (mListener != null) {
- mListener.onDataInserted(pos, data);
- }
+ final int fpos = pos;
+ mCallbackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mListener != null) {
+ mListener.onDataInserted(fpos, data);
+ }
+ }
+ });
}
/** Update all the data */
@@ -282,9 +317,14 @@
return;
}
mImages = list;
- if (mListener != null) {
- mListener.onDataLoaded();
- }
+ mCallbackHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mListener != null) {
+ mListener.onDataLoaded();
+ }
+ }
+ });
}
@Override
@@ -321,7 +361,7 @@
return getTotalNumber();
}
- private class LoadNewPhotosTask extends AsyncTask<ContentResolver, Void, List<LocalData>> {
+ private class LoadNewPhotosTask extends AsyncTask<ContentResolver, Void, Void> {
private final long mMinPhotoId;
@@ -332,34 +372,29 @@
/**
* Loads any new photos added to our storage directory since our last query.
* @param contentResolvers {@link android.content.ContentResolver} to load data.
- * @return An {@link java.util.ArrayList} containing any new data.
*/
@Override
- protected List<LocalData> doInBackground(ContentResolver... contentResolvers) {
+ protected Void doInBackground(ContentResolver... contentResolvers) {
if (mMinPhotoId != LocalMediaData.QUERY_ALL_MEDIA_ID) {
final ContentResolver cr = contentResolvers[0];
- return LocalMediaData.PhotoData.query(cr, LocalMediaData.PhotoData.CONTENT_URI,
+ List<LocalData> newPhotoData = LocalMediaData.PhotoData.query(cr, LocalMediaData.PhotoData.CONTENT_URI,
mMinPhotoId);
- }
- return new ArrayList<LocalData>(0);
- }
-
- @Override
- protected void onPostExecute(List<LocalData> newPhotoData) {
- if (!newPhotoData.isEmpty()) {
- LocalData newestPhoto = newPhotoData.get(0);
- // We may overlap with another load task or a query task, in which case we want
- // to be sure we never decrement the oldest seen id.
- mLastPhotoId = Math.max(mLastPhotoId, newestPhoto.getContentId());
- }
- // We may add data that is already present, but if we do, it will be deduped in addData.
- // addData does not dedupe session items, so we ignore them here
- for (LocalData localData : newPhotoData) {
- Uri sessionUri = Storage.getSessionUriFromContentUri(localData.getUri());
- if (sessionUri == null) {
- addData(localData);
+ if (!newPhotoData.isEmpty()) {
+ LocalData newestPhoto = newPhotoData.get(0);
+ // We may overlap with another load task or a query task, in which case we want
+ // to be sure we never decrement the oldest seen id.
+ mLastPhotoId = Math.max(mLastPhotoId, newestPhoto.getContentId());
+ }
+ // We may add data that is already present, but if we do, it will be deduped in addData.
+ // addData does not dedupe session items, so we ignore them here
+ for (LocalData localData : newPhotoData) {
+ Uri sessionUri = Storage.getSessionUriFromContentUri(localData.getUri());
+ if (sessionUri == null) {
+ addData(localData);
+ }
}
}
+ return null;
}
}
diff --git a/src/com/android/camera/one/v2/ImageCaptureManager.java b/src/com/android/camera/one/v2/ImageCaptureManager.java
index a815345..0687071 100644
--- a/src/com/android/camera/one/v2/ImageCaptureManager.java
+++ b/src/com/android/camera/one/v2/ImageCaptureManager.java
@@ -55,7 +55,7 @@
* This also manages the lifecycle of {@link Image}s within the application as
* they are passed in from the lower-level camera2 API.
*/
-@TargetApi(Build.VERSION_CODES.L)
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class ImageCaptureManager extends CameraCaptureSession.CaptureCallback implements
ImageReader.OnImageAvailableListener {
/**
diff --git a/src/com/android/camera/one/v2/OneCameraZslImpl.java b/src/com/android/camera/one/v2/OneCameraZslImpl.java
index aa03b88..63f6a90 100644
--- a/src/com/android/camera/one/v2/OneCameraZslImpl.java
+++ b/src/com/android/camera/one/v2/OneCameraZslImpl.java
@@ -75,7 +75,7 @@
* shutter lag.<br>
* TODO: Determine what the maximum number of full YUV capture frames is.
*/
-@TargetApi(Build.VERSION_CODES.L)
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class OneCameraZslImpl extends AbstractOneCamera {
private static final Tag TAG = new Tag("OneCameraZslImpl2");
diff --git a/src/com/android/camera/settings/AppUpgrader.java b/src/com/android/camera/settings/AppUpgrader.java
index b3bb0db..9ff3dfa 100644
--- a/src/com/android/camera/settings/AppUpgrader.java
+++ b/src/com/android/camera/settings/AppUpgrader.java
@@ -43,6 +43,7 @@
private static final String OLD_CAMERA_PREFERENCES_PREFIX = "_preferences_";
private static final String OLD_MODULE_PREFERENCES_PREFIX = "_preferences_module_";
private static final String OLD_GLOBAL_PREFERENCES_FILENAME = "_preferences_camera";
+ private static final String OLD_KEY_UPGRADE_VERSION = "pref_strict_upgrade_version";
/**
* With this version everyone was forced to choose their location settings
@@ -75,6 +76,12 @@
private static final int CAMERA_SETTINGS_SELECTED_MODULE_INDEX = 5;
/**
+ * With this version internal storage is changed to use only Strings, and
+ * a type conversion process should execute.
+ */
+ private static final int CAMERA_SETTINGS_STRINGS_UPGRADE = 5;
+
+ /**
* Increment this value whenever new AOSP UpgradeSteps need to be executed.
*/
public static final int APP_UPGRADE_VERSION = 6;
@@ -88,28 +95,35 @@
@Override
protected int getLastVersion(SettingsManager settingsManager) {
- // Prior appwide versions were stored in the default preferences. If
- // current
- // state indicates this is still the case, port the version and then
- // process
- // all other known app settings to the new SettingsManager string
- // scheme.
- try {
- return super.getLastVersion(settingsManager);
- } catch (ClassCastException e) {
- // We infer that a ClassCastException here means we have pre-String
- // settings that need to be upgraded, so we hack in a full upgrade
- // here.
- upgradeTypesToStrings(settingsManager);
- // Retrieve version as default now that we're sure it is converted
- return super.getLastVersion(settingsManager);
+ // Prior upgrade versions were stored in the default preferences as int
+ // and String. We create a new version location for migration to String.
+ // If we don't have a version persisted in the new location, check for
+ // the prior value from the old location. We expect the old value to be
+ // processed during {@link #upgradeTypesToStrings}.
+ SharedPreferences defaultPreferences = settingsManager.getDefaultPreferences();
+ if (defaultPreferences.contains(OLD_KEY_UPGRADE_VERSION)) {
+ Map<String, ?> allPrefs = defaultPreferences.getAll();
+ Object oldVersion = allPrefs.get(OLD_KEY_UPGRADE_VERSION);
+ defaultPreferences.edit().remove(OLD_KEY_UPGRADE_VERSION).apply();
+ if (oldVersion instanceof Integer) {
+ return (Integer) oldVersion;
+ } else if (oldVersion instanceof String) {
+ return SettingsManager.convertToInt((String) oldVersion);
+ }
}
+ return super.getLastVersion(settingsManager);
}
@Override
public void upgrade(SettingsManager settingsManager, int lastVersion, int currentVersion) {
Context context = mAppController.getAndroidContext();
+ // Do strings upgrade first before 'earlier' upgrades, since they assume
+ // valid storage of values.
+ if (lastVersion < CAMERA_SETTINGS_STRINGS_UPGRADE) {
+ upgradeTypesToStrings(settingsManager);
+ }
+
if (lastVersion < FORCE_LOCATION_CHOICE_VERSION) {
forceLocationChoice(settingsManager);
}
@@ -153,11 +167,6 @@
SharedPreferences oldGlobalPreferences =
settingsManager.openPreferences(OLD_GLOBAL_PREFERENCES_FILENAME);
- // Strict upgrade version: Integer -> String, from default.
- int strictUpgradeVersion = removeInteger(defaultPreferences, Keys.KEY_UPGRADE_VERSION);
- settingsManager.set(SettingsManager.SCOPE_GLOBAL, Keys.KEY_UPGRADE_VERSION,
- strictUpgradeVersion);
-
// Location: boolean -> String, from default.
if (defaultPreferences.contains(Keys.KEY_RECORD_LOCATION)) {
boolean location = removeBoolean(defaultPreferences, Keys.KEY_RECORD_LOCATION);
diff --git a/src/com/android/camera/settings/Keys.java b/src/com/android/camera/settings/Keys.java
index ad81782..35d0aca 100644
--- a/src/com/android/camera/settings/Keys.java
+++ b/src/com/android/camera/settings/Keys.java
@@ -62,7 +62,7 @@
"pref_release_dialog_last_shown_version";
public static final String KEY_FLASH_SUPPORTED_BACK_CAMERA =
"pref_flash_supported_back_camera";
- public static final String KEY_UPGRADE_VERSION = "pref_strict_upgrade_version";
+ public static final String KEY_UPGRADE_VERSION = "pref_upgrade_version";
public static final String KEY_REQUEST_RETURN_HDR_PLUS = "pref_request_return_hdr_plus";
public static final String KEY_SHOULD_SHOW_REFOCUS_VIEWER_CLING =
"pref_should_show_refocus_viewer_cling";
diff --git a/src/com/android/camera/settings/SettingsManager.java b/src/com/android/camera/settings/SettingsManager.java
index 297bacb..6d90832 100644
--- a/src/com/android/camera/settings/SettingsManager.java
+++ b/src/com/android/camera/settings/SettingsManager.java
@@ -320,7 +320,13 @@
*/
public String getString(String scope, String key, String defaultValue) {
SharedPreferences preferences = getPreferencesFromScope(scope);
- return preferences.getString(key, defaultValue);
+ try {
+ return preferences.getString(key, defaultValue);
+ } catch (ClassCastException e) {
+ Log.w(TAG, "existing preference with invalid type, removing and returning default", e);
+ preferences.edit().remove(key).apply();
+ return defaultValue;
+ }
}
/**
@@ -332,13 +338,13 @@
}
/**
- * Retrieve a setting's value as an Integer, manually specifiying
+ * Retrieve a setting's value as an Integer, manually specifying
* a default value.
*/
public Integer getInteger(String scope, String key, Integer defaultValue) {
String defaultValueString = Integer.toString(defaultValue);
String value = getString(scope, key, defaultValueString);
- return Integer.parseInt(value);
+ return convertToInt(value);
}
/**
@@ -356,7 +362,7 @@
public boolean getBoolean(String scope, String key, boolean defaultValue) {
String defaultValueString = defaultValue ? "1" : "0";
String value = getString(scope, key, defaultValueString);
- return (Integer.parseInt(value) != 0);
+ return convertToBoolean(value);
}
/**
@@ -517,6 +523,29 @@
}
/**
+ * Package private conversion method to turn String storage format into
+ * ints.
+ *
+ * @param value String to be converted to int
+ * @return int value of stored String
+ */
+ static int convertToInt(String value) {
+ return Integer.parseInt(value);
+ }
+
+ /**
+ * Package private conversion method to turn String storage format into
+ * booleans.
+ *
+ * @param value String to be converted to boolean
+ * @return boolean value of stored String
+ */
+ static boolean convertToBoolean(String value) {
+ return Integer.parseInt(value) != 0;
+ }
+
+
+ /**
* Package private conversion method to turn booleans into preferred
* String storage format.
*
diff --git a/src/com/android/camera/ui/BottomBar.java b/src/com/android/camera/ui/BottomBar.java
index c481a24..d5fdbf6 100644
--- a/src/com/android/camera/ui/BottomBar.java
+++ b/src/com/android/camera/ui/BottomBar.java
@@ -77,9 +77,7 @@
private final float mCircleRadius;
private CaptureLayoutHelper mCaptureLayoutHelper = null;
- // for Android L, these backgrounds are RippleDrawables (ISA LayerDrawable)
- // pre-L, they're plain old LayerDrawables
- private final LayerDrawable[] mShutterButtonBackgrounds;
+ private final Drawable.ConstantState[] mShutterButtonBackgroundConstantStates;
// a reference to the shutter background's first contained drawable
// if it's an animated circle drawable (for video mode)
private AnimatedCircleDrawable mAnimatedCircleDrawable;
@@ -102,25 +100,11 @@
TypedArray ar = context.getResources()
.obtainTypedArray(R.array.shutter_button_backgrounds);
int len = ar.length();
-
- mShutterButtonBackgrounds = new LayerDrawable[len];
+ mShutterButtonBackgroundConstantStates = new Drawable.ConstantState[len];
for (int i = 0; i < len; i++) {
int drawableId = ar.getResourceId(i, -1);
- LayerDrawable shutterBackground = mShutterButtonBackgrounds[i] =
- (LayerDrawable) context.getResources().getDrawable(drawableId).mutate();
-
- // the background for video has a circle_item drawable placeholder
- // that gets replaced by an AnimatedCircleDrawable for the cool
- // shrink-down-to-a-circle effect
- // all other modes need not do this replace
- Drawable d = shutterBackground.findDrawableByLayerId(R.id.circle_item);
- if (d != null) {
- Drawable animatedCircleDrawable =
- new AnimatedCircleDrawable((int) mCircleRadius);
- animatedCircleDrawable.setLevel(DRAWABLE_MAX_LEVEL);
- shutterBackground
- .setDrawableByLayerId(R.id.circle_item, animatedCircleDrawable);
- }
+ mShutterButtonBackgroundConstantStates[i] =
+ context.getResources().getDrawable(drawableId).getConstantState();
}
ar.recycle();
}
@@ -382,13 +366,33 @@
}
}
+ private LayerDrawable applyCircleDrawableToShutterBackground(LayerDrawable shutterBackground) {
+ // the background for video has a circle_item drawable placeholder
+ // that gets replaced by an AnimatedCircleDrawable for the cool
+ // shrink-down-to-a-circle effect
+ // all other modes need not do this replace
+ Drawable d = shutterBackground.findDrawableByLayerId(R.id.circle_item);
+ if (d != null) {
+ Drawable animatedCircleDrawable =
+ new AnimatedCircleDrawable((int) mCircleRadius);
+ animatedCircleDrawable.setLevel(DRAWABLE_MAX_LEVEL);
+ shutterBackground
+ .setDrawableByLayerId(R.id.circle_item, animatedCircleDrawable);
+ }
+
+ return shutterBackground;
+ }
+
+ private LayerDrawable newDrawableFromConstantState(Drawable.ConstantState constantState) {
+ return (LayerDrawable) constantState.newDrawable(getContext().getResources());
+ }
+
private void setupShutterBackgroundForModeIndex(int index) {
- LayerDrawable shutterBackground = mShutterButtonBackgrounds[index];
+ LayerDrawable shutterBackground = applyCircleDrawableToShutterBackground(
+ newDrawableFromConstantState(mShutterButtonBackgroundConstantStates[index]));
mShutterButton.setBackground(shutterBackground);
- // FIXME this is crashing NPE on getConstantState()
- // reverting for now
- // b/559979
- //mCancelButton.setBackground(shutterBackground.getConstantState().newDrawable());
+ mCancelButton.setBackground(applyCircleDrawableToShutterBackground(
+ newDrawableFromConstantState(mShutterButtonBackgroundConstantStates[index])));
Drawable d = shutterBackground.getDrawable(0);
mAnimatedCircleDrawable = null;
diff --git a/src/com/android/camera/util/ApiHelper.java b/src/com/android/camera/util/ApiHelper.java
index efdee85..8e25464 100644
--- a/src/com/android/camera/util/ApiHelper.java
+++ b/src/com/android/camera/util/ApiHelper.java
@@ -86,7 +86,7 @@
}
public static boolean isLOrHigher() {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.L
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
|| "L".equals(Build.VERSION.CODENAME);
}
}
diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java
index a55aa84..658e73b 100644
--- a/src/com/android/camera/util/CameraUtil.java
+++ b/src/com/android/camera/util/CameraUtil.java
@@ -301,6 +301,7 @@
// appearing, so check to ensure that the activity is not shutting down
// before attempting to attach a dialog to the window manager.
if (!activity.isFinishing()) {
+ Log.e(TAG, "Show fatal error dialog");
new AlertDialog.Builder(activity)
.setCancelable(false)
.setTitle(R.string.camera_error_title)
@@ -562,12 +563,24 @@
}
}
}
+
return optimalSizeIndex;
}
- /** Returns the largest picture size which matches the given aspect ratio. */
+ /**
+ * Returns the largest picture size which matches the given aspect ratio,
+ * except for the special WYSIWYG case where the picture size exactly matches
+ * the target size.
+ *
+ * @param sizes a list of candidate sizes, available for use
+ * @param targetWidth the ideal width of the video snapshot
+ * @param targetHeight the ideal height of the video snapshot
+ * @return the Optimal Video Snapshot Picture Size
+ */
public static com.android.ex.camera2.portability.Size getOptimalVideoSnapshotPictureSize(
- List<com.android.ex.camera2.portability.Size> sizes, double targetRatio) {
+ List<com.android.ex.camera2.portability.Size> sizes, int targetWidth,
+ int targetHeight) {
+
// Use a very small tolerance because we want an exact match.
final double ASPECT_TOLERANCE = 0.001;
if (sizes == null) {
@@ -576,7 +589,17 @@
com.android.ex.camera2.portability.Size optimalSize = null;
+ // WYSIWYG Override
+ // We assume that physical display constraints have already been
+ // imposed on the variables sizes
+ for (com.android.ex.camera2.portability.Size size : sizes) {
+ if (size.height() == targetHeight && size.width() == targetWidth) {
+ return size;
+ }
+ }
+
// Try to find a size matches aspect ratio and has the largest width
+ final double targetRatio = (double) targetWidth / targetHeight;
for (com.android.ex.camera2.portability.Size size : sizes) {
double ratio = (double) size.width() / size.height();
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) {
diff --git a/src/com/android/camera/widget/FilmstripView.java b/src/com/android/camera/widget/FilmstripView.java
index 7bf675e..0f7442e 100644
--- a/src/com/android/camera/widget/FilmstripView.java
+++ b/src/com/android/camera/widget/FilmstripView.java
@@ -959,7 +959,7 @@
}
}
- @TargetApi(Build.VERSION_CODES.L)
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setMaxElevation(View v) {
v.setElevation(Float.MAX_VALUE);
}