diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index f3fd9de8..b81dc6a7 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -49,6 +49,7 @@ import com.otaliastudios.cameraview.controls.WhiteBalance; import com.otaliastudios.cameraview.engine.Camera1Engine; import com.otaliastudios.cameraview.engine.Camera2Engine; import com.otaliastudios.cameraview.engine.CameraEngine; +import com.otaliastudios.cameraview.engine.CustomCameraEngine; import com.otaliastudios.cameraview.engine.offset.Reference; import com.otaliastudios.cameraview.engine.orchestrator.CameraState; import com.otaliastudios.cameraview.filter.Filter; @@ -1004,19 +1005,50 @@ public class CameraView extends FrameLayout implements LifecycleObserver { * if this CameraView is closed (open() was never called). * Otherwise, it has no effect. * + * Note: {@link Engine#CUSTOM} cannot be set with this method. + * Use {@link #setCustomEngine(CustomCameraEngine)} instead. + * * @see Engine#CAMERA1 * @see Engine#CAMERA2 * * @param engine desired engine */ public void setEngine(@NonNull Engine engine) { - if (!isClosed()) return; + if (!isClosed() || engine == Engine.CUSTOM) return; mEngine = engine; CameraEngine oldEngine = mCameraEngine; doInstantiateEngine(); if (mCameraPreview != null) mCameraEngine.setPreview(mCameraPreview); // Set again all parameters + copyCameraEngineParameters(oldEngine); + } + + /** + * Allows the app to implement a custom camera engine. + * Should only be called if this CameraView is closed + * (open() was never called). Otherwise it has no effect. + * + * To use a built-in engine use {@link #setEngine(Engine)} + * + * @param engine custom engine + */ + public void setCustomEngine(@NonNull CustomCameraEngine engine) { + if (!isClosed()) return; + CameraEngine oldEngine = mCameraEngine; + mCameraEngine = engine; + mEngine = Engine.CUSTOM; + engine.setCallbacks(mCameraCallbacks); + engine.setOverlay(mOverlayLayout); + if(mCameraPreview != null) mCameraEngine.setPreview(mCameraPreview); + + // Set again all parameters + copyCameraEngineParameters(oldEngine); + } + + private void copyCameraEngineParameters(CameraEngine oldEngine) { + mCameraEngine.setHasFrameProcessors(!mFrameProcessors.isEmpty()); + if (oldEngine == null) return; setFacing(oldEngine.getFacing()); setFlash(oldEngine.getFlash()); setMode(oldEngine.getMode()); @@ -1041,7 +1073,6 @@ public class CameraView extends FrameLayout implements LifecycleObserver { setFrameProcessingMaxHeight(oldEngine.getFrameProcessingMaxHeight()); setFrameProcessingFormat(0 /* this is very engine specific, so do not pass */); setFrameProcessingPoolSize(oldEngine.getFrameProcessingPoolSize()); - mCameraEngine.setHasFrameProcessors(!mFrameProcessors.isEmpty()); } /** diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Engine.java index 017b7082..a96c9b10 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Engine.java @@ -2,6 +2,7 @@ package com.otaliastudios.cameraview.controls; import com.otaliastudios.cameraview.CameraView; +import com.otaliastudios.cameraview.engine.CustomCameraEngine; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -22,7 +23,13 @@ public enum Engine implements Control { * Camera2 based engine. For API versions older than 21, * the system falls back to {@link #CAMERA1}. */ - CAMERA2(1); + CAMERA2(1), + + /** + * Custom camera engine implemented by the app. + * See {@link CameraView#setCustomEngine(CustomCameraEngine)}. + */ + CUSTOM(2); final static Engine DEFAULT = CAMERA1; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CallbackProxy.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CallbackProxy.java new file mode 100644 index 00000000..256505c5 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CallbackProxy.java @@ -0,0 +1,98 @@ +package com.otaliastudios.cameraview.engine; + +import android.content.Context; +import android.graphics.PointF; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.otaliastudios.cameraview.CameraException; +import com.otaliastudios.cameraview.CameraOptions; +import com.otaliastudios.cameraview.PictureResult; +import com.otaliastudios.cameraview.VideoResult; +import com.otaliastudios.cameraview.frame.Frame; +import com.otaliastudios.cameraview.gesture.Gesture; + +public class CallbackProxy implements CameraEngine.Callback { + private CameraEngine.Callback mCallbacks; + + public void setCallbacks(CameraEngine.Callback callbacks) { + mCallbacks = callbacks; + } + + @NonNull + @Override + public Context getContext() { + return mCallbacks.getContext(); + } + + @Override + public void dispatchOnCameraOpened(@NonNull CameraOptions options) { + mCallbacks.dispatchOnCameraOpened(options); + } + + @Override + public void dispatchOnCameraClosed() { + mCallbacks.dispatchOnCameraClosed(); + } + + @Override + public void onCameraPreviewStreamSizeChanged() { + mCallbacks.onCameraPreviewStreamSizeChanged(); + } + + @Override + public void dispatchOnPictureShutter(boolean shouldPlaySound) { + mCallbacks.dispatchOnPictureShutter(shouldPlaySound); + } + + @Override + public void dispatchOnVideoTaken(@NonNull VideoResult.Stub stub) { + mCallbacks.dispatchOnVideoTaken(stub); + } + + @Override + public void dispatchOnPictureTaken(@NonNull PictureResult.Stub stub) { + mCallbacks.dispatchOnPictureTaken(stub); + } + + @Override + public void dispatchOnFocusStart(@Nullable Gesture trigger, @NonNull PointF where) { + mCallbacks.dispatchOnFocusStart(trigger, where); + } + + @Override + public void dispatchOnFocusEnd(@Nullable Gesture trigger, boolean success, @NonNull PointF where) { + mCallbacks.dispatchOnFocusEnd(trigger, success, where); + } + + @Override + public void dispatchOnZoomChanged(float newValue, @Nullable PointF[] fingers) { + mCallbacks.dispatchOnZoomChanged(newValue, fingers); + } + + @Override + public void dispatchOnExposureCorrectionChanged(float newValue, @NonNull float[] bounds, @Nullable PointF[] fingers) { + mCallbacks.dispatchOnExposureCorrectionChanged(newValue, bounds, fingers); + } + + @Override + public void dispatchFrame(@NonNull Frame frame) { + mCallbacks.dispatchFrame(frame); + } + + @Override + public void dispatchError(CameraException exception) { + mCallbacks.dispatchError(exception); + } + + @Override + public void dispatchOnVideoRecordingStart() { + mCallbacks.dispatchOnVideoRecordingStart(); + } + + @Override + public void dispatchOnVideoRecordingEnd() { + mCallbacks.dispatchOnVideoRecordingEnd(); + } +} \ No newline at end of file diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CustomCameraEngine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CustomCameraEngine.java new file mode 100644 index 00000000..ea9974b2 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/CustomCameraEngine.java @@ -0,0 +1,11 @@ +package com.otaliastudios.cameraview.engine; + +public abstract class CustomCameraEngine extends CameraBaseEngine { + protected CustomCameraEngine() { + super(new CallbackProxy()); + } + + public void setCallbacks(Callback callbacks) { + ((CallbackProxy) getCallback()).setCallbacks(callbacks); + } +}