Initial frameserver/OneCamera integration
Change-Id: I2fe0d8acf9ce927a6a0a1dea599299c715503462
diff --git a/src/com/android/camera/one/v2/components/ImageSaver.java b/src/com/android/camera/async/BlockingCloseable.java
similarity index 62%
copy from src/com/android/camera/one/v2/components/ImageSaver.java
copy to src/com/android/camera/async/BlockingCloseable.java
index 7138029..90ef168 100644
--- a/src/com/android/camera/one/v2/components/ImageSaver.java
+++ b/src/com/android/camera/async/BlockingCloseable.java
@@ -14,16 +14,17 @@
* limitations under the License.
*/
-package com.android.camera.one.v2.components;
-
-import com.android.camera.one.v2.camera2proxy.ImageProxy;
+package com.android.camera.async;
/**
- * Interface for an image-saving object.
+ * An {@link AutoCloseable} which blocks until its associated resources are
+ * released.
*/
-public interface ImageSaver {
+public interface BlockingCloseable extends AutoCloseable {
/**
- * Implementations should save the image to disk and close it.
+ * Implementations must tolerate multiple calls to close(), and may block
+ * until their associated resources are released.
*/
- public void saveAndCloseImage(ImageProxy image);
+ @Override
+ public void close() throws InterruptedException;
}
diff --git a/src/com/android/camera/async/BufferQueueController.java b/src/com/android/camera/async/BufferQueueController.java
index 89dd2e5..e8629c0 100644
--- a/src/com/android/camera/async/BufferQueueController.java
+++ b/src/com/android/camera/async/BufferQueueController.java
@@ -20,18 +20,20 @@
* An output stream of objects which can be closed from either the producer or
* the consumer.
*/
-public interface BufferQueueController<T> extends SafeCloseable {
+public interface BufferQueueController<T> extends Updatable<T>, SafeCloseable {
/**
* Adds the given element to the stream. Streams must support calling this
* even after closed.
*
* @param element The element to add.
*/
- public void append(T element);
+ @Override
+ public void update(T element);
/**
* Closes the stream. Implementations must tolerate multiple calls to close.
*/
+ @Override
public void close();
/**
diff --git a/src/com/android/camera/async/CallbackRunnable.java b/src/com/android/camera/async/CallbackRunnable.java
new file mode 100644
index 0000000..f515043
--- /dev/null
+++ b/src/com/android/camera/async/CallbackRunnable.java
@@ -0,0 +1,37 @@
+/*
+ * 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.camera.async;
+
+import com.android.camera.util.Callback;
+
+/**
+ * A {@link Callback} which just invokes a {@link Runnable}.
+ *
+ * @param <T>
+ */
+public class CallbackRunnable<T> implements Callback<T> {
+ private final Runnable mRunnable;
+
+ public CallbackRunnable(Runnable runnable) {
+ mRunnable = runnable;
+ }
+
+ @Override
+ public void onCallback(T result) {
+ mRunnable.run();
+ }
+}
diff --git a/src/com/android/camera/async/CloseableHandlerThread.java b/src/com/android/camera/async/CloseableHandlerThread.java
new file mode 100644
index 0000000..f351b82
--- /dev/null
+++ b/src/com/android/camera/async/CloseableHandlerThread.java
@@ -0,0 +1,44 @@
+/*
+ * 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.camera.async;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+
+/**
+ * Creates a new Handler thread which can be safely destroyed when the object is
+ * closed.
+ */
+public class CloseableHandlerThread implements SafeCloseable {
+ private final HandlerThread mThread;
+ private final Handler mHandler;
+
+ public CloseableHandlerThread(String threadName) {
+ mThread = new HandlerThread(threadName);
+ mThread.start();
+ mHandler = new Handler(mThread.getLooper());
+ }
+
+ public Handler get() {
+ return mHandler;
+ }
+
+ @Override
+ public void close() {
+ mThread.quitSafely();
+ }
+}
diff --git a/src/com/android/camera/async/ConcurrentBufferQueue.java b/src/com/android/camera/async/ConcurrentBufferQueue.java
index 2347af2..8cac688 100644
--- a/src/com/android/camera/async/ConcurrentBufferQueue.java
+++ b/src/com/android/camera/async/ConcurrentBufferQueue.java
@@ -130,7 +130,7 @@
}
@Override
- public void append(T element) {
+ public void update(T element) {
boolean closed = false;
synchronized (mLock) {
closed = mClosed.get();
diff --git a/src/com/android/camera/async/ConcurrentState.java b/src/com/android/camera/async/ConcurrentState.java
new file mode 100644
index 0000000..97bd581
--- /dev/null
+++ b/src/com/android/camera/async/ConcurrentState.java
@@ -0,0 +1,143 @@
+/*
+ * 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.camera.async;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+import com.android.camera.util.Callback;
+
+/**
+ * Generic asynchronous state wrapper which supports two methods of interaction:
+ * polling for the latest value and listening for updates.
+ * <p>
+ * Note that this class only supports polling and using listeners. If
+ * synchronous consumption of state changes is required, see
+ * {@link FutureResult} or {@link BufferQueue} and its implementations.
+ * </p>
+ */
+public class ConcurrentState<T> implements Updatable<T>, Pollable<T> {
+
+ private static class ExecutorListenerPair<T> {
+ private final Executor mExecutor;
+ private final Callback<T> mListener;
+
+ public ExecutorListenerPair(Executor executor, Callback<T> listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ /**
+ * Runs the callback on the executor with the given value.
+ */
+ public void run(final T t) {
+ mExecutor.execute(new Runnable() {
+ public void run() {
+ mListener.onCallback(t);
+ }
+ });
+ }
+ }
+
+ private final Object mLock;
+ private final Set<ExecutorListenerPair<T>> mListeners;
+ private boolean mValueSet;
+ private T mValue;
+
+ public ConcurrentState() {
+ mLock = new Object();
+ mListeners = new HashSet<ExecutorListenerPair<T>>();
+ mValueSet = false;
+ }
+
+ /**
+ * Updates the state to the latest value, notifying all listeners.
+ */
+ @Override
+ public void update(T newValue) {
+ List<ExecutorListenerPair<T>> listeners = new ArrayList<ExecutorListenerPair<T>>();
+ synchronized (mLock) {
+ mValueSet = true;
+ mValue = newValue;
+ // Copy listeners out here so we can iterate over the list outside
+ // the critical section.
+ listeners.addAll(mListeners);
+ }
+ for (ExecutorListenerPair<T> pair : listeners) {
+ pair.run(newValue);
+ }
+ }
+
+ /**
+ * Adds the given callback, returning a token to be closed when the callback
+ * is no longer needed.
+ *
+ * @param callback The callback to add.
+ * @param executor The executor on which the callback will be invoked.
+ * @return A {@link SafeCloseable} token to be closed when the callback must
+ * be removed.
+ */
+ public SafeCloseable addCallback(Callback callback, Executor executor) {
+ synchronized (mLock) {
+ final ExecutorListenerPair<T> pair = new ExecutorListenerPair<>(executor, callback);
+ mListeners.add(pair);
+
+ return new SafeCloseable() {
+ @Override
+ public void close() {
+ synchronized (mLock) {
+ mListeners.remove(pair);
+ }
+ }
+ };
+ }
+ }
+
+ /**
+ * Polls for the latest value.
+ *
+ * @return The latest state, or defaultValue if no state has been set yet.
+ */
+ @Override
+ public T get(T defaultValue) {
+ try {
+ return get();
+ } catch (Pollable.NoValueSetException e) {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Polls for the latest value.
+ *
+ * @return The latest state.
+ * @throws com.android.camera.async.Pollable.NoValueSetException If no value has been set yet.
+ */
+ @Override
+ public T get() throws Pollable.NoValueSetException {
+ synchronized (mLock) {
+ if (mValueSet) {
+ return mValue;
+ } else {
+ throw new Pollable.NoValueSetException();
+ }
+ }
+ }
+}
diff --git a/src/com/android/camera/async/ConstantPollable.java b/src/com/android/camera/async/ConstantPollable.java
new file mode 100644
index 0000000..4ef37d2
--- /dev/null
+++ b/src/com/android/camera/async/ConstantPollable.java
@@ -0,0 +1,40 @@
+/*
+ * 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.camera.async;
+
+/**
+ * A {@link Pollable} which always returns a constant value.
+ *
+ * @param <T>
+ */
+public class ConstantPollable<T> implements Pollable<T> {
+ private final T mValue;
+
+ public ConstantPollable(T value) {
+ mValue = value;
+ }
+
+ @Override
+ public T get(T defaultValue) {
+ return mValue;
+ }
+
+ @Override
+ public T get() throws NoValueSetException {
+ return mValue;
+ }
+}
diff --git a/src/com/android/camera/async/FilteredUpdatable.java b/src/com/android/camera/async/FilteredUpdatable.java
new file mode 100644
index 0000000..b931101
--- /dev/null
+++ b/src/com/android/camera/async/FilteredUpdatable.java
@@ -0,0 +1,60 @@
+/*
+ * 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.camera.async;
+
+/**
+ * Wraps an {@link com.android.camera.async.Updatable} by filtering out
+ * duplicate updates.
+ */
+public class FilteredUpdatable<T> implements Updatable<T> {
+ private final Updatable<T> mUpdatable;
+ private final Object mLock;
+ private boolean mValueSet;
+ private T mLatestValue;
+
+ public FilteredUpdatable(Updatable<T> updatable) {
+ mUpdatable = updatable;
+ mLock = new Object();
+ mValueSet = false;
+ mLatestValue = null;
+ }
+
+ @Override
+ public void update(T t) {
+ synchronized (mLock) {
+ if (!mValueSet) {
+ setNewValue(t);
+ } else {
+ if (t == null && mLatestValue != null) {
+ setNewValue(t);
+ } else if (t != null) {
+ if (!t.equals(mLatestValue)) {
+ setNewValue(t);
+ }
+ }
+ }
+ }
+ }
+
+ private void setNewValue(T value) {
+ synchronized (mLock) {
+ mUpdatable.update(value);
+ mLatestValue = value;
+ mValueSet = true;
+ }
+ }
+}
diff --git a/src/com/android/camera/async/FutureResult.java b/src/com/android/camera/async/FutureResult.java
index e53d263..74e7771 100644
--- a/src/com/android/camera/async/FutureResult.java
+++ b/src/com/android/camera/async/FutureResult.java
@@ -40,12 +40,25 @@
private V mValue;
private boolean mCancelled;
private Exception mException;
+ private final Updatable<Future<V>> mDoneUpdatable;
- public FutureResult() {
+ /**
+ * @param doneUpdatable An updatable to be notified when the future is done.
+ */
+ public FutureResult(Updatable<Future<V>> doneUpdatable) {
mDone = new AtomicBoolean();
mDoneCondition = new CountDownLatch(1);
mValue = null;
mCancelled = false;
+ mDoneUpdatable = doneUpdatable;
+ }
+
+ public FutureResult() {
+ this(new Updatable<Future<V>>() {
+ @Override
+ public void update(Future<V> vFutureResult) {
+ }
+ });
}
/**
@@ -117,6 +130,7 @@
mCancelled = true;
mDoneCondition.countDown();
+ mDoneUpdatable.update(this);
return true;
}
@@ -135,6 +149,7 @@
mValue = value;
mDoneCondition.countDown();
+ mDoneUpdatable.update(this);
return true;
}
@@ -153,6 +168,7 @@
mException = e;
mDoneCondition.countDown();
+ mDoneUpdatable.update(this);
return true;
}
diff --git a/src/com/android/camera/async/HandlerExecutor.java b/src/com/android/camera/async/HandlerExecutor.java
new file mode 100644
index 0000000..87c8c0c
--- /dev/null
+++ b/src/com/android/camera/async/HandlerExecutor.java
@@ -0,0 +1,37 @@
+/*
+ * 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.camera.async;
+
+import java.util.concurrent.Executor;
+
+import android.os.Handler;
+
+/**
+ * An {@link Executor} which posts to a {@link Handler}.
+ */
+public class HandlerExecutor implements Executor {
+ private final Handler mHandler;
+
+ public HandlerExecutor(Handler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void execute(Runnable runnable) {
+ mHandler.post(runnable);
+ }
+}
diff --git a/src/com/android/camera/async/Listenable.java b/src/com/android/camera/async/Listenable.java
new file mode 100644
index 0000000..03eb017
--- /dev/null
+++ b/src/com/android/camera/async/Listenable.java
@@ -0,0 +1,38 @@
+/*
+ * 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.camera.async;
+
+import com.android.camera.util.Callback;
+
+/**
+ * Note: This interface, alone, does not provide a means of guaranteeing which
+ * thread the callback will be invoked on. Use
+ * {@link com.android.camera.async.ConcurrentState}, {@link BufferQueue}, or
+ * {@link java.util.concurrent.Future} instead to guarantee thread-safety.
+ */
+public interface Listenable<T> extends SafeCloseable {
+ /**
+ * Sets the callback, removing any existing callback first.
+ */
+ public void setCallback(Callback<T> callback);
+
+ /**
+ * Removes any existing callback.
+ */
+ @Override
+ public void close();
+}
diff --git a/src/com/android/camera/async/ListenableConcurrentState.java b/src/com/android/camera/async/ListenableConcurrentState.java
new file mode 100644
index 0000000..b54763c
--- /dev/null
+++ b/src/com/android/camera/async/ListenableConcurrentState.java
@@ -0,0 +1,69 @@
+/*
+ * 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.camera.async;
+
+import java.util.concurrent.Executor;
+
+import com.android.camera.util.Callback;
+
+/**
+ * Wraps {@link ConcurrentState} with {@link #setCallback} semantics which
+ * overwrite any existing callback.
+ */
+public class ListenableConcurrentState<T> implements Listenable<T> {
+ private final ConcurrentState<T> mState;
+ private final Executor mExecutor;
+ private final Object mLock;
+ private boolean mClosed;
+ private SafeCloseable mExistingCallbackHandle;
+
+ public ListenableConcurrentState(ConcurrentState<T> state, Executor executor) {
+ mState = state;
+ mExecutor = executor;
+ mLock = new Object();
+ mClosed = false;
+ mExistingCallbackHandle = null;
+ }
+
+ /**
+ * Sets the callback, removing any existing callback first.
+ */
+ @Override
+ public void setCallback(Callback<T> callback) {
+ synchronized (mLock) {
+ if (mClosed) {
+ return;
+ }
+ if (mExistingCallbackHandle != null) {
+ // Unregister any existing callback
+ mExistingCallbackHandle.close();
+ }
+ mExistingCallbackHandle = mState.addCallback(callback, mExecutor);
+ }
+ }
+
+ @Override
+ public void close() {
+ synchronized (mLock) {
+ mClosed = true;
+ if (mExistingCallbackHandle != null) {
+ // Unregister any existing callback
+ mExistingCallbackHandle.close();
+ }
+ }
+ }
+}
diff --git a/src/com/android/camera/async/Pollable.java b/src/com/android/camera/async/Pollable.java
new file mode 100644
index 0000000..1cea227
--- /dev/null
+++ b/src/com/android/camera/async/Pollable.java
@@ -0,0 +1,43 @@
+/*
+ * 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.camera.async;
+
+/**
+ * An interface for state which can be polled for the latest value.
+ */
+public interface Pollable<T> {
+ /**
+ * Indicates that no value has been set yet.
+ */
+ public static class NoValueSetException extends Exception {
+ }
+
+ /**
+ * Polls for the latest value.
+ *
+ * @return The latest state, or defaultValue if no state has been set yet.
+ */
+ public T get(T defaultValue);
+
+ /**
+ * Polls for the latest value.
+ *
+ * @return The latest state.
+ * @throws NoValueSetException If no value has been set yet.
+ */
+ public T get() throws NoValueSetException;
+}
diff --git a/src/com/android/camera/async/RefCountedBufferQueueController.java b/src/com/android/camera/async/RefCountedBufferQueueController.java
index 66a6807..971af68 100644
--- a/src/com/android/camera/async/RefCountedBufferQueueController.java
+++ b/src/com/android/camera/async/RefCountedBufferQueueController.java
@@ -27,8 +27,8 @@
}
@Override
- public void append(T element) {
- mBuffer.get().append(element);
+ public void update(T element) {
+ mBuffer.get().update(element);
}
@Override
diff --git a/src/com/android/camera/async/ResettingDelayedExecutor.java b/src/com/android/camera/async/ResettingDelayedExecutor.java
new file mode 100644
index 0000000..0ecbdf9
--- /dev/null
+++ b/src/com/android/camera/async/ResettingDelayedExecutor.java
@@ -0,0 +1,72 @@
+/*
+ * 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.camera.async;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An executor which executes with a delay, discarding pending executions such
+ * that at most one task is queued at any time.
+ */
+public class ResettingDelayedExecutor implements Executor, SafeCloseable {
+ private final ScheduledExecutorService mExecutor;
+ private final long mDelay;
+ private final TimeUnit mDelayUnit;
+ /**
+ * Lock for all mutable state: {@link #mLatestRunRequest} and
+ * {@link #mClosed}.
+ */
+ private final Object mLock;
+ private ScheduledFuture<?> mLatestRunRequest;
+ private boolean mClosed;
+
+ public ResettingDelayedExecutor(ScheduledExecutorService executor, long delay, TimeUnit
+ delayUnit) {
+ mExecutor = executor;
+ mDelay = delay;
+ mDelayUnit = delayUnit;
+ mLock = new Object();
+ }
+
+ @Override
+ public void execute(Runnable runnable) {
+ synchronized (mLock) {
+ if (mClosed) {
+ return;
+ }
+ // Cancel any existing, queued task before scheduling another.
+ if (mLatestRunRequest != null) {
+ boolean mayInterruptIfRunning = false;
+ mLatestRunRequest.cancel(mayInterruptIfRunning);
+ }
+ mLatestRunRequest = mExecutor.schedule(runnable, mDelay, mDelayUnit);
+ }
+ }
+
+ @Override
+ public void close() {
+ synchronized (mLock) {
+ if (mClosed) {
+ return;
+ }
+ mExecutor.shutdownNow();
+ }
+ }
+}
diff --git a/src/com/android/camera/async/SafeCloseable.java b/src/com/android/camera/async/SafeCloseable.java
index 8b61348..3a5822c 100644
--- a/src/com/android/camera/async/SafeCloseable.java
+++ b/src/com/android/camera/async/SafeCloseable.java
@@ -20,5 +20,9 @@
* An {@link AutoCloseable} which should not throw in {@link #close}.
*/
public interface SafeCloseable extends AutoCloseable {
+ /**
+ * Implementations must tolerate multiple calls to close().
+ */
+ @Override
public void close();
}
diff --git a/src/com/android/camera/async/Updatable.java b/src/com/android/camera/async/Updatable.java
new file mode 100644
index 0000000..0caff9c
--- /dev/null
+++ b/src/com/android/camera/async/Updatable.java
@@ -0,0 +1,46 @@
+/*
+ * 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.camera.async;
+
+/**
+ * An interface for updating thread-shared state from producer threads with
+ * real-time requirements.
+ * <p>
+ * Depending on how consumers need to access updates, the following
+ * implementations may be used:
+ * <ul>
+ * <li>Synchronous access to every update: {@link ConcurrentBufferQueue}</li>
+ * <li>Synchronous access to a single update: {@link UpdatableCountDownLatch}</li>
+ * <li>Polling: {@link ConcurrentState}</li>
+ * <li>Callbacks: {@link ConcurrentState}</li>
+ * </ul>
+ * </p>
+ */
+public interface Updatable<T> {
+ /**
+ * Implementations MUST ALWAYS satisfy the following constraints:
+ * <ul>
+ * <li>Return quickly, without performing any expensive work.</li>
+ * <li>Execute in a predictable, stable, amount of time.</li>
+ * <li>Never block.</li>
+ * <li>Be thread-safe.</li>
+ * <li>Never leak control of the thread on which they are invoked. (e.g.
+ * invoke callbacks which may violate the above constraints)</li>
+ * </ul>
+ */
+ public void update(T t);
+}
diff --git a/src/com/android/camera/one/v2/components/ImageSaver.java b/src/com/android/camera/async/UpdatableCountDownLatch.java
similarity index 64%
copy from src/com/android/camera/one/v2/components/ImageSaver.java
copy to src/com/android/camera/async/UpdatableCountDownLatch.java
index 7138029..cbcb7a5 100644
--- a/src/com/android/camera/one/v2/components/ImageSaver.java
+++ b/src/com/android/camera/async/UpdatableCountDownLatch.java
@@ -14,16 +14,20 @@
* limitations under the License.
*/
-package com.android.camera.one.v2.components;
+package com.android.camera.async;
-import com.android.camera.one.v2.camera2proxy.ImageProxy;
+import java.util.concurrent.CountDownLatch;
/**
- * Interface for an image-saving object.
+ * Counts down on each update.
*/
-public interface ImageSaver {
- /**
- * Implementations should save the image to disk and close it.
- */
- public void saveAndCloseImage(ImageProxy image);
+public class UpdatableCountDownLatch extends CountDownLatch implements Updatable<Void> {
+ public UpdatableCountDownLatch(int count) {
+ super(count);
+ }
+
+ @Override
+ public void update(Void v) {
+ countDown();
+ }
}
diff --git a/src/com/android/camera/one/AbstractOneCamera.java b/src/com/android/camera/one/AbstractOneCamera.java
index 6342fcf..15276ec 100644
--- a/src/com/android/camera/one/AbstractOneCamera.java
+++ b/src/com/android/camera/one/AbstractOneCamera.java
@@ -30,7 +30,6 @@
* class instead.
*/
public abstract class AbstractOneCamera implements OneCamera {
- protected CameraErrorListener mCameraErrorListener;
protected FocusStateListener mFocusStateListener;
protected ReadyStateChangedListener mReadyStateChangedListener;
@@ -41,11 +40,6 @@
static final int DEBUG_FOLDER_SERIAL_LENGTH = 4;
@Override
- public final void setCameraErrorListener(CameraErrorListener listener) {
- mCameraErrorListener = listener;
- }
-
- @Override
public final void setFocusStateListener(FocusStateListener listener) {
mFocusStateListener = listener;
}
diff --git a/src/com/android/camera/one/OneCamera.java b/src/com/android/camera/one/OneCamera.java
index a15bdb7..a45f834 100644
--- a/src/com/android/camera/one/OneCamera.java
+++ b/src/com/android/camera/one/OneCamera.java
@@ -193,15 +193,6 @@
}
/**
- * Classes implementing this interface will be called whenever the camera
- * encountered an error.
- */
- public static interface CameraErrorListener {
- /** Called when the camera encountered an error. */
- public void onCameraError();
- }
-
- /**
* Classes implementing this interface will be called when the state of the
* focus changes. Guaranteed not to stay stuck in scanning state past some
* reasonable timeout even if Camera API is stuck.
@@ -336,12 +327,6 @@
public void stopBurst();
/**
- * Sets or replaces a listener that is called whenever the camera encounters
- * an error.
- */
- public void setCameraErrorListener(CameraErrorListener listener);
-
- /**
* Sets or replaces a listener that is called whenever the focus state of
* the camera changes.
*/
diff --git a/src/com/android/camera/one/v2/SimpleJpegOneCameraFactory.java b/src/com/android/camera/one/v2/SimpleJpegOneCameraFactory.java
new file mode 100644
index 0000000..813ca9c
--- /dev/null
+++ b/src/com/android/camera/one/v2/SimpleJpegOneCameraFactory.java
@@ -0,0 +1,543 @@
+/*
+ * 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.camera.one.v2;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import android.graphics.ImageFormat;
+import android.graphics.Rect;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.media.ImageReader;
+import android.os.Handler;
+import android.view.Surface;
+
+import com.android.camera.app.OrientationManager;
+import com.android.camera.async.BufferQueue;
+import com.android.camera.async.CallbackRunnable;
+import com.android.camera.async.CloseableHandlerThread;
+import com.android.camera.async.ConcurrentBufferQueue;
+import com.android.camera.async.ConcurrentState;
+import com.android.camera.async.ConstantPollable;
+import com.android.camera.async.FilteredUpdatable;
+import com.android.camera.async.FutureResult;
+import com.android.camera.async.HandlerExecutor;
+import com.android.camera.async.Listenable;
+import com.android.camera.async.ListenableConcurrentState;
+import com.android.camera.async.Pollable;
+import com.android.camera.async.ResettingDelayedExecutor;
+import com.android.camera.async.SafeCloseable;
+import com.android.camera.async.Updatable;
+import com.android.camera.one.CameraDirectionProvider;
+import com.android.camera.one.OneCamera;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionProxy;
+import com.android.camera.one.v2.camera2proxy.CameraDeviceProxy;
+import com.android.camera.one.v2.camera2proxy.CameraDeviceRequestBuilderFactory;
+import com.android.camera.one.v2.camera2proxy.ImageProxy;
+import com.android.camera.one.v2.commands.AFScanHoldReset;
+import com.android.camera.one.v2.commands.CameraCommand;
+import com.android.camera.one.v2.commands.CameraCommandExecutor;
+import com.android.camera.one.v2.commands.FullAFScanCommand;
+import com.android.camera.one.v2.commands.LoggingCameraCommand;
+import com.android.camera.one.v2.commands.PreviewCommand;
+import com.android.camera.one.v2.commands.RunnableCameraCommand;
+import com.android.camera.one.v2.commands.StaticPictureCommand;
+import com.android.camera.one.v2.common.CaptureSessionCreator;
+import com.android.camera.one.v2.common.DeferredManualAutoFocus;
+import com.android.camera.one.v2.common.DeferredPictureTaker;
+import com.android.camera.one.v2.common.FullSizeAspectRatioProvider;
+import com.android.camera.one.v2.common.GenericOneCameraImpl;
+import com.android.camera.one.v2.common.ManualAutoFocusImpl;
+import com.android.camera.one.v2.common.MeteringParameters;
+import com.android.camera.one.v2.common.PictureCallbackAdaptor;
+import com.android.camera.one.v2.common.PollableAEMode;
+import com.android.camera.one.v2.common.PollableAERegion;
+import com.android.camera.one.v2.common.PollableAFRegion;
+import com.android.camera.one.v2.common.PollableZoomedCropRegion;
+import com.android.camera.one.v2.common.PreviewSizeSelector;
+import com.android.camera.one.v2.common.SensorOrientationProvider;
+import com.android.camera.one.v2.common.SupportedPreviewSizeProvider;
+import com.android.camera.one.v2.core.DecoratingRequestBuilderBuilder;
+import com.android.camera.one.v2.core.FrameServer;
+import com.android.camera.one.v2.core.MetadataResponseListener;
+import com.android.camera.one.v2.core.RequestBuilder;
+import com.android.camera.one.v2.core.SimpleCaptureStream;
+import com.android.camera.one.v2.core.TagDispatchCaptureSession;
+import com.android.camera.one.v2.core.TimestampResponseListener;
+import com.android.camera.one.v2.sharedimagereader.ImageDistributor;
+import com.android.camera.one.v2.sharedimagereader.ImageDistributorOnImageAvailableListener;
+import com.android.camera.one.v2.sharedimagereader.SharedImageReader;
+import com.android.camera.session.CaptureSession;
+import com.android.camera.util.ScopedFactory;
+import com.android.camera.util.Size;
+
+/**
+ */
+public class SimpleJpegOneCameraFactory {
+ /**
+ * All of the variables available when the CameraDevice is available.
+ */
+ private static class CameraScope {
+ public final CameraDevice device;
+ public final CameraCharacteristics characteristics;
+ public final Handler mainHandler;
+ public final FutureResult<GenericOneCameraImpl.PictureTaker> pictureTaker;
+ public final FutureResult<GenericOneCameraImpl.ManualAutoFocus> manualAutoFocus;
+ public final ConcurrentState<Integer> afState;
+ public final ConcurrentState<Boolean> readyState;
+ public final ConcurrentState<Float> zoomState;
+ public final ConcurrentState<Boolean> previewStartSuccess;
+ public final Size pictureSize;
+
+ private CameraScope(
+ CameraDevice device,
+ CameraCharacteristics characteristics,
+ Handler mainHandler,
+ FutureResult<GenericOneCameraImpl.PictureTaker> pictureTaker,
+ FutureResult<GenericOneCameraImpl.ManualAutoFocus> manualAutoFocus,
+ ConcurrentState<Integer> afState,
+ ConcurrentState<Boolean> readyState,
+ ConcurrentState<Float> zoomState,
+ ConcurrentState<Boolean> previewStartSuccess, Size pictureSize) {
+ this.device = device;
+ this.characteristics = characteristics;
+ this.mainHandler = mainHandler;
+ this.pictureTaker = pictureTaker;
+ this.manualAutoFocus = manualAutoFocus;
+ this.afState = afState;
+ this.readyState = readyState;
+ this.zoomState = zoomState;
+ this.previewStartSuccess = previewStartSuccess;
+ this.pictureSize = pictureSize;
+ }
+ }
+
+ private static class PreviewSurfaceScope {
+ public final CameraScope cameraScope;
+ public final Surface previewSurface;
+ public final ImageReader imageReader;
+ public final CloseableHandlerThread captureSessionOpenHandler;
+
+ private PreviewSurfaceScope(CameraScope cameraScope,
+ Surface previewSurface,
+ ImageReader imageReader,
+ CloseableHandlerThread captureSessionOpenHandler) {
+ this.cameraScope = cameraScope;
+ this.previewSurface = previewSurface;
+ this.imageReader = imageReader;
+ this.captureSessionOpenHandler = captureSessionOpenHandler;
+ }
+ }
+
+ private static class CameraCaptureSessionScope {
+ public final Runnable startPreviewRunnable;
+ public final GenericOneCameraImpl.PictureTaker pictureTaker;
+ public final GenericOneCameraImpl.ManualAutoFocus manualAutoFocus;
+
+ private CameraCaptureSessionScope(Runnable startPreviewRunnable,
+ GenericOneCameraImpl.PictureTaker pictureTaker,
+ GenericOneCameraImpl.ManualAutoFocus manualAutoFocus) {
+ this.startPreviewRunnable = startPreviewRunnable;
+ this.pictureTaker = pictureTaker;
+ this.manualAutoFocus = manualAutoFocus;
+ }
+ }
+
+ private static CameraScope provideCameraScope(CameraDevice device, CameraCharacteristics
+ characteristics, Handler mainHandler, Size pictureSize) {
+ FutureResult<GenericOneCameraImpl.PictureTaker> pictureTakerFutureResult = new FutureResult<>();
+ FutureResult<GenericOneCameraImpl.ManualAutoFocus> manualAutoFocusFutureResult = new FutureResult<>();
+ ConcurrentState<Integer> afState = new ConcurrentState<>();
+ ConcurrentState<Boolean> readyState = new ConcurrentState<>();
+ ConcurrentState<Float> zoomState = new ConcurrentState<>();
+ ConcurrentState<Boolean> previewStartSuccess = new ConcurrentState<>();
+
+ return new CameraScope(device, characteristics, mainHandler, pictureTakerFutureResult,
+ manualAutoFocusFutureResult, afState,
+ readyState, zoomState, previewStartSuccess, pictureSize);
+ }
+
+ private static Set<SafeCloseable> provideCloseListeners(final CameraScope scope) {
+ // FIXME Something in here must close() the CameraDevice, ImageReader,
+ // and CameraCaptureSession.
+ // TODO Maybe replace this with two things:
+ // 1. A blocking AutoCloseable which will wait until the device is
+ // closed.
+ // 2. A ConcurrentState<> for other things to subscribe to, or poll, for
+ // closing state.
+ // - Close the session
+ // - Close all CloseableHandlerThreads
+ // - Close the global timestamp stream
+ // - Close the CameraCommandExecutor
+ // - Close the SharedImageReader (on a separate thread to only release
+ // it when all consumers release any outstanding images.)
+ Set<SafeCloseable> closeables = new HashSet<>();
+ closeables.add(new SafeCloseable() {
+ @Override
+ public void close() {
+ scope.device.close();
+ }
+ });
+ return closeables;
+ }
+
+ private static Executor provideMainHandlerExecutor(CameraScope scope) {
+ return new HandlerExecutor(scope.mainHandler);
+ }
+
+ private static Listenable<Integer> provideAFStateListenable(CameraScope scope) {
+ return new ListenableConcurrentState<>(scope.afState, provideMainHandlerExecutor(scope));
+ }
+
+ private static Listenable<Boolean> provideReadyStateListenable(CameraScope scope) {
+ return new ListenableConcurrentState<>(scope.readyState, provideMainHandlerExecutor(scope));
+ }
+
+ private static CameraDirectionProvider provideCameraDirectionProvider(CameraScope scope) {
+ return new CameraDirectionProvider(scope.characteristics);
+ }
+
+ private static OneCamera.Facing provideCameraDirection(CameraScope scope) {
+ return provideCameraDirectionProvider(scope).getDirection();
+ }
+
+ private static float provideMaxZoom(CameraCharacteristics characteristics) {
+ return characteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
+ }
+
+ private static Updatable<Float> provideZoom(CameraScope scope) {
+ return scope.zoomState;
+ }
+
+ private static ScopedFactory<Surface, Runnable> providePreviewStarter(final CameraScope
+ cameraScope) {
+ return new ScopedFactory<Surface, Runnable>() {
+ @Override
+ public Runnable get(Surface previewSurface) {
+ PreviewSurfaceScope previewSurfaceScope = providePreviewSurfaceScope(cameraScope,
+ previewSurface);
+ return provideCaptureSessionCreator(previewSurfaceScope);
+ }
+ };
+ }
+
+ private static List<Surface> provideSurfaceList(PreviewSurfaceScope scope) {
+ List<Surface> surfaces = new ArrayList<>();
+ surfaces.add(scope.imageReader.getSurface());
+ surfaces.add(scope.previewSurface);
+ return surfaces;
+ }
+
+ private static CaptureSessionCreator provideCaptureSessionCreator(PreviewSurfaceScope scope) {
+ CameraDeviceProxy device = new CameraDeviceProxy(scope.cameraScope.device);
+ Handler cameraHandler = scope.captureSessionOpenHandler.get();
+ List<Surface> surfaces = provideSurfaceList(scope);
+ FutureResult<CameraCaptureSessionProxy> sessionFuture = provideCaptureSessionFuture(scope);
+ ScopedFactory<CameraCaptureSessionProxy, Runnable> captureSessionScopeEntrance =
+ provideCaptureSessionScopeEntrance(scope);
+ return new CaptureSessionCreator(device, cameraHandler, surfaces, sessionFuture,
+ captureSessionScopeEntrance);
+ }
+
+ private static ScopedFactory<CameraCaptureSessionProxy, Runnable>
+ provideCaptureSessionScopeEntrance(
+ final PreviewSurfaceScope previewSurfaceScope) {
+ return new ScopedFactory<CameraCaptureSessionProxy, Runnable>() {
+ @Override
+ public Runnable get(CameraCaptureSessionProxy cameraCaptureSession) {
+ final CameraCaptureSessionScope scope = provideCameraCaptureSessionScope
+ (previewSurfaceScope, cameraCaptureSession);
+ return new Runnable() {
+ @Override
+ public void run() {
+ // Update the future for image capture
+ previewSurfaceScope.cameraScope.pictureTaker.setValue(scope.pictureTaker);
+ // Update the future for tap-to-focus
+ previewSurfaceScope.cameraScope.manualAutoFocus.setValue(scope
+ .manualAutoFocus);
+
+ // Dispatch to startPreviewRunnable
+ scope.startPreviewRunnable.run();
+ }
+ };
+ }
+ };
+ }
+
+ private static CameraCaptureSessionScope provideCameraCaptureSessionScope(
+ final PreviewSurfaceScope previewSurfaceScope, CameraCaptureSessionProxy
+ cameraCaptureSession) {
+ ConcurrentBufferQueue<Long> globalTimestampStream = new ConcurrentBufferQueue<>();
+ // FIXME Wire this up to be closed when done.
+ CloseableHandlerThread imageDistributorThread = new CloseableHandlerThread
+ ("ImageDistributor");
+ ImageDistributor imageDistributor = provideImageDistributor(previewSurfaceScope
+ .imageReader, globalTimestampStream, imageDistributorThread.get());
+ final SharedImageReader sharedImageReader = provideSharedImageReader(previewSurfaceScope,
+ imageDistributor);
+ final FrameServer frameServer = new FrameServer(new TagDispatchCaptureSession
+ (cameraCaptureSession, previewSurfaceScope.captureSessionOpenHandler.get()));
+
+ ExecutorService miscThreadPool = Executors.newCachedThreadPool();
+
+ final CameraCommandExecutor commandExecutor = new CameraCommandExecutor(miscThreadPool);
+
+ SimpleCaptureStream previewSurfaceStream = new SimpleCaptureStream(
+ previewSurfaceScope.previewSurface);
+ RequestBuilder.Factory rootRequestBuilder = new DecoratingRequestBuilderBuilder(
+ new CameraDeviceRequestBuilderFactory(previewSurfaceScope.cameraScope.device))
+ .withResponseListener(new MetadataResponseListener<Integer>(CaptureResult
+ .CONTROL_AF_STATE, new FilteredUpdatable<Integer>(previewSurfaceScope
+ .cameraScope.afState)))
+ .withResponseListener(new TimestampResponseListener
+ (globalTimestampStream));
+ ConcurrentState<MeteringParameters> meteringState = new ConcurrentState<>();
+ final Pollable<Rect> cropRegion = new PollableZoomedCropRegion
+ (previewSurfaceScope.cameraScope.characteristics,
+ previewSurfaceScope.cameraScope.zoomState);
+ OrientationManager.DeviceOrientation sensorOrientation = new SensorOrientationProvider
+ (previewSurfaceScope.cameraScope
+ .characteristics).getSensorOrientation();
+ final Pollable<MeteringRectangle[]> afRegionState = new PollableAFRegion(meteringState,
+ cropRegion, sensorOrientation);
+ final Pollable<MeteringRectangle[]> aeRegionState = new PollableAERegion(meteringState,
+ cropRegion, sensorOrientation);
+ // FIXME Use settings to retrieve current flash mode.
+ final Pollable<Integer> aeModeState = new PollableAEMode(new ConstantPollable<>(OneCamera
+ .PhotoCaptureParameters.Flash.OFF));
+ final RequestBuilder.Factory zoomedRequestBuilderFactory = new DecoratingRequestBuilderBuilder(
+ rootRequestBuilder)
+ .withParam(CaptureRequest.SCALER_CROP_REGION, cropRegion);
+ final RequestBuilder.Factory meteredZoomedRequestBuilderFactory = new
+ DecoratingRequestBuilderBuilder(zoomedRequestBuilderFactory)
+ .withParam(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO)
+ .withParam(CaptureRequest.CONTROL_AF_REGIONS, afRegionState)
+ .withParam(CaptureRequest.CONTROL_AE_REGIONS, aeRegionState)
+ .withParam(CaptureRequest.CONTROL_AE_MODE, aeModeState);
+ final RequestBuilder.Factory previewRequestBuilder =
+ new DecoratingRequestBuilderBuilder(meteredZoomedRequestBuilderFactory)
+ .withStream(previewSurfaceStream);
+ // TODO Implement Manual Exposure: Decorate with current manual
+ // exposure level, or Auto.
+ final RequestBuilder.Factory continuousPreviewRequestBuilder =
+ new DecoratingRequestBuilderBuilder(previewRequestBuilder)
+ .withParam(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO)
+ .withParam(CaptureRequest.CONTROL_AF_MODE, CaptureRequest
+ .CONTROL_AF_MODE_CONTINUOUS_PICTURE);
+
+ int templateType = CameraDevice.TEMPLATE_PREVIEW;
+ PreviewCommand previewCommand = new PreviewCommand(frameServer,
+ continuousPreviewRequestBuilder, templateType);
+ final RunnableCameraCommand previewRunner = new RunnableCameraCommand(commandExecutor,
+ new LoggingCameraCommand(previewCommand, "preview"));
+ // Restart the preview whenever the zoom, ae regions, or af regions
+ // changes.
+ previewSurfaceScope.cameraScope.zoomState.addCallback(new CallbackRunnable(previewRunner)
+ , miscThreadPool);
+
+ CameraCommand afScanCommand = new LoggingCameraCommand(new FullAFScanCommand(frameServer,
+ previewRequestBuilder, templateType), "AF Scan");
+ // TODO Ensure that this is closed
+ ResettingDelayedExecutor afResetDelayedExecutor = new ResettingDelayedExecutor(Executors
+ .newSingleThreadScheduledExecutor(), 3L, TimeUnit.SECONDS);
+ AFScanHoldReset afScanHoldResetCommand = new AFScanHoldReset(afScanCommand,
+ afResetDelayedExecutor, previewRunner, meteringState);
+ final RunnableCameraCommand afRunner = new RunnableCameraCommand(commandExecutor,
+ afScanHoldResetCommand);
+
+ // TODO Implement Manual Exposure: Add a separate listener to the
+ // current exposure state to run previewRunner on each update.
+ // TODO Implement ready-state: Add a listener for shared-image-reader
+ // availability and frame-server availability, AND the results together
+ // and update the ready-state.
+ final Updatable<ImageProxy> imageSaver = new Updatable<ImageProxy>() {
+ @Override
+ public void update(ImageProxy imageProxy) {
+ // FIXME Replace stub with actual implementation.
+ imageProxy.close();
+ }
+ };
+
+ GenericOneCameraImpl.PictureTaker pictureTaker = new GenericOneCameraImpl.PictureTaker() {
+ @Override
+ public void takePicture(OneCamera.PhotoCaptureParameters params, CaptureSession session) {
+ RequestBuilder.Factory requestBuilderFactory = new
+ DecoratingRequestBuilderBuilder(previewRequestBuilder);
+ Runnable pictureTakingRunnable = providePictureRunnable(params, session,
+ commandExecutor, frameServer, requestBuilderFactory, sharedImageReader,
+ imageSaver, provideMainHandlerExecutor(previewSurfaceScope.cameraScope));
+ pictureTakingRunnable.run();
+ }
+ };
+ GenericOneCameraImpl.ManualAutoFocus manualAutoFocus = new ManualAutoFocusImpl(
+ meteringState, afRunner);
+ return new CameraCaptureSessionScope(previewRunner, pictureTaker, manualAutoFocus);
+ }
+
+ private static Runnable providePictureRunnable(OneCamera.PhotoCaptureParameters params,
+ CaptureSession session, CameraCommandExecutor cameraCommandExecutor,
+ FrameServer frameServer, RequestBuilder.Factory requestBuilderFactory,
+ SharedImageReader sharedImageReader, Updatable<ImageProxy> imageSaver,
+ Executor mainExecutor) {
+ // TODO Add Flash support via PhotoCommand & FlashCommand
+ PictureCallbackAdaptor pictureCallbackAdaptor = new PictureCallbackAdaptor(params
+ .callback, mainExecutor);
+ Updatable<Void> imageExposureUpdatable = pictureCallbackAdaptor
+ .provideQuickExposeUpdatable();
+ CameraCommand photoCommand = new StaticPictureCommand(frameServer, requestBuilderFactory,
+ sharedImageReader, imageSaver, imageExposureUpdatable);
+ return new RunnableCameraCommand(cameraCommandExecutor, new LoggingCameraCommand
+ (photoCommand, "Static Picture"));
+ }
+
+ private static CameraCommand providePhotoCommand(FrameServer frameServer, RequestBuilder
+ .Factory builder, SharedImageReader imageReader, Updatable<ImageProxy> imageSaver,
+ Updatable<Void> imageExposeUpdatable) {
+ return new StaticPictureCommand(frameServer, builder, imageReader, imageSaver,
+ imageExposeUpdatable);
+ }
+
+ private static SharedImageReader provideSharedImageReader(PreviewSurfaceScope scope,
+ ImageDistributor imageDistributor) {
+ return new SharedImageReader(scope.imageReader.getSurface(),
+ scope.imageReader.getMaxImages() - 1, imageDistributor);
+ }
+
+ private static ImageDistributor provideImageDistributor(ImageReader imageReader,
+ BufferQueue<Long> globalTimestampStream,
+ Handler imageDistributorHandler) {
+ ImageDistributor imageDistributor = new ImageDistributor(globalTimestampStream);
+ imageReader.setOnImageAvailableListener(new ImageDistributorOnImageAvailableListener
+ (imageDistributor), imageDistributorHandler);
+ return imageDistributor;
+ }
+
+ private static FutureResult<CameraCaptureSessionProxy> provideCaptureSessionFuture(
+ final PreviewSurfaceScope scope) {
+ return new FutureResult<>(new Updatable<Future<CameraCaptureSessionProxy>>() {
+ @Override
+ public void update(Future<CameraCaptureSessionProxy> cameraCaptureSessionFuture) {
+ try {
+ cameraCaptureSessionFuture.get();
+ scope.cameraScope.previewStartSuccess.update(true);
+ } catch (InterruptedException | ExecutionException | CancellationException e) {
+ scope.cameraScope.previewStartSuccess.update(false);
+ }
+ }
+ });
+ }
+
+ private static PreviewSurfaceScope providePreviewSurfaceScope(CameraScope cameraScope,
+ Surface previewSurface) {
+ CloseableHandlerThread captureSessionOpenHandler = new CloseableHandlerThread
+ ("CaptureSessionStateHandler");
+ ImageReader imageReader = provideImageReader(cameraScope.pictureSize);
+ return new PreviewSurfaceScope(cameraScope, previewSurface, imageReader,
+ captureSessionOpenHandler);
+ }
+
+ private static ImageReader provideImageReader(Size size) {
+ return ImageReader.newInstance(size.getWidth(), size.getHeight(), ImageFormat.JPEG,
+ provideImageReaderSize());
+ }
+
+ private static int provideImageReaderSize() {
+ return 10;
+ }
+
+ private static SupportedPreviewSizeProvider provideSupportedPreviewSizesProvider(
+ CameraScope scope) {
+ return new SupportedPreviewSizeProvider(scope.characteristics);
+ }
+
+ private static Size[] provideSupportedPreviewSizes(CameraScope scope) {
+ return provideSupportedPreviewSizesProvider(scope).getSupportedPreviewSizes();
+ }
+
+ private static FullSizeAspectRatioProvider provideFullSizeAspectRatioProvider(CameraScope scope) {
+ return new FullSizeAspectRatioProvider(scope.characteristics);
+ }
+
+ private static float provideFullSizeAspectRatio(CameraScope scope) {
+ return provideFullSizeAspectRatioProvider(scope).get();
+ }
+
+ private static OneCamera.Facing provideDirection(CameraScope scope) {
+ return new CameraDirectionProvider(scope.characteristics).getDirection();
+ }
+
+ private static GenericOneCameraImpl.ManualAutoFocus provideManualAutoFocus(CameraScope scope) {
+ return new DeferredManualAutoFocus(scope.manualAutoFocus);
+ }
+
+ private static GenericOneCameraImpl.PictureTaker providePictureTaker(CameraScope scope) {
+ return new DeferredPictureTaker(scope.pictureTaker);
+ }
+
+ private static Listenable<Boolean> providePreviewStartSuccessListenable(CameraScope scope) {
+ return new ListenableConcurrentState<Boolean>(scope.previewStartSuccess,
+ provideMainHandlerExecutor(scope));
+ }
+
+ private static PreviewSizeSelector providePreviewSizeSelector(CameraScope scope) {
+ return new PreviewSizeSelector(provideImageFormat(), provideSupportedPreviewSizes(scope));
+ }
+
+ private static int provideImageFormat() {
+ return ImageFormat.JPEG;
+ }
+
+ private static OneCamera provideOneCamera(CameraScope scope) {
+ Set<SafeCloseable> closeListeners = provideCloseListeners(scope);
+ GenericOneCameraImpl.PictureTaker pictureTaker = providePictureTaker(scope);
+ GenericOneCameraImpl.ManualAutoFocus manualAutoFocus = provideManualAutoFocus(scope);
+ Listenable<Integer> afStateListenable = provideAFStateListenable(scope);
+ Listenable<Boolean> readyStateListenable = provideReadyStateListenable(scope);
+ float maxZoom = provideMaxZoom(scope.characteristics);
+ Updatable<Float> zoom = provideZoom(scope);
+ ScopedFactory<Surface, Runnable> surfaceRunnableScopedFactory = providePreviewStarter(scope);
+ Size[] supportedPreviewSizes = provideSupportedPreviewSizes(scope);
+ float fullSizeAspectRatio = provideFullSizeAspectRatio(scope);
+ OneCamera.Facing direction = provideDirection(scope);
+ PreviewSizeSelector previewSizeSelector = providePreviewSizeSelector(scope);
+ Listenable<Boolean> previewStartSuccessListenable = providePreviewStartSuccessListenable(scope);
+
+ return new GenericOneCameraImpl(closeListeners, pictureTaker, manualAutoFocus,
+ afStateListenable, readyStateListenable, maxZoom, zoom,
+ supportedPreviewSizes, fullSizeAspectRatio, direction, previewSizeSelector,
+ previewStartSuccessListenable, surfaceRunnableScopedFactory);
+ }
+
+ public static OneCamera provideOneCamera(CameraDevice device, CameraCharacteristics
+ characteristics, Handler mainHandler, Size pictureSize) {
+ return provideOneCamera(provideCameraScope(device, characteristics, mainHandler,
+ pictureSize));
+ }
+}
diff --git a/src/com/android/camera/one/v2/camera2proxy/AndroidCameraCaptureSessionProxy.java b/src/com/android/camera/one/v2/camera2proxy/AndroidCameraCaptureSessionProxy.java
new file mode 100644
index 0000000..20f1ec2
--- /dev/null
+++ b/src/com/android/camera/one/v2/camera2proxy/AndroidCameraCaptureSessionProxy.java
@@ -0,0 +1,126 @@
+/*
+ * 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.camera.one.v2.camera2proxy;
+
+import java.util.List;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.os.Handler;
+
+public class AndroidCameraCaptureSessionProxy implements CameraCaptureSessionProxy {
+ private class AndroidCaptureCallback extends CameraCaptureSession.CaptureCallback {
+ private final CaptureCallback mCallback;
+
+ private AndroidCaptureCallback(CaptureCallback callback) {
+ mCallback = callback;
+ }
+
+ @Override
+ public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
+ long timestamp, long frameNumber) {
+ mCallback.onCaptureStarted(AndroidCameraCaptureSessionProxy.this, request, timestamp,
+ frameNumber);
+ }
+
+ @Override
+ public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
+ CaptureResult partialResult) {
+ mCallback.onCaptureProgressed(AndroidCameraCaptureSessionProxy.this, request,
+ partialResult);
+ }
+
+ @Override
+ public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+ TotalCaptureResult result) {
+ mCallback.onCaptureCompleted(AndroidCameraCaptureSessionProxy.this, request, result);
+ }
+
+ @Override
+ public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
+ CaptureFailure failure) {
+ mCallback.onCaptureFailed(AndroidCameraCaptureSessionProxy.this, request, failure);
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId,
+ long frameNumber) {
+ mCallback.onCaptureSequenceCompleted(AndroidCameraCaptureSessionProxy.this,
+ sequenceId, frameNumber);
+ }
+
+ @Override
+ public void onCaptureSequenceAborted(CameraCaptureSession session, int sequenceId) {
+ mCallback.onCaptureSequenceAborted(AndroidCameraCaptureSessionProxy.this, sequenceId);
+ }
+ }
+
+ private final CameraCaptureSession mSession;
+
+ public AndroidCameraCaptureSessionProxy(CameraCaptureSession session) {
+ mSession = session;
+ }
+
+ @Override
+ public void abortCaptures() throws CameraAccessException {
+ mSession.abortCaptures();
+ }
+
+ @Override
+ public int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
+ throws CameraAccessException {
+ return mSession.capture(request, new AndroidCaptureCallback(listener), handler);
+ }
+
+ @Override
+ public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener, Handler handler)
+ throws CameraAccessException {
+ return mSession.captureBurst(requests, new AndroidCaptureCallback(listener), handler);
+ }
+
+ @Override
+ public void close() {
+ mSession.close();
+ }
+
+ @Override
+ public CameraDeviceProxy getDevice() {
+ return new CameraDeviceProxy(mSession.getDevice());
+ }
+
+ @Override
+ public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener,
+ Handler handler) throws CameraAccessException {
+ return mSession.setRepeatingBurst(requests, new AndroidCaptureCallback(listener), handler);
+ }
+
+ @Override
+ public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener, Handler handler)
+ throws CameraAccessException {
+ return mSession.setRepeatingRequest(request, new AndroidCaptureCallback(listener),
+ handler);
+ }
+
+ @Override
+ public void stopRepeating() throws CameraAccessException {
+ mSession.stopRepeating();
+ }
+}
diff --git a/src/com/android/camera/one/v2/camera2proxy/AndroidImageProxy.java b/src/com/android/camera/one/v2/camera2proxy/AndroidImageProxy.java
new file mode 100644
index 0000000..80838c0
--- /dev/null
+++ b/src/com/android/camera/one/v2/camera2proxy/AndroidImageProxy.java
@@ -0,0 +1,94 @@
+/*
+ * 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.camera.one.v2.camera2proxy;
+
+import android.graphics.Rect;
+
+/**
+ * An {@link ImageProxy} backed by an {@link android.media.Image}.
+ */
+public class AndroidImageProxy implements ImageProxy {
+ private final android.media.Image mImage;
+
+ public AndroidImageProxy(android.media.Image image) {
+ mImage = image;
+ }
+
+ /**
+ * @see {@link android.media.Image#getCropRect}
+ */
+ @Override
+ public Rect getCropRect() {
+ return mImage.getCropRect();
+ }
+
+ /**
+ * @see {@link android.media.Image#setCropRect}
+ */
+ @Override
+ public void setCropRect(Rect cropRect) {
+ mImage.setCropRect(cropRect);
+ }
+
+ /**
+ * @see {@link android.media.Image#getFormat}
+ */
+ @Override
+ public int getFormat() {
+ return mImage.getFormat();
+ }
+
+ /**
+ * @see {@link android.media.Image#getHeight}
+ */
+ @Override
+ public int getHeight() {
+ return mImage.getHeight();
+ }
+
+ /**
+ * @see {@link android.media.Image#getPlanes}
+ */
+ @Override
+ public android.media.Image.Plane[] getPlanes() {
+ return mImage.getPlanes();
+ }
+
+ /**
+ * @see {@link android.media.Image#getTimestamp}
+ */
+ @Override
+ public long getTimestamp() {
+ return mImage.getTimestamp();
+ }
+
+ /**
+ * @see {@link android.media.Image#getWidth}
+ */
+ @Override
+ public int getWidth() {
+ return mImage.getWidth();
+ }
+
+ /**
+ * @see {@link android.media.Image#close}
+ */
+ @Override
+ public void close() {
+ mImage.close();
+ }
+}
diff --git a/src/com/android/camera/one/v2/components/ImageSaver.java b/src/com/android/camera/one/v2/camera2proxy/CameraCaptureSessionClosedException.java
similarity index 66%
copy from src/com/android/camera/one/v2/components/ImageSaver.java
copy to src/com/android/camera/one/v2/camera2proxy/CameraCaptureSessionClosedException.java
index 7138029..0afab3a 100644
--- a/src/com/android/camera/one/v2/components/ImageSaver.java
+++ b/src/com/android/camera/one/v2/camera2proxy/CameraCaptureSessionClosedException.java
@@ -14,16 +14,12 @@
* limitations under the License.
*/
-package com.android.camera.one.v2.components;
-
-import com.android.camera.one.v2.camera2proxy.ImageProxy;
+package com.android.camera.one.v2.camera2proxy;
/**
- * Interface for an image-saving object.
+ * A checked-exception to replace the {@link IllegalStateException} thrown by
+ * camera2 API calls when the associated
+ * {@link android.hardware.camera2.CameraCaptureSession} is closed.
*/
-public interface ImageSaver {
- /**
- * Implementations should save the image to disk and close it.
- */
- public void saveAndCloseImage(ImageProxy image);
+public class CameraCaptureSessionClosedException extends Exception {
}
diff --git a/src/com/android/camera/one/v2/camera2proxy/CameraCaptureSessionProxy.java b/src/com/android/camera/one/v2/camera2proxy/CameraCaptureSessionProxy.java
new file mode 100644
index 0000000..423391e
--- /dev/null
+++ b/src/com/android/camera/one/v2/camera2proxy/CameraCaptureSessionProxy.java
@@ -0,0 +1,90 @@
+/*
+ * 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.camera.one.v2.camera2proxy;
+
+import java.util.List;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.os.Handler;
+
+import com.android.camera.async.SafeCloseable;
+
+/**
+ * Interface for {@link android.hardware.camera2.CameraCaptureSession}.
+ * <p>
+ * Note that this also enables translation of IllegalStateException (an
+ * unchecked exception) resulting from the underlying session being closed into
+ * a checked exception, forcing callers to explicitly handle this edge case.
+ * </p>
+ */
+public interface CameraCaptureSessionProxy extends SafeCloseable {
+ public interface CaptureCallback {
+ public void onCaptureCompleted(CameraCaptureSessionProxy session, CaptureRequest request,
+ TotalCaptureResult result);
+
+ public void onCaptureFailed(CameraCaptureSessionProxy session, CaptureRequest request,
+ CaptureFailure failure);
+
+ public void onCaptureProgressed(CameraCaptureSessionProxy session, CaptureRequest request,
+ CaptureResult partialResult);
+
+ public void onCaptureSequenceAborted(CameraCaptureSessionProxy session, int sequenceId);
+
+ public void onCaptureSequenceCompleted(CameraCaptureSessionProxy session, int sequenceId,
+ long frameNumber);
+
+ public void onCaptureStarted(CameraCaptureSessionProxy session, CaptureRequest request,
+ long timestamp, long frameNumber);
+ }
+
+ public interface StateCallback {
+ public void onActive(CameraCaptureSessionProxy session);
+
+ public void onClosed(CameraCaptureSessionProxy session);
+
+ public void onConfigureFailed(CameraCaptureSessionProxy session);
+
+ public void onConfigured(CameraCaptureSessionProxy session);
+
+ public void onReady(CameraCaptureSessionProxy session);
+ }
+
+ public void abortCaptures() throws CameraAccessException;
+
+ public int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
+ throws CameraAccessException, CameraCaptureSessionClosedException;
+
+ public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener, Handler
+ handler) throws CameraAccessException, CameraCaptureSessionClosedException;
+
+ @Override
+ public void close();
+
+ public CameraDeviceProxy getDevice();
+
+ public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener, Handler
+ handler) throws CameraAccessException, CameraCaptureSessionClosedException;
+
+ public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener, Handler
+ handler) throws CameraAccessException, CameraCaptureSessionClosedException;
+
+ public void stopRepeating() throws CameraAccessException, CameraCaptureSessionClosedException;
+}
diff --git a/src/com/android/camera/one/v2/camera2proxy/CameraDeviceProxy.java b/src/com/android/camera/one/v2/camera2proxy/CameraDeviceProxy.java
new file mode 100644
index 0000000..916e205
--- /dev/null
+++ b/src/com/android/camera/one/v2/camera2proxy/CameraDeviceProxy.java
@@ -0,0 +1,83 @@
+/*
+ * 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.camera.one.v2.camera2proxy;
+
+import java.util.List;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.os.Handler;
+import android.view.Surface;
+
+public class CameraDeviceProxy {
+ private static class AndroidCaptureSessionStateCallback extends
+ CameraCaptureSession.StateCallback {
+ private final CameraCaptureSessionProxy.StateCallback mStateCallback;
+
+ private AndroidCaptureSessionStateCallback(
+ CameraCaptureSessionProxy.StateCallback stateCallback) {
+ mStateCallback = stateCallback;
+ }
+
+ public void onConfigured(CameraCaptureSession session) {
+ mStateCallback.onConfigured(new AndroidCameraCaptureSessionProxy(session));
+ }
+
+ public void onConfigureFailed(CameraCaptureSession session) {
+ mStateCallback.onConfigureFailed(new AndroidCameraCaptureSessionProxy(session));
+ }
+
+ public void onReady(CameraCaptureSession session) {
+ mStateCallback.onReady(new AndroidCameraCaptureSessionProxy(session));
+ }
+
+ public void onActive(CameraCaptureSession session) {
+ mStateCallback.onActive(new AndroidCameraCaptureSessionProxy(session));
+ }
+
+ public void onClosed(CameraCaptureSession session) {
+ mStateCallback.onClosed(new AndroidCameraCaptureSessionProxy(session));
+ }
+ }
+
+ private final CameraDevice mCameraDevice;
+
+ public CameraDeviceProxy(CameraDevice cameraDevice) {
+ mCameraDevice = cameraDevice;
+ }
+
+ public String getId() {
+ return mCameraDevice.getId();
+ }
+
+ public void createCaptureSession(List<Surface> list,
+ CameraCaptureSessionProxy.StateCallback stateCallback, Handler handler)
+ throws CameraAccessException {
+ mCameraDevice.createCaptureSession(list, new AndroidCaptureSessionStateCallback(
+ stateCallback), handler);
+ }
+
+ public CaptureRequest.Builder createCaptureRequest(int i) throws CameraAccessException {
+ return mCameraDevice.createCaptureRequest(i);
+ }
+
+ public void close() {
+ mCameraDevice.close();
+ }
+}
diff --git a/src/com/android/camera/one/v2/camera2proxy/ForwardingImageProxy.java b/src/com/android/camera/one/v2/camera2proxy/ForwardingImageProxy.java
new file mode 100644
index 0000000..ba90083
--- /dev/null
+++ b/src/com/android/camera/one/v2/camera2proxy/ForwardingImageProxy.java
@@ -0,0 +1,89 @@
+/*
+ * 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.camera.one.v2.camera2proxy;
+
+import android.graphics.Rect;
+
+import com.android.camera.async.SafeCloseable;
+
+/**
+ * Forwards all {@link ImageProxy} methods.
+ */
+public class ForwardingImageProxy implements ImageProxy {
+ private final ImageProxy mImpl;
+
+ public ForwardingImageProxy(ImageProxy proxy) {
+ mImpl = proxy;
+ }
+
+ /**
+ * @see {@link android.media.Image#getCropRect}
+ */
+ public Rect getCropRect() {
+ return mImpl.getCropRect();
+ }
+
+ /**
+ * @see {@link android.media.Image#setCropRect}
+ */
+ public void setCropRect(Rect cropRect) {
+ mImpl.setCropRect(cropRect);
+ }
+
+ /**
+ * @see {@link android.media.Image#getFormat}
+ */
+ public int getFormat() {
+ return mImpl.getFormat();
+ }
+
+ /**
+ * @see {@link android.media.Image#getHeight}
+ */
+ public int getHeight() {
+ return mImpl.getHeight();
+ }
+
+ /**
+ * @see {@link android.media.Image#getPlanes}
+ */
+ public android.media.Image.Plane[] getPlanes() {
+ return mImpl.getPlanes();
+ }
+
+ /**
+ * @see {@link android.media.Image#getTimestamp}
+ */
+ public long getTimestamp() {
+ return mImpl.getTimestamp();
+ }
+
+ /**
+ * @see {@link android.media.Image#getWidth}
+ */
+ public int getWidth() {
+ return mImpl.getWidth();
+ }
+
+ /**
+ * @see {@link android.media.Image#close}
+ */
+ @Override
+ public void close() {
+ mImpl.close();
+ }
+}
diff --git a/src/com/android/camera/one/v2/camera2proxy/ImageProxy.java b/src/com/android/camera/one/v2/camera2proxy/ImageProxy.java
index ce90bcd..6c79061 100644
--- a/src/com/android/camera/one/v2/camera2proxy/ImageProxy.java
+++ b/src/com/android/camera/one/v2/camera2proxy/ImageProxy.java
@@ -23,147 +23,45 @@
/**
* Wraps {@link android.media.Image} with a mockable interface.
*/
-public class ImageProxy implements SafeCloseable {
- private final ImageProxy mImpl;
-
- public ImageProxy(ImageProxy proxy) {
- mImpl = proxy;
- }
-
- public ImageProxy(android.media.Image image) {
- mImpl = new Impl(image);
- }
-
- private ImageProxy() {
- mImpl = null;
- }
-
+public interface ImageProxy extends SafeCloseable {
/**
* @see {@link android.media.Image#getCropRect}
*/
- public Rect getCropRect() {
- return mImpl.getCropRect();
- }
+ public Rect getCropRect();
/**
* @see {@link android.media.Image#setCropRect}
*/
- public void setCropRect(Rect cropRect) {
- mImpl.setCropRect(cropRect);
- }
+ public void setCropRect(Rect cropRect);
/**
* @see {@link android.media.Image#getFormat}
*/
- public int getFormat() {
- return mImpl.getFormat();
- }
+ public int getFormat();
/**
* @see {@link android.media.Image#getHeight}
*/
- public int getHeight() {
- return mImpl.getHeight();
- }
+ public int getHeight();
/**
* @see {@link android.media.Image#getPlanes}
*/
- public android.media.Image.Plane[] getPlanes() {
- return mImpl.getPlanes();
- }
+ public android.media.Image.Plane[] getPlanes();
/**
* @see {@link android.media.Image#getTimestamp}
*/
- public long getTimestamp() {
- return mImpl.getTimestamp();
- }
+ public long getTimestamp();
/**
* @see {@link android.media.Image#getWidth}
*/
- public int getWidth() {
- return mImpl.getWidth();
- }
+ public int getWidth();
/**
* @see {@link android.media.Image#close}
*/
@Override
- public void close() {
- mImpl.close();
- }
-
- private static class Impl extends ImageProxy {
- private final android.media.Image mImage;
-
- public Impl(android.media.Image image) {
- mImage = image;
- }
-
- /**
- * @see {@link android.media.Image#getCropRect}
- */
- @Override
- public Rect getCropRect() {
- return mImage.getCropRect();
- }
-
- /**
- * @see {@link android.media.Image#setCropRect}
- */
- @Override
- public void setCropRect(Rect cropRect) {
- mImage.setCropRect(cropRect);
- }
-
- /**
- * @see {@link android.media.Image#getFormat}
- */
- @Override
- public int getFormat() {
- return mImage.getFormat();
- }
-
- /**
- * @see {@link android.media.Image#getHeight}
- */
- @Override
- public int getHeight() {
- return mImage.getHeight();
- }
-
- /**
- * @see {@link android.media.Image#getPlanes}
- */
- @Override
- public android.media.Image.Plane[] getPlanes() {
- return mImage.getPlanes();
- }
-
- /**
- * @see {@link android.media.Image#getTimestamp}
- */
- @Override
- public long getTimestamp() {
- return mImage.getTimestamp();
- }
-
- /**
- * @see {@link android.media.Image#getWidth}
- */
- @Override
- public int getWidth() {
- return mImage.getWidth();
- }
-
- /**
- * @see {@link android.media.Image#close}
- */
- @Override
- public void close() {
- mImage.close();
- }
- }
+ public void close();
}
diff --git a/src/com/android/camera/one/v2/commands/AFScanHoldReset.java b/src/com/android/camera/one/v2/commands/AFScanHoldReset.java
new file mode 100644
index 0000000..0578945
--- /dev/null
+++ b/src/com/android/camera/one/v2/commands/AFScanHoldReset.java
@@ -0,0 +1,58 @@
+/*
+ * 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.camera.one.v2.commands;
+
+import android.graphics.PointF;
+import android.hardware.camera2.CameraAccessException;
+
+import com.android.camera.async.ResettingDelayedExecutor;
+import com.android.camera.async.Updatable;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
+import com.android.camera.one.v2.common.MeteringParameters;
+
+/**
+ * Performs a full Auto Focus scan, holds for a period of time, and then resets
+ * the preview.
+ */
+public class AFScanHoldReset implements CameraCommand {
+ private final CameraCommand mAFScanCommand;
+ private final ResettingDelayedExecutor mDelayedExecutor;
+ private final Runnable mPreviewRunnable;
+ private final Updatable<MeteringParameters> mMeteringParametersUpdatable;
+
+ public AFScanHoldReset(CameraCommand afScanCommand, ResettingDelayedExecutor delayedExecutor,
+ Runnable previewRunnable, Updatable<MeteringParameters> meteringParametersUpdatable) {
+ mAFScanCommand = afScanCommand;
+ mDelayedExecutor = delayedExecutor;
+ mPreviewRunnable = previewRunnable;
+ mMeteringParametersUpdatable = meteringParametersUpdatable;
+ }
+
+ @Override
+ public void run() throws CameraAccessException, InterruptedException,
+ CameraCaptureSessionClosedException {
+ mAFScanCommand.run();
+ mDelayedExecutor.execute(new Runnable() {
+ public void run() {
+ // Reset metering regions and restart the preview
+ mMeteringParametersUpdatable.update(new MeteringParameters(new PointF(), new
+ PointF(), MeteringParameters.Mode.GLOBAL));
+ mPreviewRunnable.run();
+ }
+ });
+ }
+}
diff --git a/src/com/android/camera/one/v2/components/CameraCommand.java b/src/com/android/camera/one/v2/commands/CameraCommand.java
similarity index 69%
rename from src/com/android/camera/one/v2/components/CameraCommand.java
rename to src/com/android/camera/one/v2/commands/CameraCommand.java
index de13899..652b456 100644
--- a/src/com/android/camera/one/v2/components/CameraCommand.java
+++ b/src/com/android/camera/one/v2/commands/CameraCommand.java
@@ -13,14 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.camera.one.v2.components;
+
+package com.android.camera.one.v2.commands;
import android.hardware.camera2.CameraAccessException;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
+
/**
* A generic camera command which may be interrupted and may throw
* {@link android.hardware.camera2.CameraAccessException}.
*/
public interface CameraCommand {
- public void run() throws InterruptedException, CameraAccessException;
+ /**
+ * @throws InterruptedException If interrupted while executing the command.
+ * @throws CameraAccessException If the camera is not available when
+ * accessed by the command.
+ */
+ public void run() throws InterruptedException, CameraAccessException,
+ CameraCaptureSessionClosedException;
}
diff --git a/src/com/android/camera/one/v2/commands/CameraCommandExecutor.java b/src/com/android/camera/one/v2/commands/CameraCommandExecutor.java
new file mode 100644
index 0000000..0358f1e
--- /dev/null
+++ b/src/com/android/camera/one/v2/commands/CameraCommandExecutor.java
@@ -0,0 +1,72 @@
+/*
+ * 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.camera.one.v2.commands;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
+
+import android.hardware.camera2.CameraAccessException;
+
+import com.android.camera.async.SafeCloseable;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionProxy;
+
+/**
+ * Executes camera commands on a thread pool.
+ */
+public class CameraCommandExecutor implements SafeCloseable {
+ private class CommandRunnable implements Runnable {
+ private final CameraCommand mCommand;
+
+ public CommandRunnable(CameraCommand command) {
+ mCommand = command;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mCommand.run();
+ } catch (InterruptedException e) {
+ // If interrupted, just return.
+ } catch (CameraAccessException e) {
+ // If the camera was closed and the command failed, just return.
+ } catch (CameraCaptureSessionClosedException e) {
+ // If the session was closed and the command failed, just return.
+ }
+ }
+ }
+
+ private final ExecutorService mExecutor;
+
+ public CameraCommandExecutor(ExecutorService threadPoolExecutor) {
+ mExecutor = threadPoolExecutor;
+ }
+
+ public void execute(CameraCommand command) {
+ try {
+ mExecutor.submit(new CommandRunnable(command));
+ } catch (RejectedExecutionException e) {
+ // If the executor is shut down, the command will not be executed.
+ // So, we can ignore this exception.
+ }
+ }
+
+ @Override
+ public void close() {
+ mExecutor.shutdownNow();
+ }
+}
diff --git a/src/com/android/camera/one/v2/commands/FlashBasedPhotoCommand.java b/src/com/android/camera/one/v2/commands/FlashBasedPhotoCommand.java
new file mode 100644
index 0000000..268e760
--- /dev/null
+++ b/src/com/android/camera/one/v2/commands/FlashBasedPhotoCommand.java
@@ -0,0 +1,66 @@
+/*
+ * 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.camera.one.v2.commands;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CaptureResult;
+
+import com.android.camera.async.Pollable;
+import com.android.camera.one.OneCamera.PhotoCaptureParameters;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
+
+/**
+ * Combines a flash-enabled command and a non-flash command into a single
+ * command which fires depending on the current flash setting and AE metadata.
+ */
+public class FlashBasedPhotoCommand implements CameraCommand {
+ private final CameraCommand mFlashPhotoCommand;
+ private final CameraCommand mNoFlashPhotoCommand;
+ private final Pollable<Integer> mAEState;
+ private final PhotoCaptureParameters.Flash mFlashState;
+
+ public FlashBasedPhotoCommand(
+ CameraCommand flashPhotoCommand,
+ CameraCommand noFlashPhotoCommand,
+ Pollable<Integer> aeState,
+ PhotoCaptureParameters.Flash flashState) {
+ mFlashPhotoCommand = flashPhotoCommand;
+ mNoFlashPhotoCommand = noFlashPhotoCommand;
+ mAEState = aeState;
+ mFlashState = flashState;
+ }
+
+ @Override
+ public void run() throws InterruptedException, CameraAccessException,
+ CameraCaptureSessionClosedException {
+ boolean needsFlash = false;
+ if (mFlashState == PhotoCaptureParameters.Flash.ON) {
+ needsFlash = true;
+ } else {
+ Integer mostRecentAEState = mAEState.get(CaptureResult.CONTROL_AE_STATE_INACTIVE);
+ if (mostRecentAEState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED) {
+ needsFlash = true;
+ }
+ }
+
+ if (needsFlash) {
+ mFlashPhotoCommand.run();
+ } else {
+ mNoFlashPhotoCommand.run();
+ }
+ }
+}
diff --git a/src/com/android/camera/one/v2/commands/FullAFScanCommand.java b/src/com/android/camera/one/v2/commands/FullAFScanCommand.java
new file mode 100644
index 0000000..76ffbf9
--- /dev/null
+++ b/src/com/android/camera/one/v2/commands/FullAFScanCommand.java
@@ -0,0 +1,104 @@
+/*
+ * 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.camera.one.v2.commands;
+
+import java.util.Arrays;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CaptureRequest;
+
+import com.android.camera.async.UpdatableCountDownLatch;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionProxy;
+import com.android.camera.one.v2.core.FrameServer;
+import com.android.camera.one.v2.core.RequestBuilder;
+import com.android.camera.one.v2.core.TriggeredAFScanStateResponseListener;
+
+/**
+ * Performs a full auto focus scan.
+ */
+public class FullAFScanCommand implements CameraCommand {
+ private final FrameServer mFrameServer;
+ private final RequestBuilder.Factory mBuilderFactory;
+ private final int mTemplateType;
+
+ /**
+ * @param frameServer Used for sending requests to the camera.
+ * @param builder Used for building requests.
+ * @param templateType See
+ * {@link android.hardware.camera2.CameraDevice#createCaptureRequest}
+ */
+ public FullAFScanCommand(FrameServer frameServer, RequestBuilder.Factory builder, int
+ templateType) {
+ mFrameServer = frameServer;
+ mBuilderFactory = builder;
+ mTemplateType = templateType;
+ }
+
+ /**
+ * Performs an auto-focus scan, blocking until the scan starts, runs, and
+ * completes.
+ */
+ public void run() throws InterruptedException, CameraAccessException,
+ CameraCaptureSessionClosedException {
+ FrameServer.Session session = mFrameServer.tryCreateExclusiveSession();
+ if (session == null) {
+ // If there are already other commands interacting with the
+ // FrameServer, don't wait to run the AF command, instead just
+ // abort.
+ return;
+ }
+ try {
+ UpdatableCountDownLatch scanDoneLatch = new UpdatableCountDownLatch(1);
+ TriggeredAFScanStateResponseListener afScanStateListener = new TriggeredAFScanStateResponseListener(
+ scanDoneLatch);
+
+ // Build a request to send a repeating AF_IDLE
+ RequestBuilder idleBuilder = mBuilderFactory.create(mTemplateType);
+ idleBuilder.addResponseListener(afScanStateListener);
+ idleBuilder.setParam(CaptureRequest.CONTROL_MODE, CaptureRequest
+ .CONTROL_MODE_AUTO);
+ idleBuilder.setParam(CaptureRequest.CONTROL_AF_MODE, CaptureRequest
+ .CONTROL_AF_MODE_AUTO);
+ idleBuilder.setParam(CaptureRequest.CONTROL_AF_TRIGGER,
+ CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
+
+ // Build a request to send a single AF_TRIGGER
+ RequestBuilder triggerBuilder = mBuilderFactory.create(mTemplateType);
+ triggerBuilder.addResponseListener(afScanStateListener);
+ triggerBuilder.setParam(CaptureRequest.CONTROL_MODE, CaptureRequest
+ .CONTROL_MODE_AUTO);
+ triggerBuilder.setParam(CaptureRequest.CONTROL_AF_MODE, CaptureRequest
+ .CONTROL_AF_MODE_AUTO);
+ triggerBuilder.setParam(CaptureRequest.CONTROL_AF_TRIGGER,
+ CaptureRequest.CONTROL_AF_TRIGGER_START);
+
+ session.submitRequest(Arrays.asList(idleBuilder.build()),
+ FrameServer.RequestType.REPEATING);
+ session.submitRequest(Arrays.asList(triggerBuilder.build()),
+ FrameServer.RequestType.NON_REPEATING);
+
+ // Block until the scan is done.
+ // TODO If the HAL never transitions out of scanning mode, this will
+ // block forever (or until interrupted because the app is paused).
+ // So, maybe use a generous timeout and log as HAL errors.
+ scanDoneLatch.await();
+ } finally {
+ session.close();
+ }
+ }
+}
diff --git a/src/com/android/camera/one/v2/commands/LoggingCameraCommand.java b/src/com/android/camera/one/v2/commands/LoggingCameraCommand.java
new file mode 100644
index 0000000..48125dc
--- /dev/null
+++ b/src/com/android/camera/one/v2/commands/LoggingCameraCommand.java
@@ -0,0 +1,50 @@
+/*
+ * 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.camera.one.v2.commands;
+
+import android.hardware.camera2.CameraAccessException;
+
+import com.android.camera.debug.Log;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
+
+/**
+ * Wraps a {@link CameraCommand} with logging.
+ */
+public class LoggingCameraCommand implements CameraCommand {
+ private static final Log.Tag TAG = new Log.Tag("CameraCommand");
+ private final CameraCommand mCommand;
+ private final String mName;
+
+ public LoggingCameraCommand(CameraCommand command, String name) {
+ mCommand = command;
+ mName = name;
+ }
+
+ @Override
+ public void run() throws InterruptedException, CameraAccessException,
+ CameraCaptureSessionClosedException {
+ Log.v(TAG, String.format("Executing Command: %s: START", mName));
+ try {
+ mCommand.run();
+ } catch (Exception e) {
+ Log.e(TAG, String.format("Executing Command: %s: Exception: ", mName));
+ e.printStackTrace();
+ throw e;
+ }
+ Log.v(TAG, String.format("Executing Command: %s: END", mName));
+ }
+}
diff --git a/src/com/android/camera/one/v2/components/PreviewCommand.java b/src/com/android/camera/one/v2/commands/PreviewCommand.java
similarity index 75%
rename from src/com/android/camera/one/v2/components/PreviewCommand.java
rename to src/com/android/camera/one/v2/commands/PreviewCommand.java
index 392d217..83f185a 100644
--- a/src/com/android/camera/one/v2/components/PreviewCommand.java
+++ b/src/com/android/camera/one/v2/commands/PreviewCommand.java
@@ -14,17 +14,18 @@
* limitations under the License.
*/
-package com.android.camera.one.v2.components;
+package com.android.camera.one.v2.commands;
import java.util.Arrays;
import android.hardware.camera2.CameraAccessException;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
import com.android.camera.one.v2.core.FrameServer;
import com.android.camera.one.v2.core.RequestBuilder;
/**
- * Sends preview requests to a {@link FrameServer}.
+ * Sends repeating preview requests to a {@link FrameServer}.
*/
public class PreviewCommand implements CameraCommand {
private final FrameServer mFrameServer;
@@ -32,8 +33,8 @@
private final int mRequestType;
/**
- * Constructs a Preview. Note that it is the {@link RequestBuilder.Factory}
- * 's responsibility to attach the relevant
+ * Constructs a Preview. Note that it is the responsiblity of the
+ * {@link RequestBuilder.Factory} to attach the relevant
* {@link com.android.camera.one.v2.core.CaptureStream}s, such as the
* viewfinder surface.
*/
@@ -44,13 +45,12 @@
mRequestType = requestType;
}
- public void run() throws InterruptedException, CameraAccessException {
- FrameServer.Session session = mFrameServer.createSession();
- try {
+ public void run() throws InterruptedException, CameraAccessException,
+ CameraCaptureSessionClosedException {
+ try (FrameServer.Session session = mFrameServer.createSession()) {
RequestBuilder photoRequest = mBuilderFactory.create(mRequestType);
- session.submitRequest(Arrays.asList(photoRequest.build()), true);
- } finally {
- session.close();
+ session.submitRequest(Arrays.asList(photoRequest.build()),
+ FrameServer.RequestType.REPEATING);
}
}
}
diff --git a/src/com/android/camera/one/v2/commands/RunnableCameraCommand.java b/src/com/android/camera/one/v2/commands/RunnableCameraCommand.java
new file mode 100644
index 0000000..902bf07
--- /dev/null
+++ b/src/com/android/camera/one/v2/commands/RunnableCameraCommand.java
@@ -0,0 +1,36 @@
+/*
+ * 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.camera.one.v2.commands;
+
+/**
+ * Provides a convenient {@link Runnable} for executing a {@link CameraCommand}
+ * on a predetermined {@link CameraCommandExecutor}.
+ */
+public class RunnableCameraCommand implements Runnable {
+ private final CameraCommandExecutor mExecutor;
+ private final CameraCommand mCommand;
+
+ public RunnableCameraCommand(CameraCommandExecutor executor, CameraCommand command) {
+ mExecutor = executor;
+ mCommand = command;
+ }
+
+ @Override
+ public void run() {
+ mExecutor.execute(mCommand);
+ }
+}
diff --git a/src/com/android/camera/one/v2/components/StaticPictureCommand.java b/src/com/android/camera/one/v2/commands/StaticPictureCommand.java
similarity index 70%
rename from src/com/android/camera/one/v2/components/StaticPictureCommand.java
rename to src/com/android/camera/one/v2/commands/StaticPictureCommand.java
index b7e837c..547ba55 100644
--- a/src/com/android/camera/one/v2/components/StaticPictureCommand.java
+++ b/src/com/android/camera/one/v2/commands/StaticPictureCommand.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.camera.one.v2.components;
+package com.android.camera.one.v2.commands;
import java.util.Arrays;
@@ -22,7 +22,10 @@
import android.hardware.camera2.CameraDevice;
import com.android.camera.async.BufferQueue;
+import com.android.camera.async.Updatable;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
import com.android.camera.one.v2.camera2proxy.ImageProxy;
+import com.android.camera.one.v2.core.FrameExposureResponseListener;
import com.android.camera.one.v2.core.FrameServer;
import com.android.camera.one.v2.core.RequestBuilder;
import com.android.camera.one.v2.sharedimagereader.SharedImageReader;
@@ -34,42 +37,43 @@
private final FrameServer mFrameServer;
private final RequestBuilder.Factory mBuilderFactory;
private final SharedImageReader mImageReader;
- private final ImageSaver mImageSaver;
+ private final Updatable<ImageProxy> mImageSaver;
+ private final Updatable<Void> mImageExposureUpdatable;
public StaticPictureCommand(FrameServer frameServer, RequestBuilder.Factory builder,
- SharedImageReader imageReader, ImageSaver imageSaver) {
+ SharedImageReader imageReader, Updatable<ImageProxy> imageSaver,
+ Updatable<Void> imageExposureUpdatable) {
mFrameServer = frameServer;
mBuilderFactory = builder;
mImageReader = imageReader;
mImageSaver = imageSaver;
+ mImageExposureUpdatable = imageExposureUpdatable;
}
/**
* Sends a request to take a picture and blocks until it completes.
*/
public void run() throws
- InterruptedException, CameraAccessException {
- FrameServer.Session session = mFrameServer.createSession();
- try {
+ InterruptedException, CameraAccessException, CameraCaptureSessionClosedException {
+ try (FrameServer.Session session = mFrameServer.createSession()) {
RequestBuilder photoRequest = mBuilderFactory.create(CameraDevice
.TEMPLATE_STILL_CAPTURE);
try (SharedImageReader.ImageCaptureBufferQueue imageStream = mImageReader
.createStream(1)) {
photoRequest.addStream(imageStream);
- // TODO Add a {@link ResponseListener} to notify the caller of
- // when the frame is exposed.
- session.submitRequest(Arrays.asList(photoRequest.build()), false);
+ photoRequest.addResponseListener(new FrameExposureResponseListener(
+ mImageExposureUpdatable));
+ session.submitRequest(Arrays.asList(photoRequest.build()),
+ FrameServer.RequestType.NON_REPEATING);
ImageProxy image = imageStream.getNext();
- mImageSaver.saveAndCloseImage(image);
+ mImageSaver.update(image);
} catch (BufferQueue.BufferQueueClosedException e) {
// If we get here, the request was submitted, but the image
// never arrived.
// TODO Log failure and notify the caller
}
- } finally {
- session.close();
}
}
}
diff --git a/src/com/android/camera/one/v2/common/CaptureSessionCreator.java b/src/com/android/camera/one/v2/common/CaptureSessionCreator.java
new file mode 100644
index 0000000..88446ea
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/CaptureSessionCreator.java
@@ -0,0 +1,103 @@
+/*
+ * 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.camera.one.v2.common;
+
+import java.util.List;
+
+import android.hardware.camera2.CameraAccessException;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Surface;
+
+import com.android.camera.async.FutureResult;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionProxy;
+import com.android.camera.one.v2.camera2proxy.CameraDeviceProxy;
+import com.android.camera.util.ScopedFactory;
+
+/**
+ * Asynchronously creates capture sessions.
+ */
+public class CaptureSessionCreator implements Runnable {
+ private final CameraDeviceProxy mDevice;
+ private final Handler mCameraHandler;
+ private final List<Surface> mSurfaces;
+ private final FutureResult<CameraCaptureSessionProxy> mSessionFuture;
+ private final ScopedFactory<CameraCaptureSessionProxy, Runnable> mCaptureSessionScopeEntrance;
+
+ /**
+ * @param device The device on which to create the capture session.
+ * @param cameraHandler The handler on which to process capture session
+ * state callbacks.
+ * @param surfaces The surfaces to configure the session with.
+ * @param sessionFuture The {@link com.android.camera.async.FutureResult} in
+ * which to place the asynchronously-created.
+ * @param captureSessionScopeEntrance A factory to produce a runnable to
+ * execute if/when the CameraCaptureSession is available.
+ */
+ public CaptureSessionCreator(CameraDeviceProxy device, Handler cameraHandler,
+ List<Surface> surfaces, FutureResult<CameraCaptureSessionProxy> sessionFuture,
+ ScopedFactory<CameraCaptureSessionProxy, Runnable> captureSessionScopeEntrance) {
+ mDevice = device;
+ mCameraHandler = cameraHandler;
+ mSurfaces = surfaces;
+ mSessionFuture = sessionFuture;
+ mCaptureSessionScopeEntrance = captureSessionScopeEntrance;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mDevice.createCaptureSession(mSurfaces, new CameraCaptureSessionProxy.StateCallback() {
+ @Override
+ public void onActive(CameraCaptureSessionProxy session) {
+ // Ignore.
+ }
+
+ @Override
+ public void onConfigureFailed(CameraCaptureSessionProxy session) {
+ mSessionFuture.setCancelled();
+ session.close();
+ }
+
+ @Override
+ public void onConfigured(CameraCaptureSessionProxy session) {
+ boolean valueSet = mSessionFuture.setValue(session);
+ if (valueSet) {
+ mCaptureSessionScopeEntrance.get(session).run();
+ } else {
+ // If the future was already marked with cancellation or
+ // an exception, close the session.
+ session.close();
+ }
+ }
+
+ @Override
+ public void onReady(CameraCaptureSessionProxy session) {
+ // Ignore.
+ }
+
+ @Override
+ public void onClosed(CameraCaptureSessionProxy session) {
+ mSessionFuture.setCancelled();
+ session.close();
+ }
+ }, mCameraHandler);
+ } catch (CameraAccessException e) {
+ mSessionFuture.setException(e);
+ }
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/DeferredManualAutoFocus.java b/src/com/android/camera/one/v2/common/DeferredManualAutoFocus.java
new file mode 100644
index 0000000..30ce112
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/DeferredManualAutoFocus.java
@@ -0,0 +1,48 @@
+/*
+ * 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.camera.one.v2.common;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * A
+ * {@link com.android.camera.one.v2.common.GenericOneCameraImpl.ManualAutoFocus}
+ * on which {@link #triggerFocusAndMeterAtPoint} may be called even if the
+ * underlying camera is not yet ready.
+ */
+public class DeferredManualAutoFocus implements GenericOneCameraImpl.ManualAutoFocus {
+ private final Future<GenericOneCameraImpl.ManualAutoFocus> mManualAutoFocusFuture;
+
+ public DeferredManualAutoFocus(Future<GenericOneCameraImpl.ManualAutoFocus> manualAutoFocusFuture) {
+ mManualAutoFocusFuture = manualAutoFocusFuture;
+ }
+
+ @Override
+ public void triggerFocusAndMeterAtPoint(float nx, float ny) {
+ if (mManualAutoFocusFuture.isDone()) {
+ try {
+ GenericOneCameraImpl.ManualAutoFocus af = mManualAutoFocusFuture.get();
+ af.triggerFocusAndMeterAtPoint(nx, ny);
+ } catch (InterruptedException | ExecutionException | CancellationException e) {
+ // If the {@link Future} is not ready, do nothing.
+ return;
+ }
+ }
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/DeferredPictureTaker.java b/src/com/android/camera/one/v2/common/DeferredPictureTaker.java
new file mode 100644
index 0000000..40aa903
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/DeferredPictureTaker.java
@@ -0,0 +1,48 @@
+/*
+ * 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.camera.one.v2.common;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import com.android.camera.one.OneCamera;
+import com.android.camera.session.CaptureSession;
+
+/**
+ * A {@link com.android.camera.one.v2.common.GenericOneCameraImpl.PictureTaker}
+ * on which {@link #takePicture} may be called even if the underlying camera is
+ * not yet ready.
+ */
+public class DeferredPictureTaker implements GenericOneCameraImpl.PictureTaker {
+ private final Future<GenericOneCameraImpl.PictureTaker> mPictureTakerFuture;
+
+ public DeferredPictureTaker(Future<GenericOneCameraImpl.PictureTaker> pictureTakerFuture) {
+ mPictureTakerFuture = pictureTakerFuture;
+ }
+
+ public void takePicture(OneCamera.PhotoCaptureParameters params, CaptureSession session) {
+ if (mPictureTakerFuture.isDone()) {
+ try {
+ GenericOneCameraImpl.PictureTaker taker = mPictureTakerFuture.get();
+ taker.takePicture(params, session);
+ } catch (InterruptedException | ExecutionException | CancellationException e) {
+ return;
+ }
+ }
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/FullSizeAspectRatioProvider.java b/src/com/android/camera/one/v2/common/FullSizeAspectRatioProvider.java
new file mode 100644
index 0000000..29e4914
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/FullSizeAspectRatioProvider.java
@@ -0,0 +1,43 @@
+/*
+ * 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.camera.one.v2.common;
+
+import android.graphics.Rect;
+import android.hardware.camera2.CameraCharacteristics;
+
+/**
+ * Provides the full-size aspect ratio of the camera with the given characteristics.
+ */
+public class FullSizeAspectRatioProvider {
+ private final CameraCharacteristics mCharacteristics;
+
+ public FullSizeAspectRatioProvider(CameraCharacteristics characteristics) {
+ mCharacteristics = characteristics;
+ }
+
+ /**
+ * Calculate the aspect ratio of the full size capture on this device.
+ *
+ * @return The aspect ratio, in terms of width/height of the full capture
+ * size.
+ */
+ public float get() {
+ Rect activeArraySize = mCharacteristics.get(
+ CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ return ((float) (activeArraySize.width())) / activeArraySize.height();
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/GenericOneCameraImpl.java b/src/com/android/camera/one/v2/common/GenericOneCameraImpl.java
new file mode 100644
index 0000000..a19fd28
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/GenericOneCameraImpl.java
@@ -0,0 +1,193 @@
+/*
+ * 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.camera.one.v2.common;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import android.content.Context;
+import android.view.Surface;
+
+import com.android.camera.async.Listenable;
+import com.android.camera.async.SafeCloseable;
+import com.android.camera.async.Updatable;
+import com.android.camera.one.OneCamera;
+import com.android.camera.one.v2.AutoFocusHelper;
+import com.android.camera.session.CaptureSession;
+import com.android.camera.util.Callback;
+import com.android.camera.util.ScopedFactory;
+import com.android.camera.util.Size;
+
+/**
+ * A generic, composable {@link OneCamera}.
+ * <p>
+ * Note: This implementation assumes that these four methods are invoked in
+ * sequences matching the following regex:
+ * <p>
+ * startPreview (takePicture | triggerFocusAndMeterAtPoint)* close
+ * <p>
+ * All other methods may be called at any time.
+ */
+public class GenericOneCameraImpl implements OneCamera {
+ public interface ManualAutoFocus {
+ /**
+ * @See {@link OneCamera#triggerFocusAndMeterAtPoint}
+ */
+ void triggerFocusAndMeterAtPoint(float nx, float ny);
+ }
+
+ public interface PictureTaker {
+ /**
+ * @See {@link OneCamera#takePicture}
+ */
+ public void takePicture(OneCamera.PhotoCaptureParameters params, CaptureSession session);
+ }
+
+ private final Set<SafeCloseable> mCloseListeners;
+ private final PictureTaker mPictureTaker;
+ private final ManualAutoFocus mManualAutoFocus;
+ private final Listenable<Integer> mAFStateListenable;
+ private final Listenable<Boolean> mReadyStateListenable;
+ private final float mMaxZoom;
+ private final Updatable<Float> mZoom;
+ private final Size[] mSupportedPreviewSizes;
+ private final float mFullSizeAspectRatio;
+ private final Facing mDirection;
+ private final PreviewSizeSelector mPreviewSizeSelector;
+ private final Listenable<Boolean> mPreviewStartSuccessListenable;
+ private final ScopedFactory<Surface, Runnable> mPreviewScopeEntrance;
+
+ public GenericOneCameraImpl(Set<SafeCloseable> closeListeners, PictureTaker pictureTaker,
+ ManualAutoFocus manualAutoFocus, Listenable<Integer> afStateProvider,
+ Listenable<Boolean> readyStateListenable, float maxZoom, Updatable<Float> zoom,
+ Size[] supportedPreviewSizes, float fullSizeAspectRatio, Facing direction,
+ PreviewSizeSelector previewSizeSelector,
+ Listenable<Boolean> previewStartSuccessListenable,
+ ScopedFactory<Surface, Runnable> previewScopeEntrance) {
+ mPreviewStartSuccessListenable = previewStartSuccessListenable;
+ mPreviewScopeEntrance = previewScopeEntrance;
+ mCloseListeners = new HashSet<>(closeListeners);
+ mMaxZoom = maxZoom;
+ mSupportedPreviewSizes = supportedPreviewSizes;
+ mFullSizeAspectRatio = fullSizeAspectRatio;
+ mDirection = direction;
+ mPreviewSizeSelector = previewSizeSelector;
+ mPictureTaker = pictureTaker;
+ mManualAutoFocus = manualAutoFocus;
+ mAFStateListenable = afStateProvider;
+ mReadyStateListenable = readyStateListenable;
+ mZoom = zoom;
+ }
+
+ @Override
+ public void triggerFocusAndMeterAtPoint(float nx, float ny) {
+ mManualAutoFocus.triggerFocusAndMeterAtPoint(nx, ny);
+ }
+
+ @Override
+ public void takePicture(PhotoCaptureParameters params, CaptureSession session) {
+ mPictureTaker.takePicture(params, session);
+ }
+
+ @Override
+ public void startBurst(BurstParameters params, CaptureSession session) {
+ // TODO delete from OneCamera interface
+ }
+
+ @Override
+ public void stopBurst() {
+ // TODO delete from OneCamera interface
+ }
+
+ @Override
+ public void setFocusStateListener(final FocusStateListener listener) {
+ mAFStateListenable.setCallback(new Callback<Integer>() {
+ @Override
+ public void onCallback(Integer afState) {
+ // TODO delete frameNumber from FocusStateListener callback. It
+ // is optional and never actually used.
+ long frameNumber = -1;
+ listener.onFocusStatusUpdate(AutoFocusHelper.stateFromCamera2State(afState),
+ frameNumber);
+ }
+ });
+ }
+
+ @Override
+ public void setReadyStateChangedListener(final ReadyStateChangedListener listener) {
+ mReadyStateListenable.setCallback(new Callback<Boolean>() {
+ @Override
+ public void onCallback(Boolean result) {
+ listener.onReadyStateChanged(result);
+ }
+ });
+ }
+
+ @Override
+ public void startPreview(Surface surface, final CaptureReadyCallback listener) {
+ mPreviewStartSuccessListenable.setCallback(new Callback<Boolean>() {
+ @Override
+ public void onCallback(Boolean success) {
+ if (success) {
+ listener.onReadyForCapture();
+ } else {
+ listener.onSetupFailed();
+ }
+ }
+ });
+
+ mPreviewScopeEntrance.get(surface).run();
+ }
+
+ @Override
+ public void close(CloseCallback closeCallback) {
+ // TODO Remove CloseCallback from the interface. It is always null.
+ for (SafeCloseable listener : mCloseListeners) {
+ listener.close();
+ }
+ }
+
+ @Override
+ public Size[] getSupportedPreviewSizes() {
+ return mSupportedPreviewSizes;
+ }
+
+ @Override
+ public float getFullSizeAspectRatio() {
+ return mFullSizeAspectRatio;
+ }
+
+ @Override
+ public Facing getDirection() {
+ return mDirection;
+ }
+
+ @Override
+ public float getMaxZoom() {
+ return mMaxZoom;
+ }
+
+ @Override
+ public void setZoom(float zoom) {
+ mZoom.update(zoom);
+ }
+
+ @Override
+ public Size pickPreviewSize(Size pictureSize, Context context) {
+ return mPreviewSizeSelector.pickPreviewSize(pictureSize, context);
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/ManualAutoFocusImpl.java b/src/com/android/camera/one/v2/common/ManualAutoFocusImpl.java
new file mode 100644
index 0000000..9b2c52c
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/ManualAutoFocusImpl.java
@@ -0,0 +1,44 @@
+/*
+ * 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.camera.one.v2.common;
+
+import android.graphics.PointF;
+
+import com.android.camera.async.Updatable;
+
+/**
+ * A ManualAutoFocus implementation which updates metering parameters and runs
+ * an AF Scan.
+ */
+public class ManualAutoFocusImpl implements GenericOneCameraImpl.ManualAutoFocus {
+ private final Updatable<MeteringParameters> mMeteringParameters;
+ private final Runnable mAFScanRunnable;
+
+ public ManualAutoFocusImpl(Updatable<MeteringParameters> meteringParameters,
+ Runnable afScanRunnable) {
+ mMeteringParameters = meteringParameters;
+ mAFScanRunnable = afScanRunnable;
+ }
+
+ @Override
+ public void triggerFocusAndMeterAtPoint(float nx, float ny) {
+ PointF point = new PointF(nx, ny);
+ mMeteringParameters.update(new MeteringParameters(point, point, MeteringParameters.Mode
+ .POINT));
+ mAFScanRunnable.run();
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/MeteringParameters.java b/src/com/android/camera/one/v2/common/MeteringParameters.java
new file mode 100644
index 0000000..ffd26e5
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/MeteringParameters.java
@@ -0,0 +1,48 @@
+/*
+ * 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.camera.one.v2.common;
+
+import android.graphics.PointF;
+
+public class MeteringParameters {
+ public static enum Mode {
+ POINT,
+ GLOBAL
+ };
+
+ private final PointF mAFPoint;
+ private final PointF mAEPoint;
+ private final Mode mMode;
+
+ public MeteringParameters(PointF afPoint, PointF aePoint, Mode mode) {
+ mAFPoint = afPoint;
+ mAEPoint = aePoint;
+ mMode = mode;
+ }
+
+ public PointF getAFPoint() {
+ return mAFPoint;
+ }
+
+ public PointF getAEPoint() {
+ return mAEPoint;
+ }
+
+ public Mode getMode() {
+ return mMode;
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/PictureCallbackAdaptor.java b/src/com/android/camera/one/v2/common/PictureCallbackAdaptor.java
new file mode 100644
index 0000000..03a89d2
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/PictureCallbackAdaptor.java
@@ -0,0 +1,108 @@
+/*
+ * 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.camera.one.v2.common;
+
+import java.util.concurrent.Executor;
+
+import android.net.Uri;
+
+import com.android.camera.async.ConcurrentState;
+import com.android.camera.async.Updatable;
+import com.android.camera.one.OneCamera;
+import com.android.camera.session.CaptureSession;
+import com.android.camera.util.Callback;
+
+/**
+ * Provides {@link Updatable}s linked to the given
+ * {@link OneCamera.PictureCallback}.
+ */
+public class PictureCallbackAdaptor {
+ private final OneCamera.PictureCallback mPictureCallback;
+ private final Executor mMainExecutor;
+
+ public PictureCallbackAdaptor(OneCamera.PictureCallback pictureCallback,
+ Executor mainExecutor) {
+ mPictureCallback = pictureCallback;
+ mMainExecutor = mainExecutor;
+ }
+
+ public Updatable<Void> provideQuickExposeUpdatable() {
+ ConcurrentState<Void> exposeState = new ConcurrentState<>();
+ exposeState.addCallback(new Callback<Long>() {
+ @Override
+ public void onCallback(Long timestamp) {
+ mPictureCallback.onQuickExpose();
+ }
+ }, mMainExecutor);
+ return exposeState;
+ }
+
+ public Updatable<byte[]> provideThumbnailUpdatable() {
+ ConcurrentState<byte[]> thumbnailState = new ConcurrentState<>();
+ thumbnailState.addCallback(new Callback<byte[]>() {
+ @Override
+ public void onCallback(byte[] jpegData) {
+ mPictureCallback.onThumbnailResult(jpegData);
+ }
+ }, mMainExecutor);
+ return thumbnailState;
+ }
+
+ public Updatable<CaptureSession> providePictureTakenUpdatable() {
+ ConcurrentState<CaptureSession> state = new ConcurrentState<>();
+ state.addCallback(new Callback<CaptureSession>() {
+ @Override
+ public void onCallback(CaptureSession session) {
+ mPictureCallback.onPictureTaken(session);
+ }
+ }, mMainExecutor);
+ return state;
+ }
+
+ public Updatable<Uri> providePictureSavedUpdatable() {
+ ConcurrentState<Uri> state = new ConcurrentState<>();
+ state.addCallback(new Callback<Uri>() {
+ @Override
+ public void onCallback(Uri uri) {
+ mPictureCallback.onPictureSaved(uri);
+ }
+ }, mMainExecutor);
+ return state;
+ }
+
+ public Updatable<Void> providePictureTakingFailedUpdatable() {
+ ConcurrentState<Void> state = new ConcurrentState<>();
+ state.addCallback(new Callback<Void>() {
+ @Override
+ public void onCallback(Void v) {
+ mPictureCallback.onPictureTakingFailed();
+ }
+ }, mMainExecutor);
+ return state;
+ }
+
+ public Updatable<Float> providePictureTakingProgressUpdatable() {
+ ConcurrentState<Float> state = new ConcurrentState<>();
+ state.addCallback(new Callback<Float>() {
+ @Override
+ public void onCallback(Float progress) {
+ mPictureCallback.onTakePictureProgress(progress);
+ }
+ }, mMainExecutor);
+ return state;
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/PollableAEMode.java b/src/com/android/camera/one/v2/common/PollableAEMode.java
new file mode 100644
index 0000000..fe9e068
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/PollableAEMode.java
@@ -0,0 +1,57 @@
+/*
+ * 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.camera.one.v2.common;
+
+import android.hardware.camera2.CaptureRequest;
+
+import com.android.camera.async.Pollable;
+import com.android.camera.one.OneCamera;
+
+/**
+ * Enables polling for the current AE Mode to use based on the current flash
+ * state.
+ */
+public class PollableAEMode implements Pollable<Integer> {
+ private final Pollable<OneCamera.PhotoCaptureParameters.Flash> mFlashPollable;
+
+ public PollableAEMode(Pollable<OneCamera.PhotoCaptureParameters.Flash> flashPollable) {
+ mFlashPollable = flashPollable;
+ }
+
+ @Override
+ public Integer get(Integer defaultValue) {
+ try {
+ return get();
+ } catch (NoValueSetException e) {
+ return defaultValue;
+ }
+ }
+
+ @Override
+ public Integer get() throws NoValueSetException {
+ switch (mFlashPollable.get()) {
+ case AUTO:
+ return CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH;
+ case ON:
+ return CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH;
+ case OFF:
+ return CaptureRequest.CONTROL_AE_MODE_ON;
+ default:
+ return CaptureRequest.CONTROL_AE_MODE_ON;
+ }
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/PollableAERegion.java b/src/com/android/camera/one/v2/common/PollableAERegion.java
new file mode 100644
index 0000000..93f221d
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/PollableAERegion.java
@@ -0,0 +1,64 @@
+/*
+ * 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.camera.one.v2.common;
+
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.hardware.camera2.params.MeteringRectangle;
+
+import com.android.camera.app.OrientationManager;
+import com.android.camera.async.Pollable;
+import com.android.camera.one.v2.AutoFocusHelper;
+
+/**
+ * Enables polling for the current AE metering rectangles based on the current
+ * metering parameters and crop region.
+ */
+public class PollableAERegion implements Pollable<MeteringRectangle[]> {
+ private final Pollable<MeteringParameters> mMeteringParameters;
+ private final Pollable<Rect> mCropRegion;
+ private final OrientationManager.DeviceOrientation mSensorOrientation;
+
+ public PollableAERegion(Pollable<MeteringParameters> meteringParameters,
+ Pollable<Rect> cropRegion, OrientationManager.DeviceOrientation sensorOrientation) {
+ mMeteringParameters = meteringParameters;
+ mCropRegion = cropRegion;
+ mSensorOrientation = sensorOrientation;
+ }
+
+ @Override
+ public MeteringRectangle[] get(MeteringRectangle[] defaultValue) {
+ try {
+ return get();
+ } catch (NoValueSetException e) {
+ return defaultValue;
+ }
+ }
+
+ @Override
+ public MeteringRectangle[] get() throws NoValueSetException {
+ MeteringParameters parameters = mMeteringParameters.get();
+ if (parameters.getMode() == MeteringParameters.Mode.POINT) {
+ Rect cropRegion = mCropRegion.get();
+ PointF point = parameters.getAEPoint();
+ return AutoFocusHelper.aeRegionsForNormalizedCoord(point.x, point.y, cropRegion,
+ mSensorOrientation.getDegrees());
+ } else {
+ return AutoFocusHelper.getZeroWeightRegion();
+ }
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/PollableAFRegion.java b/src/com/android/camera/one/v2/common/PollableAFRegion.java
new file mode 100644
index 0000000..71759d3
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/PollableAFRegion.java
@@ -0,0 +1,65 @@
+/*
+ * 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.camera.one.v2.common;
+
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.hardware.camera2.params.MeteringRectangle;
+
+import com.android.camera.app.OrientationManager;
+import com.android.camera.async.Pollable;
+import com.android.camera.one.v2.AutoFocusHelper;
+
+/**
+ * Enables polling for the current AF metering rectangles based on the current
+ * metering parameters and crop region.
+ */
+public class PollableAFRegion implements Pollable<MeteringRectangle[]> {
+ private final Pollable<MeteringParameters> mMeteringParameters;
+ private final Pollable<Rect> mCropRegion;
+ private final OrientationManager.DeviceOrientation mSensorOrientation;
+
+ public PollableAFRegion(Pollable<MeteringParameters> meteringParameters,
+ Pollable<Rect> cropRegion,
+ OrientationManager.DeviceOrientation sensorOrientation) {
+ mMeteringParameters = meteringParameters;
+ mCropRegion = cropRegion;
+ mSensorOrientation = sensorOrientation;
+ }
+
+ @Override
+ public MeteringRectangle[] get(MeteringRectangle[] defaultValue) {
+ try {
+ return get();
+ } catch (NoValueSetException e) {
+ return defaultValue;
+ }
+ }
+
+ @Override
+ public MeteringRectangle[] get() throws NoValueSetException {
+ MeteringParameters parameters = mMeteringParameters.get();
+ if (parameters.getMode() == MeteringParameters.Mode.POINT) {
+ Rect cropRegion = mCropRegion.get();
+ PointF point = parameters.getAEPoint();
+ return AutoFocusHelper.afRegionsForNormalizedCoord(point.x, point.y, cropRegion,
+ mSensorOrientation.getDegrees());
+ } else {
+ return AutoFocusHelper.getZeroWeightRegion();
+ }
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/PollableZoomedCropRegion.java b/src/com/android/camera/one/v2/common/PollableZoomedCropRegion.java
new file mode 100644
index 0000000..9e78262
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/PollableZoomedCropRegion.java
@@ -0,0 +1,56 @@
+/*
+ * 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.camera.one.v2.common;
+
+import android.graphics.Rect;
+import android.hardware.camera2.CameraCharacteristics;
+
+import com.android.camera.async.Pollable;
+import com.android.camera.one.v2.AutoFocusHelper;
+
+/**
+ * Enables polling for the current crop region based on the current zoom.
+ */
+public class PollableZoomedCropRegion implements Pollable<Rect> {
+ private final CameraCharacteristics mCharacteristics;
+ private final Pollable<Float> mZoom;
+
+ public PollableZoomedCropRegion(CameraCharacteristics characteristics, Pollable<Float> zoom) {
+ mCharacteristics = characteristics;
+ mZoom = zoom;
+ }
+
+ @Override
+ public Rect get(Rect defaultValue) {
+ try {
+ return get();
+ } catch (NoValueSetException e) {
+ return defaultValue;
+ }
+ }
+
+ @Override
+ public Rect get() throws NoValueSetException {
+ float zoom = mZoom.get();
+ Rect sensor = mCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ int xCenter = sensor.width() / 2;
+ int yCenter = sensor.height() / 2;
+ int xDelta = (int) (0.5f * sensor.width() / zoom);
+ int yDelta = (int) (0.5f * sensor.height() / zoom);
+ return new Rect(xCenter - xDelta, yCenter - yDelta, xCenter + xDelta, yCenter + yDelta);
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/PreviewSizeSelector.java b/src/com/android/camera/one/v2/common/PreviewSizeSelector.java
new file mode 100644
index 0000000..89cc3a1
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/PreviewSizeSelector.java
@@ -0,0 +1,72 @@
+/*
+ * 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.camera.one.v2.common;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+
+import com.android.camera.CaptureModuleUtil;
+import com.android.camera.util.Size;
+
+/**
+ * Picks a preview size.
+ */
+public class PreviewSizeSelector {
+ private final int mImageFormat;
+ private final Size[] mSupportedPreviewSizes;
+
+ public PreviewSizeSelector(int imageFormat, Size[] supportedPreviewSizes) {
+ mImageFormat = imageFormat;
+ mSupportedPreviewSizes = supportedPreviewSizes;
+ }
+
+ public Size pickPreviewSize(Size pictureSize, Context context) {
+ if (pictureSize == null) {
+ // TODO The default should be selected by the caller, and
+ // pictureSize should never be null.
+ pictureSize = getDefaultPictureSize();
+ }
+ float pictureAspectRatio = pictureSize.getWidth() / (float) pictureSize.getHeight();
+
+ // Since devices only have one raw resolution we need to be more
+ // flexible for selecting a matching preview resolution.
+ Double aspectRatioTolerance = mImageFormat == ImageFormat.RAW_SENSOR ? 10d : null;
+ Size size = CaptureModuleUtil.getOptimalPreviewSize(context, mSupportedPreviewSizes,
+ pictureAspectRatio, aspectRatioTolerance);
+ return size;
+ }
+
+ /**
+ * @return The largest supported picture size.
+ */
+ private Size getDefaultPictureSize() {
+ Size[] supportedSizes = mSupportedPreviewSizes;
+
+ // Find the largest supported size.
+ Size largestSupportedSize = supportedSizes[0];
+ long largestSupportedSizePixels =
+ largestSupportedSize.getWidth() * largestSupportedSize.getHeight();
+ for (int i = 1; i < supportedSizes.length; i++) {
+ long numPixels = supportedSizes[i].getWidth() * supportedSizes[i].getHeight();
+ if (numPixels > largestSupportedSizePixels) {
+ largestSupportedSize = supportedSizes[i];
+ largestSupportedSizePixels = numPixels;
+ }
+ }
+ return new Size(largestSupportedSize.getWidth(), largestSupportedSize.getHeight());
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/SensorOrientationProvider.java b/src/com/android/camera/one/v2/common/SensorOrientationProvider.java
new file mode 100644
index 0000000..890aad6
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/SensorOrientationProvider.java
@@ -0,0 +1,45 @@
+/*
+ * 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.camera.one.v2.common;
+
+import android.hardware.camera2.CameraCharacteristics;
+
+import com.android.camera.app.OrientationManager;
+
+public class SensorOrientationProvider {
+ private final CameraCharacteristics mCameraCharacteristics;
+
+ public SensorOrientationProvider(CameraCharacteristics cameraCharacteristics) {
+ mCameraCharacteristics = cameraCharacteristics;
+ }
+
+ public OrientationManager.DeviceOrientation getSensorOrientation() {
+ switch (mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)) {
+ case 0:
+ return OrientationManager.DeviceOrientation.CLOCKWISE_0;
+ case 90:
+ return OrientationManager.DeviceOrientation.CLOCKWISE_90;
+ case 180:
+ return OrientationManager.DeviceOrientation.CLOCKWISE_180;
+ case 270:
+ return OrientationManager.DeviceOrientation.CLOCKWISE_270;
+ default:
+ // Per API documentation, this case should never execute.
+ return OrientationManager.DeviceOrientation.CLOCKWISE_0;
+ }
+ }
+}
diff --git a/src/com/android/camera/one/v2/common/SupportedPreviewSizeProvider.java b/src/com/android/camera/one/v2/common/SupportedPreviewSizeProvider.java
new file mode 100644
index 0000000..78e9d8c
--- /dev/null
+++ b/src/com/android/camera/one/v2/common/SupportedPreviewSizeProvider.java
@@ -0,0 +1,37 @@
+/*
+ * 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.camera.one.v2.common;
+
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.params.StreamConfigurationMap;
+
+import com.android.camera.util.Size;
+
+public class SupportedPreviewSizeProvider {
+ private final CameraCharacteristics mCharacteristics;
+
+ public SupportedPreviewSizeProvider(CameraCharacteristics characteristics) {
+ mCharacteristics = characteristics;
+ }
+
+ public Size[] getSupportedPreviewSizes() {
+ StreamConfigurationMap config = mCharacteristics
+ .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ return Size.convert(config.getOutputSizes(SurfaceTexture.class));
+ }
+}
diff --git a/src/com/android/camera/one/v2/components/AutoFocusMonitor.java b/src/com/android/camera/one/v2/components/AutoFocusMonitor.java
deleted file mode 100644
index 3b47e05..0000000
--- a/src/com/android/camera/one/v2/components/AutoFocusMonitor.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.camera.one.v2.components;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executor;
-
-import android.hardware.camera2.CaptureFailure;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.TotalCaptureResult;
-
-import com.android.camera.one.v2.core.ResponseListener;
-
-/**
- * A {@link com.android.camera.one.v2.core.ResponseListener} which monitors the auto-focus state.
- * TODO Replace listener+executor pattern with generic event listener
- */
-public class AutoFocusMonitor implements ResponseListener {
- private final Executor mListenerExecutor;
- private final List<Listener> mListeners;
- private volatile int mLatestAFState;
- private long mLatestUpdateTimestamp;
-
- /**
- * @param listeners The list of listeners to notify of changes in auto focus state.
- * @param listenerExecutor The executor on which to invoke the listeners.
- */
- public AutoFocusMonitor(List<Listener> listeners, Executor listenerExecutor) {
- mListenerExecutor = listenerExecutor;
- mListeners = new ArrayList<>(listeners);
- mLatestAFState = CaptureResult.CONTROL_AF_STATE_INACTIVE;
- mLatestUpdateTimestamp = -1;
- }
-
- /**
- * @return The most recently-observed auto-focus state. One of
- * {@link CaptureResult}.CONTROL_AF_STATE_*
- */
- public int getLatestAFState() {
- return mLatestAFState;
- }
-
- @Override
- public void onStarted(long timestamp) {
-
- }
-
- @Override
- public void onProgressed(long timestamp, CaptureResult partialResult) {
- if (timestamp > mLatestUpdateTimestamp) {
- final Integer newValue = partialResult.get(CaptureResult.CONTROL_AF_STATE);
- if (newValue != null && newValue != mLatestAFState) {
- mLatestAFState = newValue;
- mLatestUpdateTimestamp = timestamp;
- for (final Listener listener : mListeners) {
- mListenerExecutor.execute(new Runnable() {
- @Override
- public void run() {
- listener.onAutoFocusStateChange(newValue.intValue());
- }
- });
- }
- }
- }
- }
-
- @Override
- public void onCompleted(long timestamp, TotalCaptureResult result) {
-
- }
-
- @Override
- public void onFailed(CaptureFailure failure) {
-
- }
-
- public static interface Listener {
- /**
- * @param newAFState One of {@link CaptureResult}.CONTROL_AF_STATE_*
- */
- public void onAutoFocusStateChange(int newAFState);
- }
-}
diff --git a/src/com/android/camera/one/v2/components/FullAFScanCommand.java b/src/com/android/camera/one/v2/components/FullAFScanCommand.java
deleted file mode 100644
index 20f2477..0000000
--- a/src/com/android/camera/one/v2/components/FullAFScanCommand.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * 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.camera.one.v2.components;
-
-import java.util.Arrays;
-
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-
-import com.android.camera.async.BufferQueue;
-import com.android.camera.one.v2.core.FrameServer;
-import com.android.camera.one.v2.core.MetadataChangeResponseListener;
-import com.android.camera.one.v2.core.RequestBuilder;
-
-/**
- * Performs a full auto focus scan.
- */
-public class FullAFScanCommand implements CameraCommand {
- private final FrameServer mFrameServer;
- private final RequestBuilder.Factory mBuilderFactory;
- private final int mTemplateType;
-
- /**
- * @param frameServer Used for sending requests to the camera.
- * @param builder Used for building requests.
- * @param templateType See
- * {@link android.hardware.camera2.CameraDevice#createCaptureRequest}
- * .
- */
- public FullAFScanCommand(FrameServer frameServer, RequestBuilder.Factory builder, int
- templateType) {
- mFrameServer = frameServer;
- mBuilderFactory = builder;
- mTemplateType = templateType;
- }
-
- /**
- * Performs an auto-focus scan, blocking until the scan starts, runs, and
- * completes.
- */
- public void run() throws InterruptedException, CameraAccessException {
- FrameServer.Session session = mFrameServer.createExclusiveSession();
- try {
- // Build a request to send a repeating AF_IDLE
- RequestBuilder idleBuilder = mBuilderFactory.create(mTemplateType);
- // Listen to AF state changes resulting from this repeating AF_IDLE
- // request
- MetadataChangeResponseListener<Integer> mFocusStateChangeListener = new
- MetadataChangeResponseListener<>(CaptureResult.CONTROL_AF_STATE);
- idleBuilder.addResponseListener(mFocusStateChangeListener);
-
- // Build a request to send a single AF_TRIGGER
- RequestBuilder triggerBuilder = mBuilderFactory.create(mTemplateType);
- triggerBuilder.setParam(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
- triggerBuilder.setParam(CaptureRequest.CONTROL_AF_MODE, CaptureRequest
- .CONTROL_AF_MODE_CONTINUOUS_PICTURE);
- triggerBuilder.setParam(CaptureRequest.CONTROL_AF_TRIGGER,
- CaptureRequest.CONTROL_AF_TRIGGER_START);
-
- // Request a stream of changes to the AF-state
- try (BufferQueue<Integer> afStateBufferQueue =
- mFocusStateChangeListener.getValueStream()) {
- // Submit the repeating request first.
- session.submitRequest(Arrays.asList(idleBuilder.build()), true);
- session.submitRequest(Arrays.asList(triggerBuilder.build()), false);
-
- while (true) {
- try {
- // FIXME The current MetadataChangeResponseListener
- // doesn't actually close the stream, so this may block
- // forever.
- // TODO Using a timeout here would solve this problem.
- Integer newAFState = afStateBufferQueue.getNext();
- if (newAFState == CaptureResult.CONTROL_AF_STATE_INACTIVE ||
- newAFState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
- newAFState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
- return;
- }
- } catch (BufferQueue.BufferQueueClosedException e) {
- // No more CaptureResults are being provided for the
- // repeating request from {@link idleBuilder}. If we get
- // here, it's because capture has been aborted, or a
- // framework error (since we have an exclusive session,
- // nothing else should have submitted repeating requests
- // which would end this one). Either way, we should just
- // return and release control of the session.
- return;
- }
- }
- }
- } finally {
- session.close();
- }
- }
-}
diff --git a/src/com/android/camera/one/v2/core/DecoratingRequestBuilderBuilder.java b/src/com/android/camera/one/v2/core/DecoratingRequestBuilderBuilder.java
new file mode 100644
index 0000000..3399268
--- /dev/null
+++ b/src/com/android/camera/one/v2/core/DecoratingRequestBuilderBuilder.java
@@ -0,0 +1,100 @@
+/*
+ * 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.camera.one.v2.core;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CaptureRequest;
+
+import com.android.camera.async.ConstantPollable;
+import com.android.camera.async.Pollable;
+
+/**
+ * A {@link RequestBuilder.Factory} which allows modifying each
+ * {@link RequestBuilder} that is created. <br>
+ * TODO Write Tests
+ */
+public class DecoratingRequestBuilderBuilder implements RequestBuilder.Factory {
+ private static class Parameter<T> {
+ private final CaptureRequest.Key<T> key;
+ private final Pollable<T> value;
+
+ private Parameter(CaptureRequest.Key<T> key, Pollable<T> value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public void addToBuilder(RequestBuilder builder) {
+ try {
+ builder.setParam(key, value.get());
+ } catch (Pollable.NoValueSetException e) {
+ // If there is no value to set, do nothing.
+ }
+ }
+ }
+
+ private final RequestBuilder.Factory mRequestBuilderFactory;
+ private final Set<ResponseListener> mResponseListeners;
+ private final List<Parameter<?>> mParameters;
+ private final List<CaptureStream> mCaptureStreams;
+
+ public DecoratingRequestBuilderBuilder(RequestBuilder.Factory requestBuilderFactory) {
+ mRequestBuilderFactory = requestBuilderFactory;
+ mResponseListeners = new HashSet<>();
+ mParameters = new ArrayList<>();
+ mCaptureStreams = new ArrayList<>();
+ }
+
+ public <T> DecoratingRequestBuilderBuilder withParam(CaptureRequest.Key<T> key, T value) {
+ return withParam(key, new ConstantPollable<T>(value));
+ }
+
+ public <T> DecoratingRequestBuilderBuilder withParam(CaptureRequest.Key<T> key,
+ Pollable<T> value) {
+ mParameters.add(new Parameter<T>(key, value));
+ return this;
+ }
+
+ public DecoratingRequestBuilderBuilder withResponseListener(ResponseListener listener) {
+ mResponseListeners.add(listener);
+ return this;
+ }
+
+ public DecoratingRequestBuilderBuilder withStream(CaptureStream stream) {
+ mCaptureStreams.add(stream);
+ return this;
+ }
+
+ @Override
+ public RequestBuilder create(int templateType) throws CameraAccessException {
+ RequestBuilder builder = mRequestBuilderFactory.create(templateType);
+ for (Parameter param : mParameters) {
+ param.addToBuilder(builder);
+ }
+ for (ResponseListener listener : mResponseListeners) {
+ builder.addResponseListener(listener);
+ }
+ for (CaptureStream stream : mCaptureStreams) {
+ builder.addStream(stream);
+ }
+ return builder;
+ }
+}
diff --git a/src/com/android/camera/one/v2/core/DecoratingRequestBuilderFactory.java b/src/com/android/camera/one/v2/core/DecoratingRequestBuilderFactory.java
deleted file mode 100644
index e49fe86..0000000
--- a/src/com/android/camera/one/v2/core/DecoratingRequestBuilderFactory.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.camera.one.v2.core;
-
-import java.util.List;
-
-import android.hardware.camera2.CameraAccessException;
-
-/**
- * A {@link RequestBuilder.Factory} which allows modifying each
- * {@link RequestBuilder} that is created.
- */
-public class DecoratingRequestBuilderFactory implements RequestBuilder.Factory {
- private final RequestBuilder.Factory mRequestBuilderFactory;
- private final List<RequestBuilderDecorator> mDecorators;
-
- public DecoratingRequestBuilderFactory(RequestBuilder.Factory requestBuilderFactory,
- List<RequestBuilderDecorator> decorators) {
- mRequestBuilderFactory = requestBuilderFactory;
- mDecorators = decorators;
- }
-
- @Override
- public RequestBuilder create(int templateType) throws CameraAccessException {
- RequestBuilder builder = mRequestBuilderFactory.create(templateType);
- for (RequestBuilderDecorator decorator : mDecorators) {
- decorator.decorateRequest(builder);
- }
- return builder;
- }
-
- public static interface RequestBuilderDecorator {
- public void decorateRequest(RequestBuilder builder);
- }
-}
diff --git a/src/com/android/camera/one/v2/core/FrameExposureResponseListener.java b/src/com/android/camera/one/v2/core/FrameExposureResponseListener.java
new file mode 100644
index 0000000..8d09129
--- /dev/null
+++ b/src/com/android/camera/one/v2/core/FrameExposureResponseListener.java
@@ -0,0 +1,32 @@
+/*
+ * 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.camera.one.v2.core;
+
+import com.android.camera.async.Updatable;
+
+public class FrameExposureResponseListener extends ResponseListener {
+ private final Updatable<Void> mExposureUpdatable;
+
+ public FrameExposureResponseListener(Updatable<Void> exposureUpdatable) {
+ mExposureUpdatable = exposureUpdatable;
+ }
+
+ @Override
+ public void onStarted(long timestamp) {
+ mExposureUpdatable.update(null);
+ }
+}
diff --git a/src/com/android/camera/one/v2/core/FrameServer.java b/src/com/android/camera/one/v2/core/FrameServer.java
index 7bc7131..fd24d5b 100644
--- a/src/com/android/camera/one/v2/core/FrameServer.java
+++ b/src/com/android/camera/one/v2/core/FrameServer.java
@@ -21,6 +21,8 @@
import android.hardware.camera2.CameraAccessException;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
+
/**
* Provides thread-safe concurrent access to a camera.
* <p>
@@ -37,6 +39,14 @@
public static class SessionClosedException extends RuntimeException {
}
+ public static enum RequestType {
+ REPEATING, NON_REPEATING;
+
+ public boolean isRepeating() {
+ return equals(REPEATING);
+ }
+ }
+
/**
* A Session enables submitting multiple Requests for frames.
*/
@@ -61,8 +71,9 @@
* @throws java.lang.InterruptedException if interrupted before the
* request is be submitted.
*/
- public synchronized void submitRequest(List<Request> burstRequests, boolean repeating)
- throws CameraAccessException, InterruptedException {
+ public synchronized void submitRequest(List<Request> burstRequests, RequestType type)
+ throws CameraAccessException, InterruptedException,
+ CameraCaptureSessionClosedException {
try {
if (mClosed) {
throw new SessionClosedException();
@@ -74,7 +85,7 @@
mCameraLock.acquire();
}
try {
- mCaptureSession.submitRequest(burstRequests, repeating);
+ mCaptureSession.submitRequest(burstRequests, type.isRepeating());
} finally {
if (!mExclusive) {
mCameraLock.release();
diff --git a/src/com/android/camera/one/v2/core/FrameworkFailureResponseListener.java b/src/com/android/camera/one/v2/core/FrameworkFailureResponseListener.java
index 59b243a..bacea2a 100644
--- a/src/com/android/camera/one/v2/core/FrameworkFailureResponseListener.java
+++ b/src/com/android/camera/one/v2/core/FrameworkFailureResponseListener.java
@@ -18,43 +18,24 @@
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.TotalCaptureResult;
-import com.android.camera.async.ConcurrentBufferQueue;
-import com.android.camera.async.BufferQueue;
+import com.android.camera.async.Updatable;
/**
- * A {@link ResponseListener} which provides a
- * stream of any CaptureRequests which triggered framework errors.
+ * A {@link ResponseListener} which provides a stream of any CaptureRequests
+ * which triggered framework errors.
*/
-public class FrameworkFailureResponseListener implements ResponseListener {
- private final ConcurrentBufferQueue<CaptureRequest> mFailureStream;
+public class FrameworkFailureResponseListener extends ResponseListener {
+ private final Updatable<CaptureRequest> mFailureStream;
- public FrameworkFailureResponseListener() {
- mFailureStream = new ConcurrentBufferQueue<>();
- }
-
- public BufferQueue getFailureStream() {
- return mFailureStream;
- }
-
- @Override
- public void onStarted(long timestamp) {
- }
-
- @Override
- public void onProgressed(long timestamp, CaptureResult partialResult) {
- }
-
- @Override
- public void onCompleted(long timestamp, TotalCaptureResult result) {
+ public FrameworkFailureResponseListener(Updatable<CaptureRequest> failureStream) {
+ mFailureStream = failureStream;
}
@Override
public void onFailed(CaptureFailure failure) {
if (failure.getReason() == CaptureFailure.REASON_ERROR) {
- mFailureStream.append(failure.getRequest());
+ mFailureStream.update(failure.getRequest());
}
}
}
diff --git a/src/com/android/camera/one/v2/core/MetadataChangeResponseListener.java b/src/com/android/camera/one/v2/core/MetadataChangeResponseListener.java
deleted file mode 100644
index 9c2606d..0000000
--- a/src/com/android/camera/one/v2/core/MetadataChangeResponseListener.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.camera.one.v2.core;
-
-import android.hardware.camera2.CaptureFailure;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.TotalCaptureResult;
-
-import com.android.camera.async.ConcurrentBufferQueue;
-import com.android.camera.async.BufferQueue;
-
-/**
- * A {@link ResponseListener} which listens for changes to a particular metadata
- * key and serializes changes in the value to a blocking queue.
- */
-public class MetadataChangeResponseListener<V> implements ResponseListener {
- private final ConcurrentBufferQueue<V> mNewValues;
- private final CaptureResult.Key<V> mKey;
-
- private long mMostRecentTimestamp = -1;
- private V mMostRecentValue = null;
-
- /**
- * @param key The key associated with the value for which to listen to
- * changes.
- */
- public MetadataChangeResponseListener(CaptureResult.Key<V> key) {
- mNewValues = new ConcurrentBufferQueue<>();
- mKey = key;
- }
-
- public BufferQueue<V> getValueStream() {
- return mNewValues;
- }
-
- /**
- * @return The most recent value, or null if no value has been set yet.
- */
- public V getMostRecentValue() {
- return mMostRecentValue;
- }
-
- @Override
- public void onStarted(long timestamp) {
- }
-
- @Override
- public void onProgressed(long timestamp, CaptureResult partialResult) {
- if (timestamp > mMostRecentTimestamp) {
- V newValue = partialResult.get(mKey);
- if (newValue != null) {
- if (mMostRecentValue != newValue) {
- mNewValues.append(newValue);
- }
- mMostRecentValue = newValue;
- mMostRecentTimestamp = timestamp;
- }
- }
- }
-
- @Override
- public void onCompleted(long timestamp, TotalCaptureResult result) {
- }
-
- @Override
- public void onFailed(CaptureFailure failure) {
- }
-}
diff --git a/src/com/android/camera/one/v2/core/MetadataResponseListener.java b/src/com/android/camera/one/v2/core/MetadataResponseListener.java
new file mode 100644
index 0000000..0b25ef7
--- /dev/null
+++ b/src/com/android/camera/one/v2/core/MetadataResponseListener.java
@@ -0,0 +1,58 @@
+/*
+ * 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.camera.one.v2.core;
+
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+
+import com.android.camera.async.Updatable;
+
+/**
+ * A {@link ResponseListener} which listens for a particular metadata key.
+ */
+public class MetadataResponseListener<V> extends ResponseListener {
+ private final Updatable<V> mUpdatable;
+ private final CaptureResult.Key<V> mKey;
+
+ private long mMostRecentTimestamp = -1;
+ private V mMostRecentValue = null;
+
+ /**
+ * @param key The key associated with the value for which to listen to
+ * changes.
+ */
+ public MetadataResponseListener(CaptureResult.Key<V> key, Updatable<V> updatable) {
+ mKey = key;
+ mUpdatable = updatable;
+ }
+
+ @Override
+ public void onProgressed(CaptureResult partialResult) {
+ V newValue = partialResult.get(mKey);
+ if (newValue != null) {
+ mUpdatable.update(newValue);
+ }
+ }
+
+ @Override
+ public void onCompleted(TotalCaptureResult totalCaptureResult) {
+ V newValue = totalCaptureResult.get(mKey);
+ if (newValue != null) {
+ mUpdatable.update(newValue);
+ }
+ }
+}
diff --git a/src/com/android/camera/one/v2/core/RefCountedImageProxy.java b/src/com/android/camera/one/v2/core/RefCountedImageProxy.java
index 9e5b2f1..80a7795 100644
--- a/src/com/android/camera/one/v2/core/RefCountedImageProxy.java
+++ b/src/com/android/camera/one/v2/core/RefCountedImageProxy.java
@@ -17,13 +17,14 @@
package com.android.camera.one.v2.core;
import com.android.camera.async.RefCountBase;
+import com.android.camera.one.v2.camera2proxy.ForwardingImageProxy;
import com.android.camera.one.v2.camera2proxy.ImageProxy;
/**
* Wraps ImageProxy with reference counting, starting with a fixed number of
* references.
*/
-public class RefCountedImageProxy extends ImageProxy {
+public class RefCountedImageProxy extends ForwardingImageProxy {
private final RefCountBase<ImageProxy> mRefCount;
/**
diff --git a/src/com/android/camera/one/v2/core/RequestBuilder.java b/src/com/android/camera/one/v2/core/RequestBuilder.java
index e56c83e..338fa01 100644
--- a/src/com/android/camera/one/v2/core/RequestBuilder.java
+++ b/src/com/android/camera/one/v2/core/RequestBuilder.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.camera.one.v2.core;
import java.util.ArrayList;
@@ -26,6 +27,7 @@
import android.view.Surface;
import com.android.camera.async.BufferQueue;
+import com.android.camera.async.ConcurrentBufferQueue;
import com.android.camera.one.v2.camera2proxy.CaptureRequestBuilderProxy;
/**
@@ -50,8 +52,8 @@
private final CaptureRequestBuilderProxy mBuilderProxy;
private UnregisteredStreamProvider(CaptureStream captureStream,
- BufferQueue<Long> timestampQueue,
- CaptureRequestBuilderProxy builderProxy) {
+ BufferQueue<Long> timestampQueue,
+ CaptureRequestBuilderProxy builderProxy) {
mCaptureStream = captureStream;
mTimestampQueue = timestampQueue;
mAllocated = new AtomicBoolean(false);
@@ -69,12 +71,17 @@
}
private static class RequestImpl implements Request {
+ private static interface Allocation {
+ public void allocate() throws InterruptedException;
+
+ public void abort();
+ }
private final CaptureRequestBuilderProxy mCaptureRequestBuilder;
private final List<Allocation> mAllocations;
private final ResponseListener mResponseListener;
public RequestImpl(CaptureRequestBuilderProxy builder, List<Allocation> allocations,
- ResponseListener responseListener) {
+ ResponseListener responseListener) {
mCaptureRequestBuilder = builder;
mAllocations = allocations;
mResponseListener = responseListener;
@@ -99,12 +106,6 @@
allocation.abort();
}
}
-
- private static interface Allocation {
- public void allocate() throws InterruptedException;
-
- public void abort();
- }
}
private final CaptureRequestBuilderProxy mBuilder;
@@ -137,6 +138,7 @@
/**
* Sets the given key-value pair.
+ *
* @see {@link CaptureRequest.Builder#set}.
*/
public <T> void setParam(CaptureRequest.Key<T> key, T value) {
@@ -153,10 +155,12 @@
* @param captureStream
*/
public void addStream(CaptureStream captureStream) {
- TimestampResponseListener timestampResponseListener = new TimestampResponseListener();
+ ConcurrentBufferQueue<Long> timestamps = new ConcurrentBufferQueue<>();
+ TimestampResponseListener timestampResponseListener = new TimestampResponseListener(
+ timestamps);
mAllocations.add(new UnregisteredStreamProvider(captureStream,
- timestampResponseListener.getTimestamps(), mBuilder));
+ timestamps, mBuilder));
mResponseListeners.add(timestampResponseListener);
}
diff --git a/src/com/android/camera/one/v2/core/ResponseListener.java b/src/com/android/camera/one/v2/core/ResponseListener.java
index 20ce850..a1034b6 100644
--- a/src/com/android/camera/one/v2/core/ResponseListener.java
+++ b/src/com/android/camera/one/v2/core/ResponseListener.java
@@ -24,7 +24,7 @@
* Like {@link android.hardware.camera2.CameraCaptureSession.CaptureCallback},
* but for events related to single requests.
*/
-public interface ResponseListener {
+public abstract class ResponseListener {
/**
* Note that this is typically invoked on the camera thread and at high
* frequency, so implementations must execute quickly and not make
@@ -32,7 +32,8 @@
*
* @See {@link android.hardware.camera2.CameraCaptureSession.CaptureCallback#onCaptureStarted}
*/
- public void onStarted(long timestamp);
+ public void onStarted(long timestamp) {
+ }
/**
* Note that this is typically invoked on the camera thread and at high
@@ -41,7 +42,8 @@
*
* @See {@link android.hardware.camera2.CameraCaptureSession.CaptureCallback#onCaptureProgressed}
*/
- public void onProgressed(long timestamp, CaptureResult partialResult);
+ public void onProgressed(CaptureResult partialResult) {
+ }
/**
* Note that this is typically invoked on the camera thread and at high
@@ -50,7 +52,8 @@
*
* @See {@link android.hardware.camera2.CameraCaptureSession.CaptureCallback#onCaptureCompleted}
*/
- public void onCompleted(long timestamp, TotalCaptureResult result);
+ public void onCompleted(TotalCaptureResult result) {
+ }
/**
* Note that this is typically invoked on the camera thread and at high
@@ -59,5 +62,6 @@
*
* @See {@link android.hardware.camera2.CameraCaptureSession.CaptureCallback#onCaptureFailed}
*/
- public void onFailed(CaptureFailure failure);
+ public void onFailed(CaptureFailure failure) {
+ }
}
diff --git a/src/com/android/camera/one/v2/core/ResponseListenerBroadcaster.java b/src/com/android/camera/one/v2/core/ResponseListenerBroadcaster.java
index baca7ec..5acc62b 100644
--- a/src/com/android/camera/one/v2/core/ResponseListenerBroadcaster.java
+++ b/src/com/android/camera/one/v2/core/ResponseListenerBroadcaster.java
@@ -27,7 +27,7 @@
* Combines multiple {@link ResponseListener}s into a single one which
* dispatches to all listeners for each callback.
*/
-public class ResponseListenerBroadcaster implements ResponseListener {
+public class ResponseListenerBroadcaster extends ResponseListener {
private final List<ResponseListener> mListeners;
public ResponseListenerBroadcaster(List<ResponseListener> listeners) {
@@ -42,16 +42,16 @@
}
@Override
- public void onProgressed(long timestamp, CaptureResult partialResult) {
+ public void onProgressed(CaptureResult partialResult) {
for (ResponseListener listener : mListeners) {
- listener.onProgressed(timestamp, partialResult);
+ listener.onProgressed(partialResult);
}
}
@Override
- public void onCompleted(long timestamp, TotalCaptureResult result) {
+ public void onCompleted(TotalCaptureResult result) {
for (ResponseListener listener : mListeners) {
- listener.onCompleted(timestamp, result);
+ listener.onCompleted(result);
}
}
diff --git a/src/com/android/camera/one/v2/core/SingleCloseImageProxy.java b/src/com/android/camera/one/v2/core/SingleCloseImageProxy.java
index af968e7..7ac9f7f 100644
--- a/src/com/android/camera/one/v2/core/SingleCloseImageProxy.java
+++ b/src/com/android/camera/one/v2/core/SingleCloseImageProxy.java
@@ -19,12 +19,13 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import com.android.camera.one.v2.camera2proxy.ForwardingImageProxy;
import com.android.camera.one.v2.camera2proxy.ImageProxy;
/**
* Wraps {@link ImageProxy} to filter out multiple calls to {@link #close}.
*/
-public class SingleCloseImageProxy extends ImageProxy {
+public class SingleCloseImageProxy extends ForwardingImageProxy {
private final AtomicBoolean mClosed;
/**
diff --git a/src/com/android/camera/one/v2/core/TagDispatchCaptureSession.java b/src/com/android/camera/one/v2/core/TagDispatchCaptureSession.java
index c568f96..36d4783 100644
--- a/src/com/android/camera/one/v2/core/TagDispatchCaptureSession.java
+++ b/src/com/android/camera/one/v2/core/TagDispatchCaptureSession.java
@@ -22,13 +22,14 @@
import java.util.Map;
import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.os.Handler;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionClosedException;
+import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionProxy;
import com.android.camera.one.v2.camera2proxy.CaptureRequestBuilderProxy;
/**
@@ -36,9 +37,11 @@
* {@link Request}s and dispatches to the appropriate {@link ResponseListener}
* on a per-request basis, instead of for every {@link CaptureRequest} submitted
* at the same time.
+ * <p/>
+ * TODO Write Tests
*/
public class TagDispatchCaptureSession {
- private static class CaptureCallback extends CameraCaptureSession.CaptureCallback {
+ private static class CaptureCallback implements CameraCaptureSessionProxy.CaptureCallback {
private final Map<Object, ResponseListener> mListeners;
/**
@@ -50,52 +53,51 @@
}
@Override
- public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
+ public void onCaptureStarted(CameraCaptureSessionProxy session, CaptureRequest request,
long timestamp, long frameNumber) {
Object tag = request.getTag();
mListeners.get(tag).onStarted(timestamp);
}
@Override
- public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
+ public void onCaptureProgressed(CameraCaptureSessionProxy session, CaptureRequest request,
CaptureResult partialResult) {
Object tag = request.getTag();
- long timestamp = partialResult.get(CaptureResult.SENSOR_TIMESTAMP);
- mListeners.get(tag).onProgressed(timestamp, partialResult);
+ mListeners.get(tag).onProgressed(partialResult);
}
@Override
- public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+ public void onCaptureCompleted(CameraCaptureSessionProxy session, CaptureRequest request,
TotalCaptureResult result) {
Object tag = request.getTag();
- long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
- mListeners.get(tag).onCompleted(timestamp, result);
+ mListeners.get(tag).onCompleted(result);
}
@Override
- public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
+ public void onCaptureFailed(CameraCaptureSessionProxy session, CaptureRequest request,
CaptureFailure failure) {
Object tag = request.getTag();
mListeners.get(tag).onFailed(failure);
}
@Override
- public void onCaptureSequenceAborted(CameraCaptureSession session, int sequenceId) {
+ public void onCaptureSequenceAborted(CameraCaptureSessionProxy session, int sequenceId) {
// Ignored
}
@Override
- public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId,
+ public void onCaptureSequenceCompleted(CameraCaptureSessionProxy session, int sequenceId,
long frameNumber) {
// Ignored
}
}
- private final CameraCaptureSession mCaptureSession;
+ private final CameraCaptureSessionProxy mCaptureSession;
private final Handler mCameraHandler;
private long mTagCounter;
- public TagDispatchCaptureSession(CameraCaptureSession captureSession, Handler cameraHandler) {
+ public TagDispatchCaptureSession(CameraCaptureSessionProxy captureSession, Handler
+ cameraHandler) {
mCaptureSession = captureSession;
mCameraHandler = cameraHandler;
mTagCounter = 0;
@@ -109,7 +111,7 @@
/**
* Submits the given burst request to the underlying
- * {@link CameraCaptureSession}.
+ * {@link CameraCaptureSessionProxy}.
* <p/>
* Note that the Tag associated with the {@link CaptureRequest} from each
* {@link Request} will be overwritten.
@@ -125,7 +127,7 @@
* resources necessary for each {@link Request}.
*/
public void submitRequest(List<Request> burstRequests, boolean repeating) throws
- CameraAccessException, InterruptedException {
+ CameraAccessException, InterruptedException, CameraCaptureSessionClosedException {
try {
Map<Object, ResponseListener> tagListenerMap = new HashMap<Object, ResponseListener>();
List<CaptureRequest> captureRequests = new ArrayList<>(burstRequests.size());
diff --git a/src/com/android/camera/one/v2/core/TimestampResponseListener.java b/src/com/android/camera/one/v2/core/TimestampResponseListener.java
index a9c5683..039ed73 100644
--- a/src/com/android/camera/one/v2/core/TimestampResponseListener.java
+++ b/src/com/android/camera/one/v2/core/TimestampResponseListener.java
@@ -22,35 +22,20 @@
import com.android.camera.async.ConcurrentBufferQueue;
import com.android.camera.async.BufferQueue;
+import com.android.camera.async.Updatable;
/**
* A {@link ResponseListener} which provides a stream of timestamps.
*/
-public class TimestampResponseListener implements ResponseListener {
- private final ConcurrentBufferQueue<Long> mTimestamps;
+public class TimestampResponseListener extends ResponseListener {
+ private final Updatable<Long> mTimestamps;
- public TimestampResponseListener() {
- mTimestamps = new ConcurrentBufferQueue<>();
- }
-
- public BufferQueue<Long> getTimestamps() {
- return mTimestamps;
+ public TimestampResponseListener(Updatable<Long> timestamps) {
+ mTimestamps = timestamps;
}
@Override
public void onStarted(long timestamp) {
- mTimestamps.append(timestamp);
- }
-
- @Override
- public void onProgressed(long timestamp, CaptureResult partialResult) {
- }
-
- @Override
- public void onCompleted(long timestamp, TotalCaptureResult result) {
- }
-
- @Override
- public void onFailed(CaptureFailure failure) {
+ mTimestamps.update(timestamp);
}
}
diff --git a/src/com/android/camera/one/v2/core/TotalCaptureResultResponseListener.java b/src/com/android/camera/one/v2/core/TotalCaptureResultResponseListener.java
index f6be60d..46b1c38 100644
--- a/src/com/android/camera/one/v2/core/TotalCaptureResultResponseListener.java
+++ b/src/com/android/camera/one/v2/core/TotalCaptureResultResponseListener.java
@@ -22,36 +22,21 @@
import com.android.camera.async.ConcurrentBufferQueue;
import com.android.camera.async.BufferQueue;
+import com.android.camera.async.Updatable;
/**
* A {@link ResponseListener} which provides a stream of
* {@link TotalCaptureResult}s.
*/
-public class TotalCaptureResultResponseListener implements ResponseListener {
- private final ConcurrentBufferQueue<TotalCaptureResult> mResults;
+public class TotalCaptureResultResponseListener extends ResponseListener {
+ private final Updatable<TotalCaptureResult> mResults;
- public TotalCaptureResultResponseListener() {
- mResults = new ConcurrentBufferQueue<>();
- }
-
- public BufferQueue<TotalCaptureResult> getResult() {
- return mResults;
+ public TotalCaptureResultResponseListener(Updatable<TotalCaptureResult> results) {
+ mResults = results;
}
@Override
- public void onStarted(long timestamp) {
- }
-
- @Override
- public void onProgressed(long timestamp, CaptureResult partialResult) {
- }
-
- @Override
- public void onCompleted(long timestamp, TotalCaptureResult result) {
- mResults.append(result);
- }
-
- @Override
- public void onFailed(CaptureFailure failure) {
+ public void onCompleted(TotalCaptureResult result) {
+ mResults.update(result);
}
}
diff --git a/src/com/android/camera/one/v2/core/TriggeredAFScanStateResponseListener.java b/src/com/android/camera/one/v2/core/TriggeredAFScanStateResponseListener.java
new file mode 100644
index 0000000..f65238f
--- /dev/null
+++ b/src/com/android/camera/one/v2/core/TriggeredAFScanStateResponseListener.java
@@ -0,0 +1,87 @@
+/*
+ * 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.camera.one.v2.core;
+
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+
+import com.android.camera.async.Updatable;
+
+/**
+ * Maintains the current state of auto-focus scans resulting from explicit
+ * trigger requests. This maintains the subset of the finite state machine of
+ * {@link android.hardware.camera2.CaptureResult#CONTROL_AF_STATE} which relates
+ * to AF_TRIGGER.
+ */
+public class TriggeredAFScanStateResponseListener extends ResponseListener {
+ private final Updatable<Void> mScanCompleteUpdatable;
+ private boolean mTriggered;
+ // If mCurrentState is SCANNING, then this is the frame number of the
+ // trigger start.
+ private long mTriggerFrameNumber;
+
+ /**
+ * @param scanCompleteUpdatable The {@link Updatable} to be notified when an
+ * AF scan has been completed.
+ */
+ public TriggeredAFScanStateResponseListener(Updatable<Void> scanCompleteUpdatable) {
+ mScanCompleteUpdatable = scanCompleteUpdatable;
+ mTriggered = false;
+ }
+
+ @Override
+ public void onProgressed(CaptureResult result) {
+ processUpdate(
+ result.getRequest().get(CaptureRequest.CONTROL_AF_TRIGGER),
+ result.get(CaptureResult.CONTROL_AF_STATE),
+ result.getFrameNumber());
+ }
+
+ @Override
+ public void onCompleted(TotalCaptureResult result) {
+ processUpdate(
+ result.getRequest().get(CaptureRequest.CONTROL_AF_TRIGGER),
+ result.get(CaptureResult.CONTROL_AF_STATE),
+ result.getFrameNumber());
+ }
+
+ private void processUpdate(Integer afTrigger, Integer afState, long frameNumber) {
+ if (!mTriggered) {
+ if (afTrigger != null) {
+ if (afTrigger == CaptureRequest.CONTROL_AF_TRIGGER_START) {
+ mTriggered = true;
+ mTriggerFrameNumber = frameNumber;
+ }
+ }
+ } else {
+ // Only process results in-order. That is, only transition from
+ // SCANNING to IDLE if a result for a frame *after* the trigger
+ // indicates that the AF system has stopped scanning.
+ if (frameNumber > mTriggerFrameNumber) {
+ if (afState != null) {
+ if (afState == CaptureResult.CONTROL_AF_STATE_INACTIVE ||
+ afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
+ afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
+ mTriggered = false;
+ mScanCompleteUpdatable.update(null);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/camera/one/v2/sharedimagereader/BoundedImageBufferQueue.java b/src/com/android/camera/one/v2/sharedimagereader/BoundedImageBufferQueue.java
index ce1e893..ca090cc 100644
--- a/src/com/android/camera/one/v2/sharedimagereader/BoundedImageBufferQueue.java
+++ b/src/com/android/camera/one/v2/sharedimagereader/BoundedImageBufferQueue.java
@@ -23,6 +23,7 @@
import com.android.camera.async.BoundedBufferQueue;
import com.android.camera.async.ConcurrentBufferQueue;
import com.android.camera.async.BufferQueueController;
+import com.android.camera.one.v2.camera2proxy.ForwardingImageProxy;
import com.android.camera.one.v2.camera2proxy.ImageProxy;
/**
@@ -66,7 +67,7 @@
* An {@link ImageProxy} which overrides close() to release the logical
* ticket associated with the image.
*/
- private class TicketReleasingImageProxy extends ImageProxy {
+ private class TicketReleasingImageProxy extends ForwardingImageProxy {
private final AtomicBoolean mClosed;
public TicketReleasingImageProxy(ImageProxy image) {
@@ -187,7 +188,7 @@
}
@Override
- public void append(ImageProxy image) {
+ public void update(ImageProxy image) {
synchronized (mLock) {
if (mClosed) {
image.close();
@@ -200,7 +201,7 @@
}
mTicketsAvailable--;
- mImageSequence.append(new TicketReleasingImageProxy(image));
+ mImageSequence.update(new TicketReleasingImageProxy(image));
}
}
diff --git a/src/com/android/camera/one/v2/sharedimagereader/ImageDistributor.java b/src/com/android/camera/one/v2/sharedimagereader/ImageDistributor.java
index 6ce1908..01ba3f9 100644
--- a/src/com/android/camera/one/v2/sharedimagereader/ImageDistributor.java
+++ b/src/com/android/camera/one/v2/sharedimagereader/ImageDistributor.java
@@ -109,7 +109,6 @@
while (mGlobalTimestampBufferQueue.getNext() <= timestamp) {
}
} catch (InterruptedException e) {
- e.printStackTrace();
image.close();
return;
} catch (BufferQueue.BufferQueueClosedException e) {
@@ -151,7 +150,7 @@
// before the underlying reference count is decremented, regardless
// of how many times it is closed from each stream.
ImageProxy singleCloseImage = new SingleCloseImageProxy(sharedImage);
- outputStream.append(singleCloseImage);
+ outputStream.update(singleCloseImage);
}
}
diff --git a/src/com/android/camera/one/v2/sharedimagereader/ImageDistributorOnImageAvailableListener.java b/src/com/android/camera/one/v2/sharedimagereader/ImageDistributorOnImageAvailableListener.java
new file mode 100644
index 0000000..0d39967
--- /dev/null
+++ b/src/com/android/camera/one/v2/sharedimagereader/ImageDistributorOnImageAvailableListener.java
@@ -0,0 +1,39 @@
+/*
+ * 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.camera.one.v2.sharedimagereader;
+
+import android.media.ImageReader;
+
+import com.android.camera.one.v2.camera2proxy.AndroidImageProxy;
+
+/**
+ * Connects an {@link ImageReader} to an {@link ImageDistributor} with a new
+ * thread to handle image availability callbacks.
+ */
+public class ImageDistributorOnImageAvailableListener implements
+ ImageReader.OnImageAvailableListener {
+ private final ImageDistributor mImageDistributor;
+
+ public ImageDistributorOnImageAvailableListener(ImageDistributor imageDistributor) {
+ mImageDistributor = imageDistributor;
+ }
+
+ @Override
+ public void onImageAvailable(ImageReader imageReader) {
+ mImageDistributor.distributeImage(new AndroidImageProxy(imageReader.acquireNextImage()));
+ }
+}
diff --git a/src/com/android/camera/one/v2/sharedimagereader/SharedImageReader.java b/src/com/android/camera/one/v2/sharedimagereader/SharedImageReader.java
index fe340d4..0249790 100644
--- a/src/com/android/camera/one/v2/sharedimagereader/SharedImageReader.java
+++ b/src/com/android/camera/one/v2/sharedimagereader/SharedImageReader.java
@@ -24,6 +24,7 @@
import android.media.ImageReader;
import android.view.Surface;
+import com.android.camera.async.BlockingCloseable;
import com.android.camera.async.RefCountedBufferQueueController;
import com.android.camera.async.BufferQueue;
import com.android.camera.one.v2.camera2proxy.ImageProxy;
@@ -33,7 +34,7 @@
* Enables the creation of multiple logical image streams, each with their own
* guaranteed capacity, over a single ImageReader.
*/
-public class SharedImageReader implements AutoCloseable {
+public class SharedImageReader implements BlockingCloseable {
public class ImageCaptureBufferQueue implements CaptureStream, BufferQueue<ImageProxy> {
private final int mCapacity;
private final BoundedImageBufferQueue mImageStream;
diff --git a/src/com/android/camera/one/v2/components/ImageSaver.java b/src/com/android/camera/util/ScopedFactory.java
similarity index 66%
rename from src/com/android/camera/one/v2/components/ImageSaver.java
rename to src/com/android/camera/util/ScopedFactory.java
index 7138029..a113f75 100644
--- a/src/com/android/camera/one/v2/components/ImageSaver.java
+++ b/src/com/android/camera/util/ScopedFactory.java
@@ -13,17 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-package com.android.camera.one.v2.components;
-
-import com.android.camera.one.v2.camera2proxy.ImageProxy;
+package com.android.camera.util;
/**
- * Interface for an image-saving object.
+ * A factory which returns a TResult when TScope is available.
*/
-public interface ImageSaver {
- /**
- * Implementations should save the image to disk and close it.
- */
- public void saveAndCloseImage(ImageProxy image);
+public interface ScopedFactory<TScope, TResult> {
+ public TResult get(TScope scope);
}