diff options
author | Sol Boucher <solb@google.com> | 2014-07-31 15:46:03 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-07-29 21:36:00 +0000 |
commit | d4e5286bb4145e0371b783158fc8411565429c9b (patch) | |
tree | 7ae90e2fb07c9656dc8d27a62938f216f1b22243 | |
parent | 15773a64b42023e31e2aade23ae44273862a9c04 (diff) | |
parent | 8ba391e3f88936557ad6d44bbef32cb08f4ca310 (diff) | |
download | android_frameworks_ex-d4e5286bb4145e0371b783158fc8411565429c9b.tar.gz android_frameworks_ex-d4e5286bb4145e0371b783158fc8411565429c9b.tar.bz2 android_frameworks_ex-d4e5286bb4145e0371b783158fc8411565429c9b.zip |
Merge "Create new com.android.ex.camera2.utils package" into lmp-dev
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) |