From f48c6bd5b7b5a526faf3a3362c7504efca87abac Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Tue, 3 Sep 2019 22:23:58 +0200 Subject: [PATCH] Create action package --- .../cameraview/engine/Camera2Engine.java | 57 ++++++- .../cameraview/engine/action/Action.java | 27 ++++ .../engine/action/ActionCallback.java | 7 + .../engine/action/ActionHolder.java | 26 ++++ .../cameraview/engine/action/Actions.java | 143 ++++++++++++++++++ .../cameraview/engine/action/BaseAction.java | 73 +++++++++ 6 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Action.java create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionCallback.java create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionHolder.java create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Actions.java create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/BaseAction.java diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java index 552860ad..311348d1 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -42,6 +42,8 @@ import com.otaliastudios.cameraview.controls.Flash; import com.otaliastudios.cameraview.controls.Hdr; import com.otaliastudios.cameraview.controls.Mode; import com.otaliastudios.cameraview.controls.WhiteBalance; +import com.otaliastudios.cameraview.engine.action.Action; +import com.otaliastudios.cameraview.engine.action.ActionHolder; import com.otaliastudios.cameraview.engine.mappers.Camera2Mapper; import com.otaliastudios.cameraview.engine.offset.Axis; import com.otaliastudios.cameraview.engine.offset.Reference; @@ -68,6 +70,7 @@ import java.util.concurrent.atomic.AtomicBoolean; @RequiresApi(Build.VERSION_CODES.LOLLIPOP) public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAvailableListener, + ActionHolder, Meter.Callback, Locker.Callback { @@ -84,7 +87,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv private CameraCaptureSession mSession; private CaptureRequest.Builder mRepeatingRequestBuilder; private CameraCaptureSession.CaptureCallback mRepeatingRequestCallback; - private CaptureResult mLastRepeatingResult; + private TotalCaptureResult mLastRepeatingResult; private final Camera2Mapper mMapper = Camera2Mapper.get(); // Frame processing @@ -103,6 +106,9 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv private ImageReader mPictureReader; private final boolean mPictureCaptureStopsPreview = false; // can make configurable at some point + // Actions + private final List mActions = new ArrayList<>(); + // 3A metering private Meter mMeter; private Gesture mMeteringGesture; @@ -242,6 +248,9 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv if (mPictureRecorder instanceof Full2PictureRecorder) { ((Full2PictureRecorder) mPictureRecorder).onCaptureStarted(request); } + for (Action action : mActions) { + action.onCaptureStarted(Camera2Engine.this, request); + } } @Override @@ -253,6 +262,9 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv if (mLocker != null && mLocker.isLocking()) { mLocker.onCapture(partialResult); } + for (Action action : mActions) { + action.onCaptureProgressed(Camera2Engine.this, request, partialResult); + } } @Override @@ -271,6 +283,9 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv if (mLocker != null && mLocker.isLocking()) { mLocker.onCapture(result); } + for (Action action : mActions) { + action.onCaptureProgressed(Camera2Engine.this, request, result); + } Integer aeMode = result.get(CaptureResult.CONTROL_AE_MODE); Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); @@ -597,6 +612,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv @Override protected Task onStopPreview() { LOG.i("onStopPreview:", "About to clean up."); + // TODO clear actions? if (mVideoRecorder != null) { // This should synchronously call onVideoResult that will reset the repeating builder // to the PREVIEW template. This is very important. @@ -1412,4 +1428,43 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv } //endregion + + //region Actions + + @Override + public void addAction(@NonNull Action action) { + if (!mActions.contains(action)) { + mActions.add(action); + } + } + + @Override + public void removeAction(@NonNull Action action) { + mActions.remove(action); + } + + @NonNull + @Override + public CameraCharacteristics getCharacteristics(@NonNull Action action) { + return mCameraCharacteristics; + } + + @NonNull + @Override + public TotalCaptureResult getLastResult(@NonNull Action action) { + return mLastRepeatingResult; + } + + @NonNull + @Override + public CaptureRequest.Builder getBuilder(@NonNull Action action) { + return mRepeatingRequestBuilder; + } + + @Override + public void applyBuilder(@NonNull Action source) { + applyRepeatingRequestBuilder(); + } + + //endregion } \ No newline at end of file diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Action.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Action.java new file mode 100644 index 00000000..af69ad15 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Action.java @@ -0,0 +1,27 @@ +package com.otaliastudios.cameraview.engine.action; + +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; + +import androidx.annotation.NonNull; + +public interface Action { + + int STATE_CREATED = Integer.MIN_VALUE; + int STATE_COMPLETED = Integer.MAX_VALUE; + + int getState(); + + void start(@NonNull ActionHolder holder); + + void addCallback(@NonNull ActionCallback callback); + + void removeCallback(@NonNull ActionCallback callback); + + void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request); + + void onCaptureProgressed(@NonNull ActionHolder holder, @NonNull CaptureRequest request, @NonNull CaptureResult result); + + void onCaptureCompleted(@NonNull ActionHolder holder, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result); +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionCallback.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionCallback.java new file mode 100644 index 00000000..e28cc748 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionCallback.java @@ -0,0 +1,7 @@ +package com.otaliastudios.cameraview.engine.action; + +import androidx.annotation.NonNull; + +public interface ActionCallback { + void onActionStateChanged(@NonNull Action action, int state); +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionHolder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionHolder.java new file mode 100644 index 00000000..ee2ca2c2 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionHolder.java @@ -0,0 +1,26 @@ +package com.otaliastudios.cameraview.engine.action; + +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; + +import androidx.annotation.NonNull; + +public interface ActionHolder { + + void addAction(@NonNull Action action); + + void removeAction(@NonNull Action action); + + @NonNull + CameraCharacteristics getCharacteristics(@NonNull Action action); + + @NonNull + TotalCaptureResult getLastResult(@NonNull Action action); + + @NonNull + CaptureRequest.Builder getBuilder(@NonNull Action action); + + void applyBuilder(@NonNull Action source); +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Actions.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Actions.java new file mode 100644 index 00000000..cf81bf89 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Actions.java @@ -0,0 +1,143 @@ +package com.otaliastudios.cameraview.engine.action; + +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; + +import androidx.annotation.NonNull; + +import java.util.Arrays; +import java.util.List; + +public class Actions { + + @NonNull + public static Action together(@NonNull BaseAction... actions) { + return new TogetherAction(Arrays.asList(actions)); + } + + @NonNull + public static Action sequence(@NonNull BaseAction... actions) { + return new SequenceAction(Arrays.asList(actions)); + } + + private static class TogetherAction extends BaseAction { + // Need to be BaseAction so we can call onStart() instead of start() + private final List actions; + + private TogetherAction(@NonNull final List actions) { + this.actions = actions; + for (BaseAction action : actions) { + action.addCallback(new ActionCallback() { + @Override + public void onActionStateChanged(@NonNull Action action, int state) { + if (state == STATE_COMPLETED) { + //noinspection SuspiciousMethodCalls + actions.remove(action); + } + if (actions.isEmpty()) { + setState(STATE_COMPLETED); + } + } + }); + } + } + + @Override + protected void onStart(@NonNull ActionHolder holder) { + super.onStart(holder); + for (BaseAction action : actions) { + action.onStart(holder); + } + } + + @Override + public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) { + super.onCaptureStarted(holder, request); + for (BaseAction action : actions) { + action.onCaptureStarted(holder, request); + } + } + + @Override + public void onCaptureProgressed(@NonNull ActionHolder holder, @NonNull CaptureRequest request, + @NonNull CaptureResult result) { + super.onCaptureProgressed(holder, request, result); + for (BaseAction action : actions) { + action.onCaptureProgressed(holder, request, result); + } + } + + @Override + public void onCaptureCompleted(@NonNull ActionHolder holder, @NonNull CaptureRequest request, + @NonNull TotalCaptureResult result) { + super.onCaptureCompleted(holder, request, result); + for (BaseAction action : actions) { + action.onCaptureCompleted(holder, request, result); + } + } + } + + private static class SequenceAction extends BaseAction { + // Need to be BaseAction so we can call onStart() instead of start() + private final List actions; + private int runningAction = -1; + + private SequenceAction(@NonNull List actions) { + this.actions = actions; + increaseRunningAction(); + } + + private void increaseRunningAction() { + if (runningAction == actions.size() - 1) { + // This was the last action. We're done. + setState(STATE_COMPLETED); + } else { + runningAction++; + actions.get(runningAction).addCallback(new ActionCallback() { + @Override + public void onActionStateChanged(@NonNull Action action, int state) { + if (state == STATE_COMPLETED) { + action.removeCallback(this); + increaseRunningAction(); + } + } + }); + } + } + + @Override + protected void onStart(@NonNull ActionHolder holder) { + super.onStart(holder); + if (runningAction >= 0) { + actions.get(runningAction).onStart(holder); + } + } + + @Override + public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) { + super.onCaptureStarted(holder, request); + if (runningAction >= 0) { + actions.get(runningAction).onCaptureStarted(holder, request); + } + } + + @Override + public void onCaptureProgressed(@NonNull ActionHolder holder, @NonNull CaptureRequest request, + @NonNull CaptureResult result) { + super.onCaptureProgressed(holder, request, result); + if (runningAction >= 0) { + actions.get(runningAction).onCaptureProgressed(holder, request, result); + } + } + + @Override + public void onCaptureCompleted(@NonNull ActionHolder holder, @NonNull CaptureRequest request, + @NonNull TotalCaptureResult result) { + super.onCaptureCompleted(holder, request, result); + if (runningAction >= 0) { + actions.get(runningAction).onCaptureCompleted(holder, request, result); + } + } + } +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/BaseAction.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/BaseAction.java new file mode 100644 index 00000000..2e475a71 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/BaseAction.java @@ -0,0 +1,73 @@ +package com.otaliastudios.cameraview.engine.action; + +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +public abstract class BaseAction implements Action { + + private final List callbacks = new ArrayList<>(); + private int state; + private ActionHolder holder; + + @Override + public int getState() { + return state; + } + + @Override + public final void start(@NonNull ActionHolder holder) { + this.holder = holder; + holder.addAction(this); + onStart(holder); + } + + protected void onStart(@NonNull ActionHolder holder) { + // Overrideable + } + + @Override + public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) { + // Overrideable + } + + @Override + public void onCaptureProgressed(@NonNull ActionHolder holder, @NonNull CaptureRequest request, @NonNull CaptureResult result) { + // Overrideable + } + + @Override + public void onCaptureCompleted(@NonNull ActionHolder holder, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { + // Overrideable + } + + protected void setState(int newState) { + if (newState != state) { + state = newState; + for (ActionCallback callback : callbacks) { + callback.onActionStateChanged(this, state); + } + if (state == STATE_COMPLETED) { + holder.removeAction(this); + } + } + } + + @Override + public void addCallback(@NonNull ActionCallback callback) { + if (!callbacks.contains(callback)) { + callbacks.add(callback); + callback.onActionStateChanged(this, getState()); + } + } + + @Override + public void removeCallback(@NonNull ActionCallback callback) { + callbacks.remove(callback); + } +}