summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSol Boucher <solb@google.com>2014-07-24 10:28:14 -0700
committerSol Boucher <solb@google.com>2014-07-30 14:13:52 -0700
commit8ba391e3f88936557ad6d44bbef32cb08f4ca310 (patch)
tree2e2af725f365bef500146fbd1af93f814a1802cc
parent90aa7e817d1969b4fbbfc36dbeca6c500b6aaa0b (diff)
downloadandroid_frameworks_ex-8ba391e3f88936557ad6d44bbef32cb08f4ca310.tar.gz
android_frameworks_ex-8ba391e3f88936557ad6d44bbef32cb08f4ca310.tar.bz2
android_frameworks_ex-8ba391e3f88936557ad6d44bbef32cb08f4ca310.zip
Create new com.android.ex.camera2.utils package
It contains: - Camera2RequestSettingsSet class for storing/unioning CaptureRequest.Builders - A Camera2CaptureListenerSplitter class for connecting multiple such listeners - A Camera2CpatureListenerForwarder for relaying callbacks to another Handler Change-Id: I47381e4a45b4232b86e5498a1cc39cc600b5c5c5
-rw-r--r--camera2/utils/Android.mk17
-rw-r--r--camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerForwarder.java99
-rw-r--r--camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerSplitter.java95
-rw-r--r--camera2/utils/src/com/android/ex/camera2/utils/Camera2RequestSettingsSet.java238
-rw-r--r--camera2/utils/tests/Android.mk24
-rw-r--r--camera2/utils/tests/AndroidManifest.xml27
-rw-r--r--camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2UtilsTest.java478
-rw-r--r--camera2/utils/utils.mk23
8 files changed, 1001 insertions, 0 deletions
diff --git a/camera2/utils/Android.mk b/camera2/utils/Android.mk
new file mode 100644
index 0000000..36595e1
--- /dev/null
+++ b/camera2/utils/Android.mk
@@ -0,0 +1,17 @@
+# Copyright 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(LOCAL_PATH)/utils.mk \
+ $(call all-subdir-makefiles)
diff --git a/camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerForwarder.java b/camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerForwarder.java
new file mode 100644
index 0000000..35b1c6d
--- /dev/null
+++ b/camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerForwarder.java
@@ -0,0 +1,99 @@
+/*
+ * 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.utils;
+
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCaptureSession.CaptureListener;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.os.Handler;
+
+/**
+ * Proxy that forwards all updates to another {@link CaptureListener}, invoking
+ * its callbacks on a separate {@link Handler}.
+ */
+public class Camera2CaptureListenerForwarder extends CaptureListener {
+ private CaptureListener mListener;
+ private Handler mHandler;
+
+ public Camera2CaptureListenerForwarder(CaptureListener listener, Handler handler) {
+ mListener = listener;
+ mHandler = handler;
+ }
+
+ @Override
+ public void onCaptureCompleted(final CameraCaptureSession session, final CaptureRequest request,
+ final TotalCaptureResult result) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onCaptureCompleted(session, request, result);
+ }});
+ }
+
+ @Override
+ public void onCaptureFailed(final CameraCaptureSession session, final CaptureRequest request,
+ final CaptureFailure failure) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onCaptureFailed(session, request, failure);
+ }});
+ }
+
+ @Override
+ public void onCaptureProgressed(final CameraCaptureSession session,
+ final CaptureRequest request,
+ final CaptureResult partialResult) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onCaptureProgressed(session, request, partialResult);
+ }});
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(final CameraCaptureSession session, final int sequenceId) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onCaptureSequenceAborted(session, sequenceId);
+ }});
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(final CameraCaptureSession session, final int sequenceId,
+ final long frameNumber) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onCaptureSequenceCompleted(session, sequenceId, frameNumber);
+ }});
+ }
+
+ @Override
+ public void onCaptureStarted(final CameraCaptureSession session, final CaptureRequest request,
+ final long timestamp) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onCaptureStarted(session, request, timestamp);
+ }});
+ }
+}
diff --git a/camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerSplitter.java b/camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerSplitter.java
new file mode 100644
index 0000000..a13dc04
--- /dev/null
+++ b/camera2/utils/src/com/android/ex/camera2/utils/Camera2CaptureListenerSplitter.java
@@ -0,0 +1,95 @@
+/*
+ * 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.utils;
+
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCaptureSession.CaptureListener;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Junction that allows notifying multiple {@link CaptureListener}s whenever
+ * the {@link CameraCaptureSession} posts a capture-related update.
+ */
+public class Camera2CaptureListenerSplitter extends CaptureListener {
+ private final List<CaptureListener> mRecipients = new LinkedList<>();
+
+ /**
+ * @param recipients The listeners to notify. Any {@code null} passed here
+ * will be completely ignored.
+ */
+ public Camera2CaptureListenerSplitter(CaptureListener... recipients) {
+ for (CaptureListener listener : recipients) {
+ if (listener != null) {
+ mRecipients.add(listener);
+ }
+ }
+ }
+
+ @Override
+ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+ TotalCaptureResult result) {
+ for (CaptureListener target : mRecipients) {
+ target.onCaptureCompleted(session, request, result);
+ }
+ }
+
+ @Override
+ public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
+ CaptureFailure failure) {
+ for (CaptureListener target : mRecipients) {
+ target.onCaptureFailed(session, request, failure);
+ }
+ }
+
+ @Override
+ public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
+ CaptureResult partialResult) {
+ for (CaptureListener target : mRecipients) {
+ target.onCaptureProgressed(session, request, partialResult);
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(CameraCaptureSession session, int sequenceId) {
+ for (CaptureListener target : mRecipients) {
+ target.onCaptureSequenceAborted(session, sequenceId);
+ }
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId,
+ long frameNumber) {
+ for (CaptureListener target : mRecipients) {
+ target.onCaptureSequenceCompleted(session, sequenceId, frameNumber);
+ }
+ }
+
+ @Override
+ public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
+ long timestamp) {
+ for (CaptureListener target : mRecipients) {
+ target.onCaptureStarted(session, request, timestamp);
+ }
+ }
+}
diff --git a/camera2/utils/src/com/android/ex/camera2/utils/Camera2RequestSettingsSet.java b/camera2/utils/src/com/android/ex/camera2/utils/Camera2RequestSettingsSet.java
new file mode 100644
index 0000000..fac8f1a
--- /dev/null
+++ b/camera2/utils/src/com/android/ex/camera2/utils/Camera2RequestSettingsSet.java
@@ -0,0 +1,238 @@
+/*
+ * 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.utils;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureRequest.Builder;
+import android.hardware.camera2.CaptureRequest.Key;
+import android.view.Surface;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * A set of settings to be used when filing a {@link CaptureRequest}.
+ */
+public class Camera2RequestSettingsSet {
+ private final Map<Key<?>, Object> mDictionary;
+ private long mRevision;
+
+ /**
+ * Create a new instance with no settings defined.
+ *
+ * <p>Creating a request from this object without first specifying any
+ * properties on it is equivalent to just creating a request directly
+ * from the template of choice. Its revision identifier is initially
+ * {@code 0}, and will remain thus until its first modification.</p>
+ */
+ public Camera2RequestSettingsSet() {
+ mDictionary = new HashMap<>();
+ mRevision = 0;
+ }
+
+ /**
+ * Perform a deep copy of the defined settings and revision number.
+ *
+ * @param other The reference instance.
+ *
+ * @throws NullPointerException If {@code other} is {@code null}.
+ */
+ public Camera2RequestSettingsSet(Camera2RequestSettingsSet other) {
+ if (other == null) {
+ throw new NullPointerException("Tried to copy null Camera2RequestSettingsSet");
+ }
+
+ mDictionary = new HashMap<>(other.mDictionary);
+ mRevision = other.mRevision;
+ }
+
+ /**
+ * Specify a setting, potentially overriding the template's default choice.
+ *
+ * <p>Providing a {@code null} {@code value} will indicate a forced use of
+ * the template's selection for that {@code key}; the difference here is
+ * that this information will be propagated with unions as documented in
+ * {@link #union}. This method increments the revision identifier if the new
+ * choice is different than the existing selection.</p>
+ *
+ * @param key Which setting to alter.
+ * @param value The new selection for that setting, or {@code null} to force
+ * the use of the template's default selection for this field.
+ * @return Whether the settings were updated, which only occurs if the
+ * {@code value} is different from any already stored.
+ *
+ * @throws NullPointerException If {@code key} is {@code null}.
+ */
+ public <T> boolean set(Key<T> key, T value) {
+ if (key == null) {
+ throw new NullPointerException("Received a null key");
+ }
+
+ Object currentValue = get(key);
+ // Only save the value if it's different from the one we already have
+ if (!mDictionary.containsKey(key) || !Objects.equals(value, currentValue)) {
+ mDictionary.put(key, value);
+ ++mRevision;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Unsets a setting, preventing it from being propagated with unions or from
+ * overriding the default when creating a capture request.
+ *
+ * <p>This method increments the revision identifier if a selection had
+ * previously been made for that parameter.</p>
+ *
+ * @param key Which setting to reset.
+ * @return Whether the settings were updated, which only occurs if the
+ * specified setting already had a value or was forced to default.
+ *
+ * @throws NullPointerException If {@code key} is {@code null}.
+ */
+ public boolean unset(Key<?> key) {
+ if (key == null) {
+ throw new NullPointerException("Received a null key");
+ }
+
+ if (mDictionary.containsKey(key)) {
+ mDictionary.remove(key);
+ ++mRevision;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Interrogate the current specialization of a setting.
+ *
+ * @param key Which setting to check.
+ * @return The current selection for that setting, or {@code null} if the
+ * setting is unset or forced to the template-defined default.
+ *
+ * @throws NullPointerException If {@code key} is {@code null}.
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T get(Key<T> key) {
+ if (key == null) {
+ throw new NullPointerException("Received a null key");
+ }
+ return (T) mDictionary.get(key);
+ }
+
+ /**
+ * Query this instance for whether it prefers a particular choice for the
+ * given request parameter.
+ *
+ * <p>This method can be used to detect whether a particular field is forced
+ * to its default value or simply unset. While {@link #get} will return
+ * {@code null} in both these cases, this method will return {@code true}
+ * and {@code false}, respectively.</p>
+
+ * @param key Which setting to look for.
+ * @return Whether that setting has a value that will propagate with unions.
+ *
+ * @throws NullPointerException If {@code key} is {@code null}.
+ */
+ public boolean contains(Key<?> key) {
+ if (key == null) {
+ throw new NullPointerException("Received a null key");
+ }
+ return mDictionary.containsKey(key);
+ }
+
+ /**
+ * Get this set of settings's revision identifier, which can be compared
+ * against cached past values to determine whether it has been modified.
+ * <p>Distinct revisions across the same object do not necessarily indicate
+ * that the object's key/value pairs have changed at all, but the same
+ * revision on the same object does imply that they've stayed the same.</p>
+ *
+ * @return The number of modifications made since the beginning of this
+ * object's heritage.
+ */
+ public long getRevision() {
+ return mRevision;
+ }
+
+ /**
+ * Add all settings choices defined by {@code moreSettings} to this object.
+ *
+ * <p>For any settings defined in both, the choice stored in the argument
+ * to this method take precedence. Unset settings are not propagated, but
+ * those forced to default as described in {@link set} are also forced to
+ * default in {@code this} set. Invoking this method increments {@code this}
+ * object's revision counter, but leaves the argument's unchanged.</p>
+ *
+ * @param moreSettings The source of the additional settings ({@code null}
+ * is allowed here).
+ * @return Whether these settings were updated, which can only fail if the
+ * target itself is also given as the argument.
+ */
+ public boolean union(Camera2RequestSettingsSet moreSettings) {
+ if (moreSettings == null || moreSettings == this) {
+ return false;
+ }
+
+ mDictionary.putAll(moreSettings.mDictionary);
+ ++mRevision;
+ return true;
+ }
+
+ /**
+ * Create a {@link CaptureRequest} specialized for the specified
+ * {@link CameraDevice} and targeting the given {@link Surface}s.
+ *
+ * @param camera The camera from which to capture.
+ * @param template A {@link CaptureRequest} template defined in
+ * {@link CameraDevice}.
+ * @param targets The location(s) to draw the resulting image onto.
+ * @return The request, ready to be passed to the camera framework.
+ *
+ * @throws CameraAccessException Upon an underlying framework API failure.
+ * @throws NullPointerException If any argument is {@code null}.
+ */
+ public CaptureRequest createRequest(CameraDevice camera, int template, Surface... targets)
+ throws CameraAccessException {
+ if (camera == null) {
+ throw new NullPointerException("Tried to create request using null CameraDevice");
+ }
+
+ Builder reqBuilder = camera.createCaptureRequest(template);
+ for (Key<?> key : mDictionary.keySet()) {
+ setRequestFieldIfNonNull(reqBuilder, key);
+ }
+ for (Surface target : targets) {
+ if (target == null) {
+ throw new NullPointerException("Tried to add null Surface as request target");
+ }
+ reqBuilder.addTarget(target);
+ }
+ return reqBuilder.build();
+ }
+
+ private <T> void setRequestFieldIfNonNull(Builder requestBuilder, Key<T> key) {
+ T value = get(key);
+ if (value != null) {
+ requestBuilder.set(key, value);
+ }
+ }
+}
diff --git a/camera2/utils/tests/Android.mk b/camera2/utils/tests/Android.mk
new file mode 100644
index 0000000..6b3a569
--- /dev/null
+++ b/camera2/utils/tests/Android.mk
@@ -0,0 +1,24 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := android-ex-camera2-utils-tests
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_STATIC_JAVA_LIBRARIES := android-ex-camera2-utils android-support-test mockito-target
+
+include $(BUILD_PACKAGE)
diff --git a/camera2/utils/tests/AndroidManifest.xml b/camera2/utils/tests/AndroidManifest.xml
new file mode 100644
index 0000000..2f3cc08
--- /dev/null
+++ b/camera2/utils/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.ex.camera2.utils.tests">
+ <uses-permission android:name="android.permission.CAMERA" />
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.ex.camera2.utils.tests" />
+</manifest>
diff --git a/camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2UtilsTest.java b/camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2UtilsTest.java
new file mode 100644
index 0000000..dd9566b
--- /dev/null
+++ b/camera2/utils/tests/src/com/android/ex/camera2/utils/Camera2UtilsTest.java
@@ -0,0 +1,478 @@
+/*
+ * 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.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCaptureSession.CaptureListener;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureRequest.Key;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.support.test.InjectContext;
+import android.view.Surface;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class Camera2UtilsTest {
+ private void captureListenerSplitterAllCallbacksReceived(CaptureListener splitter,
+ CaptureListener... terminals) {
+ splitter.onCaptureCompleted(null, null, null);
+ for (CaptureListener each : terminals) {
+ verify(each).onCaptureCompleted(null, null, null);
+ }
+ splitter.onCaptureFailed(null, null, null);
+ for (CaptureListener each : terminals) {
+ verify(each).onCaptureFailed(null, null, null);
+ }
+ splitter.onCaptureProgressed(null, null, null);
+ for (CaptureListener each : terminals) {
+ verify(each).onCaptureProgressed(null, null, null);
+ }
+ splitter.onCaptureSequenceAborted(null, 0);
+ for (CaptureListener each : terminals) {
+ verify(each).onCaptureSequenceAborted(null, 0);
+ }
+ splitter.onCaptureSequenceCompleted(null, 0, 0L);
+ for (CaptureListener each : terminals) {
+ verify(each).onCaptureSequenceCompleted(null, 0, 0L);
+ }
+ splitter.onCaptureStarted(null, null, 0L);
+ for (CaptureListener each : terminals) {
+ verify(each).onCaptureStarted(null, null, 0L);
+ }
+ }
+
+ @Test
+ public void captureListenerSplitter() {
+ CaptureListener firstBackingListener = mock(CaptureListener.class);
+ CaptureListener secondBackingListener = mock(CaptureListener.class);
+ captureListenerSplitterAllCallbacksReceived(
+ new Camera2CaptureListenerSplitter(firstBackingListener, secondBackingListener),
+ firstBackingListener, secondBackingListener);
+ }
+
+ @Test
+ public void captureListenerSplitterEmpty() {
+ captureListenerSplitterAllCallbacksReceived(new Camera2CaptureListenerSplitter());
+ }
+
+ @Test
+ public void captureListenerSplitterNoNpe() {
+ captureListenerSplitterAllCallbacksReceived(
+ new Camera2CaptureListenerSplitter((CaptureListener) null));
+ }
+
+ @Test
+ public void captureListenerSplitterMultipleNulls() {
+ captureListenerSplitterAllCallbacksReceived(
+ new Camera2CaptureListenerSplitter(null, null, null));
+ }
+
+ @Test
+ public void captureListenerSplitterValidAndNull() {
+ CaptureListener onlyRealBackingListener = mock(CaptureListener.class);
+ captureListenerSplitterAllCallbacksReceived(
+ new Camera2CaptureListenerSplitter(null, onlyRealBackingListener),
+ onlyRealBackingListener);
+ }
+
+ private <T> void requestSettingsSetAndForget(Camera2RequestSettingsSet s, Key<T> k, T v) {
+ s.set(k, v);
+ assertEquals(v, s.get(k));
+ }
+
+ @Test
+ public void requestSettingsSet() {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ // Try a boolean
+ requestSettingsSetAndForget(setUp, CaptureRequest.CONTROL_AE_LOCK, false);
+ requestSettingsSetAndForget(setUp, CaptureRequest.CONTROL_AE_LOCK, true);
+ // Try an int
+ requestSettingsSetAndForget(setUp, CaptureRequest.CONTROL_AE_MODE, 1);
+ requestSettingsSetAndForget(setUp, CaptureRequest.CONTROL_AE_MODE, -1);
+ requestSettingsSetAndForget(setUp, CaptureRequest.CONTROL_AE_MODE, 0);
+ // Try an int[]
+ requestSettingsSetAndForget(setUp, CaptureRequest.SENSOR_TEST_PATTERN_DATA, new int[] {1});
+ requestSettingsSetAndForget(setUp, CaptureRequest.SENSOR_TEST_PATTERN_DATA,
+ new int[] {2, 2});
+ }
+
+ @Test
+ public void requestSettingsSetNullValue() {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ requestSettingsSetAndForget(setUp, CaptureRequest.SENSOR_TEST_PATTERN_DATA, new int[] {1});
+ requestSettingsSetAndForget(setUp, CaptureRequest.SENSOR_TEST_PATTERN_DATA, null);
+ requestSettingsSetAndForget(setUp, CaptureRequest.SENSOR_TEST_PATTERN_DATA,
+ new int[] {2, 2});
+ }
+
+ @Test
+ public void requestSettingsSetUnsetAndContains() {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ assertFalse(setUp.contains(CaptureRequest.CONTROL_AE_LOCK));
+ setUp.set(CaptureRequest.CONTROL_AE_LOCK, false);
+ assertTrue(setUp.contains(CaptureRequest.CONTROL_AE_LOCK));
+ setUp.set(CaptureRequest.CONTROL_AE_LOCK, null);
+ assertTrue(setUp.contains(CaptureRequest.CONTROL_AE_LOCK));
+ setUp.unset(CaptureRequest.CONTROL_AE_LOCK);
+ assertFalse(setUp.contains(CaptureRequest.CONTROL_AE_LOCK));
+
+ setUp.set(CaptureRequest.CONTROL_AE_LOCK, null);
+ assertTrue(setUp.contains(CaptureRequest.CONTROL_AE_LOCK));
+ setUp.set(CaptureRequest.CONTROL_AE_LOCK, false);
+ assertTrue(setUp.contains(CaptureRequest.CONTROL_AE_LOCK));
+ setUp.unset(CaptureRequest.CONTROL_AE_LOCK);
+ assertFalse(setUp.contains(CaptureRequest.CONTROL_AE_LOCK));
+ }
+
+ private static HandlerThread sThread;
+
+ private static Handler sHandler;
+
+ @BeforeClass
+ public static void setupBackgroundHandler() {
+ sThread = new HandlerThread("CameraFramework");
+ sThread.start();
+ sHandler = new Handler(sThread.getLooper());
+ }
+
+ @AfterClass
+ public static void teardownBackgroundHandler() throws Exception {
+ sThread.quitSafely();
+ sThread.join();
+ }
+
+ @InjectContext
+ public Context mContext;
+
+ public class DeviceCapturer extends CameraDevice.StateListener {
+ private CameraDevice mCamera;
+
+ public CameraDevice captureCameraDevice() throws Exception {
+ CameraManager manager =
+ (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ String id = manager.getCameraIdList()[0];
+ synchronized (this) {
+ manager.openCamera(id, this, sHandler);
+ wait();
+ }
+ return mCamera;
+ }
+
+ @Override
+ public synchronized void onOpened(CameraDevice camera) {
+ mCamera = camera;
+ notify();
+ }
+
+ @Override
+ public void onDisconnected(CameraDevice camera) {}
+
+ @Override
+ public void onError(CameraDevice camera, int error) {}
+ }
+
+ private CameraDevice mCamera;
+
+ @Before
+ public void obtainCameraCaptureRequestBuilderFactory() throws Exception {
+ mCamera = new DeviceCapturer().captureCameraDevice();
+ }
+
+ @After
+ public void releaseCameraCaptureRequestBuilderFactory() {
+ mCamera.close();
+ }
+
+ @Test
+ public void requestSettingsSetStartsWithoutChanges() {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ assertEquals(0, setUp.getRevision());
+ }
+
+ private <T> void requestSettingsSetAndAssertChanged(Camera2RequestSettingsSet settings,
+ Key<T> key, T value,
+ boolean shouldHaveChanged) {
+ long revision = settings.getRevision();
+ assertEquals(shouldHaveChanged, settings.set(key, value));
+ assertEquals(shouldHaveChanged ? revision + 1 : revision, settings.getRevision());
+ }
+
+ @Test
+ public void requestSettingsSetChangesReportedCorrectly() {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ requestSettingsSetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, false, true);
+ requestSettingsSetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, false, false);
+ requestSettingsSetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, true, true);
+ }
+
+ @Test
+ public void requestSettingsSetDetectsNoopChanges() {
+ Camera2RequestSettingsSet s = new Camera2RequestSettingsSet();
+ int[] one = {1}, two = {2};
+
+ requestSettingsSetAndAssertChanged(s, CaptureRequest.SENSOR_TEST_PATTERN_DATA, one, true);
+ requestSettingsSetAndAssertChanged(s, CaptureRequest.SENSOR_TEST_PATTERN_DATA, one, false);
+
+ requestSettingsSetAndAssertChanged(s, CaptureRequest.SENSOR_TEST_PATTERN_DATA, null, true);
+ requestSettingsSetAndAssertChanged(s, CaptureRequest.SENSOR_TEST_PATTERN_DATA, null, false);
+
+ requestSettingsSetAndAssertChanged(s, CaptureRequest.SENSOR_TEST_PATTERN_DATA, two, true);
+ requestSettingsSetAndAssertChanged(s, CaptureRequest.SENSOR_TEST_PATTERN_DATA, two, false);
+ }
+
+ private <T> void requestSettingsUnsetAndAssertChanged(Camera2RequestSettingsSet settings,
+ Key<T> key, boolean shouldHaveChanged) {
+ long revision = settings.getRevision();
+ assertEquals(shouldHaveChanged, settings.unset(key));
+ assertEquals(shouldHaveChanged ? revision + 1 : revision, settings.getRevision());
+ }
+
+ @Test
+ public void requestSettingsSetUnsetMakesChangesAndDetectsNoops() {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ requestSettingsUnsetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, false);
+
+ requestSettingsSetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, false, true);
+ requestSettingsUnsetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, true);
+
+ requestSettingsSetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, false, true);
+ requestSettingsSetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, false, false);
+ requestSettingsUnsetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, true);
+ requestSettingsUnsetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, false);
+
+ requestSettingsSetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, false, true);
+ requestSettingsSetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, true, true);
+ requestSettingsUnsetAndAssertChanged(setUp, CaptureRequest.CONTROL_AE_LOCK, true);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void requestSettingsSetNullArgToCopyConstructor() {
+ Camera2RequestSettingsSet flop = new Camera2RequestSettingsSet(null);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void requestSettingsSetNullArgToSetKey() {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ setUp.set(null, null);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void requestSettingsSetNullArgToUnset() {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ setUp.unset(null);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void requestSettingsSetNullArgToContains() {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ setUp.contains(null);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void requestSettingsSetNullArgToGet() {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ setUp.get(null);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void requestSettingsSetNullArgToCreateRequest0() throws Exception {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ setUp.createRequest(null, 0);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void requestSettingsSetNullArgToCreateRequest2() throws Exception {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ setUp.createRequest(mCamera, 0, (Surface) null);
+ }
+
+ @Test(expected=NullPointerException.class)
+ public void requestSettingsSetNullArgToCreateRequest02() throws Exception {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ setUp.createRequest(null, 0, (Surface) null);
+ }
+
+ @Test
+ public void requestSettingsSetNullArgToUnion() {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ assertFalse(setUp.union(null));
+ assertEquals(0, setUp.getRevision());
+ }
+
+ @Test
+ public void requestSettingsSetSelfArgToUnion() {
+ Camera2RequestSettingsSet setUp = new Camera2RequestSettingsSet();
+ assertFalse(setUp.union(setUp));
+ assertEquals(0, setUp.getRevision());
+ }
+
+ @Test
+ public void requestSettingsSetCopyConstructor() {
+ Camera2RequestSettingsSet original = new Camera2RequestSettingsSet();
+ Camera2RequestSettingsSet unchanged = new Camera2RequestSettingsSet(original);
+
+ requestSettingsSetAndForget(original, CaptureRequest.CONTROL_AE_LOCK, true);
+ Camera2RequestSettingsSet changed = new Camera2RequestSettingsSet(original);
+ assertEquals(true, changed.get(CaptureRequest.CONTROL_AE_LOCK));
+ }
+
+ @Test
+ public void requestSettingsSetCopyConstructorPreservesChangedStatus() {
+ Camera2RequestSettingsSet original = new Camera2RequestSettingsSet();
+ Camera2RequestSettingsSet unchanged = new Camera2RequestSettingsSet(original);
+ assertEquals(original.getRevision(), unchanged.getRevision());
+
+ requestSettingsSetAndAssertChanged(original, CaptureRequest.CONTROL_AE_LOCK, true, true);
+ Camera2RequestSettingsSet changed = new Camera2RequestSettingsSet(original);
+ assertEquals(original.getRevision(), changed.getRevision());
+ assertNotSame(original.getRevision(), unchanged.getRevision());
+ }
+
+ @Test
+ public void requestSettingsSetCopyConstructorPerformsDeepCopy() {
+ Camera2RequestSettingsSet original = new Camera2RequestSettingsSet();
+ requestSettingsSetAndForget(original, CaptureRequest.CONTROL_AE_LOCK, true);
+
+ Camera2RequestSettingsSet changed = new Camera2RequestSettingsSet(original);
+ requestSettingsSetAndForget(changed, CaptureRequest.CONTROL_AE_LOCK, false);
+ assertEquals(true, original.get(CaptureRequest.CONTROL_AE_LOCK));
+ }
+
+ @Test
+ public void requestSettingsSetNullMeansDefault() throws Exception {
+ Camera2RequestSettingsSet s = new Camera2RequestSettingsSet();
+ CaptureRequest r1 = s.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW);
+ assertEquals((Object) CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW,
+ r1.get(CaptureRequest.CONTROL_CAPTURE_INTENT));
+
+ requestSettingsSetAndForget(s, CaptureRequest.CONTROL_CAPTURE_INTENT,
+ CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
+ CaptureRequest r2 = s.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW);
+ assertEquals((Object) CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE,
+ r2.get(CaptureRequest.CONTROL_CAPTURE_INTENT));
+
+ requestSettingsSetAndForget(s, CaptureRequest.CONTROL_CAPTURE_INTENT, null);
+ CaptureRequest r3 = s.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW);
+ assertEquals((Object) CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW,
+ r3.get(CaptureRequest.CONTROL_CAPTURE_INTENT));
+
+ s.unset(CaptureRequest.CONTROL_CAPTURE_INTENT);
+ CaptureRequest r4 = s.createRequest(mCamera, CameraDevice.TEMPLATE_PREVIEW);
+ assertEquals((Object) CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW,
+ r4.get(CaptureRequest.CONTROL_CAPTURE_INTENT));
+ }
+
+ @Test
+ public void requestSettingsSetNullPreservedByUnions() {
+ Camera2RequestSettingsSet master = new Camera2RequestSettingsSet();
+ requestSettingsSetAndForget(master, CaptureRequest.CONTROL_CAPTURE_INTENT,
+ CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW);
+
+ Camera2RequestSettingsSet slave = new Camera2RequestSettingsSet();
+ master.union(slave);
+ assertEquals((Object) CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW,
+ master.get(CaptureRequest.CONTROL_CAPTURE_INTENT));
+
+ requestSettingsSetAndForget(slave, CaptureRequest.CONTROL_CAPTURE_INTENT, null);
+ master.union(slave);
+ assertEquals(null, master.get(CaptureRequest.CONTROL_CAPTURE_INTENT));
+
+ requestSettingsSetAndForget(slave, CaptureRequest.CONTROL_CAPTURE_INTENT,
+ CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
+ master.union(slave);
+ assertEquals((Object) CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE,
+ master.get(CaptureRequest.CONTROL_CAPTURE_INTENT));
+
+ slave.unset(CaptureRequest.CONTROL_CAPTURE_INTENT);
+ master.union(slave);
+ assertEquals((Object) CaptureRequest.CONTROL_CAPTURE_INTENT_STILL_CAPTURE,
+ master.get(CaptureRequest.CONTROL_CAPTURE_INTENT));
+ }
+
+ @Test
+ public void requestSettingsSetNullChangesRecorded() throws Exception {
+ Camera2RequestSettingsSet s = new Camera2RequestSettingsSet();
+ requestSettingsSetAndAssertChanged(s, CaptureRequest.CONTROL_CAPTURE_INTENT, null, true);
+ requestSettingsSetAndAssertChanged(s, CaptureRequest.CONTROL_CAPTURE_INTENT,
+ CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW, true);
+ requestSettingsSetAndAssertChanged(s, CaptureRequest.CONTROL_CAPTURE_INTENT, null, true);
+ requestSettingsSetAndAssertChanged(s, CaptureRequest.CONTROL_CAPTURE_INTENT, null, false);
+ }
+
+ @Test
+ public void requestSettingsSetUnionChangesRecorded() {
+ Camera2RequestSettingsSet[] sets = { new Camera2RequestSettingsSet(),
+ new Camera2RequestSettingsSet() };
+ sets[0].union(sets[1]);
+ assertEquals(1, sets[0].getRevision());
+ assertEquals(0, sets[1].getRevision());
+ }
+
+ private <T> void requestSettingsSetsCheckPairOfProperties(Camera2RequestSettingsSet firstSet,
+ Camera2RequestSettingsSet secondSet,
+ Key<T> firstKey,
+ Key<T> secondKey,
+ T expectedFirstSetFirstValue,
+ T expectedFirstSetSecondValue,
+ T expectedSecondSetFirstValue,
+ T expectedSecondSetSecondValue) {
+ assertEquals(expectedFirstSetFirstValue, firstSet.get(firstKey));
+ assertEquals(expectedFirstSetSecondValue, firstSet.get(secondKey));
+ assertEquals(expectedSecondSetFirstValue, secondSet.get(firstKey));
+ assertEquals(expectedSecondSetSecondValue, secondSet.get(secondKey));
+ }
+
+ @Test
+ public void requestSettingsSetUnionChangesReflected() {
+ Camera2RequestSettingsSet[] sets = { new Camera2RequestSettingsSet(),
+ new Camera2RequestSettingsSet() };
+
+ sets[0].set(CaptureRequest.CONTROL_AE_LOCK, true);
+ sets[1].set(CaptureRequest.CONTROL_AWB_LOCK, true);
+ sets[0].union(sets[1]);
+ sets[1].set(CaptureRequest.CONTROL_AE_LOCK, false);
+ requestSettingsSetsCheckPairOfProperties(sets[0], sets[1],
+ CaptureRequest.CONTROL_AE_LOCK, CaptureRequest.CONTROL_AWB_LOCK,
+ true, true, false, true);
+
+ sets[0].union(sets[1]);
+ requestSettingsSetsCheckPairOfProperties(sets[0], sets[1],
+ CaptureRequest.CONTROL_AE_LOCK, CaptureRequest.CONTROL_AWB_LOCK,
+ false, true, false, true);
+
+ sets[1].set(CaptureRequest.CONTROL_AE_LOCK, false);
+ sets[1].set(CaptureRequest.CONTROL_AWB_LOCK, false);
+ sets[0].union(sets[1]);
+ requestSettingsSetsCheckPairOfProperties(sets[0], sets[1],
+ CaptureRequest.CONTROL_AE_LOCK, CaptureRequest.CONTROL_AWB_LOCK,
+ false, false, false, false);
+ }
+}
diff --git a/camera2/utils/utils.mk b/camera2/utils/utils.mk
new file mode 100644
index 0000000..a193582
--- /dev/null
+++ b/camera2/utils/utils.mk
@@ -0,0 +1,23 @@
+# Copyright 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := android-ex-camera2-utils
+LOCAL_MODULE_TAGS := optional
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)