Merge "Remove deprecated carousel from frameworks/ex." into lmp-dev
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java
index 65047f1..e675796 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2AgentImpl.java
@@ -19,7 +19,9 @@
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.ImageFormat;
+import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
@@ -215,6 +217,8 @@
mOpenCallback = openCallback;
mCameraIndex = cameraIndex;
mCameraId = mCameraDevices.get(mCameraIndex);
+ Log.i(TAG, String.format("Opening camera index %d (id %s) with camera2 API",
+ cameraIndex, mCameraId));
if (mCameraId == null) {
mOpenCallback.onCameraDisabled(msg.arg1);
@@ -458,7 +462,6 @@
}*/
case CameraActions.SET_DISPLAY_ORIENTATION: {
- // TODO: Need to handle preview in addition to capture
// Only set the JPEG capture orientation if requested to do so; otherwise,
// capture in the sensor's physical orientation
mPersistentSettings.set(CaptureRequest.JPEG_ORIENTATION, msg.arg2 > 0 ?
@@ -858,6 +861,9 @@
public void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb)
{}
+ // TODO: Implement
+ public void addCallbackBuffer(final byte[] callbackBuffer) {}
+
@Override
public void autoFocus(final Handler handler, final CameraAFCallback cb) {
mDispatchThread.runJob(new Runnable() {
@@ -1139,11 +1145,51 @@
}
@Override
+ public Matrix getPreviewTransform(int currentDisplayOrientation,
+ RectF surfaceDimensions,
+ RectF desiredBounds) {
+ if (!orientationIsValid(currentDisplayOrientation)) {
+ return new Matrix();
+ }
+
+ // The system transparently transforms the image to fill the surface
+ // when the device is in its natural orientation. We rotate the
+ // coordinates of the rectangle's corners to be relative to the
+ // original image, instead of to the current screen orientation.
+ float[] surfacePolygon = rotate(convertRectToPoly(surfaceDimensions),
+ 2 * currentDisplayOrientation / 90);
+ float[] desiredPolygon = convertRectToPoly(desiredBounds);
+
+ Matrix transform = new Matrix();
+ // Use polygons instead of rectangles so that rotation will be
+ // calculated, since that is not done by the new camera API.
+ transform.setPolyToPoly(surfacePolygon, 0, desiredPolygon, 0, 4);
+ return transform;
+ }
+
+ @Override
public boolean canDisableShutterSound() {
// The new API doesn't support this operation, so don't encourage people to try it.
// TODO: What kind of assumptions have callers made about this result's meaning?
return false;
}
+
+ private static float[] convertRectToPoly(RectF rf) {
+ return new float[] {rf.left, rf.top, rf.right, rf.top,
+ rf.right, rf.bottom, rf.left, rf.bottom};
+ }
+
+ private static float[] rotate(float[] arr, int times) {
+ if (times < 0) {
+ times = times % arr.length + arr.length;
+ }
+
+ float[] res = new float[arr.length];
+ for (int offset = 0; offset < arr.length; ++offset) {
+ res[offset] = arr[(times + offset) % arr.length];
+ }
+ return res;
+ }
}
}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java
index 4bdbe64..51c1422 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Capabilities.java
@@ -18,14 +18,14 @@
import static android.hardware.camera2.CameraCharacteristics.*;
+import android.graphics.ImageFormat;
import android.graphics.Point;
+import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.params.StreamConfigurationMap;
-import android.media.ImageReader;
import android.media.MediaRecorder;
import android.util.Range;
import android.util.Rational;
-import android.view.SurfaceHolder;
import com.android.ex.camera2.portability.debug.Log;
@@ -47,20 +47,20 @@
mSupportedPreviewFpsRange.add(new int[] { fpsRange.getLower(), fpsRange.getUpper() });
}
- // TODO: We only support SurfaceView preview rendering
+ // TODO: We only support TextureView preview rendering
mSupportedPreviewSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList(
- s.getOutputSizes(SurfaceHolder.class))));
+ s.getOutputSizes(SurfaceTexture.class))));
for (int format : s.getOutputFormats()) {
mSupportedPreviewFormats.add(format);
}
- // TODO: We only support MediaRecorder videos capture
+ // TODO: We only support MediaRecorder video capture
mSupportedVideoSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList(
s.getOutputSizes(MediaRecorder.class))));
- // TODO: We only support ImageReader image capture
+ // TODO: We only support JPEG image capture
mSupportedPhotoSizes.addAll(Size.buildListFromAndroidSizes(Arrays.asList(
- s.getOutputSizes(ImageReader.class))));
+ s.getOutputSizes(ImageFormat.JPEG))));
mSupportedPhotoFormats.addAll(mSupportedPreviewFormats);
buildSceneModes(p);
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
index 288ded7..efa68e8 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
@@ -84,13 +84,21 @@
// TODO: mCurrentZoomIndex
mExposureCompensationIndex =
queryTemplateDefaultOrMakeOneUp(CONTROL_AE_EXPOSURE_COMPENSATION, 0);
+
mCurrentFlashMode = flashModeFromRequest();
- mCurrentFocusMode = AndroidCamera2Capabilities.focusModeFromInt(
- mTemplateSettings.get(CONTROL_AF_MODE));
- mCurrentSceneMode = AndroidCamera2Capabilities.sceneModeFromInt(
- mTemplateSettings.get(CONTROL_SCENE_MODE));
- mWhiteBalance = AndroidCamera2Capabilities.whiteBalanceFromInt(
- mTemplateSettings.get(CONTROL_AWB_MODE));
+ Integer currentFocusMode = mTemplateSettings.get(CONTROL_AF_MODE);
+ if (currentFocusMode != null) {
+ mCurrentFocusMode = AndroidCamera2Capabilities.focusModeFromInt(currentFocusMode);
+ }
+ Integer currentSceneMode = mTemplateSettings.get(CONTROL_SCENE_MODE);
+ if (currentSceneMode != null) {
+ mCurrentSceneMode = AndroidCamera2Capabilities.sceneModeFromInt(currentSceneMode);
+ }
+ Integer whiteBalance = mTemplateSettings.get(CONTROL_AWB_MODE);
+ if (whiteBalance != null) {
+ mWhiteBalance = AndroidCamera2Capabilities.whiteBalanceFromInt(whiteBalance);
+ }
+
mVideoStabilizationEnabled = queryTemplateDefaultOrMakeOneUp(
CONTROL_VIDEO_STABILIZATION_MODE, CONTROL_VIDEO_STABILIZATION_MODE_OFF) ==
CONTROL_VIDEO_STABILIZATION_MODE_ON;
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java
index 95f0320..c26a1a3 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java
@@ -46,8 +46,6 @@
class AndroidCameraAgentImpl extends CameraAgent {
private static final Log.Tag TAG = new Log.Tag("AndCamAgntImp");
- private Parameters mParameters;
- private boolean mParametersIsDirty;
private CameraDeviceInfo.Characteristics mCharacteristics;
private AndroidCameraCapabilities mCapabilities;
@@ -131,6 +129,7 @@
Camera.getCameraInfo(i, cameraInfos[i]);
}
} catch (RuntimeException ex) {
+ Log.e(TAG, "Exception while creating CameraDeviceInfo", ex);
return null;
}
@@ -204,15 +203,39 @@
}
}
+ private static class ParametersCache {
+ private Parameters mParameters;
+ private Camera mCamera;
+
+ public ParametersCache(Camera camera) {
+ mCamera = camera;
+ }
+
+ public synchronized void invalidate() {
+ mParameters = null;
+ }
+
+ /**
+ * Access parameters from the cache. If cache is empty, block by
+ * retrieving parameters directly from Camera, but if cache is present,
+ * returns immediately.
+ */
+ public synchronized Parameters getBlocking() {
+ if (mParameters == null) {
+ mParameters = mCamera.getParameters();
+ }
+ return mParameters;
+ }
+ }
+
/**
* The handler on which the actual camera operations happen.
*/
private class CameraHandler extends HistoryHandler {
- // Used to retain a copy of Parameters for setting parameters.
- private Parameters mParamsToSet;
private Camera mCamera;
private int mCameraId;
+ private ParametersCache mParameterCache;
private class CaptureCallbacks {
public final ShutterCallback mShutter;
@@ -304,16 +327,16 @@
break;
}
+ Log.i(TAG, "Opening camera " + cameraId + " with camera1 API");
mCamera = android.hardware.Camera.open(cameraId);
if (mCamera != null) {
mCameraId = cameraId;
- mParametersIsDirty = true;
+ mParameterCache = new ParametersCache(mCamera);
- // Get an instance of Camera.Parameters for later use.
- mParamsToSet = mCamera.getParameters();
mCharacteristics =
AndroidCameraDeviceInfo.create().getCharacteristics(cameraId);
- mCapabilities = new AndroidCameraCapabilities(mParamsToSet);
+ mCapabilities = new AndroidCameraCapabilities(
+ mParameterCache.getBlocking());
mCameraState.setState(AndroidCameraStateHolder.CAMERA_IDLE);
if (openCallback != null) {
@@ -442,9 +465,10 @@
mCharacteristics.getPreviewOrientation(msg.arg1));
// Only set the JPEG capture orientation if requested to do so; otherwise,
// capture in the sensor's physical orientation
- mParamsToSet.setRotation(
+ Parameters parameters = mParameterCache.getBlocking();
+ parameters.setRotation(
msg.arg2 > 0 ? mCharacteristics.getJpegOrientation(msg.arg1) : 0);
- mCamera.setParameters(mParamsToSet);
+ mCamera.setParameters(parameters);
break;
}
@@ -474,25 +498,26 @@
}
case CameraActions.APPLY_SETTINGS: {
- mParametersIsDirty = true;
+ Parameters parameters = mParameterCache.getBlocking();
CameraSettings settings = (CameraSettings) msg.obj;
- applyToParameters(settings);
- mCamera.setParameters(mParamsToSet);
+ applySettingsToParameters(settings, parameters);
+ mCamera.setParameters(parameters);
+ mParameterCache.invalidate();
break;
}
case CameraActions.SET_PARAMETERS: {
- mParametersIsDirty = true;
- mParamsToSet.unflatten((String) msg.obj);
- mCamera.setParameters(mParamsToSet);
+ Parameters parameters = mParameterCache.getBlocking();
+ parameters.unflatten((String) msg.obj);
+ mCamera.setParameters(parameters);
+ mParameterCache.invalidate();
break;
}
case CameraActions.GET_PARAMETERS: {
- if (mParametersIsDirty) {
- mParameters = mCamera.getParameters();
- mParametersIsDirty = false;
- }
+ Parameters[] parametersHolder = (Parameters[]) msg.obj;
+ Parameters parameters = mParameterCache.getBlocking();
+ parametersHolder[0] = parameters;
break;
}
@@ -507,7 +532,7 @@
}
case CameraActions.REFRESH_PARAMETERS: {
- mParametersIsDirty = true;
+ mParameterCache.invalidate();;
break;
}
@@ -555,69 +580,70 @@
}
}
- private void applyToParameters(final CameraSettings settings) {
+ private void applySettingsToParameters(final CameraSettings settings,
+ final Parameters parameters) {
final CameraCapabilities.Stringifier stringifier = mCapabilities.getStringifier();
Size photoSize = settings.getCurrentPhotoSize();
- mParamsToSet.setPictureSize(photoSize.width(), photoSize.height());
+ parameters.setPictureSize(photoSize.width(), photoSize.height());
Size previewSize = settings.getCurrentPreviewSize();
- mParamsToSet.setPreviewSize(previewSize.width(), previewSize.height());
+ parameters.setPreviewSize(previewSize.width(), previewSize.height());
if (settings.getPreviewFrameRate() == -1) {
- mParamsToSet.setPreviewFpsRange(settings.getPreviewFpsRangeMin(),
+ parameters.setPreviewFpsRange(settings.getPreviewFpsRangeMin(),
settings.getPreviewFpsRangeMax());
} else {
- mParamsToSet.setPreviewFrameRate(settings.getPreviewFrameRate());
+ parameters.setPreviewFrameRate(settings.getPreviewFrameRate());
}
- mParamsToSet.setPreviewFormat(settings.getCurrentPreviewFormat());
- mParamsToSet.setJpegQuality(settings.getPhotoJpegCompressionQuality());
+ parameters.setPreviewFormat(settings.getCurrentPreviewFormat());
+ parameters.setJpegQuality(settings.getPhotoJpegCompressionQuality());
if (mCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
// Should use settings.getCurrentZoomRatio() instead here.
- mParamsToSet.setZoom(settings.getCurrentZoomIndex());
+ parameters.setZoom(settings.getCurrentZoomIndex());
}
- mParamsToSet.setExposureCompensation(settings.getExposureCompensationIndex());
+ parameters.setExposureCompensation(settings.getExposureCompensationIndex());
if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)) {
- mParamsToSet.setAutoExposureLock(settings.isAutoExposureLocked());
+ parameters.setAutoExposureLock(settings.isAutoExposureLocked());
}
- mParamsToSet.setFocusMode(stringifier.stringify(settings.getCurrentFocusMode()));
+ parameters.setFocusMode(stringifier.stringify(settings.getCurrentFocusMode()));
if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK)) {
- mParamsToSet.setAutoWhiteBalanceLock(settings.isAutoWhiteBalanceLocked());
+ parameters.setAutoWhiteBalanceLock(settings.isAutoWhiteBalanceLocked());
}
if (mCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA)) {
if (settings.getFocusAreas().size() != 0) {
- mParamsToSet.setFocusAreas(settings.getFocusAreas());
+ parameters.setFocusAreas(settings.getFocusAreas());
}
}
if (mCapabilities.supports(CameraCapabilities.Feature.METERING_AREA)) {
if (settings.getMeteringAreas().size() != 0) {
- mParamsToSet.setMeteringAreas(settings.getMeteringAreas());
+ parameters.setMeteringAreas(settings.getMeteringAreas());
}
}
if (settings.getCurrentFlashMode() != CameraCapabilities.FlashMode.NO_FLASH) {
- mParamsToSet.setFlashMode(stringifier.stringify(settings.getCurrentFlashMode()));
+ parameters.setFlashMode(stringifier.stringify(settings.getCurrentFlashMode()));
}
if (settings.getCurrentSceneMode() != CameraCapabilities.SceneMode.NO_SCENE_MODE) {
if (settings.getCurrentSceneMode() != null) {
- mParamsToSet
+ parameters
.setSceneMode(stringifier.stringify(settings.getCurrentSceneMode()));
}
}
- mParamsToSet.setRecordingHint(settings.isRecordingHintEnabled());
+ parameters.setRecordingHint(settings.isRecordingHintEnabled());
Size jpegThumbSize = settings.getExifThumbnailSize();
- mParamsToSet.setJpegThumbnailSize(jpegThumbSize.width(), jpegThumbSize.height());
- mParamsToSet.setPictureFormat(settings.getCurrentPhotoFormat());
+ parameters.setJpegThumbnailSize(jpegThumbSize.width(), jpegThumbSize.height());
+ parameters.setPictureFormat(settings.getCurrentPhotoFormat());
CameraSettings.GpsData gpsData = settings.getGpsData();
if (gpsData == null) {
- mParamsToSet.removeGpsData();
+ parameters.removeGpsData();
} else {
- mParamsToSet.setGpsTimestamp(gpsData.timeStamp);
+ parameters.setGpsTimestamp(gpsData.timeStamp);
if (gpsData.processingMethod != null) {
// It's a hack since we always use GPS time stamp but does
// not use other fields sometimes. Setting processing
// method to null means the other fields should not be used.
- mParamsToSet.setGpsAltitude(gpsData.altitude);
- mParamsToSet.setGpsLatitude(gpsData.latitude);
- mParamsToSet.setGpsLongitude(gpsData.longitude);
- mParamsToSet.setGpsProcessingMethod(gpsData.processingMethod);
+ parameters.setGpsAltitude(gpsData.altitude);
+ parameters.setGpsLatitude(gpsData.latitude);
+ parameters.setGpsLongitude(gpsData.longitude);
+ parameters.setGpsProcessingMethod(gpsData.processingMethod);
}
}
@@ -644,6 +670,7 @@
mCapabilities = capabilities;
}
+ @Deprecated
@Override
public android.hardware.Camera getCamera() {
return mCamera;
@@ -826,6 +853,7 @@
});
}
+ @Deprecated
@Override
public void setParameters(final Parameters params) {
if (params == null) {
@@ -844,17 +872,21 @@
});
}
+ @Deprecated
@Override
public Parameters getParameters() {
final WaitDoneBundle bundle = new WaitDoneBundle();
+ final Parameters[] parametersHolder = new Parameters[1];
mDispatchThread.runJobSync(new Runnable() {
@Override
public void run() {
- mCameraHandler.sendEmptyMessage(CameraActions.GET_PARAMETERS);
+ Message getParametersMessage = mCameraHandler.obtainMessage(
+ CameraActions.GET_PARAMETERS, parametersHolder);
+ mCameraHandler.sendMessage(getParametersMessage);
mCameraHandler.post(bundle.mUnlockRunnable);
}
}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "get parameters");
- return mParameters;
+ return parametersHolder[0];
}
@Override
@@ -870,14 +902,19 @@
@Override
public String dumpDeviceSettings() {
- String flattened = mParameters.flatten();
- StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
- String dumpedSettings = new String();
- while (tokenizer.hasMoreElements()) {
- dumpedSettings += tokenizer.nextToken() + '\n';
- }
+ Parameters parameters = getParameters();
+ if (parameters != null) {
+ String flattened = getParameters().flatten();
+ StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
+ String dumpedSettings = new String();
+ while (tokenizer.hasMoreElements()) {
+ dumpedSettings += tokenizer.nextToken() + '\n';
+ }
- return dumpedSettings;
+ return dumpedSettings;
+ } else {
+ return "[no parameters retrieved]";
+ }
}
@Override
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgentFactory.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgentFactory.java
index 00dc280..77ad0c3 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgentFactory.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgentFactory.java
@@ -17,42 +17,150 @@
package com.android.ex.camera2.portability;
import android.content.Context;
+import android.os.Build;
+
+import com.android.ex.camera2.portability.debug.Log;
+import com.android.ex.camera2.portability.util.SystemProperties;
/**
* A factory class for {@link CameraAgent}.
+ *
+ * <p>The choice of framework API to use can be made automatically based on the
+ * system API level, explicitly forced by the client app, or overridden entirely
+ * by setting the system property com.camera2.portability.fwk_api to 1 or 2.</p>
*/
public class CameraAgentFactory {
+ private static final Log.Tag TAG = new Log.Tag("CamAgntFact");
+
+ /** Android release replacing the Camera class with the camera2 package. */
+ private static final int FIRST_SDK_WITH_API_2 = 21;
+
+ // The debugging override, which overrides *all* API level selections if set
+ // to API_LEVEL_OVERRIDE_API{1,2}; otherwise, this has no effect. Note that
+ // we check this once when the library is first loaded so that #recycle()
+ // doesn't try to clean up the wrong type of CameraAgent.
+ private static final String API_LEVEL_OVERRIDE_KEY = "camera2.portability.force_api";
+ private static final String API_LEVEL_OVERRIDE_DEFAULT = "0";
+ private static final String API_LEVEL_OVERRIDE_API1 = "1";
+ private static final String API_LEVEL_OVERRIDE_API2 = "2";
+ private static final String API_LEVEL_OVERRIDE_VALUE =
+ SystemProperties.get(API_LEVEL_OVERRIDE_KEY, API_LEVEL_OVERRIDE_DEFAULT);
private static CameraAgent sAndroidCameraAgent;
+ private static CameraAgent sAndroidCamera2Agent;
private static int sAndroidCameraAgentClientCount;
+ private static int sAndroidCamera2AgentClientCount;
/**
- * Returns the android camera implementation of {@link com.android.camera.cameradevice.CameraAgent}.
- *
- * @return The {@link CameraAgent} to control the camera device.
+ * Used to indicate which camera framework should be used.
*/
- public static synchronized CameraAgent getAndroidCameraAgent(Context context) {
- if (sAndroidCameraAgent == null) {
- if (false) {
- sAndroidCameraAgent = new AndroidCamera2AgentImpl(context);
- } else {
- sAndroidCameraAgent = new AndroidCameraAgentImpl();
- }
- sAndroidCameraAgentClientCount = 1;
+ public static enum CameraApi {
+ /** Automatically select based on the device's SDK level. */
+ AUTO,
+
+ /** Use the {@link android.hardware.Camera} class. */
+ API_1,
+
+ /** Use the {@link android.hardware.camera2} package. */
+ API_2
+ };
+
+ private static CameraApi highestSupportedApi() {
+ // TODO: Check SDK_INT instead of RELEASE before L launch
+ if (Build.VERSION.SDK_INT >= FIRST_SDK_WITH_API_2 || Build.VERSION.CODENAME.equals("L")) {
+ return CameraApi.API_2;
} else {
- ++sAndroidCameraAgentClientCount;
+ return CameraApi.API_1;
}
- return sAndroidCameraAgent;
+ }
+
+ private static CameraApi validateApiChoice(CameraApi choice) {
+ if (API_LEVEL_OVERRIDE_VALUE.equals(API_LEVEL_OVERRIDE_API1)) {
+ Log.d(TAG, "API level overridden by system property: forced to 1");
+ return CameraApi.API_1;
+ } else if (API_LEVEL_OVERRIDE_VALUE.equals(API_LEVEL_OVERRIDE_API2)) {
+ Log.d(TAG, "API level overridden by system property: forced to 2");
+ return CameraApi.API_2;
+ }
+
+ if (choice == null) {
+ Log.w(TAG, "null API level request, so assuming AUTO");
+ choice = CameraApi.AUTO;
+ }
+ if (choice == CameraApi.AUTO) {
+ choice = highestSupportedApi();
+ }
+
+ return choice;
+ }
+
+ /**
+ * Returns the android camera implementation of
+ * {@link com.android.camera.cameradevice.CameraAgent}.
+ *
+ * <p>To clean up the resources allocated by this call, be sure to invoke
+ * {@link #recycle(boolean)} with the same {@code api} value provided
+ * here.</p>
+ *
+ * @param context The application context.
+ * @param api Which camera framework to use.
+ * @return The {@link CameraAgent} to control the camera device.
+ *
+ * @throws UnsupportedOperationException If {@code CameraApi.API_2} was
+ * requested on an unsupported device.
+ */
+ public static synchronized CameraAgent getAndroidCameraAgent(Context context, CameraApi api) {
+ api = validateApiChoice(api);
+
+ if (api == CameraApi.API_1) {
+ if (sAndroidCameraAgent == null) {
+ sAndroidCameraAgent = new AndroidCameraAgentImpl();
+ sAndroidCameraAgentClientCount = 1;
+ } else {
+ ++sAndroidCameraAgentClientCount;
+ }
+ return sAndroidCameraAgent;
+ } else { // API_2
+ if (highestSupportedApi() == CameraApi.API_1) {
+ throw new UnsupportedOperationException("Camera API_2 unavailable on this device");
+ }
+
+ if (sAndroidCamera2Agent == null) {
+ sAndroidCamera2Agent = new AndroidCamera2AgentImpl(context);
+ sAndroidCamera2AgentClientCount = 1;
+ } else {
+ ++sAndroidCamera2AgentClientCount;
+ }
+ return sAndroidCamera2Agent;
+ }
}
/**
* Recycles the resources. Always call this method when the activity is
* stopped.
+ *
+ * @param api Which camera framework handle to recycle.
+ *
+ * @throws UnsupportedOperationException If {@code CameraApi.API_2} was
+ * requested on an unsupported device.
*/
- public static synchronized void recycle() {
- if (--sAndroidCameraAgentClientCount == 0 && sAndroidCameraAgent != null) {
- sAndroidCameraAgent.recycle();
- sAndroidCameraAgent = null;
+ public static synchronized void recycle(CameraApi api) {
+ api = validateApiChoice(api);
+
+ if (api == CameraApi.API_1) {
+ if (--sAndroidCameraAgentClientCount == 0 && sAndroidCameraAgent != null) {
+ sAndroidCameraAgent.recycle();
+ sAndroidCameraAgent = null;
+ }
+ } else { // API_2
+ if (highestSupportedApi() == CameraApi.API_1) {
+ throw new UnsupportedOperationException("Camera API_2 unavailable on this device");
+ }
+
+ if (--sAndroidCamera2AgentClientCount == 0 && sAndroidCamera2Agent != null) {
+ sAndroidCamera2Agent.recycle();
+ sAndroidCamera2Agent = null;
+ }
}
}
}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraDeviceInfo.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraDeviceInfo.java
index a657170..72a641e 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/CameraDeviceInfo.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraDeviceInfo.java
@@ -16,7 +16,8 @@
package com.android.ex.camera2.portability;
-import android.hardware.Camera;
+import android.graphics.Matrix;
+import android.graphics.RectF;
import com.android.ex.camera2.portability.debug.Log;
@@ -67,17 +68,18 @@
public abstract boolean isFacingFront();
/**
- * @return The camera image orientation, or the clockwise rotation angle
- * that must be applied to display it in its natural orientation
- * (in degrees, always a multiple of 90, and between [90,270]).
+ * @return The camera sensor orientation, or the counterclockwise angle
+ * from its natural position that the device must be held at
+ * for the sensor to be right side up (in degrees, always a
+ * multiple of 90, and between 0 and 270, inclusive).
*/
public abstract int getSensorOrientation();
/**
* @param currentDisplayOrientation
- * The current display orientation, as measured clockwise from
- * the device's natural orientation (in degrees, always a
- * multiple of 90, and between 0 and 270, inclusive).
+ * The current display orientation, measured counterclockwise
+ * from to the device's natural orientation (in degrees, always
+ * a multiple of 90, and between 0 and 270, inclusive).
* @return
* The relative preview image orientation, or the clockwise
* rotation angle that must be applied to display preview
@@ -92,9 +94,9 @@
/**
* @param currentDisplayOrientation
- * The current display orientation, as measured clockwise from
- * the device's natural orientation (in degrees, always a
- * multiple of 90, and between 0 and 270, inclusive).
+ * The current display orientation, measured counterclockwise
+ * from to the device's natural orientation (in degrees, always
+ * a multiple of 90, and between 0 and 270, inclusive).
* @return
* The relative capture image orientation, or the clockwise
* rotation angle that must be applied to display these frames
@@ -120,11 +122,8 @@
*/
protected int getRelativeImageOrientation(int currentDisplayOrientation,
boolean compensateForMirroring) {
- if (currentDisplayOrientation % 90 != 0) {
- Log.e(TAG, "Provided display orientation is not divisible by 90");
- }
- if (currentDisplayOrientation < 0 || currentDisplayOrientation > 270) {
- Log.e(TAG, "Provided display orientation is outside expected range");
+ if (!orientationIsValid(currentDisplayOrientation)) {
+ return 0;
}
int result = 0;
@@ -142,8 +141,70 @@
}
/**
+ * @param currentDisplayOrientation
+ * The current display orientation, measured counterclockwise
+ * from to the device's natural orientation (in degrees, always
+ * a multiple of 90, and between 0 and 270, inclusive).
+ * @param surfaceDimensions
+ * The dimensions of the {@link android.view.Surface} on which
+ * the preview image is being rendered. It usually only makes
+ * sense for the upper-left corner to be at the origin.
+ * @return
+ * The transform matrix that should be applied to the
+ * {@link android.view.Surface} in order for the image to
+ * display properly in the device's current orientation.
+ */
+ public Matrix getPreviewTransform(int currentDisplayOrientation, RectF surfaceDimensions) {
+ return getPreviewTransform(currentDisplayOrientation, surfaceDimensions,
+ new RectF(surfaceDimensions));
+ }
+
+ /**
+ * @param currentDisplayOrientation
+ * The current display orientation, measured counterclockwise
+ * from to the device's natural orientation (in degrees, always
+ * a multiple of 90, and between 0 and 270, inclusive).
+ * @param surfaceDimensions
+ * The dimensions of the {@link android.view.Surface} on which
+ * the preview image is being rendered. It usually only makes
+ * sense for the upper-left corner to be at the origin.
+ * @param desiredBounds
+ * The boundaries within the {@link android.view.Surface} where
+ * the final image should appear. These can be used to
+ * translate and scale the output, but note that the image will
+ * be stretched to fit, possibly changing its aspect ratio.
+ * @return
+ * The transform matrix that should be applied to the
+ * {@link android.view.Surface} in order for the image to
+ * display properly in the device's current orientation.
+ */
+ public Matrix getPreviewTransform(int currentDisplayOrientation, RectF surfaceDimensions,
+ RectF desiredBounds) {
+ if (!orientationIsValid(currentDisplayOrientation) ||
+ surfaceDimensions.equals(desiredBounds)) {
+ return new Matrix();
+ }
+
+ Matrix transform = new Matrix();
+ transform.setRectToRect(surfaceDimensions, desiredBounds, Matrix.ScaleToFit.FILL);
+ return transform;
+ }
+
+ /**
* @return Whether the shutter sound can be disabled.
*/
public abstract boolean canDisableShutterSound();
+
+ protected static boolean orientationIsValid(int angle) {
+ if (angle % 90 != 0) {
+ Log.e(TAG, "Provided display orientation is not divisible by 90");
+ return false;
+ }
+ if (angle < 0 || angle > 270) {
+ Log.e(TAG, "Provided display orientation is outside expected range");
+ return false;
+ }
+ return true;
+ }
}
}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/util/SystemProperties.java b/camera2/portability/src/com/android/ex/camera2/portability/util/SystemProperties.java
new file mode 100644
index 0000000..81493f3
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/util/SystemProperties.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package com.android.ex.camera2.portability.util;
+
+import com.android.ex.camera2.portability.debug.Log;
+
+import java.lang.reflect.Method;
+
+/**
+ * Mirrors hidden class {@link android.os.SystemProperties} (available since API Level 1).
+ */
+public final class SystemProperties {
+ private static final Log.Tag TAG = new Log.Tag("SysProps");
+
+ /**
+ * Gets system properties set by <code>adb shell setprop <em>key</em> <em>value</em></code>
+ *
+ * @param key the property key.
+ * @param defaultValue the value to return if the property is undefined or empty (this parameter
+ * may be {@code null}).
+ * @return the system property value or the default value.
+ */
+ public static String get(String key, String defaultValue) {
+ try {
+ final Class<?> systemProperties = Class.forName("android.os.SystemProperties");
+ final Method get = systemProperties.getMethod("get", String.class, String.class);
+ return (String) get.invoke(null, key, defaultValue);
+ } catch (Exception e) {
+ // This should never happen
+ Log.e(TAG, "Exception while getting system property: ", e);
+ return defaultValue;
+ }
+ }
+
+ private SystemProperties() {
+ }
+}
diff --git a/common/java/com/android/common/widget/CompositeCursorAdapter.java b/common/java/com/android/common/widget/CompositeCursorAdapter.java
index 114065f..dddbcf6 100644
--- a/common/java/com/android/common/widget/CompositeCursorAdapter.java
+++ b/common/java/com/android/common/widget/CompositeCursorAdapter.java
@@ -54,6 +54,10 @@
public boolean getHasHeader() {
return hasHeader;
}
+
+ public boolean isEmpty() {
+ return count == 0;
+ }
}
private final Context mContext;
@@ -110,7 +114,7 @@
/**
* Removes cursors for all partitions.
*/
- // TODO: Is this really what this is supposed to do? Just remove the cursors? Not close them?
+ // TODO: Is this really what this is supposed to do? Just remove the cursors? Not close them?
// Not remove the partitions themselves? Isn't this leaking?
public void clearPartitions() {