From 71179b1fdc7f6feebbdf138050702ca5f479efc2 Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Wed, 4 Sep 2019 17:19:32 +0200 Subject: [PATCH] Add action.abort() --- .../cameraview/engine/Camera2Engine.java | 7 +- .../cameraview/engine/action/Action.java | 6 + .../engine/action/ActionWrapper.java | 6 + .../cameraview/engine/action/Actions.java | 143 ++---------------- .../cameraview/engine/action/BaseAction.java | 19 +++ .../engine/action/SequenceAction.java | 94 ++++++++++++ .../engine/action/TimeoutAction.java | 7 +- .../engine/action/TogetherAction.java | 84 ++++++++++ .../picture/Snapshot2PictureRecorder.java | 3 +- .../cameraview/video/Full2VideoRecorder.java | 4 +- 10 files changed, 232 insertions(+), 141 deletions(-) create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/SequenceAction.java create mode 100644 cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/TogetherAction.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 6d2c45ee..b1ce5276 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -46,7 +46,6 @@ import com.otaliastudios.cameraview.engine.action.Action; import com.otaliastudios.cameraview.engine.action.ActionHolder; import com.otaliastudios.cameraview.engine.action.Actions; import com.otaliastudios.cameraview.engine.action.CompletionCallback; -import com.otaliastudios.cameraview.engine.action.TimeoutAction; import com.otaliastudios.cameraview.engine.lock.UnlockAction; import com.otaliastudios.cameraview.engine.mappers.Camera2Mapper; import com.otaliastudios.cameraview.engine.meter.MeterAction; @@ -637,7 +636,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv protected void onTakePictureSnapshot(@NonNull final PictureResult.Stub stub, @NonNull final AspectRatio outputRatio, boolean doMetering) { if (doMetering) { LOG.i("onTakePictureSnapshot:", "doMetering is true. Delaying."); - Action action = new TimeoutAction(METER_TIMEOUT, createMeterAction(null)); + Action action = Actions.timeout(METER_TIMEOUT, createMeterAction(null)); action.addCallback(new CompletionCallback() { @Override protected void onActionCompleted(@NonNull Action action) { @@ -665,7 +664,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv protected void onTakePicture(@NonNull final PictureResult.Stub stub, boolean doMetering) { if (doMetering) { LOG.i("onTakePicture:", "doMetering is true. Delaying."); - Action action = new TimeoutAction(METER_TIMEOUT, createMeterAction(null)); + Action action = Actions.timeout(METER_TIMEOUT, createMeterAction(null)); action.addCallback(new CompletionCallback() { @Override protected void onActionCompleted(@NonNull Action action) { @@ -1238,7 +1237,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv // Create the meter and start. mCallback.dispatchOnFocusStart(gesture, point); final MeterAction action = createMeterAction(point); - final TimeoutAction wrapper = new TimeoutAction(METER_TIMEOUT, action); + Action wrapper = Actions.timeout(METER_TIMEOUT, action); wrapper.start(Camera2Engine.this); wrapper.addCallback(new CompletionCallback() { @Override 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 index 2ee40437..ba4a00de 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Action.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Action.java @@ -41,6 +41,12 @@ public interface Action { */ void start(@NonNull ActionHolder holder); + /** + * Aborts this action. + * @param holder the holder + */ + void abort(@NonNull ActionHolder holder); + /** * Adds an {@link ActionCallback} to receive state * change events. diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionWrapper.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionWrapper.java index e3d92a54..799cf31e 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionWrapper.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionWrapper.java @@ -37,6 +37,12 @@ public abstract class ActionWrapper extends BaseAction { getAction().onStart(holder); } + @Override + protected void onAbort(@NonNull ActionHolder holder) { + super.onAbort(holder); + getAction().onAbort(holder); + } + @Override public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) { super.onCaptureStarted(holder, request); 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 index bd88f694..533af79b 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Actions.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Actions.java @@ -1,16 +1,11 @@ package com.otaliastudios.cameraview.engine.action; -import android.hardware.camera2.CaptureRequest; -import android.hardware.camera2.CaptureResult; -import android.hardware.camera2.TotalCaptureResult; import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; /** * Utilities for creating {@link Action} sequences. @@ -22,6 +17,7 @@ public class Actions { * Creates a {@link BaseAction} that executes all the child actions * together, at the same time, and completes once all of them are * completed. + * * @param actions input actions * @return a new action */ @@ -34,6 +30,7 @@ public class Actions { * Creates a {@link BaseAction} that executes all the child actions * in sequence, waiting for the first to complete, then going on with * the second and so on, finally completing when all are completed. + * * @param actions input actions * @return a new action */ @@ -42,130 +39,18 @@ public class 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 final List runningActions; - - private TogetherAction(@NonNull final List actions) { - this.actions = new ArrayList<>(actions); - this.runningActions = new ArrayList<>(actions); - for (BaseAction action : actions) { - action.addCallback(new ActionCallback() { - @Override - public void onActionStateChanged(@NonNull Action action, int state) { - if (state == STATE_COMPLETED) { - //noinspection SuspiciousMethodCalls - runningActions.remove(action); - } - if (runningActions.isEmpty()) { - setState(STATE_COMPLETED); - } - } - }); - } - } - - @Override - protected void onStart(@NonNull ActionHolder holder) { - super.onStart(holder); - for (BaseAction action : actions) { - if (!action.isCompleted()) action.onStart(holder); - } - } - - @Override - public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) { - super.onCaptureStarted(holder, request); - for (BaseAction action : actions) { - if (!action.isCompleted()) 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) { - if (!action.isCompleted()) 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) { - if (!action.isCompleted()) action.onCaptureCompleted(holder, request, result); - } - } + /** + * Creates a {@link BaseAction} that completes as normal, but is also + * forced to complete if the given timeout is reached, by calling + * {@link Action#abort(ActionHolder)}. + * + * @param timeoutMillis timeout in milliseconds + * @param action action + * @return a new action + */ + @NonNull + public static BaseAction timeout(long timeoutMillis, @NonNull BaseAction action) { + return new TimeoutAction(timeoutMillis, action); } - 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() { - boolean first = runningAction == -1; - boolean last = runningAction == actions.size() - 1; - if (last) { - // 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(); - } - } - }); - if (!first) { - actions.get(runningAction).onStart(getHolder()); - } - } - } - - @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 index bae42217..f744d38a 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/BaseAction.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/BaseAction.java @@ -42,6 +42,15 @@ public abstract class BaseAction implements Action { onStart(holder); } + @Override + public final void abort(@NonNull ActionHolder holder) { + holder.removeAction(this); + if (!isCompleted()) { + onAbort(holder); + setState(STATE_COMPLETED); + } + } + /** * Action was started and will soon receive events from the * holder stream. @@ -53,6 +62,16 @@ public abstract class BaseAction implements Action { // Overrideable } + /** + * Action was aborted and will not receive events from the + * holder stream anymore. It will soon be marked as completed. + * @param holder holder + */ + @SuppressWarnings({"WeakerAccess", "unused"}) + protected void onAbort(@NonNull ActionHolder holder) { + // Overrideable + } + @Override public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) { // Overrideable diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/SequenceAction.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/SequenceAction.java new file mode 100644 index 00000000..4453877a --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/SequenceAction.java @@ -0,0 +1,94 @@ +package com.otaliastudios.cameraview.engine.action; + +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.os.Build; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import java.util.List; + +/** + * Executes a list of actions in sequence, completing once + * the last of them has been completed. + */ +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +class SequenceAction extends BaseAction { + // Need to be BaseAction so we can call onStart() instead of start() + private final List actions; + private int runningAction = -1; + + SequenceAction(@NonNull List actions) { + this.actions = actions; + increaseRunningAction(); + } + + private void increaseRunningAction() { + boolean first = runningAction == -1; + boolean last = runningAction == actions.size() - 1; + if (last) { + // 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(); + } + } + }); + if (!first) { + actions.get(runningAction).onStart(getHolder()); + } + } + } + + @Override + protected void onStart(@NonNull ActionHolder holder) { + super.onStart(holder); + if (runningAction >= 0) { + actions.get(runningAction).onStart(holder); + } + } + + @Override + protected void onAbort(@NonNull ActionHolder holder) { + super.onAbort(holder); + if (runningAction >= 0) { + // Previous actions have been completed already. + // Future actions will never start. So this is OK. + actions.get(runningAction).onAbort(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/TimeoutAction.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/TimeoutAction.java index 0e88d02e..c1e7ed55 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/TimeoutAction.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/TimeoutAction.java @@ -12,13 +12,13 @@ import androidx.annotation.RequiresApi; * after the given timeout in milliseconds is reached. */ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) -public class TimeoutAction extends ActionWrapper { +class TimeoutAction extends ActionWrapper { private long startMillis; private long timeoutMillis; private BaseAction action; - public TimeoutAction(long timeoutMillis, @NonNull BaseAction action) { + TimeoutAction(long timeoutMillis, @NonNull BaseAction action) { this.timeoutMillis = timeoutMillis; this.action = action; } @@ -42,7 +42,8 @@ public class TimeoutAction extends ActionWrapper { super.onCaptureCompleted(holder, request, result); if (!isCompleted()) { if (System.currentTimeMillis() > startMillis + timeoutMillis) { - setState(STATE_COMPLETED); + // This will set our state to COMPLETED and stop requests. + getAction().abort(holder); } } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/TogetherAction.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/TogetherAction.java new file mode 100644 index 00000000..a21da923 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/TogetherAction.java @@ -0,0 +1,84 @@ +package com.otaliastudios.cameraview.engine.action; + +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.os.Build; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import java.util.ArrayList; +import java.util.List; + +/** + * Performs a list of actions together, completing + * once all of them have completed. + */ +@RequiresApi(Build.VERSION_CODES.LOLLIPOP) +class TogetherAction extends BaseAction { + // Need to be BaseAction so we can call onStart() instead of start() + private final List actions; + private final List runningActions; + + TogetherAction(@NonNull final List actions) { + this.actions = new ArrayList<>(actions); + this.runningActions = new ArrayList<>(actions); + for (BaseAction action : actions) { + action.addCallback(new ActionCallback() { + @Override + public void onActionStateChanged(@NonNull Action action, int state) { + if (state == STATE_COMPLETED) { + //noinspection SuspiciousMethodCalls + runningActions.remove(action); + } + if (runningActions.isEmpty()) { + setState(STATE_COMPLETED); + } + } + }); + } + } + + @Override + protected void onStart(@NonNull ActionHolder holder) { + super.onStart(holder); + for (BaseAction action : actions) { + if (!action.isCompleted()) action.onStart(holder); + } + } + + @Override + protected void onAbort(@NonNull ActionHolder holder) { + super.onAbort(holder); + for (BaseAction action : actions) { + if (!action.isCompleted()) action.onAbort(holder); + } + } + + @Override + public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) { + super.onCaptureStarted(holder, request); + for (BaseAction action : actions) { + if (!action.isCompleted()) 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) { + if (!action.isCompleted()) 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) { + if (!action.isCompleted()) action.onCaptureCompleted(holder, request, result); + } + } +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot2PictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot2PictureRecorder.java index 1ae0229a..a9653945 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot2PictureRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot2PictureRecorder.java @@ -18,7 +18,6 @@ import com.otaliastudios.cameraview.engine.action.ActionHolder; import com.otaliastudios.cameraview.engine.action.Actions; import com.otaliastudios.cameraview.engine.action.BaseAction; import com.otaliastudios.cameraview.engine.action.CompletionCallback; -import com.otaliastudios.cameraview.engine.action.TimeoutAction; import com.otaliastudios.cameraview.engine.lock.LockAction; import com.otaliastudios.cameraview.preview.GlCameraPreview; import com.otaliastudios.cameraview.size.AspectRatio; @@ -91,7 +90,7 @@ public class Snapshot2PictureRecorder extends SnapshotGlPictureRecorder { mHolder = engine; mAction = Actions.sequence( - new TimeoutAction(LOCK_TIMEOUT, new LockAction()), + Actions.timeout(LOCK_TIMEOUT, new LockAction()), new FlashAction()); mAction.addCallback(new CompletionCallback() { @Override diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/video/Full2VideoRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/video/Full2VideoRecorder.java index 6c9306a2..8ef1f65a 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/video/Full2VideoRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/video/Full2VideoRecorder.java @@ -34,12 +34,10 @@ public class Full2VideoRecorder extends FullVideoRecorder { private static final CameraLogger LOG = CameraLogger.create(TAG); private ActionHolder mHolder; - private boolean mGotFirstFrame; private final String mCameraId; private Surface mInputSurface; - public Full2VideoRecorder(@NonNull Camera2Engine engine, - @NonNull String cameraId) { + public Full2VideoRecorder(@NonNull Camera2Engine engine, @NonNull String cameraId) { super(engine); mHolder = engine; mCameraId = cameraId;