Merge "Fix for crash when PhotoApp deletes last Cam pic" into ub-camera-glacier
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index d05248a..af9af0f 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -538,8 +538,9 @@
@Override
public void onCameraDisabled(int cameraId) {
- UsageStatistics.instance().cameraFailure(eventprotos.CameraFailure.FailureReason.SECURITY,
- null);
+ UsageStatistics.instance().cameraFailure(
+ eventprotos.CameraFailure.FailureReason.SECURITY, null,
+ UsageStatistics.NONE, UsageStatistics.NONE);
Log.w(TAG, "Camera disabled: " + cameraId);
CameraUtil.showErrorAndFinish(this, R.string.camera_disabled);
}
@@ -547,7 +548,8 @@
@Override
public void onDeviceOpenFailure(int cameraId, String info) {
UsageStatistics.instance().cameraFailure(
- eventprotos.CameraFailure.FailureReason.OPEN_FAILURE, info);
+ eventprotos.CameraFailure.FailureReason.OPEN_FAILURE, info,
+ UsageStatistics.NONE, UsageStatistics.NONE);
Log.w(TAG, "Camera open failure: " + info);
CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
}
@@ -561,7 +563,8 @@
@Override
public void onReconnectionFailure(CameraAgent mgr, String info) {
UsageStatistics.instance().cameraFailure(
- eventprotos.CameraFailure.FailureReason.RECONNECT_FAILURE, null);
+ eventprotos.CameraFailure.FailureReason.RECONNECT_FAILURE, null,
+ UsageStatistics.NONE, UsageStatistics.NONE);
Log.w(TAG, "Camera reconnection failure:" + info);
CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera);
}
@@ -1325,13 +1328,20 @@
Log.e(TAG, "Camera error callback. error=" + errorCode);
}
@Override
- public void onCameraException(RuntimeException ex) {
+ public void onCameraException(
+ RuntimeException ex, String commandHistory, int action, int state) {
Log.e(TAG, "Camera Exception", ex);
+ UsageStatistics.instance().cameraFailure(
+ eventprotos.CameraFailure.FailureReason.API_RUNTIME_EXCEPTION,
+ commandHistory, action, state);
onFatalError();
}
@Override
public void onDispatchThreadException(RuntimeException ex) {
Log.e(TAG, "DispatchThread Exception", ex);
+ UsageStatistics.instance().cameraFailure(
+ eventprotos.CameraFailure.FailureReason.API_TIMEOUT,
+ null, UsageStatistics.NONE, UsageStatistics.NONE);
onFatalError();
}
private void onFatalError() {
@@ -1462,7 +1472,7 @@
mPanoramaViewHelper = new PanoramaViewHelper(this);
mPanoramaViewHelper.onCreate();
// Set up the camera preview first so the preview shows up ASAP.
- mDataAdapter = new CameraDataAdapter(mAppContext, mMainHandler, R.color.photo_placeholder);
+ mDataAdapter = new CameraDataAdapter(mAppContext, R.color.photo_placeholder);
mDataAdapter.setLocalDataListener(mLocalDataListener);
mPreloader = new Preloader<Integer, AsyncTask>(FILMSTRIP_PRELOAD_AHEAD_ITEMS, mDataAdapter,
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index 8c1c88c..d363591 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -313,6 +313,10 @@
}
private void checkDisplayRotation() {
+ // Need to just be a no-op for the quick resume-pause scenario.
+ if (mPaused) {
+ return;
+ }
// Set the display orientation if display rotation has changed.
// Sometimes this happens when the device is held upside
// down and camera app is opened. Rotation animation will
@@ -456,6 +460,7 @@
}
mAppController.getCameraAppUI().transitionToCapture();
mAppController.getCameraAppUI().showModeOptions();
+ mAppController.setShutterEnabled(true);
}
@Override
@@ -1267,9 +1272,11 @@
// If we are already in the middle of taking a snapshot or the image
// save request is full then ignore.
if (mCameraDevice == null || mCameraState == SNAPSHOT_IN_PROGRESS
- || mCameraState == SWITCHING_CAMERA || !mAppController.isShutterEnabled()) {
+ || mCameraState == SWITCHING_CAMERA) {
return false;
}
+ setCameraState(SNAPSHOT_IN_PROGRESS);
+
mCaptureStartTime = System.currentTimeMillis();
mPostViewPictureCallbackTime = 0;
@@ -1293,9 +1300,6 @@
mJpegRotation = info.getJpegOrientation(orientation);
mCameraDevice.setJpegOrientation(mJpegRotation);
- // We don't want user to press the button again while taking a
- // multi-second HDR photo.
- mAppController.setShutterEnabled(false);
mCameraDevice.takePicture(mHandler,
new ShutterCallback(!animateBefore),
mRawPictureCallback, mPostViewPictureCallback,
@@ -1304,7 +1308,6 @@
mNamedImages.nameNewImage(mCaptureStartTime);
mFaceDetectionStarted = false;
- setCameraState(SNAPSHOT_IN_PROGRESS);
return true;
}
@@ -1498,7 +1501,8 @@
@Override
public void onShutterButtonClick() {
if (mPaused || (mCameraState == SWITCHING_CAMERA)
- || (mCameraState == PREVIEW_STOPPED)) {
+ || (mCameraState == PREVIEW_STOPPED)
+ || !mAppController.isShutterEnabled()) {
mVolumeButtonClickedFlag = false;
return;
}
@@ -1513,6 +1517,8 @@
Log.d(TAG, "onShutterButtonClick: mCameraState=" + mCameraState +
" mVolumeButtonClickedFlag=" + mVolumeButtonClickedFlag);
+ mAppController.setShutterEnabled(false);
+
int countDownDuration = mActivity.getSettingsManager()
.getInteger(SettingsManager.SCOPE_GLOBAL, Keys.KEY_COUNTDOWN_DURATION);
mTimerDuration = countDownDuration;
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 19ae6f6..721b213 100644
--- a/src/com/android/camera/data/CameraDataAdapter.java
+++ b/src/com/android/camera/data/CameraDataAdapter.java
@@ -20,7 +20,6 @@
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;
@@ -41,7 +40,6 @@
private static final int DEFAULT_DECODE_SIZE = 1600;
private final Context mContext;
- private final Handler mCallbackHandler;
private LocalDataList mImages;
@@ -55,9 +53,8 @@
private LocalData mLocalDataToDelete;
- public CameraDataAdapter(Context context, Handler callbackHandler, int placeholderResource) {
+ public CameraDataAdapter(Context context, int placeholderResource) {
mContext = context;
- mCallbackHandler = callbackHandler;
mImages = new LocalDataList();
mPlaceHolderResourceId = placeholderResource;
}
@@ -147,17 +144,10 @@
}
@Override
- public void setListener(final Listener listener) {
+ public void setListener(Listener listener) {
mListener = listener;
if (mImages.size() != 0) {
- mCallbackHandler.post(new Runnable() {
- @Override
- public void run() {
- if (listener != null) {
- listener.onDataLoaded();
- }
- }
- });
+ mListener.onDataLoaded();
}
}
@@ -170,8 +160,8 @@
}
@Override
- public void removeData(final int dataID) {
- final LocalData d = mImages.remove(dataID);
+ public void removeData(int dataID) {
+ LocalData d = mImages.remove(dataID);
if (d == null) {
return;
}
@@ -179,14 +169,7 @@
// Delete previously removed data first.
executeDeletion();
mLocalDataToDelete = d;
- mCallbackHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mListener != null) {
- mListener.onDataRemoved(dataID, d);
- }
- }
- });
+ mListener.onDataRemoved(dataID, d);
}
@Override
@@ -248,19 +231,12 @@
return;
}
- final LocalData data = mImages.get(pos);
+ LocalData data = mImages.get(pos);
LocalData refreshedData = data.refresh(mContext);
// Refresh failed. Probably removed already.
- if (refreshedData == null) {
- mCallbackHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mListener != null) {
- mListener.onDataRemoved(pos, data);
- }
- }
- });
+ if (refreshedData == null && mListener != null) {
+ mListener.onDataRemoved(pos, data);
return;
}
updateData(pos, refreshedData);
@@ -269,27 +245,22 @@
@Override
public void updateData(final int pos, LocalData data) {
mImages.set(pos, data);
- 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);
- }
- });
+ if (mListener != null) {
+ mListener.onDataUpdated(new UpdateReporter() {
+ @Override
+ public boolean isDataRemoved(int dataID) {
+ return false;
}
- }
- });
+
+ @Override
+ public boolean isDataUpdated(int dataID) {
+ return (dataID == pos);
+ }
+ });
+ }
}
- private void insertData(final LocalData data) {
+ private void insertData(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.
@@ -300,15 +271,9 @@
;
}
mImages.add(pos, data);
- final int fpos = pos;
- mCallbackHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mListener != null) {
- mListener.onDataInserted(fpos, data);
- }
- }
- });
+ if (mListener != null) {
+ mListener.onDataInserted(pos, data);
+ }
}
/** Update all the data */
@@ -317,14 +282,9 @@
return;
}
mImages = list;
- mCallbackHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mListener != null) {
- mListener.onDataLoaded();
- }
- }
- });
+ if (mListener != null) {
+ mListener.onDataLoaded();
+ }
}
@Override
@@ -361,7 +321,7 @@
return getTotalNumber();
}
- private class LoadNewPhotosTask extends AsyncTask<ContentResolver, Void, Void> {
+ private class LoadNewPhotosTask extends AsyncTask<ContentResolver, Void, List<LocalData>> {
private final long mMinPhotoId;
@@ -372,29 +332,34 @@
/**
* 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 Void doInBackground(ContentResolver... contentResolvers) {
+ protected List<LocalData> doInBackground(ContentResolver... contentResolvers) {
if (mMinPhotoId != LocalMediaData.QUERY_ALL_MEDIA_ID) {
final ContentResolver cr = contentResolvers[0];
- List<LocalData> newPhotoData = LocalMediaData.PhotoData.query(cr, LocalMediaData.PhotoData.CONTENT_URI,
+ return LocalMediaData.PhotoData.query(cr, LocalMediaData.PhotoData.CONTENT_URI,
mMinPhotoId);
- 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 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);
}
}
- return null;
}
}
diff --git a/src/com/android/camera/settings/CameraSettingsActivity.java b/src/com/android/camera/settings/CameraSettingsActivity.java
index f09f33b..db9c62c 100644
--- a/src/com/android/camera/settings/CameraSettingsActivity.java
+++ b/src/com/android/camera/settings/CameraSettingsActivity.java
@@ -50,6 +50,13 @@
* 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";
@Override
@@ -61,7 +68,10 @@
actionBar.setTitle(R.string.mode_settings);
String prefKey = getIntent().getStringExtra(PREF_SCREEN_EXTRA);
- CameraSettingsFragment dialog = new CameraSettingsFragment(prefKey);
+ CameraSettingsFragment dialog = new CameraSettingsFragment();
+ Bundle bundle = new Bundle(1);
+ bundle.putString(PREF_SCREEN_EXTRA, prefKey);
+ dialog.setArguments(bundle);
getFragmentManager().beginTransaction().replace(android.R.id.content, dialog).commit();
}
@@ -85,7 +95,7 @@
private static DecimalFormat sMegaPixelFormat = new DecimalFormat("##0.0");
private String[] mCamcorderProfileNames;
private CameraDeviceInfo mInfos;
- private final String mPrefKey;
+ private String mPrefKey;
private boolean mGetSubPrefAsRoot = true;
// Selected resolutions for the different cameras and sizes.
@@ -96,13 +106,13 @@
private SelectedVideoQualities mVideoQualitiesBack;
private SelectedVideoQualities mVideoQualitiesFront;
- public CameraSettingsFragment(String prefKey) {
- mPrefKey = prefKey;
- }
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Bundle arguments = getArguments();
+ if (arguments != null) {
+ mPrefKey = arguments.getString(PREF_SCREEN_EXTRA);
+ }
Context context = this.getActivity().getApplicationContext();
addPreferencesFromResource(R.xml.camera_preferences);
diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java
index b415901..658e73b 100644
--- a/src/com/android/camera/util/CameraUtil.java
+++ b/src/com/android/camera/util/CameraUtil.java
@@ -563,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) {
@@ -577,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/util/QuickActivity.java b/src/com/android/camera/util/QuickActivity.java
index 965b541..039d8fc 100644
--- a/src/com/android/camera/util/QuickActivity.java
+++ b/src/com/android/camera/util/QuickActivity.java
@@ -179,7 +179,7 @@
private boolean delayOnResumeOnStart() {
String action = getIntent().getAction();
boolean isSecureLockscreenCamera =
- action.equals(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
+ MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action);
return isSecureLockscreenCamera;
}
diff --git a/src_pd/com/android/camera/util/UsageStatistics.java b/src_pd/com/android/camera/util/UsageStatistics.java
index d68dec9..6c208de 100644
--- a/src_pd/com/android/camera/util/UsageStatistics.java
+++ b/src_pd/com/android/camera/util/UsageStatistics.java
@@ -26,6 +26,7 @@
public class UsageStatistics {
public static final long VIEW_TIMEOUT_MILLIS = 0;
+ public static final int NONE = -1;
private static UsageStatistics sInstance;
@@ -66,7 +67,7 @@
Boolean volumeButtonShutter) {
}
- public void cameraFailure(int cause, String info) {
+ public void cameraFailure(int cause, String info, int agentAction, int agentState) {
}
public void changeScreen(int newScreen, Integer interactionCause) {
diff --git a/src_pd/com/google/common/logging/eventprotos.java b/src_pd/com/google/common/logging/eventprotos.java
index afb7eb3..292d2a6 100644
--- a/src_pd/com/google/common/logging/eventprotos.java
+++ b/src_pd/com/google/common/logging/eventprotos.java
@@ -54,6 +54,8 @@
public static final int SECURITY = 10000;
public static final int OPEN_FAILURE = 10000;
public static final int RECONNECT_FAILURE = 10000;
+ public static final int API_RUNTIME_EXCEPTION = 10000;
+ public static final int API_TIMEOUT = 10000;
}
}