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 e111b1e4..6d2c45ee 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -69,6 +69,7 @@ import com.otaliastudios.cameraview.video.Full2VideoRecorder; import com.otaliastudios.cameraview.video.SnapshotVideoRecorder; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -270,7 +271,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv @NonNull TotalCaptureResult result) { mLastRepeatingResult = result; for (Action action : mActions) { - action.onCaptureProgressed(Camera2Engine.this, request, result); + action.onCaptureCompleted(Camera2Engine.this, request, result); } } }; @@ -1287,15 +1288,29 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv //region Actions @Override - public void addAction(@NonNull Action action) { - if (!mActions.contains(action)) { - mActions.add(action); - } + public void addAction(final @NonNull Action action) { + // This is likely to be called during a Capture callback while we iterate + // on the actions list, or worse, from other threads. We must use mHandler.post. + mHandler.post(new Runnable() { + @Override + public void run() { + if (!mActions.contains(action)) { + mActions.add(action); + } + } + }); } @Override - public void removeAction(@NonNull Action action) { - mActions.remove(action); + public void removeAction(final @NonNull Action action) { + // This is likely to be called during a Capture callback while we iterate + // on the actions list, or worse, from other threads. We must use mHandler.post. + mHandler.post(new Runnable() { + @Override + public void run() { + mActions.remove(action); + } + }); } @NonNull 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 f6ba9838..bd88f694 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 @@ -8,6 +8,7 @@ import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -44,18 +45,20 @@ public class 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 = 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 - actions.remove(action); + runningActions.remove(action); } - if (actions.isEmpty()) { + if (runningActions.isEmpty()) { setState(STATE_COMPLETED); } } @@ -67,7 +70,7 @@ public class Actions { protected void onStart(@NonNull ActionHolder holder) { super.onStart(holder); for (BaseAction action : actions) { - action.onStart(holder); + if (!action.isCompleted()) action.onStart(holder); } } @@ -75,7 +78,7 @@ public class Actions { public void onCaptureStarted(@NonNull ActionHolder holder, @NonNull CaptureRequest request) { super.onCaptureStarted(holder, request); for (BaseAction action : actions) { - action.onCaptureStarted(holder, request); + if (!action.isCompleted()) action.onCaptureStarted(holder, request); } } @@ -84,7 +87,7 @@ public class Actions { @NonNull CaptureResult result) { super.onCaptureProgressed(holder, request, result); for (BaseAction action : actions) { - action.onCaptureProgressed(holder, request, result); + if (!action.isCompleted()) action.onCaptureProgressed(holder, request, result); } } @@ -93,7 +96,7 @@ public class Actions { @NonNull TotalCaptureResult result) { super.onCaptureCompleted(holder, request, result); for (BaseAction action : actions) { - action.onCaptureCompleted(holder, request, result); + if (!action.isCompleted()) action.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 7c13e60d..bae42217 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 @@ -6,6 +6,7 @@ import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.os.Build; +import androidx.annotation.CallSuper; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; @@ -37,7 +38,6 @@ public abstract class BaseAction implements Action { @Override public final void start(@NonNull ActionHolder holder) { - this.holder = holder; holder.addAction(this); onStart(holder); } @@ -47,7 +47,9 @@ public abstract class BaseAction implements Action { * holder stream. * @param holder holder */ + @CallSuper protected void onStart(@NonNull ActionHolder holder) { + this.holder = holder; // must be here // Overrideable } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/BaseMeter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/BaseMeter.java index f6814191..d0c5154d 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/BaseMeter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/BaseMeter.java @@ -6,6 +6,7 @@ import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; +import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.engine.action.ActionHolder; import com.otaliastudios.cameraview.engine.action.BaseAction; @@ -14,6 +15,9 @@ import java.util.List; @RequiresApi(Build.VERSION_CODES.LOLLIPOP) public abstract class BaseMeter extends BaseAction { + private final static String TAG = BaseMeter.class.getSimpleName(); + private final static CameraLogger LOG = CameraLogger.create(TAG); + private final List areas; private boolean isSuccessful; private boolean skipIfPossible; @@ -30,8 +34,10 @@ public abstract class BaseMeter extends BaseAction { boolean isSkipped = skipIfPossible && checkShouldSkip(holder); boolean isSupported = checkIsSupported(holder); if (isSupported && !isSkipped) { + LOG.i("onStart:", "supported and not skipped. Dispatching onStarted."); onStarted(holder, areas); } else { + LOG.i("onStart:", "not supported or skipped. Dispatching COMPLETED state."); setSuccessful(true); setState(STATE_COMPLETED); } @@ -49,6 +55,7 @@ public abstract class BaseMeter extends BaseAction { isSuccessful = successful; } + @SuppressWarnings("WeakerAccess") public boolean isSuccessful() { return isSuccessful; } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/ExposureMeter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/ExposureMeter.java index df1da34b..37b626d7 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/ExposureMeter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/ExposureMeter.java @@ -57,6 +57,8 @@ public class ExposureMeter extends BaseMeter { @Override protected void onStarted(@NonNull ActionHolder holder, @NonNull List areas) { + LOG.i("onStarted:", "with areas:", areas); + // Launch the precapture trigger. holder.getBuilder(this).set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/FocusMeter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/FocusMeter.java index 7dfc7974..a3e72e7c 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/FocusMeter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/FocusMeter.java @@ -50,6 +50,7 @@ public class FocusMeter extends BaseMeter { @Override protected void onStarted(@NonNull ActionHolder holder, @NonNull List areas) { + LOG.i("onStarted:", "with areas:", areas); holder.getBuilder(this).set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); int maxRegions = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AF, 0); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/MeterAction.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/MeterAction.java index d06a0014..cc8f0ff4 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/MeterAction.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/MeterAction.java @@ -4,6 +4,7 @@ import android.graphics.PointF; import android.graphics.Rect; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.MeteringRectangle; import android.os.Build; @@ -11,6 +12,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; +import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.engine.CameraEngine; import com.otaliastudios.cameraview.engine.action.ActionHolder; import com.otaliastudios.cameraview.engine.action.ActionWrapper; @@ -28,6 +30,9 @@ import java.util.List; @RequiresApi(Build.VERSION_CODES.LOLLIPOP) public class MeterAction extends ActionWrapper { + private final static String TAG = MeterAction.class.getSimpleName(); + private final static CameraLogger LOG = CameraLogger.create(TAG); + private List meters; private BaseAction action; private ActionHolder holder; @@ -56,15 +61,19 @@ public class MeterAction extends ActionWrapper { public boolean isSuccessful() { for (BaseMeter meter : meters) { if (!meter.isSuccessful()) { + LOG.i("isSuccessful:", "returning false."); return false; } } + LOG.i("isSuccessful:", "returning true."); return true; } @Override protected void onStart(@NonNull ActionHolder holder) { + LOG.w("onStart:", "initializing."); initialize(holder); + LOG.w("onStart:", "initialized."); super.onStart(holder); } @@ -208,8 +217,10 @@ public class MeterAction extends ActionWrapper { referencePoint.x += cropRect == null ? 0 : cropRect.left; referencePoint.y += cropRect == null ? 0 : cropRect.top; // Finally, get the active rect width and height from characteristics. - Rect activeRect = readCharacteristic(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE, - new Rect(0, 0, referenceSize.getWidth(), referenceSize.getHeight())); + Rect activeRect = holder.getCharacteristics(this).get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); + if (activeRect == null) { // Should never happen + activeRect = new Rect(0, 0, referenceSize.getWidth(), referenceSize.getHeight()); + } return new Size(activeRect.width(), activeRect.height()); } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/WhiteBalanceMeter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/WhiteBalanceMeter.java index c2158a7e..4828838d 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/WhiteBalanceMeter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/WhiteBalanceMeter.java @@ -45,6 +45,7 @@ public class WhiteBalanceMeter extends BaseMeter { @Override protected void onStarted(@NonNull ActionHolder holder, @NonNull List areas) { + LOG.i("onStarted:", "with areas:", areas); int maxRegions = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB, 0); if (!areas.isEmpty() && maxRegions > 0) { int max = Math.min(maxRegions, areas.size()); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/WhiteBalanceReset.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/WhiteBalanceReset.java index 6bbfa7e0..c65c399d 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/WhiteBalanceReset.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/meter/WhiteBalanceReset.java @@ -28,6 +28,7 @@ public class WhiteBalanceReset extends BaseReset { @Override protected void onStarted(@NonNull ActionHolder holder, @Nullable MeteringRectangle area) { + LOG.w("onStarted:", "with area:", area); int maxRegions = readCharacteristic(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB, 0); if (area != null && maxRegions > 0) { holder.getBuilder(this).set(CaptureRequest.CONTROL_AWB_REGIONS, new MeteringRectangle[]{area}); diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java index f60480a2..50508a71 100644 --- a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java @@ -213,7 +213,7 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis PicturePreviewActivity.setPictureResult(result); Intent intent = new Intent(CameraActivity.this, PicturePreviewActivity.class); intent.putExtra("delay", callbackTime - mCaptureTime); - // startActivity(intent); + startActivity(intent); mCaptureTime = 0; LOG.w("onPictureTaken called! Launched activity."); } diff --git a/demo/src/main/res/layout/activity_camera.xml b/demo/src/main/res/layout/activity_camera.xml index 13475133..2cc1d8a5 100644 --- a/demo/src/main/res/layout/activity_camera.xml +++ b/demo/src/main/res/layout/activity_camera.xml @@ -20,8 +20,7 @@ app:cameraPreview="glSurface" app:cameraPlaySounds="true" app:cameraGrid="off" - app:cameraFlash="on" - app:cameraPictureSnapshotMetering="true" + app:cameraFlash="auto" app:cameraAudio="on" app:cameraFacing="back" app:cameraGestureTap="autoFocus"