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 c51574ea..2ee40437 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 @@ -1,5 +1,7 @@ package com.otaliastudios.cameraview.engine.action; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCaptureSession.CaptureCallback; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; @@ -8,22 +10,70 @@ import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; +/** + * The Action class encapsulates logic for completing an action in a Camera2 environment. + * In this case, we are often interested in constantly receiving the {@link CaptureResult} + * and {@link CaptureRequest} callbacks, as well as applying changes to a {@link CaptureRequest.Builder} + * and having them applied to the sensor. + * + * The Action class receives the given callbacks and can operate over the engine + * through the {@link ActionHolder} object. + * + * Each Action operates on a given state in a given moment. This base class offers the + * {@link #STATE_COMPLETED} state which is common to all actions. + * + * See {@link BaseAction} for a base implementation. + */ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) public interface Action { int STATE_COMPLETED = Integer.MAX_VALUE; + /** + * Returns the current state. + * @return the state + */ int getState(); + /** + * Starts this action. + * @param holder the holder + */ void start(@NonNull ActionHolder holder); + /** + * Adds an {@link ActionCallback} to receive state + * change events. + * @param callback a callback + */ void addCallback(@NonNull ActionCallback callback); + /** + * Removes a previously added callback. + * @param callback a callback + */ void removeCallback(@NonNull ActionCallback callback); + /** + * Called from {@link CaptureCallback#onCaptureStarted(CameraCaptureSession, CaptureRequest, long, long)}. + * @param holder the holder + * @param request the request + */ void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request); + /** + * Called from {@link CaptureCallback#onCaptureProgressed(CameraCaptureSession, CaptureRequest, CaptureResult)}. + * @param holder the holder + * @param request the request + * @param result the result + */ void onCaptureProgressed(@NonNull ActionHolder holder, @NonNull CaptureRequest request, @NonNull CaptureResult result); + /** + * Called from {@link CaptureCallback#onCaptureCompleted(CameraCaptureSession, CaptureRequest, TotalCaptureResult)}. + * @param holder the holder + * @param request the request + * @param result the 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 index 087f0b0a..6d327cb7 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionCallback.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionCallback.java @@ -5,7 +5,19 @@ import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; +/** + * A callback for {@link Action} state changes. + * See the action class. + * + * See also {@link CompletionCallback}. + */ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) public interface ActionCallback { + + /** + * Action state has just changed. + * @param action action + * @param state new state + */ 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 index f23c1910..1673d10a 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionHolder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/ActionHolder.java @@ -10,23 +10,72 @@ import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; +/** + * The holder of {@link Action}. + * + * This class should keep a list or set of currently running actions, and offers + * to them the base Camera2 objects that are needed to apply changes. + * + * This class, or an holder of it, should also forward the capture callbacks + * to all {@link Action}s. See {@link com.otaliastudios.cameraview.engine.Camera2Engine} for + * our implementation. + */ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) public interface ActionHolder { + /** + * Adds a new action + * @param action action + */ void addAction(@NonNull Action action); + /** + * Removes a previously added action + * @param action action + */ void removeAction(@NonNull Action action); + /** + * Returns the {@link CameraCharacteristics} of the current + * camera device. + * @param action action + * @return characteristics + */ @NonNull CameraCharacteristics getCharacteristics(@NonNull Action action); + /** + * Returns the latest {@link TotalCaptureResult}. Can be used + * by actions to start querying the state before receiving their + * first frame. + * @param action action + * @return last result + */ @NonNull TotalCaptureResult getLastResult(@NonNull Action action); + /** + * Returns the current {@link CaptureRequest.Builder} so that + * actions can apply changes to it and later submit them. + * @param action action + * @return the builder + */ @NonNull CaptureRequest.Builder getBuilder(@NonNull Action action); + /** + * Applies the current builder (as per {@link #getBuilder(Action)}) + * as a repeating request on the preview. + * @param source action + */ void applyBuilder(@NonNull Action source); + /** + * Applies the given builder as a single capture request. + * Callers can catch the exception and choose what to do. + * @param source action + * @param builder builder + * @throws CameraAccessException camera exception + */ void applyBuilder(@NonNull Action source, @NonNull CaptureRequest.Builder builder) throws CameraAccessException; } 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 e6e1c030..e3d92a54 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 @@ -8,9 +8,17 @@ import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; +/** + * A simple wrapper around a {@link BaseAction}. + * This can be used to add functionality around a base action. + */ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) public abstract class ActionWrapper extends BaseAction { + /** + * Should return the wrapped action. + * @return the wrapped action + */ @NonNull public abstract BaseAction getAction(); 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 e052a60f..f85a0973 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 @@ -11,14 +11,31 @@ import androidx.annotation.RequiresApi; import java.util.Arrays; import java.util.List; +/** + * Utilities for creating {@link Action} sequences. + */ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 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 + */ @NonNull public static BaseAction together(@NonNull BaseAction... actions) { return new TogetherAction(Arrays.asList(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 + */ @NonNull public static BaseAction sequence(@NonNull BaseAction... actions) { return new SequenceAction(Arrays.asList(actions)); 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 025f1850..814abcd0 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 @@ -11,6 +11,17 @@ import androidx.annotation.RequiresApi; import java.util.ArrayList; import java.util.List; +/** + * The base implementation of {@link Action} that should always be subclassed, + * instead of implementing the root interface itself. + * + * It holds a list of callbacks and dispatches events to them, plus it cares about + * its own lifecycle: + * - when {@link #start(ActionHolder)} is called, we add ourselves to the holder list + * - when {@link #STATE_COMPLETED} is reached, we remove ouverselves from the holder list + * + * This is very important in all cases. + */ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) public abstract class BaseAction implements Action { @@ -30,6 +41,11 @@ public abstract class BaseAction implements Action { onStart(holder); } + /** + * Action was started and will soon receive events from the + * holder stream. + * @param holder holder + */ protected void onStart(@NonNull ActionHolder holder) { // Overrideable } @@ -49,6 +65,11 @@ public abstract class BaseAction implements Action { // Overrideable } + /** + * Called by subclasses to notify of their state. If state is {@link #STATE_COMPLETED}, + * this removes this action from the holder. + * @param newState new state + */ protected void setState(int newState) { if (newState != state) { state = newState; @@ -61,10 +82,19 @@ public abstract class BaseAction implements Action { } } + /** + * Whether this action has reached the completed state. + * @return true if completed + */ + @SuppressWarnings("WeakerAccess") public boolean isCompleted() { return state == STATE_COMPLETED; } + /** + * Returns the holder. + * @return the holder + */ @NonNull protected ActionHolder getHolder() { return holder; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/CompletionCallback.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/CompletionCallback.java index a5ffab12..5029693b 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/CompletionCallback.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/action/CompletionCallback.java @@ -5,16 +5,23 @@ import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; +/** + * A special {@link ActionCallback} that just checks for the + * completed state. Handy as an inner anonymous class. + */ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) public abstract class CompletionCallback implements ActionCallback { @Override - public void onActionStateChanged(@NonNull Action action, int state) { + public final void onActionStateChanged(@NonNull Action action, int state) { if (state == Action.STATE_COMPLETED) { onActionCompleted(action); } } - @SuppressWarnings("WeakerAccess") + /** + * The given action has just reached the completed state. + * @param action action + */ protected abstract void onActionCompleted(@NonNull Action action); } 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 0fd91493..0e88d02e 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 @@ -7,6 +7,10 @@ import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; +/** + * An {@link Action} that wraps another, and forces the completion + * after the given timeout in milliseconds is reached. + */ @RequiresApi(Build.VERSION_CODES.LOLLIPOP) public class TimeoutAction extends ActionWrapper { diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/AutoExposureLock.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/ExposureLock.java similarity index 96% rename from cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/AutoExposureLock.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/ExposureLock.java index a4b7ba56..4e221280 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/AutoExposureLock.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/ExposureLock.java @@ -13,9 +13,9 @@ import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.engine.action.ActionHolder; @RequiresApi(Build.VERSION_CODES.LOLLIPOP) -public class AutoExposureLock extends BaseLock { +public class ExposureLock extends BaseLock { - private final static String TAG = AutoExposureLock.class.getSimpleName(); + private final static String TAG = ExposureLock.class.getSimpleName(); private final static CameraLogger LOG = CameraLogger.create(TAG); @Override diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/AutoFocusLock.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/FocusLock.java similarity index 96% rename from cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/AutoFocusLock.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/FocusLock.java index 02747063..adfa4d2f 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/AutoFocusLock.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/FocusLock.java @@ -13,9 +13,9 @@ import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.engine.action.ActionHolder; @RequiresApi(Build.VERSION_CODES.LOLLIPOP) -public class AutoFocusLock extends BaseLock { +public class FocusLock extends BaseLock { - private final static String TAG = AutoFocusLock.class.getSimpleName(); + private final static String TAG = FocusLock.class.getSimpleName(); private final static CameraLogger LOG = CameraLogger.create(TAG); @Override diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/LockAction.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/LockAction.java index dbf35e1f..10aec763 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/LockAction.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/LockAction.java @@ -13,9 +13,9 @@ import com.otaliastudios.cameraview.engine.action.BaseAction; public class LockAction extends ActionWrapper { private final BaseAction action = Actions.together( - new AutoExposureLock(), - new AutoFocusLock(), - new AutoWhiteBalanceLock() + new ExposureLock(), + new FocusLock(), + new WhiteBalanceLock() ); @NonNull diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/AutoWhiteBalanceLock.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/WhiteBalanceLock.java similarity index 94% rename from cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/AutoWhiteBalanceLock.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/WhiteBalanceLock.java index 4acd2d92..b49c2ab1 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/AutoWhiteBalanceLock.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/lock/WhiteBalanceLock.java @@ -13,9 +13,9 @@ import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.engine.action.ActionHolder; @RequiresApi(Build.VERSION_CODES.LOLLIPOP) -public class AutoWhiteBalanceLock extends BaseLock { +public class WhiteBalanceLock extends BaseLock { - private final static String TAG = AutoWhiteBalanceLock.class.getSimpleName(); + private final static String TAG = WhiteBalanceLock.class.getSimpleName(); private final static CameraLogger LOG = CameraLogger.create(TAG); @Override