Add action.abort()

pull/580/head
Mattia Iavarone 6 years ago
parent a9f6e50ac4
commit 71179b1fdc
  1. 7
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java
  2. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Action.java
  3. 6
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionWrapper.java
  4. 143
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/Actions.java
  5. 19
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/BaseAction.java
  6. 94
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/SequenceAction.java
  7. 7
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/TimeoutAction.java
  8. 84
      cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/TogetherAction.java
  9. 3
      cameraview/src/main/java/com/otaliastudios/cameraview/picture/Snapshot2PictureRecorder.java
  10. 4
      cameraview/src/main/java/com/otaliastudios/cameraview/video/Full2VideoRecorder.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.ActionHolder;
import com.otaliastudios.cameraview.engine.action.Actions; import com.otaliastudios.cameraview.engine.action.Actions;
import com.otaliastudios.cameraview.engine.action.CompletionCallback; 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.lock.UnlockAction;
import com.otaliastudios.cameraview.engine.mappers.Camera2Mapper; import com.otaliastudios.cameraview.engine.mappers.Camera2Mapper;
import com.otaliastudios.cameraview.engine.meter.MeterAction; 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) { protected void onTakePictureSnapshot(@NonNull final PictureResult.Stub stub, @NonNull final AspectRatio outputRatio, boolean doMetering) {
if (doMetering) { if (doMetering) {
LOG.i("onTakePictureSnapshot:", "doMetering is true. Delaying."); 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() { action.addCallback(new CompletionCallback() {
@Override @Override
protected void onActionCompleted(@NonNull Action action) { 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) { protected void onTakePicture(@NonNull final PictureResult.Stub stub, boolean doMetering) {
if (doMetering) { if (doMetering) {
LOG.i("onTakePicture:", "doMetering is true. Delaying."); 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() { action.addCallback(new CompletionCallback() {
@Override @Override
protected void onActionCompleted(@NonNull Action action) { protected void onActionCompleted(@NonNull Action action) {
@ -1238,7 +1237,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv
// Create the meter and start. // Create the meter and start.
mCallback.dispatchOnFocusStart(gesture, point); mCallback.dispatchOnFocusStart(gesture, point);
final MeterAction action = createMeterAction(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.start(Camera2Engine.this);
wrapper.addCallback(new CompletionCallback() { wrapper.addCallback(new CompletionCallback() {
@Override @Override

@ -41,6 +41,12 @@ public interface Action {
*/ */
void start(@NonNull ActionHolder holder); void start(@NonNull ActionHolder holder);
/**
* Aborts this action.
* @param holder the holder
*/
void abort(@NonNull ActionHolder holder);
/** /**
* Adds an {@link ActionCallback} to receive state * Adds an {@link ActionCallback} to receive state
* change events. * change events.

@ -37,6 +37,12 @@ public abstract class ActionWrapper extends BaseAction {
getAction().onStart(holder); getAction().onStart(holder);
} }
@Override
protected void onAbort(@NonNull ActionHolder holder) {
super.onAbort(holder);
getAction().onAbort(holder);
}
@Override @Override
public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) { public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) {
super.onCaptureStarted(holder, request); super.onCaptureStarted(holder, request);

@ -1,16 +1,11 @@
package com.otaliastudios.cameraview.engine.action; 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 android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
/** /**
* Utilities for creating {@link Action} sequences. * Utilities for creating {@link Action} sequences.
@ -22,6 +17,7 @@ public class Actions {
* Creates a {@link BaseAction} that executes all the child actions * Creates a {@link BaseAction} that executes all the child actions
* together, at the same time, and completes once all of them are * together, at the same time, and completes once all of them are
* completed. * completed.
*
* @param actions input actions * @param actions input actions
* @return a new action * @return a new action
*/ */
@ -34,6 +30,7 @@ public class Actions {
* Creates a {@link BaseAction} that executes all the child actions * Creates a {@link BaseAction} that executes all the child actions
* in sequence, waiting for the first to complete, then going on with * in sequence, waiting for the first to complete, then going on with
* the second and so on, finally completing when all are completed. * the second and so on, finally completing when all are completed.
*
* @param actions input actions * @param actions input actions
* @return a new action * @return a new action
*/ */
@ -42,130 +39,18 @@ public class Actions {
return new SequenceAction(Arrays.asList(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() * Creates a {@link BaseAction} that completes as normal, but is also
private final List<BaseAction> actions; * forced to complete if the given timeout is reached, by calling
private final List<BaseAction> runningActions; * {@link Action#abort(ActionHolder)}.
*
private TogetherAction(@NonNull final List<BaseAction> actions) { * @param timeoutMillis timeout in milliseconds
this.actions = new ArrayList<>(actions); * @param action action
this.runningActions = new ArrayList<>(actions); * @return a new action
for (BaseAction action : actions) { */
action.addCallback(new ActionCallback() { @NonNull
@Override public static BaseAction timeout(long timeoutMillis, @NonNull BaseAction action) {
public void onActionStateChanged(@NonNull Action action, int state) { return new TimeoutAction(timeoutMillis, action);
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);
}
}
}
private static class SequenceAction extends BaseAction {
// Need to be BaseAction so we can call onStart() instead of start()
private final List<BaseAction> actions;
private int runningAction = -1;
private SequenceAction(@NonNull List<BaseAction> 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);
}
}
}
} }

@ -42,6 +42,15 @@ public abstract class BaseAction implements Action {
onStart(holder); 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 * Action was started and will soon receive events from the
* holder stream. * holder stream.
@ -53,6 +62,16 @@ public abstract class BaseAction implements Action {
// Overrideable // 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 @Override
public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) { public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) {
// Overrideable // Overrideable

@ -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<BaseAction> actions;
private int runningAction = -1;
SequenceAction(@NonNull List<BaseAction> 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);
}
}
}

@ -12,13 +12,13 @@ import androidx.annotation.RequiresApi;
* after the given timeout in milliseconds is reached. * after the given timeout in milliseconds is reached.
*/ */
@RequiresApi(Build.VERSION_CODES.LOLLIPOP) @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public class TimeoutAction extends ActionWrapper { class TimeoutAction extends ActionWrapper {
private long startMillis; private long startMillis;
private long timeoutMillis; private long timeoutMillis;
private BaseAction action; private BaseAction action;
public TimeoutAction(long timeoutMillis, @NonNull BaseAction action) { TimeoutAction(long timeoutMillis, @NonNull BaseAction action) {
this.timeoutMillis = timeoutMillis; this.timeoutMillis = timeoutMillis;
this.action = action; this.action = action;
} }
@ -42,7 +42,8 @@ public class TimeoutAction extends ActionWrapper {
super.onCaptureCompleted(holder, request, result); super.onCaptureCompleted(holder, request, result);
if (!isCompleted()) { if (!isCompleted()) {
if (System.currentTimeMillis() > startMillis + timeoutMillis) { if (System.currentTimeMillis() > startMillis + timeoutMillis) {
setState(STATE_COMPLETED); // This will set our state to COMPLETED and stop requests.
getAction().abort(holder);
} }
} }
} }

@ -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<BaseAction> actions;
private final List<BaseAction> runningActions;
TogetherAction(@NonNull final List<BaseAction> 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);
}
}
}

@ -18,7 +18,6 @@ import com.otaliastudios.cameraview.engine.action.ActionHolder;
import com.otaliastudios.cameraview.engine.action.Actions; import com.otaliastudios.cameraview.engine.action.Actions;
import com.otaliastudios.cameraview.engine.action.BaseAction; import com.otaliastudios.cameraview.engine.action.BaseAction;
import com.otaliastudios.cameraview.engine.action.CompletionCallback; 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.engine.lock.LockAction;
import com.otaliastudios.cameraview.preview.GlCameraPreview; import com.otaliastudios.cameraview.preview.GlCameraPreview;
import com.otaliastudios.cameraview.size.AspectRatio; import com.otaliastudios.cameraview.size.AspectRatio;
@ -91,7 +90,7 @@ public class Snapshot2PictureRecorder extends SnapshotGlPictureRecorder {
mHolder = engine; mHolder = engine;
mAction = Actions.sequence( mAction = Actions.sequence(
new TimeoutAction(LOCK_TIMEOUT, new LockAction()), Actions.timeout(LOCK_TIMEOUT, new LockAction()),
new FlashAction()); new FlashAction());
mAction.addCallback(new CompletionCallback() { mAction.addCallback(new CompletionCallback() {
@Override @Override

@ -34,12 +34,10 @@ public class Full2VideoRecorder extends FullVideoRecorder {
private static final CameraLogger LOG = CameraLogger.create(TAG); private static final CameraLogger LOG = CameraLogger.create(TAG);
private ActionHolder mHolder; private ActionHolder mHolder;
private boolean mGotFirstFrame;
private final String mCameraId; private final String mCameraId;
private Surface mInputSurface; private Surface mInputSurface;
public Full2VideoRecorder(@NonNull Camera2Engine engine, public Full2VideoRecorder(@NonNull Camera2Engine engine, @NonNull String cameraId) {
@NonNull String cameraId) {
super(engine); super(engine);
mHolder = engine; mHolder = engine;
mCameraId = cameraId; mCameraId = cameraId;

Loading…
Cancel
Save