Show dialog when camera device is not connected.
bug:10726516
Change-Id: I3d3433d0b2eced54027b19910473fd55135d0e1c
diff --git a/src/com/android/camera/AndroidCameraManagerImpl.java b/src/com/android/camera/AndroidCameraManagerImpl.java
index 00fe905..bed7ba7 100644
--- a/src/com/android/camera/AndroidCameraManagerImpl.java
+++ b/src/com/android/camera/AndroidCameraManagerImpl.java
@@ -202,6 +202,10 @@
if (mParamsToSet == null) {
mParamsToSet = mCamera.getParameters();
}
+ } else {
+ if (msg.obj != null) {
+ ((CameraOpenErrorCallback) msg.obj).onDeviceOpenFailure(msg.arg1);
+ }
}
return;
@@ -336,8 +340,11 @@
}
@Override
- public CameraManager.CameraProxy cameraOpen(int cameraId) {
- mCameraHandler.obtainMessage(OPEN_CAMERA, cameraId, 0).sendToTarget();
+ public CameraManager.CameraProxy cameraOpen(
+ Handler handler, int cameraId, CameraOpenErrorCallback callback) {
+ mCameraHandler.obtainMessage(OPEN_CAMERA, cameraId, 0,
+ CameraOpenErrorCallbackForward.getNewInstance(
+ handler, callback)).sendToTarget();
mCameraHandler.waitDone();
if (mCamera != null) {
return new AndroidCameraProxyImpl();
@@ -347,8 +354,10 @@
}
/**
- * A class which implements {@link CameraManager.CameraProxy} and
+ * A class which implements {@link CameraManager.CameraProxy} and
* camera handler thread.
+ * TODO: Save the handler for the callback here to avoid passing the same
+ * handler multiple times.
*/
public class AndroidCameraProxyImpl implements CameraManager.CameraProxy {
@@ -370,12 +379,18 @@
}
@Override
- public void reconnect() throws IOException {
+ public boolean reconnect(Handler handler, CameraOpenErrorCallback cb) {
mCameraHandler.sendEmptyMessage(RECONNECT);
mCameraHandler.waitDone();
+ CameraOpenErrorCallback cbforward =
+ CameraOpenErrorCallbackForward.getNewInstance(handler, cb);
if (mReconnectIOException != null) {
- throw mReconnectIOException;
+ if (cbforward != null) {
+ cbforward.onReconnectionFailure(AndroidCameraManagerImpl.this);
+ }
+ return false;
}
+ return true;
}
@Override
@@ -776,4 +791,65 @@
});
}
}
+
+ /**
+ * A callback helps to invoke the original callback on another
+ * {@link android.os.Handler}.
+ */
+ private static class CameraOpenErrorCallbackForward implements CameraOpenErrorCallback {
+ private final Handler mHandler;
+ private final CameraOpenErrorCallback mCallback;
+
+ /**
+ * Returns a new instance of {@link FaceDetectionCallbackForward}.
+ *
+ * @param handler The handler in which the callback will be invoked in.
+ * @param cb The callback to be invoked.
+ * @return The instance of the {@link FaceDetectionCallbackForward}, or
+ * null if any parameter is null.
+ */
+ public static CameraOpenErrorCallbackForward getNewInstance(
+ Handler handler, CameraOpenErrorCallback cb) {
+ if (handler == null || cb == null) {
+ return null;
+ }
+ return new CameraOpenErrorCallbackForward(handler, cb);
+ }
+
+ private CameraOpenErrorCallbackForward(
+ Handler h, CameraOpenErrorCallback cb) {
+ mHandler = h;
+ mCallback = cb;
+ }
+
+ @Override
+ public void onCameraDisabled(final int cameraId) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onCameraDisabled(cameraId);
+ }
+ });
+ }
+
+ @Override
+ public void onDeviceOpenFailure(final int cameraId) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onDeviceOpenFailure(cameraId);
+ }
+ });
+ }
+
+ @Override
+ public void onReconnectionFailure(final CameraManager mgr) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onReconnectionFailure(mgr);
+ }
+ });
+ }
+ }
}
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index fe65c02..3638aac 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -19,10 +19,12 @@
import android.animation.Animator;
import android.app.ActionBar;
import android.app.Activity;
+import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
@@ -75,6 +77,8 @@
import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper;
import com.android.camera2.R;
+import static com.android.camera.CameraManager.CameraOpenErrorCallback;
+
public class CameraActivity extends Activity
implements ModuleSwitcher.ModuleSwitchListener {
@@ -203,6 +207,27 @@
}
};
+ private CameraOpenErrorCallback mCameraOpenErrorCallback =
+ new CameraOpenErrorCallback() {
+ @Override
+ public void onCameraDisabled(int cameraId) {
+ CameraUtil.showErrorAndFinish(CameraActivity.this,
+ R.string.camera_disabled);
+ }
+
+ @Override
+ public void onDeviceOpenFailure(int cameraId) {
+ CameraUtil.showErrorAndFinish(CameraActivity.this,
+ R.string.cannot_connect_camera);
+ }
+
+ @Override
+ public void onReconnectionFailure(CameraManager mgr) {
+ CameraUtil.showErrorAndFinish(CameraActivity.this,
+ R.string.cannot_connect_camera);
+ }
+ };
+
// close activity when screen turns off
private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
@Override
@@ -1254,4 +1279,8 @@
return (mCurrentModule instanceof VideoModule) ?
((VideoModule) mCurrentModule).isRecording() : false;
}
+
+ public CameraOpenErrorCallback getCameraOpenErrorCallback() {
+ return mCameraOpenErrorCallback;
+ }
}
diff --git a/src/com/android/camera/CameraButtonIntentReceiver.java b/src/com/android/camera/CameraButtonIntentReceiver.java
index a65942d..253105a 100644
--- a/src/com/android/camera/CameraButtonIntentReceiver.java
+++ b/src/com/android/camera/CameraButtonIntentReceiver.java
@@ -38,7 +38,9 @@
CameraHolder holder = CameraHolder.instance();
ComboPreferences pref = new ComboPreferences(context);
int cameraId = CameraSettings.readPreferredCameraId(pref);
- if (holder.tryOpen(cameraId) == null) return;
+ if (holder.tryOpen(null, cameraId, null) == null) {
+ return;
+ }
// We are going to launch the camera, so hold the camera for later use
holder.keep();
diff --git a/src/com/android/camera/CameraHolder.java b/src/com/android/camera/CameraHolder.java
index 0ffc736..32eae82 100644
--- a/src/com/android/camera/CameraHolder.java
+++ b/src/com/android/camera/CameraHolder.java
@@ -188,8 +188,9 @@
return mInfo;
}
- public synchronized CameraProxy open(int cameraId)
- throws CameraHardwareException {
+ public synchronized CameraProxy open(
+ Handler handler, int cameraId,
+ CameraManager.CameraOpenErrorCallback cb) {
if (DEBUG_OPEN_RELEASE) {
collectState(cameraId, mCameraDevice);
if (mCameraOpened) {
@@ -204,28 +205,28 @@
mCameraId = -1;
}
if (mCameraDevice == null) {
- try {
- Log.v(TAG, "open camera " + cameraId);
- if (mMockCameraInfo == null) {
- mCameraDevice = CameraManagerFactory
- .getAndroidCameraManager().cameraOpen(cameraId);
- } else {
- if (mMockCamera == null)
- throw new RuntimeException();
+ Log.v(TAG, "open camera " + cameraId);
+ if (mMockCameraInfo == null) {
+ mCameraDevice = CameraManagerFactory
+ .getAndroidCameraManager().cameraOpen(handler, cameraId, cb);
+ } else {
+ if (mMockCamera != null) {
mCameraDevice = mMockCamera[cameraId];
+ } else {
+ Log.e(TAG, "MockCameraInfo found, but no MockCamera provided.");
+ mCameraDevice = null;
}
- mCameraId = cameraId;
- } catch (RuntimeException e) {
- Log.e(TAG, "fail to connect Camera", e);
- throw new CameraHardwareException(e);
}
+ if (mCameraDevice == null) {
+ Log.e(TAG, "fail to connect Camera:" + mCameraId + ", aborting.");
+ return null;
+ }
+ mCameraId = cameraId;
mParameters = mCameraDevice.getParameters();
} else {
- try {
- mCameraDevice.reconnect();
- } catch (IOException e) {
- Log.e(TAG, "reconnect failed.");
- throw new CameraHardwareException(e);
+ if (!mCameraDevice.reconnect(handler, cb)) {
+ Log.e(TAG, "fail to reconnect Camera:" + mCameraId + ", aborting.");
+ return null;
}
mCameraDevice.setParameters(mParameters);
}
@@ -239,17 +240,9 @@
* Tries to open the hardware camera. If the camera is being used or
* unavailable then return {@code null}.
*/
- public synchronized CameraProxy tryOpen(int cameraId) {
- try {
- return !mCameraOpened ? open(cameraId) : null;
- } catch (CameraHardwareException e) {
- // In eng build, we throw the exception so that test tool
- // can detect it and report it
- if ("eng".equals(Build.TYPE)) {
- throw new RuntimeException(e);
- }
- return null;
- }
+ public synchronized CameraProxy tryOpen(
+ Handler handler, int cameraId, CameraManager.CameraOpenErrorCallback cb) {
+ return (!mCameraOpened ? open(handler, cameraId, cb) : null);
}
public synchronized void release() {
diff --git a/src/com/android/camera/CameraManager.java b/src/com/android/camera/CameraManager.java
index 4a8057d..07b8150 100644
--- a/src/com/android/camera/CameraManager.java
+++ b/src/com/android/camera/CameraManager.java
@@ -101,12 +101,49 @@
}
/**
+ * An interface to be called for any exception caught when opening the
+ * camera device. This error callback is different from the one defined
+ * in the framework, {@link android.hardware.Camera.ErrorCallback}, which
+ * is used after the camera is opened.
+ */
+ public interface CameraOpenErrorCallback {
+ /**
+ * Callback when {@link com.android.camera.CameraDisabledException} is
+ * caught.
+ *
+ * @param cameraId The disabled camera.
+ */
+ public void onCameraDisabled(int cameraId);
+
+ /**
+ * Callback when {@link com.android.camera.CameraHardwareException} is
+ * caught.
+ *
+ * @param cameraId The camera with the hardware failure.
+ */
+ public void onDeviceOpenFailure(int cameraId);
+
+ /**
+ * Callback when {@link java.io.IOException} is caught during
+ * {@link android.hardware.Camera#reconnect()}.
+ *
+ * @param mgr The {@link com.android.camera.CameraManager}
+ * with the reconnect failure.
+ */
+ public void onReconnectionFailure(CameraManager mgr);
+ }
+
+ /**
* Opens the camera of the specified ID synchronously.
*
- * @param cameraId The camera ID to open.
+ * @param handler The {@link android.os.Handler} in which the callback
+ * was handled.
+ * @param callback The callback when any error happens.
+ * @param cameraId The camera ID to open.
* @return An instance of {@link CameraProxy} on success. null on failure.
*/
- public CameraProxy cameraOpen(int cameraId);
+ public CameraProxy cameraOpen(
+ Handler handler, int cameraId, CameraOpenErrorCallback callback);
/**
* An interface that takes camera operation requests and post messages to the
@@ -132,10 +169,14 @@
/**
* Reconnects to the camera device.
- *
* @see android.hardware.Camera#reconnect()
+ *
+ * @param handler The {@link android.os.Handler} in which the callback
+ * was handled.
+ * @param cb The callback when any error happens.
+ * @return {@code false} on errors.
*/
- public void reconnect() throws IOException;
+ public boolean reconnect(Handler handler, CameraOpenErrorCallback cb);
/**
* Unlocks the camera device.
@@ -180,7 +221,7 @@
/**
* Sets the callback for preview data.
*
- * @param handler handler in which the callback was handled.
+ * @param handler The {@link android.os.Handler} in which the callback was handled.
* @param cb The callback to be invoked when the preview data is available.
* @see android.hardware.Camera#setPreviewCallback(android.hardware.Camera.PreviewCallback)
*/
diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java
index d00da19..e3bdc25 100644
--- a/src/com/android/camera/PhotoModule.java
+++ b/src/com/android/camera/PhotoModule.java
@@ -462,16 +462,14 @@
// Restart the camera and initialize the UI. From onCreate.
mPreferences.setLocalId(mActivity, mCameraId);
CameraSettings.upgradeLocalPreferences(mPreferences.getLocal());
- try {
- mCameraDevice = CameraUtil.openCamera(mActivity, mCameraId);
- mParameters = mCameraDevice.getParameters();
- } catch (CameraHardwareException e) {
- CameraUtil.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
- return;
- } catch (CameraDisabledException e) {
- CameraUtil.showErrorAndFinish(mActivity, R.string.camera_disabled);
+ mCameraDevice = CameraUtil.openCamera(
+ mActivity, mCameraId, mHandler,
+ mActivity.getCameraOpenErrorCallback());
+ if (mCameraDevice == null) {
+ Log.e(TAG, "Failed to open camera:" + mCameraId + ", aborting.");
return;
}
+ mParameters = mCameraDevice.getParameters();
initializeCapabilities();
CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
@@ -1166,26 +1164,32 @@
mPaused = false;
}
- private void prepareCamera() {
- try {
- // We need to check whether the activity is paused before long
- // operations to ensure that onPause() can be done ASAP.
- mCameraDevice = CameraUtil.openCamera(mActivity, mCameraId);
- mParameters = mCameraDevice.getParameters();
-
- initializeCapabilities();
- if (mFocusManager == null) initializeFocusManager();
- setCameraParameters(UPDATE_PARAM_ALL);
- mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
- mCameraPreviewParamsReady = true;
- startPreview();
- mOnResumeTime = SystemClock.uptimeMillis();
- checkDisplayRotation();
- } catch (CameraHardwareException e) {
- mHandler.sendEmptyMessage(OPEN_CAMERA_FAIL);
- } catch (CameraDisabledException e) {
- mHandler.sendEmptyMessage(CAMERA_DISABLED);
+ /**
+ * Opens the camera device.
+ *
+ * @return Whether the camera was opened successfully.
+ */
+ private boolean prepareCamera() {
+ // We need to check whether the activity is paused before long
+ // operations to ensure that onPause() can be done ASAP.
+ mCameraDevice = CameraUtil.openCamera(
+ mActivity, mCameraId, mHandler,
+ mActivity.getCameraOpenErrorCallback());
+ if (mCameraDevice == null) {
+ Log.e(TAG, "Failed to open camera:" + mCameraId);
+ return false;
}
+ mParameters = mCameraDevice.getParameters();
+
+ initializeCapabilities();
+ if (mFocusManager == null) initializeFocusManager();
+ setCameraParameters(UPDATE_PARAM_ALL);
+ mHandler.sendEmptyMessage(CAMERA_OPEN_DONE);
+ mCameraPreviewParamsReady = true;
+ startPreview();
+ mOnResumeTime = SystemClock.uptimeMillis();
+ checkDisplayRotation();
+ return true;
}
@@ -1196,7 +1200,10 @@
mJpegPictureCallbackTime = 0;
mZoomValue = 0;
resetExposureCompensation();
- prepareCamera();
+ if (!prepareCamera()) {
+ // Camera failure.
+ return;
+ }
// If first time initialization is not finished, put it in the
// message queue.
@@ -1720,7 +1727,7 @@
private void updateAutoFocusMoveCallback() {
if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) {
mCameraDevice.setAutoFocusMoveCallback(mHandler,
- (CameraManager.CameraAFMoveCallback) mAutoFocusMoveCallback);
+ (CameraAFMoveCallback) mAutoFocusMoveCallback);
} else {
mCameraDevice.setAutoFocusMoveCallback(null, null);
}
diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java
index 56882e1..bf5ef89 100644
--- a/src/com/android/camera/VideoModule.java
+++ b/src/com/android/camera/VideoModule.java
@@ -208,16 +208,16 @@
}
private void openCamera() {
- try {
- if (mCameraDevice == null) {
- mCameraDevice = CameraUtil.openCamera(mActivity, mCameraId);
- }
- mParameters = mCameraDevice.getParameters();
- } catch (CameraHardwareException e) {
- mOpenCameraFail = true;
- } catch (CameraDisabledException e) {
- mCameraDisabled = true;
+ if (mCameraDevice == null) {
+ mCameraDevice = CameraUtil.openCamera(
+ mActivity, mCameraId, mHandler,
+ mActivity.getCameraOpenErrorCallback());
}
+ if (mCameraDevice == null) {
+ // Error.
+ return;
+ }
+ mParameters = mCameraDevice.getParameters();
}
// This Handler is used to post message back onto the main thread of the
diff --git a/src/com/android/camera/WideAnglePanoramaModule.java b/src/com/android/camera/WideAnglePanoramaModule.java
index abdb248..59c4350 100644
--- a/src/com/android/camera/WideAnglePanoramaModule.java
+++ b/src/com/android/camera/WideAnglePanoramaModule.java
@@ -316,11 +316,19 @@
}
}
- private void setupCamera() throws CameraHardwareException, CameraDisabledException {
- openCamera();
+ /**
+ * Opens camera and sets the parameters.
+ *
+ * @return Whether the camera was opened successfully.
+ */
+ private boolean setupCamera() {
+ if (!openCamera()) {
+ return false;
+ }
Parameters parameters = mCameraDevice.getParameters();
setupCaptureParams(parameters);
configureCamera(parameters);
+ return true;
}
private void releaseCamera() {
@@ -331,16 +339,27 @@
}
}
- private void openCamera() throws CameraHardwareException, CameraDisabledException {
+ /**
+ * Opens the camera device. The back camera has priority over the front
+ * one.
+ *
+ * @return Whether the camera was opened successfully.
+ */
+ private boolean openCamera() {
int cameraId = CameraHolder.instance().getBackCameraId();
// If there is no back camera, use the first camera. Camera id starts
// from 0. Currently if a camera is not back facing, it is front facing.
// This is also forward compatible if we have a new facing other than
// back or front in the future.
if (cameraId == -1) cameraId = 0;
- mCameraDevice = CameraUtil.openCamera(mActivity, cameraId);
+ mCameraDevice = CameraUtil.openCamera(mActivity, cameraId,
+ mMainHandler, mActivity.getCameraOpenErrorCallback());
+ if (mCameraDevice == null) {
+ return false;
+ }
mCameraOrientation = CameraUtil.getCameraOrientation(cameraId);
if (cameraId == CameraHolder.instance().getFrontCameraId()) mUsingFrontCamera = true;
+ return true;
}
private boolean findBestPreviewSize(List<Size> supportedSizes, boolean need4To3,
@@ -820,13 +839,8 @@
mCaptureState = CAPTURE_STATE_VIEWFINDER;
- try {
- setupCamera();
- } catch (CameraHardwareException e) {
- CameraUtil.showErrorAndFinish(mActivity, R.string.cannot_connect_camera);
- return;
- } catch (CameraDisabledException e) {
- CameraUtil.showErrorAndFinish(mActivity, R.string.camera_disabled);
+ if (!setupCamera()) {
+ Log.e(TAG, "Failed to open camera, aborting");
return;
}
diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java
index ca0109a..adaaaa7 100644
--- a/src/com/android/camera/util/CameraUtil.java
+++ b/src/com/android/camera/util/CameraUtil.java
@@ -48,6 +48,7 @@
import android.location.Location;
import android.net.Uri;
import android.os.Build;
+import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;
@@ -337,21 +338,20 @@
}
public static CameraManager.CameraProxy openCamera(
- Activity activity, int cameraId)
- throws CameraHardwareException, CameraDisabledException {
- throwIfCameraDisabled(activity);
-
+ Activity activity, final int cameraId,
+ Handler handler, final CameraManager.CameraOpenErrorCallback cb) {
try {
- return CameraHolder.instance().open(cameraId);
- } catch (CameraHardwareException e) {
- // In eng build, we throw the exception so that test tool
- // can detect it and report it
- if ("eng".equals(Build.TYPE)) {
- throw new RuntimeException("openCamera failed", e);
- } else {
- throw e;
- }
+ throwIfCameraDisabled(activity);
+ return CameraHolder.instance().open(handler, cameraId, cb);
+ } catch (CameraDisabledException ex) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ cb.onCameraDisabled(cameraId);
+ }
+ });
}
+ return null;
}
public static void showErrorAndFinish(final Activity activity, int msgId) {