diff --git a/cameraview/build.gradle b/cameraview/build.gradle index 739cfd01..e4218c3e 100644 --- a/cameraview/build.gradle +++ b/cameraview/build.gradle @@ -32,7 +32,6 @@ android { } sourceSets { - main.java.srcDirs += 'src/main/options' main.java.srcDirs += 'src/main/views' main.java.srcDirs += 'src/main/utils' main.java.srcDirs += 'src/main/gles' diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraOptions1Test.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraOptions1Test.java index 7ab9e4da..175891af 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraOptions1Test.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraOptions1Test.java @@ -3,6 +3,18 @@ package com.otaliastudios.cameraview; import android.hardware.Camera; +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.gesture.GestureAction; +import com.otaliastudios.cameraview.controls.Grid; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.Mode; +import com.otaliastudios.cameraview.controls.VideoCodec; +import com.otaliastudios.cameraview.controls.WhiteBalance; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java index 85c2374d..db66df59 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraPreviewTest.java @@ -5,6 +5,9 @@ import android.content.Context; import androidx.test.rule.ActivityTestRule; import android.view.ViewGroup; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; + import org.junit.After; import org.junit.Before; import org.junit.Rule; diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewCallbacksTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewCallbacksTest.java index f66b74fe..8c6b2253 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewCallbacksTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewCallbacksTest.java @@ -9,6 +9,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.MediumTest; import android.view.ViewGroup; +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.gesture.Gesture; +import com.otaliastudios.cameraview.gesture.GestureAction; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java index 04a42fad..9fdeb4e7 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java @@ -2,6 +2,7 @@ package com.otaliastudios.cameraview; import android.content.Context; +import android.content.res.TypedArray; import android.location.Location; import androidx.annotation.NonNull; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -9,6 +10,25 @@ import androidx.test.filters.MediumTest; import android.view.MotionEvent; import android.view.ViewGroup; +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.controls.ControlParser; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.gesture.Gesture; +import com.otaliastudios.cameraview.gesture.GestureAction; +import com.otaliastudios.cameraview.controls.Grid; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.Mode; +import com.otaliastudios.cameraview.controls.VideoCodec; +import com.otaliastudios.cameraview.controls.WhiteBalance; +import com.otaliastudios.cameraview.gesture.GestureParser; +import com.otaliastudios.cameraview.gesture.PinchGestureLayout; +import com.otaliastudios.cameraview.gesture.ScrollGestureLayout; +import com.otaliastudios.cameraview.gesture.TapGestureLayout; +import com.otaliastudios.cameraview.size.Size; +import com.otaliastudios.cameraview.size.SizeSelector; +import com.otaliastudios.cameraview.size.SizeSelectors; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -106,14 +126,16 @@ public class CameraViewTest extends BaseTest { @Test public void testDefaults() { // CameraController - assertEquals(cameraView.getFlash(), Flash.DEFAULT); - assertEquals(cameraView.getFacing(), Facing.DEFAULT(context())); - assertEquals(cameraView.getGrid(), Grid.DEFAULT); - assertEquals(cameraView.getWhiteBalance(), WhiteBalance.DEFAULT); - assertEquals(cameraView.getMode(), Mode.DEFAULT); - assertEquals(cameraView.getHdr(), Hdr.DEFAULT); - assertEquals(cameraView.getAudio(), Audio.DEFAULT); - assertEquals(cameraView.getVideoCodec(), VideoCodec.DEFAULT); + TypedArray empty = context().obtainStyledAttributes(new int[]{}); + ControlParser controls = new ControlParser(context(), empty); + assertEquals(cameraView.getFlash(), controls.getFlash()); + assertEquals(cameraView.getFacing(), controls.getFacing()); + assertEquals(cameraView.getGrid(), controls.getGrid()); + assertEquals(cameraView.getWhiteBalance(), controls.getWhiteBalance()); + assertEquals(cameraView.getMode(), controls.getMode()); + assertEquals(cameraView.getHdr(), controls.getHdr()); + assertEquals(cameraView.getAudio(), controls.getAudio()); + assertEquals(cameraView.getVideoCodec(), controls.getVideoCodec()); assertEquals(cameraView.getLocation(), null); assertEquals(cameraView.getExposureCorrection(), 0f, 0f); assertEquals(cameraView.getZoom(), 0f, 0f); @@ -121,12 +143,13 @@ public class CameraViewTest extends BaseTest { assertEquals(cameraView.getVideoMaxSize(), 0, 0); // Self managed + GestureParser gestures = new GestureParser(empty); assertEquals(cameraView.getPlaySounds(), CameraView.DEFAULT_PLAY_SOUNDS); - assertEquals(cameraView.getGestureAction(Gesture.TAP), GestureAction.DEFAULT_TAP); - assertEquals(cameraView.getGestureAction(Gesture.LONG_TAP), GestureAction.DEFAULT_LONG_TAP); - assertEquals(cameraView.getGestureAction(Gesture.PINCH), GestureAction.DEFAULT_PINCH); - assertEquals(cameraView.getGestureAction(Gesture.SCROLL_HORIZONTAL), GestureAction.DEFAULT_SCROLL_HORIZONTAL); - assertEquals(cameraView.getGestureAction(Gesture.SCROLL_VERTICAL), GestureAction.DEFAULT_SCROLL_VERTICAL); + assertEquals(cameraView.getGestureAction(Gesture.TAP), gestures.getTapAction()); + assertEquals(cameraView.getGestureAction(Gesture.LONG_TAP), gestures.getLongTapAction()); + assertEquals(cameraView.getGestureAction(Gesture.PINCH), gestures.getPinchAction()); + assertEquals(cameraView.getGestureAction(Gesture.SCROLL_HORIZONTAL), gestures.getHorizontalScrollAction()); + assertEquals(cameraView.getGestureAction(Gesture.SCROLL_VERTICAL), gestures.getVerticalScrollAction()); } //endregion @@ -159,21 +182,21 @@ public class CameraViewTest extends BaseTest { // PinchGestureLayout cameraView.mapGesture(Gesture.PINCH, GestureAction.ZOOM); - assertTrue(cameraView.mPinchGestureLayout.enabled()); + assertTrue(cameraView.mPinchGestureLayout.isActive()); cameraView.clearGesture(Gesture.PINCH); - assertFalse(cameraView.mPinchGestureLayout.enabled()); + assertFalse(cameraView.mPinchGestureLayout.isActive()); // TapGestureLayout cameraView.mapGesture(Gesture.TAP, GestureAction.CAPTURE); - assertTrue(cameraView.mTapGestureLayout.enabled()); + assertTrue(cameraView.mTapGestureLayout.isActive()); cameraView.clearGesture(Gesture.TAP); - assertFalse(cameraView.mPinchGestureLayout.enabled()); + assertFalse(cameraView.mPinchGestureLayout.isActive()); // ScrollGestureLayout cameraView.mapGesture(Gesture.SCROLL_HORIZONTAL, GestureAction.ZOOM); - assertTrue(cameraView.mScrollGestureLayout.enabled()); + assertTrue(cameraView.mScrollGestureLayout.isActive()); cameraView.clearGesture(Gesture.SCROLL_HORIZONTAL); - assertFalse(cameraView.mScrollGestureLayout.enabled()); + assertFalse(cameraView.mScrollGestureLayout.isActive()); } //endregion @@ -188,10 +211,11 @@ public class CameraViewTest extends BaseTest { @Override public void run() { cameraView.mTapGestureLayout = new TapGestureLayout(cameraView.getContext()) { - public boolean onTouchEvent(MotionEvent event) { return true; } - + protected boolean handleTouchEvent(@NonNull MotionEvent event) { + setGesture(Gesture.TAP); + return true; + } }; - cameraView.mTapGestureLayout.setGestureType(Gesture.TAP); } }); cameraView.mapGesture(Gesture.TAP, GestureAction.CAPTURE); @@ -207,9 +231,11 @@ public class CameraViewTest extends BaseTest { @Override public void run() { cameraView.mTapGestureLayout = new TapGestureLayout(cameraView.getContext()) { - public boolean onTouchEvent(MotionEvent event) { return true; } + protected boolean handleTouchEvent(@NonNull MotionEvent event) { + setGesture(Gesture.TAP); + return true; + } }; - cameraView.mTapGestureLayout.setGestureType(Gesture.TAP); } }); mockController.mFocusStarted = false; @@ -224,30 +250,41 @@ public class CameraViewTest extends BaseTest { assertTrue(mockController.mFocusStarted); } + private class FactorHolder { float value; } + @Test public void testGestureAction_zoom() { mockController.mockStarted(true); mockController.mZoomChanged = false; MotionEvent event = MotionEvent.obtain(0L, 0L, 0, 0f, 0f, 0); + final FactorHolder factor = new FactorHolder(); ui(new Runnable() { @Override public void run() { cameraView.mPinchGestureLayout = new PinchGestureLayout(cameraView.getContext()) { - public boolean onTouchEvent(MotionEvent event) { return true; } + @Override + protected boolean handleTouchEvent(@NonNull MotionEvent event) { + setGesture(Gesture.PINCH); + return true; + } + + @Override + protected float getFactor() { + return factor.value; + } }; - cameraView.mPinchGestureLayout.setGestureType(Gesture.PINCH); cameraView.mapGesture(Gesture.PINCH, GestureAction.ZOOM); } }); // If factor is 0, we return the same value. The controller should not be notified. - cameraView.mPinchGestureLayout.mFactor = 0f; + factor.value = 0f; cameraView.dispatchTouchEvent(event); assertFalse(mockController.mZoomChanged); // For larger factors, the value is scaled. The controller should be notified. - cameraView.mPinchGestureLayout.mFactor = 1f; + factor.value = 1f; cameraView.dispatchTouchEvent(event); assertTrue(mockController.mZoomChanged); } @@ -262,24 +299,33 @@ public class CameraViewTest extends BaseTest { mockController.mockStarted(true); mockController.mExposureCorrectionChanged = false; MotionEvent event = MotionEvent.obtain(0L, 0L, 0, 0f, 0f, 0); + final FactorHolder factor = new FactorHolder(); ui(new Runnable() { @Override public void run() { cameraView.mScrollGestureLayout = new ScrollGestureLayout(cameraView.getContext()) { - public boolean onTouchEvent(MotionEvent event) { return true; } + @Override + protected boolean handleTouchEvent(@NonNull MotionEvent event) { + setGesture(Gesture.SCROLL_HORIZONTAL); + return true; + } + + @Override + protected float getFactor() { + return factor.value; + } }; - cameraView.mScrollGestureLayout.setGestureType(Gesture.SCROLL_HORIZONTAL); cameraView.mapGesture(Gesture.SCROLL_HORIZONTAL, GestureAction.EXPOSURE_CORRECTION); } }); // If factor is 0, we return the same value. The controller should not be notified. - cameraView.mScrollGestureLayout.mFactor = 0f; + factor.value = 0f; cameraView.dispatchTouchEvent(event); assertFalse(mockController.mExposureCorrectionChanged); // For larger factors, the value is scaled. The controller should be notified. - cameraView.mScrollGestureLayout.mFactor = 1f; + factor.value = 1f; cameraView.dispatchTouchEvent(event); assertTrue(mockController.mExposureCorrectionChanged); } diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CropHelperTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CropHelperTest.java index dd59e724..be36e795 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CropHelperTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/CropHelperTest.java @@ -3,6 +3,9 @@ package com.otaliastudios.cameraview; import android.graphics.Rect; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/GestureLayoutTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/GestureLayoutTest.java index a0b10152..b5cd720d 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/GestureLayoutTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/GestureLayoutTest.java @@ -3,17 +3,16 @@ package com.otaliastudios.cameraview; import android.annotation.TargetApi; import android.content.Context; -import androidx.test.espresso.Espresso; -import androidx.test.espresso.Root; -import androidx.test.espresso.ViewAssertion; + import androidx.test.espresso.ViewInteraction; -import androidx.test.espresso.assertion.ViewAssertions; import androidx.test.espresso.matcher.RootMatchers; -import androidx.test.espresso.matcher.ViewMatchers; import androidx.test.rule.ActivityTestRule; import android.view.MotionEvent; import android.view.View; +import com.otaliastudios.cameraview.gesture.Gesture; +import com.otaliastudios.cameraview.gesture.GestureLayout; + import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Rule; @@ -40,7 +39,7 @@ public abstract class GestureLayoutTest extends BaseTes public void run() { TestActivity a = rule.getActivity(); layout = create(a); - layout.enable(true); + layout.setActive(true); a.inflate(layout); touch = new Task<>(); @@ -48,7 +47,7 @@ public abstract class GestureLayoutTest extends BaseTes @Override public boolean onTouch(View view, MotionEvent motionEvent) { boolean found = layout.onTouchEvent(motionEvent); - if (found) touch.end(layout.getGestureType()); + if (found) touch.end(layout.getGesture()); return true; } }); diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/GridLinesLayoutTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/GridLinesLayoutTest.java index 424a8787..c151fe40 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/GridLinesLayoutTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/GridLinesLayoutTest.java @@ -1,6 +1,8 @@ package com.otaliastudios.cameraview; +import com.otaliastudios.cameraview.controls.Grid; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.MediumTest; import androidx.test.rule.ActivityTestRule; diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/IntegrationTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/IntegrationTest.java index 227d7833..b6864fea 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/IntegrationTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/IntegrationTest.java @@ -6,6 +6,13 @@ import android.graphics.PointF; import android.hardware.Camera; import android.os.Build; +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.Mode; +import com.otaliastudios.cameraview.controls.WhiteBalance; +import com.otaliastudios.cameraview.size.Size; + import androidx.annotation.NonNull; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.MediumTest; diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/Mapper1Test.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/Mapper1Test.java index 38f82579..30338923 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/Mapper1Test.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/Mapper1Test.java @@ -3,6 +3,11 @@ package com.otaliastudios.cameraview; import android.hardware.Camera; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.WhiteBalance; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java index 1026b215..e6f7501e 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/MockCameraController.java @@ -3,6 +3,17 @@ package com.otaliastudios.cameraview; import android.graphics.PointF; import android.location.Location; + +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.gesture.Gesture; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.Mode; +import com.otaliastudios.cameraview.controls.WhiteBalance; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PictureResultTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PictureResultTest.java index a99531a5..fd014f84 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PictureResultTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PictureResultTest.java @@ -3,6 +3,9 @@ package com.otaliastudios.cameraview; import android.location.Location; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.size.Size; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PinchGestureLayoutTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PinchGestureLayoutTest.java index 8746a8ea..fb57d21a 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PinchGestureLayoutTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/PinchGestureLayoutTest.java @@ -2,6 +2,10 @@ package com.otaliastudios.cameraview; import android.content.Context; + +import com.otaliastudios.cameraview.gesture.Gesture; +import com.otaliastudios.cameraview.gesture.PinchGestureLayout; + import androidx.test.espresso.ViewAction; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -26,7 +30,7 @@ public class PinchGestureLayoutTest extends GestureLayoutTest curr); assertTrue(newValue <= max); diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/ScrollGestureLayoutTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/ScrollGestureLayoutTest.java index 1414b1fd..f158af3d 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/ScrollGestureLayoutTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/ScrollGestureLayoutTest.java @@ -2,6 +2,10 @@ package com.otaliastudios.cameraview; import android.content.Context; + +import com.otaliastudios.cameraview.gesture.Gesture; +import com.otaliastudios.cameraview.gesture.ScrollGestureLayout; + import androidx.test.espresso.ViewAction; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -31,7 +35,7 @@ public class ScrollGestureLayoutTest extends GestureLayoutTest curr); assertTrue(newValue <= max); diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/TapGestureLayoutTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/TapGestureLayoutTest.java index 7bd75a83..b65097f3 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/TapGestureLayoutTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/TapGestureLayoutTest.java @@ -11,6 +11,10 @@ import androidx.test.filters.SmallTest; import android.view.InputDevice; import android.view.MotionEvent; +import com.otaliastudios.cameraview.gesture.Gesture; +import com.otaliastudios.cameraview.gesture.TapGestureLayout; +import com.otaliastudios.cameraview.size.Size; + import org.junit.Test; import org.junit.runner.RunWith; @@ -28,7 +32,7 @@ public class TapGestureLayoutTest extends GestureLayoutTest { @Test public void testDefaults() { - assertNull(layout.getGestureType()); + assertNull(layout.getGesture()); assertEquals(layout.getPoints().length, 1); assertEquals(layout.getPoints()[0].x, 0, 0); assertEquals(layout.getPoints()[0].y, 0, 0); @@ -52,7 +56,7 @@ public class TapGestureLayoutTest extends GestureLayoutTest { @Test public void testTapWhileDisabled() { - layout.enable(false); + layout.setActive(false); touch.listen(); touch.start(); onLayout().perform(click()); diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/TestActivity.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/TestActivity.java index 96588852..48637d7a 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/TestActivity.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/TestActivity.java @@ -12,6 +12,8 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; +import com.otaliastudios.cameraview.size.Size; + import static android.view.ViewGroup.LayoutParams.*; public class TestActivity extends Activity { diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/VideoResultTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/VideoResultTest.java index 503523b1..303cc7b7 100644 --- a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/VideoResultTest.java +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/VideoResultTest.java @@ -3,6 +3,11 @@ package com.otaliastudios.cameraview; import android.location.Location; +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.VideoCodec; +import com.otaliastudios.cameraview.size.Size; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; diff --git a/cameraview/src/main/gles/com/otaliastudios/cameraview/Pool.java b/cameraview/src/main/gles/com/otaliastudios/cameraview/Pool.java index 4429d55c..36ffaf22 100644 --- a/cameraview/src/main/gles/com/otaliastudios/cameraview/Pool.java +++ b/cameraview/src/main/gles/com/otaliastudios/cameraview/Pool.java @@ -26,8 +26,8 @@ class Pool { this.factory = factory; } - boolean canGet() { - return count() < maxPoolSize; + boolean isEmpty() { + return count() >= maxPoolSize; } @Nullable @@ -39,7 +39,7 @@ class Pool { return buffer; } - if (!canGet()) { + if (isEmpty()) { LOG.v("GET: Returning null. Too much items requested.", this); return null; } diff --git a/cameraview/src/main/gles/com/otaliastudios/cameraview/TextureMediaEncoder.java b/cameraview/src/main/gles/com/otaliastudios/cameraview/TextureMediaEncoder.java index 6c40b58a..27e90060 100644 --- a/cameraview/src/main/gles/com/otaliastudios/cameraview/TextureMediaEncoder.java +++ b/cameraview/src/main/gles/com/otaliastudios/cameraview/TextureMediaEncoder.java @@ -3,7 +3,6 @@ package com.otaliastudios.cameraview; import android.opengl.EGLContext; import android.opengl.Matrix; import android.os.Build; -import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -63,7 +62,7 @@ class TextureMediaEncoder extends VideoMediaEncoder @NonNull TextureFrame acquireFrame() { - if (!mFramePool.canGet()) { + if (mFramePool.isEmpty()) { throw new RuntimeException("Need more frames than this! Please increase the pool size."); } else { //noinspection ConstantConditions diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java index 1d25a8c8..9ba13760 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java @@ -14,6 +14,16 @@ import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import android.view.SurfaceHolder; +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.gesture.Gesture; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.Mode; +import com.otaliastudios.cameraview.controls.WhiteBalance; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -236,10 +246,10 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera Camera.Parameters params = mCamera.getParameters(); mCameraOptions = new CameraOptions(params, flip(REF_SENSOR, REF_VIEW)); applyDefaultFocus(params); - applyFlash(params, Flash.DEFAULT); + applyFlash(params, Flash.OFF); applyLocation(params, null); - applyWhiteBalance(params, WhiteBalance.DEFAULT); - applyHdr(params, Hdr.DEFAULT); + applyWhiteBalance(params, WhiteBalance.AUTO); + applyHdr(params, Hdr.OFF); applyPlaySounds(mPlaySounds); params.setRecordingHint(mMode == Mode.VIDEO); mCamera.setParameters(params); @@ -596,7 +606,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera result.facing = mFacing; result.size = getUncroppedSnapshotSize(REF_OUTPUT); // Not the real size: it will be cropped to match the view ratio result.rotation = offset(REF_SENSOR, REF_OUTPUT); // Actually it will be rotated and set to 0. - AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.inverse() : viewAspectRatio; + AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.flip() : viewAspectRatio; // LOG.e("ROTBUG_pic", "aspectRatio (REF_VIEW):", viewAspectRatio); // LOG.e("ROTBUG_pic", "aspectRatio (REF_OUTPUT):", outputRatio); // LOG.e("ROTBUG_pic", "sizeUncropped (REF_OUTPUT):", result.size); @@ -766,7 +776,7 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera // and maybe we can improve. The reason why this happen is beyond my understanding. Size outputSize = getUncroppedSnapshotSize(REF_OUTPUT); - AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.inverse() : viewAspectRatio; + AspectRatio outputRatio = flip(REF_OUTPUT, REF_VIEW) ? viewAspectRatio.flip() : viewAspectRatio; Rect outputCrop = CropHelper.computeCrop(outputSize, outputRatio); outputSize = new Size(outputCrop.width(), outputCrop.height()); videoResult.size = outputSize; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java index 027e4a5d..10f88d69 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java @@ -6,6 +6,20 @@ import android.location.Location; import android.os.Handler; import android.os.Looper; + +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.gesture.Gesture; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.Mode; +import com.otaliastudios.cameraview.controls.VideoCodec; +import com.otaliastudios.cameraview.controls.WhiteBalance; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; +import com.otaliastudios.cameraview.size.SizeSelector; +import com.otaliastudios.cameraview.size.SizeSelectors; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -638,7 +652,7 @@ abstract class CameraController implements // is null, or if it fails in finding a size. Size targetMinSize = getPreviewSurfaceSize(REF_VIEW); AspectRatio targetRatio = AspectRatio.of(mCaptureSize.getWidth(), mCaptureSize.getHeight()); - if (flip) targetRatio = targetRatio.inverse(); + if (flip) targetRatio = targetRatio.flip(); LOG.i("size:", "computePreviewStreamSize:", "targetRatio:", targetRatio, "targetMinSize:", targetMinSize); SizeSelector matchRatio = SizeSelectors.and( // Match this aspect ratio and sort by biggest SizeSelectors.aspectRatio(targetRatio, 0), diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraException.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraException.java index c371989c..dd88ce81 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraException.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraException.java @@ -1,6 +1,8 @@ package com.otaliastudios.cameraview; +import com.otaliastudios.cameraview.controls.Facing; + /** * Holds an error with the camera configuration. */ diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java index dc566960..795e11ee 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraOptions.java @@ -4,6 +4,20 @@ package com.otaliastudios.cameraview; import android.annotation.TargetApi; import android.hardware.Camera; import android.hardware.camera2.CameraCharacteristics; + +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.controls.Control; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.gesture.GestureAction; +import com.otaliastudios.cameraview.controls.Grid; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.Mode; +import com.otaliastudios.cameraview.controls.VideoCodec; +import com.otaliastudios.cameraview.controls.WhiteBalance; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; + import androidx.annotation.NonNull; import java.util.Arrays; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index eaf74e88..8ee63764 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -28,6 +28,30 @@ import android.view.MotionEvent; import android.view.ViewGroup; import android.widget.FrameLayout; +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.controls.Control; +import com.otaliastudios.cameraview.controls.ControlParser; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.gesture.Gesture; +import com.otaliastudios.cameraview.gesture.GestureAction; +import com.otaliastudios.cameraview.controls.Grid; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.Mode; +import com.otaliastudios.cameraview.controls.Preview; +import com.otaliastudios.cameraview.controls.VideoCodec; +import com.otaliastudios.cameraview.controls.WhiteBalance; +import com.otaliastudios.cameraview.gesture.GestureLayout; +import com.otaliastudios.cameraview.gesture.GestureParser; +import com.otaliastudios.cameraview.gesture.PinchGestureLayout; +import com.otaliastudios.cameraview.gesture.ScrollGestureLayout; +import com.otaliastudios.cameraview.gesture.TapGestureLayout; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; +import com.otaliastudios.cameraview.size.SizeSelector; +import com.otaliastudios.cameraview.size.SizeSelectorParser; +import com.otaliastudios.cameraview.size.SizeSelectors; + import java.io.File; import java.util.ArrayList; import java.util.HashMap; @@ -94,11 +118,12 @@ public class CameraView extends FrameLayout implements LifecycleObserver { private void init(@NonNull Context context, @Nullable AttributeSet attrs) { setWillNotDraw(false); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CameraView, 0, 0); + ControlParser controls = new ControlParser(context, a); // Self managed boolean playSounds = a.getBoolean(R.styleable.CameraView_cameraPlaySounds, DEFAULT_PLAY_SOUNDS); mExperimental = a.getBoolean(R.styleable.CameraView_cameraExperimental, false); - mPreview = Preview.fromValue(a.getInteger(R.styleable.CameraView_cameraPreview, Preview.DEFAULT.value())); + mPreview = controls.getPreview(); // Camera controller params Facing facing = Facing.fromValue(a.getInteger(R.styleable.CameraView_cameraFacing, Facing.DEFAULT(context).value())); @@ -116,73 +141,9 @@ public class CameraView extends FrameLayout implements LifecycleObserver { int audioBitRate = a.getInteger(R.styleable.CameraView_cameraAudioBitRate, 0); long autoFocusResetDelay = (long) a.getInteger(R.styleable.CameraView_cameraAutoFocusResetDelay, (int) DEFAULT_AUTOFOCUS_RESET_DELAY_MILLIS); - // Picture size selector - List pictureConstraints = new ArrayList<>(3); - if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMinWidth)) { - pictureConstraints.add(SizeSelectors.minWidth(a.getInteger(R.styleable.CameraView_cameraPictureSizeMinWidth, 0))); - } - if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMaxWidth)) { - pictureConstraints.add(SizeSelectors.maxWidth(a.getInteger(R.styleable.CameraView_cameraPictureSizeMaxWidth, 0))); - } - if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMinHeight)) { - pictureConstraints.add(SizeSelectors.minHeight(a.getInteger(R.styleable.CameraView_cameraPictureSizeMinHeight, 0))); - } - if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMaxHeight)) { - pictureConstraints.add(SizeSelectors.maxHeight(a.getInteger(R.styleable.CameraView_cameraPictureSizeMaxHeight, 0))); - } - if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMinArea)) { - pictureConstraints.add(SizeSelectors.minArea(a.getInteger(R.styleable.CameraView_cameraPictureSizeMinArea, 0))); - } - if (a.hasValue(R.styleable.CameraView_cameraPictureSizeMaxArea)) { - pictureConstraints.add(SizeSelectors.maxArea(a.getInteger(R.styleable.CameraView_cameraPictureSizeMaxArea, 0))); - } - if (a.hasValue(R.styleable.CameraView_cameraPictureSizeAspectRatio)) { - //noinspection ConstantConditions - pictureConstraints.add(SizeSelectors.aspectRatio(AspectRatio.parse(a.getString(R.styleable.CameraView_cameraPictureSizeAspectRatio)), 0)); - } - - if (a.getBoolean(R.styleable.CameraView_cameraPictureSizeSmallest, false)) pictureConstraints.add(SizeSelectors.smallest()); - if (a.getBoolean(R.styleable.CameraView_cameraPictureSizeBiggest, false)) pictureConstraints.add(SizeSelectors.biggest()); - SizeSelector pictureSelector = !pictureConstraints.isEmpty() ? - SizeSelectors.and(pictureConstraints.toArray(new SizeSelector[0])) : - SizeSelectors.biggest(); - - // Video size selector - List videoConstraints = new ArrayList<>(3); - if (a.hasValue(R.styleable.CameraView_cameraVideoSizeMinWidth)) { - videoConstraints.add(SizeSelectors.minWidth(a.getInteger(R.styleable.CameraView_cameraVideoSizeMinWidth, 0))); - } - if (a.hasValue(R.styleable.CameraView_cameraVideoSizeMaxWidth)) { - videoConstraints.add(SizeSelectors.maxWidth(a.getInteger(R.styleable.CameraView_cameraVideoSizeMaxWidth, 0))); - } - if (a.hasValue(R.styleable.CameraView_cameraVideoSizeMinHeight)) { - videoConstraints.add(SizeSelectors.minHeight(a.getInteger(R.styleable.CameraView_cameraVideoSizeMinHeight, 0))); - } - if (a.hasValue(R.styleable.CameraView_cameraVideoSizeMaxHeight)) { - videoConstraints.add(SizeSelectors.maxHeight(a.getInteger(R.styleable.CameraView_cameraVideoSizeMaxHeight, 0))); - } - if (a.hasValue(R.styleable.CameraView_cameraVideoSizeMinArea)) { - videoConstraints.add(SizeSelectors.minArea(a.getInteger(R.styleable.CameraView_cameraVideoSizeMinArea, 0))); - } - if (a.hasValue(R.styleable.CameraView_cameraVideoSizeMaxArea)) { - videoConstraints.add(SizeSelectors.maxArea(a.getInteger(R.styleable.CameraView_cameraVideoSizeMaxArea, 0))); - } - if (a.hasValue(R.styleable.CameraView_cameraVideoSizeAspectRatio)) { - //noinspection ConstantConditions - videoConstraints.add(SizeSelectors.aspectRatio(AspectRatio.parse(a.getString(R.styleable.CameraView_cameraVideoSizeAspectRatio)), 0)); - } - if (a.getBoolean(R.styleable.CameraView_cameraVideoSizeSmallest, false)) videoConstraints.add(SizeSelectors.smallest()); - if (a.getBoolean(R.styleable.CameraView_cameraVideoSizeBiggest, false)) videoConstraints.add(SizeSelectors.biggest()); - SizeSelector videoSelector = !videoConstraints.isEmpty() ? - SizeSelectors.and(videoConstraints.toArray(new SizeSelector[0])) : - SizeSelectors.biggest(); - - // Gestures - GestureAction tapGesture = GestureAction.fromValue(a.getInteger(R.styleable.CameraView_cameraGestureTap, GestureAction.DEFAULT_TAP.value())); - GestureAction longTapGesture = GestureAction.fromValue(a.getInteger(R.styleable.CameraView_cameraGestureLongTap, GestureAction.DEFAULT_LONG_TAP.value())); - GestureAction pinchGesture = GestureAction.fromValue(a.getInteger(R.styleable.CameraView_cameraGesturePinch, GestureAction.DEFAULT_PINCH.value())); - GestureAction scrollHorizontalGesture = GestureAction.fromValue(a.getInteger(R.styleable.CameraView_cameraGestureScrollHorizontal, GestureAction.DEFAULT_SCROLL_HORIZONTAL.value())); - GestureAction scrollVerticalGesture = GestureAction.fromValue(a.getInteger(R.styleable.CameraView_cameraGestureScrollVertical, GestureAction.DEFAULT_SCROLL_VERTICAL.value())); + // Size selectors and gestures + SizeSelectorParser sizeSelectors = new SizeSelectorParser(a); + GestureParser gestures = new GestureParser(a); a.recycle(); @@ -206,29 +167,29 @@ public class CameraView extends FrameLayout implements LifecycleObserver { setPlaySounds(playSounds); // Apply camera controller params - setFacing(facing); - setFlash(flash); - setMode(mode); - setWhiteBalance(whiteBalance); - setGrid(grid); + setFacing(controls.getFacing()); + setFlash(controls.getFlash()); + setMode(controls.getMode()); + setWhiteBalance(controls.getWhiteBalance()); + setGrid(controls.getGrid()); setGridColor(gridColor); - setHdr(hdr); - setAudio(audio); + setHdr(controls.getHdr()); + setAudio(controls.getAudio()); setAudioBitRate(audioBitRate); - setPictureSize(pictureSelector); - setVideoSize(videoSelector); - setVideoCodec(codec); + setPictureSize(sizeSelectors.getPictureSizeSelector()); + setVideoSize(sizeSelectors.getVideoSizeSelector()); + setVideoCodec(controls.getVideoCodec()); setVideoMaxSize(videoMaxSize); setVideoMaxDuration(videoMaxDuration); setVideoBitRate(videoBitRate); setAutoFocusResetDelay(autoFocusResetDelay); // Apply gestures - mapGesture(Gesture.TAP, tapGesture); - mapGesture(Gesture.LONG_TAP, longTapGesture); - mapGesture(Gesture.PINCH, pinchGesture); - mapGesture(Gesture.SCROLL_HORIZONTAL, scrollHorizontalGesture); - mapGesture(Gesture.SCROLL_VERTICAL, scrollVerticalGesture); + mapGesture(Gesture.TAP, gestures.getTapAction()); + mapGesture(Gesture.LONG_TAP, gestures.getLongTapAction()); + mapGesture(Gesture.PINCH, gestures.getPinchAction()); + mapGesture(Gesture.SCROLL_HORIZONTAL, gestures.getHorizontalScrollAction()); + mapGesture(Gesture.SCROLL_VERTICAL, gestures.getVerticalScrollAction()); if (!isInEditMode()) { mOrientationHelper = new OrientationHelper(context, mCameraCallbacks); @@ -457,19 +418,19 @@ public class CameraView extends FrameLayout implements LifecycleObserver { mGestureMap.put(gesture, action); switch (gesture) { case PINCH: - mPinchGestureLayout.enable(mGestureMap.get(Gesture.PINCH) != none); + mPinchGestureLayout.setActive(mGestureMap.get(Gesture.PINCH) != none); break; case TAP: // case DOUBLE_TAP: case LONG_TAP: - mTapGestureLayout.enable( + mTapGestureLayout.setActive( mGestureMap.get(Gesture.TAP) != none || // mGestureMap.get(Gesture.DOUBLE_TAP) != none || mGestureMap.get(Gesture.LONG_TAP) != none); break; case SCROLL_HORIZONTAL: case SCROLL_VERTICAL: - mScrollGestureLayout.enable( + mScrollGestureLayout.setActive( mGestureMap.get(Gesture.SCROLL_HORIZONTAL) != none || mGestureMap.get(Gesture.SCROLL_VERTICAL) != none); break; @@ -532,7 +493,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { // (1) if it was mapped to some action (we check here) // (2) if it's supported by the camera (CameraController checks) private void onGesture(GestureLayout source, @NonNull CameraOptions options) { - Gesture gesture = source.getGestureType(); + Gesture gesture = source.getGesture(); GestureAction action = mGestureMap.get(gesture); PointF[] points = source.getPoints(); float oldValue, newValue; @@ -549,7 +510,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { case ZOOM: oldValue = mCameraController.getZoomValue(); - newValue = source.scaleValue(oldValue, 0, 1); + newValue = source.computeValue(oldValue, 0, 1); if (newValue != oldValue) { mCameraController.setZoom(newValue, points, true); } @@ -559,7 +520,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver { oldValue = mCameraController.getExposureCorrectionValue(); float minValue = options.getExposureCorrectionMinValue(); float maxValue = options.getExposureCorrectionMaxValue(); - newValue = source.scaleValue(oldValue, minValue, maxValue); + newValue = source.computeValue(oldValue, minValue, maxValue); if (newValue != oldValue) { float[] bounds = new float[]{minValue, maxValue}; mCameraController.setExposureCorrection(newValue, bounds, points, true); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Frame.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Frame.java index 44a83928..5c162eef 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Frame.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Frame.java @@ -1,7 +1,8 @@ package com.otaliastudios.cameraview; +import com.otaliastudios.cameraview.size.Size; + import androidx.annotation.NonNull; -import androidx.annotation.Nullable; /** * A preview frame to be processed by {@link FrameProcessor}s. @@ -20,7 +21,7 @@ public class Frame { mManager = manager; } - boolean isAlive() { + private boolean isAlive() { return mData != null; } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/FrameManager.java b/cameraview/src/main/java/com/otaliastudios/cameraview/FrameManager.java index f08eaf1c..7042ce07 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/FrameManager.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/FrameManager.java @@ -1,6 +1,8 @@ package com.otaliastudios.cameraview; +import com.otaliastudios.cameraview.size.Size; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/FullVideoRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/FullVideoRecorder.java index 7fdd3915..96330d2c 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/FullVideoRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/FullVideoRecorder.java @@ -3,6 +3,10 @@ package com.otaliastudios.cameraview; import android.hardware.Camera; import android.media.CamcorderProfile; import android.media.MediaRecorder; + +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.size.Size; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper.java index c6359580..87432753 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper.java @@ -1,10 +1,9 @@ package com.otaliastudios.cameraview; -import android.hardware.Camera; -import android.media.MediaRecorder; -import android.os.Build; - -import java.util.HashMap; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.WhiteBalance; abstract class Mapper { diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper1.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper1.java index 76c9d981..b186bf41 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper1.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Mapper1.java @@ -3,6 +3,11 @@ package com.otaliastudios.cameraview; import android.hardware.Camera; import android.os.Build; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.WhiteBalance; + import java.util.HashMap; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/PictureResult.java b/cameraview/src/main/java/com/otaliastudios/cameraview/PictureResult.java index 282a2980..25af5338 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/PictureResult.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/PictureResult.java @@ -3,6 +3,9 @@ package com.otaliastudios.cameraview; import android.graphics.BitmapFactory; import android.location.Location; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.size.Size; + import java.io.File; import androidx.annotation.NonNull; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotPictureRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotPictureRecorder.java index bf197bd8..2199d919 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotPictureRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotPictureRecorder.java @@ -12,6 +12,9 @@ import android.opengl.EGL14; import android.opengl.EGLContext; import android.opengl.Matrix; import android.os.Build; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; import androidx.annotation.NonNull; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java b/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java index bf54ab14..008dd809 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/SnapshotVideoRecorder.java @@ -4,6 +4,9 @@ import android.graphics.SurfaceTexture; import android.opengl.EGL14; import android.os.Build; +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.size.Size; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/VideoResult.java b/cameraview/src/main/java/com/otaliastudios/cameraview/VideoResult.java index 806556b6..a450804d 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/VideoResult.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/VideoResult.java @@ -1,6 +1,12 @@ package com.otaliastudios.cameraview; import android.location.Location; + +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.VideoCodec; +import com.otaliastudios.cameraview.size.Size; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/Audio.java b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Audio.java similarity index 88% rename from cameraview/src/main/options/com/otaliastudios/cameraview/Audio.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/controls/Audio.java index 29478e70..a1a03f0a 100644 --- a/cameraview/src/main/options/com/otaliastudios/cameraview/Audio.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Audio.java @@ -1,6 +1,8 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.controls; +import com.otaliastudios.cameraview.CameraView; + import androidx.annotation.Nullable; /** diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/Control.java b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Control.java similarity index 73% rename from cameraview/src/main/options/com/otaliastudios/cameraview/Control.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/controls/Control.java index 0f853f94..2be90a06 100644 --- a/cameraview/src/main/options/com/otaliastudios/cameraview/Control.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Control.java @@ -1,4 +1,4 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.controls; /** * Base interface for controls like {@link Audio}, diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/controls/ControlParser.java b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/ControlParser.java new file mode 100644 index 00000000..5f9fa87f --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/ControlParser.java @@ -0,0 +1,72 @@ +package com.otaliastudios.cameraview.controls; + +import android.content.Context; +import android.content.res.TypedArray; + +import com.otaliastudios.cameraview.R; + +import androidx.annotation.NonNull; + +/** + * Parses controls from XML attributes. + */ +public class ControlParser { + + private int preview; + private int facing; + private int flash; + private int grid; + private int whiteBalance; + private int mode; + private int hdr; + private int audio; + private int videoCodec; + + public ControlParser(@NonNull Context context, @NonNull TypedArray array) { + this.preview = array.getInteger(R.styleable.CameraView_cameraPreview, Preview.DEFAULT.value()); + this.facing = array.getInteger(R.styleable.CameraView_cameraFacing, Facing.DEFAULT(context).value()); + this.flash = array.getInteger(R.styleable.CameraView_cameraFlash, Flash.DEFAULT.value()); + this.grid = array.getInteger(R.styleable.CameraView_cameraGrid, Grid.DEFAULT.value()); + this.whiteBalance = array.getInteger(R.styleable.CameraView_cameraWhiteBalance, WhiteBalance.DEFAULT.value()); + this.mode = array.getInteger(R.styleable.CameraView_cameraMode, Mode.DEFAULT.value()); + this.hdr = array.getInteger(R.styleable.CameraView_cameraHdr, Hdr.DEFAULT.value()); + this.audio = array.getInteger(R.styleable.CameraView_cameraAudio, Audio.DEFAULT.value()); + this.videoCodec = array.getInteger(R.styleable.CameraView_cameraVideoCodec, VideoCodec.DEFAULT.value()); + } + + public Preview getPreview() { + return Preview.fromValue(preview); + } + + public Facing getFacing() { + return Facing.fromValue(facing); + } + + public Flash getFlash() { + return Flash.fromValue(flash); + } + + public Grid getGrid() { + return Grid.fromValue(grid); + } + + public Mode getMode() { + return Mode.fromValue(mode); + } + + public WhiteBalance getWhiteBalance() { + return WhiteBalance.fromValue(whiteBalance); + } + + public Hdr getHdr() { + return Hdr.fromValue(hdr); + } + + public Audio getAudio() { + return Audio.fromValue(audio); + } + + public VideoCodec getVideoCodec() { + return VideoCodec.fromValue(videoCodec); + } +} diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/Facing.java b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Facing.java similarity index 89% rename from cameraview/src/main/options/com/otaliastudios/cameraview/Facing.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/controls/Facing.java index ce1f67f6..90684b11 100644 --- a/cameraview/src/main/options/com/otaliastudios/cameraview/Facing.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Facing.java @@ -1,7 +1,11 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.controls; import android.content.Context; + +import com.otaliastudios.cameraview.CameraUtils; +import com.otaliastudios.cameraview.CameraView; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/Flash.java b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Flash.java similarity index 88% rename from cameraview/src/main/options/com/otaliastudios/cameraview/Flash.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/controls/Flash.java index 79199e8f..cf98c91d 100644 --- a/cameraview/src/main/options/com/otaliastudios/cameraview/Flash.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Flash.java @@ -1,6 +1,9 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.controls; +import com.otaliastudios.cameraview.CameraOptions; +import com.otaliastudios.cameraview.CameraView; + import androidx.annotation.Nullable; /** diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/Grid.java b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Grid.java similarity index 90% rename from cameraview/src/main/options/com/otaliastudios/cameraview/Grid.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/controls/Grid.java index 11dde8b2..d452935d 100644 --- a/cameraview/src/main/options/com/otaliastudios/cameraview/Grid.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Grid.java @@ -1,6 +1,8 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.controls; +import com.otaliastudios.cameraview.CameraView; + import androidx.annotation.Nullable; /** diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/Hdr.java b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Hdr.java similarity index 88% rename from cameraview/src/main/options/com/otaliastudios/cameraview/Hdr.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/controls/Hdr.java index 062a4538..9131d34e 100644 --- a/cameraview/src/main/options/com/otaliastudios/cameraview/Hdr.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Hdr.java @@ -1,6 +1,8 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.controls; +import com.otaliastudios.cameraview.CameraView; + import androidx.annotation.Nullable; /** diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/Mode.java b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Mode.java similarity index 93% rename from cameraview/src/main/options/com/otaliastudios/cameraview/Mode.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/controls/Mode.java index cd2e9e46..1352e626 100644 --- a/cameraview/src/main/options/com/otaliastudios/cameraview/Mode.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Mode.java @@ -1,6 +1,8 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.controls; +import com.otaliastudios.cameraview.CameraView; + import androidx.annotation.Nullable; import java.io.File; diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/Preview.java b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Preview.java similarity index 92% rename from cameraview/src/main/options/com/otaliastudios/cameraview/Preview.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/controls/Preview.java index 062a149d..7e6dd271 100644 --- a/cameraview/src/main/options/com/otaliastudios/cameraview/Preview.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/Preview.java @@ -1,6 +1,8 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.controls; +import com.otaliastudios.cameraview.CameraView; + import androidx.annotation.Nullable; /** diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/VideoCodec.java b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/VideoCodec.java similarity index 90% rename from cameraview/src/main/options/com/otaliastudios/cameraview/VideoCodec.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/controls/VideoCodec.java index df77cc55..1b136953 100644 --- a/cameraview/src/main/options/com/otaliastudios/cameraview/VideoCodec.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/VideoCodec.java @@ -1,7 +1,8 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.controls; -import androidx.annotation.NonNull; +import com.otaliastudios.cameraview.CameraView; + import androidx.annotation.Nullable; /** diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/WhiteBalance.java b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/WhiteBalance.java similarity index 91% rename from cameraview/src/main/options/com/otaliastudios/cameraview/WhiteBalance.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/controls/WhiteBalance.java index 100b9dd0..20150e7e 100644 --- a/cameraview/src/main/options/com/otaliastudios/cameraview/WhiteBalance.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/controls/WhiteBalance.java @@ -1,6 +1,9 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.controls; +import com.otaliastudios.cameraview.CameraOptions; +import com.otaliastudios.cameraview.CameraView; + import androidx.annotation.Nullable; /** diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/Gesture.java b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/Gesture.java similarity index 62% rename from cameraview/src/main/options/com/otaliastudios/cameraview/Gesture.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/gesture/Gesture.java index d99ae069..ffa0e817 100644 --- a/cameraview/src/main/options/com/otaliastudios/cameraview/Gesture.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/Gesture.java @@ -1,6 +1,8 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.gesture; +import com.otaliastudios.cameraview.CameraView; + import androidx.annotation.NonNull; import java.util.Arrays; @@ -19,65 +21,69 @@ public enum Gesture { /** * Pinch gesture, typically assigned to the zoom control. - * This gesture can be mapped to: + * This gesture can be mapped to continuous actions: * * - {@link GestureAction#ZOOM} * - {@link GestureAction#EXPOSURE_CORRECTION} * - {@link GestureAction#NONE} */ - PINCH(GestureAction.ZOOM, GestureAction.EXPOSURE_CORRECTION), + PINCH(GestureType.CONTINUOUS), /** * Single tap gesture, typically assigned to the focus control. - * This gesture can be mapped to: + * This gesture can be mapped to one shot actions: * * - {@link GestureAction#FOCUS} * - {@link GestureAction#FOCUS_WITH_MARKER} * - {@link GestureAction#CAPTURE} * - {@link GestureAction#NONE} */ - TAP(GestureAction.FOCUS, GestureAction.FOCUS_WITH_MARKER, GestureAction.CAPTURE), - // DOUBLE_TAP(GestureAction.FOCUS, GestureAction.FOCUS_WITH_MARKER, GestureAction.CAPTURE), + TAP(GestureType.ONE_SHOT), /** * Long tap gesture. - * This gesture can be mapped to: + * This gesture can be mapped to one shot actions: * * - {@link GestureAction#FOCUS} * - {@link GestureAction#FOCUS_WITH_MARKER} * - {@link GestureAction#CAPTURE} * - {@link GestureAction#NONE} */ - LONG_TAP(GestureAction.FOCUS, GestureAction.FOCUS_WITH_MARKER, GestureAction.CAPTURE), + LONG_TAP(GestureType.ONE_SHOT), /** * Horizontal scroll gesture. - * This gesture can be mapped to: + * This gesture can be mapped to continuous actions: * * - {@link GestureAction#ZOOM} * - {@link GestureAction#EXPOSURE_CORRECTION} * - {@link GestureAction#NONE} */ - SCROLL_HORIZONTAL(GestureAction.ZOOM, GestureAction.EXPOSURE_CORRECTION), + SCROLL_HORIZONTAL(GestureType.CONTINUOUS), /** * Vertical scroll gesture. - * This gesture can be mapped to: + * This gesture can be mapped to continuous actions: * * - {@link GestureAction#ZOOM} * - {@link GestureAction#EXPOSURE_CORRECTION} * - {@link GestureAction#NONE} */ - SCROLL_VERTICAL(GestureAction.ZOOM, GestureAction.EXPOSURE_CORRECTION); + SCROLL_VERTICAL(GestureType.CONTINUOUS); - Gesture(GestureAction... controls) { - mControls = Arrays.asList(controls); + Gesture(@NonNull GestureType type) { + this.type = type; } - private List mControls; + private GestureType type; - boolean isAssignableTo(@NonNull GestureAction control) { - return control == GestureAction.NONE || mControls.contains(control); + /** + * Whether this gesture can be assigned to the given {@link GestureAction}. + * @param action the action to be checked + * @return true if assignable + */ + public boolean isAssignableTo(@NonNull GestureAction action) { + return action == GestureAction.NONE || action.type() == type; } } diff --git a/cameraview/src/main/options/com/otaliastudios/cameraview/GestureAction.java b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureAction.java similarity index 72% rename from cameraview/src/main/options/com/otaliastudios/cameraview/GestureAction.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureAction.java index 0d2d4702..453674d5 100644 --- a/cameraview/src/main/options/com/otaliastudios/cameraview/GestureAction.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureAction.java @@ -1,6 +1,9 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.gesture; +import com.otaliastudios.cameraview.CameraView; + +import androidx.annotation.NonNull; import androidx.annotation.Nullable; /** @@ -16,55 +19,55 @@ public enum GestureAction { /** * No action. This can be mapped to any gesture to disable it. */ - NONE(0), + NONE(0, GestureType.ONE_SHOT), /** * Auto focus control, typically assigned to the tap gesture. - * This action can be mapped to: + * This action can be mapped to one shot gestures: * * - {@link Gesture#TAP} * - {@link Gesture#LONG_TAP} */ - FOCUS(1), + FOCUS(1, GestureType.ONE_SHOT), /** * Auto focus control, typically assigned to the tap gesture. * On top of {@link #FOCUS}, this will draw a default marker on screen. - * This action can be mapped to: + * This action can be mapped to one shot gestures: * * - {@link Gesture#TAP} * - {@link Gesture#LONG_TAP} */ - FOCUS_WITH_MARKER(2), + FOCUS_WITH_MARKER(2, GestureType.ONE_SHOT), /** * When triggered, this action will fire a picture shoot. - * This action can be mapped to: + * This action can be mapped to one shot gestures: * * - {@link Gesture#TAP} * - {@link Gesture#LONG_TAP} */ - CAPTURE(3), + CAPTURE(3, GestureType.ONE_SHOT), /** * Zoom control, typically assigned to the pinch gesture. - * This action can be mapped to: + * This action can be mapped to continuous gestures: * * - {@link Gesture#PINCH} * - {@link Gesture#SCROLL_HORIZONTAL} * - {@link Gesture#SCROLL_VERTICAL} */ - ZOOM(4), + ZOOM(4, GestureType.CONTINUOUS), /** * Exposure correction control. - * This action can be mapped to: + * This action can be mapped to continuous gestures: * * - {@link Gesture#PINCH} * - {@link Gesture#SCROLL_HORIZONTAL} * - {@link Gesture#SCROLL_VERTICAL} */ - EXPOSURE_CORRECTION(5); + EXPOSURE_CORRECTION(5, GestureType.CONTINUOUS); final static GestureAction DEFAULT_PINCH = NONE; @@ -74,15 +77,22 @@ public enum GestureAction { final static GestureAction DEFAULT_SCROLL_VERTICAL = NONE; private int value; + private GestureType type; - GestureAction(int value) { + GestureAction(int value, @NonNull GestureType type) { this.value = value; + this.type = type; } int value() { return value; } + @NonNull + GestureType type() { + return type; + } + @Nullable static GestureAction fromValue(int value) { GestureAction[] list = GestureAction.values(); diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureLayout.java b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureLayout.java new file mode 100644 index 00000000..69f24d95 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureLayout.java @@ -0,0 +1,159 @@ +package com.otaliastudios.cameraview.gesture; + +import android.content.Context; +import android.graphics.PointF; +import androidx.annotation.NonNull; +import android.view.MotionEvent; +import android.widget.FrameLayout; + +/** + * Base class for gesture layouts, that is, layouts that will capture + * gestures. + */ +public abstract class GestureLayout extends FrameLayout { + + // The number of possible values between minValue and maxValue, for the getValue method. + // We could make this non-static (e.g. larger granularity for exposure correction). + private final static int GRANULARITY = 50; + private boolean mActive; + private Gesture mType; + private PointF[] mPoints; + + GestureLayout(@NonNull Context context, int points) { + super(context); + mPoints = new PointF[points]; + for (int i = 0; i < points; i++) { + mPoints[i] = new PointF(0, 0); + } + } + + /** + * Makes this instance active, which means, listening to events. + * @param active whether this should be active or not + */ + public void setActive(boolean active) { + mActive = active; + } + + /** + * Whether this instance is active, which means, it is listening + * to events and identifying new gestures. + * @return true if active + */ + public boolean isActive() { + return mActive; + } + + /** + * Called when new events are available. + * If true is returned, users will call {@link #getGesture()}, {@link #getPoints()} + * and maybe {@link #getValue(float, float, float)} to know more about the gesture. + * + * @param event the new event + * @return true if a gesture was detected + */ + public final boolean onTouchEvent(@NonNull MotionEvent event) { + if (!mActive) return false; + return handleTouchEvent(event); + } + + /** + * Called when new events are available. + * If true is returned, users will call {@link #getGesture()}, {@link #getPoints()} + * and maybe {@link #getValue(float, float, float)} to know more about the gesture. + * + * @param event the new event + * @return true if a gesture was detected + */ + protected abstract boolean handleTouchEvent(@NonNull MotionEvent event); + + /** + * Returns the gesture that this instance is currently detecting. + * This is mutable - for instance, a scroll layout can detect both + * horizontal and vertical scroll gestures. + * + * @return the current gesture + */ + @NonNull + public final Gesture getGesture() { + return mType; + } + + /** + * Sets the currently detected gesture. + * @see #getGesture() + * + * @param gesture the current gesture + */ + protected final void setGesture(Gesture gesture) { + mType = gesture; + } + + /** + * Returns an array of points that identify the currently + * detected gesture. If no gesture was detected, this returns + * an array of points with x and y set to 0. + * + * @return array of gesture points + */ + @NonNull + public final PointF[] getPoints() { + return mPoints; + } + + /** + * Utility function to access an item in the + * {@link #getPoints()} array. + * + * @param which the array position + * @return the point + */ + @NonNull + protected final PointF getPoint(int which) { + return mPoints[which]; + } + + /** + * For {@link GestureType#CONTINUOUS} gestures, returns the float value at the current + * gesture state. This means, for example, scaling the old value with a pinch factor, + * taking into account the minimum and maximum values. + * + * @param currValue the last value + * @param minValue the min possible value + * @param maxValue the max possible value + * @return the new continuous value + */ + public final float computeValue(float currValue, float minValue, float maxValue) { + return capValue(currValue, getValue(currValue, minValue, maxValue), minValue, maxValue); + } + + /** + * For {@link GestureType#CONTINUOUS} gestures, returns the float value at the current + * gesture state. This means, for example, scaling the old value with a pinch factor, + * taking into account the minimum and maximum values. + * + * @param currValue the last value + * @param minValue the min possible value + * @param maxValue the max possible value + * @return the new continuous value + */ + protected abstract float getValue(float currValue, float minValue, float maxValue); + + /** + * Checks for newValue to be between minValue and maxValue, + * and checks that it is 'far enough' from the oldValue, in order + * to reduce useless updates. + */ + private static float capValue(float oldValue, float newValue, float minValue, float maxValue) { + if (newValue < minValue) newValue = minValue; + if (newValue > maxValue) newValue = maxValue; + + float distance = (maxValue - minValue) / (float) GRANULARITY; + float half = distance / 2; + if (newValue >= oldValue - half && newValue <= oldValue + half) { + // Too close! Return the oldValue. + return oldValue; + } + return newValue; + } +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureParser.java b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureParser.java new file mode 100644 index 00000000..c44d85e1 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureParser.java @@ -0,0 +1,53 @@ +package com.otaliastudios.cameraview.gesture; + +import android.content.Context; +import android.content.res.TypedArray; + +import com.otaliastudios.cameraview.R; + +import androidx.annotation.NonNull; + +/** + * Parses gestures from XML attributes. + */ +public class GestureParser { + + private int tapAction; + private int longTapAction; + private int pinchAction; + private int horizontalScrollAction; + private int verticalScrollAction; + + public GestureParser(@NonNull TypedArray array) { + this.tapAction = array.getInteger(R.styleable.CameraView_cameraGestureTap, GestureAction.DEFAULT_TAP.value()); + this.longTapAction = array.getInteger(R.styleable.CameraView_cameraGestureLongTap, GestureAction.DEFAULT_LONG_TAP.value()); + this.pinchAction = array.getInteger(R.styleable.CameraView_cameraGesturePinch, GestureAction.DEFAULT_PINCH.value()); + this.horizontalScrollAction = array.getInteger(R.styleable.CameraView_cameraGestureScrollHorizontal, GestureAction.DEFAULT_SCROLL_HORIZONTAL.value()); + this.verticalScrollAction = array.getInteger(R.styleable.CameraView_cameraGestureScrollVertical, GestureAction.DEFAULT_SCROLL_VERTICAL.value()); + } + + private GestureAction get(int which) { + return GestureAction.fromValue(which); + } + + public GestureAction getTapAction() { + return get(tapAction); + } + + public GestureAction getLongTapAction() { + return get(longTapAction); + } + + public GestureAction getPinchAction() { + return get(pinchAction); + } + + public GestureAction getHorizontalScrollAction() { + return get(horizontalScrollAction); + } + + public GestureAction getVerticalScrollAction() { + return get(verticalScrollAction); + } + +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureType.java b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureType.java new file mode 100644 index 00000000..e4d96ea8 --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureType.java @@ -0,0 +1,31 @@ +package com.otaliastudios.cameraview.gesture; + + +import com.otaliastudios.cameraview.CameraView; + +import java.util.Arrays; +import java.util.List; + +import androidx.annotation.NonNull; + + +/** + * Gestures and gesture actions can both have a type. For a gesture to be able to be mapped to + * a certain {@link GestureAction}, both of them might be of the same type. + */ +public enum GestureType { + + /** + * Defines gestures or gesture actions that consist of a single operation. + * Gesture example: a tap. + * Gesture action example: taking a picture. + */ + ONE_SHOT, + + /** + * Defines gestures or gesture actions that consist of a continuous operation. + * Gesture example: pinching. + * Gesture action example: controlling zoom. + */ + CONTINUOUS +} diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/PinchGestureLayout.java b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/PinchGestureLayout.java similarity index 68% rename from cameraview/src/main/views/com/otaliastudios/cameraview/PinchGestureLayout.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/gesture/PinchGestureLayout.java index 249a7189..6e77b724 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/PinchGestureLayout.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/PinchGestureLayout.java @@ -1,4 +1,4 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.gesture; import android.annotation.SuppressLint; import android.content.Context; @@ -8,22 +8,20 @@ import androidx.annotation.NonNull; import android.view.MotionEvent; import android.view.ScaleGestureDetector; -class PinchGestureLayout extends GestureLayout { +/** + * A {@link GestureLayout} that detects {@link Gesture#PINCH} gestures. + */ +public class PinchGestureLayout extends GestureLayout { private final static float ADD_SENSITIVITY = 2f; ScaleGestureDetector mDetector; private boolean mNotify; - /* tests */ float mFactor = 0; + private float mFactor = 0; public PinchGestureLayout(@NonNull Context context) { - super(context); - } - - @Override - protected void onInitialize(@NonNull Context context) { - super.onInitialize(context); - mPoints = new PointF[]{ new PointF(0, 0), new PointF(0, 0) }; + super(context, 2); + setGesture(Gesture.PINCH); mDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() { @Override public boolean onScale(ScaleGestureDetector detector) { @@ -36,17 +34,10 @@ class PinchGestureLayout extends GestureLayout { if (Build.VERSION.SDK_INT >= 19) { mDetector.setQuickScaleEnabled(false); } - - // We listen only to the pinch type. - mType = Gesture.PINCH; } - - @SuppressLint("ClickableViewAccessibility") @Override - public boolean onTouchEvent(MotionEvent event) { - if (!mEnabled) return false; - + protected boolean handleTouchEvent(@NonNull MotionEvent event) { // Reset the mNotify flag on a new gesture. // This is to ensure that the mNotify flag stays on until the // previous gesture ends. @@ -59,11 +50,11 @@ class PinchGestureLayout extends GestureLayout { // Keep notifying CameraView as long as the gesture goes. if (mNotify) { - mPoints[0].x = event.getX(0); - mPoints[0].y = event.getY(0); + getPoint(0).x = event.getX(0); + getPoint(0).y = event.getY(0); if (event.getPointerCount() > 1) { - mPoints[1].x = event.getX(1); - mPoints[1].y = event.getY(1); + getPoint(1).x = event.getX(1); + getPoint(1).y = event.getY(1); } return true; } @@ -71,8 +62,8 @@ class PinchGestureLayout extends GestureLayout { } @Override - public float scaleValue(float currValue, float minValue, float maxValue) { - float add = mFactor; + public float getValue(float currValue, float minValue, float maxValue) { + float add = getFactor(); // ^ This works well if minValue = 0, maxValue = 1. // Account for the different range: add *= (maxValue - minValue); @@ -84,6 +75,11 @@ class PinchGestureLayout extends GestureLayout { } else if (add < 0) { add *= (currValue - minValue); } Nope, I don't like this, it slows everything down. */ - return capValue(currValue, currValue + add, minValue, maxValue); + return currValue + add; + } + + + /* for tests */ protected float getFactor() { + return mFactor; } } diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/ScrollGestureLayout.java b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/ScrollGestureLayout.java similarity index 68% rename from cameraview/src/main/views/com/otaliastudios/cameraview/ScrollGestureLayout.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/gesture/ScrollGestureLayout.java index 54ca6362..8ffa8da8 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/ScrollGestureLayout.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/ScrollGestureLayout.java @@ -1,4 +1,4 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.gesture; import android.annotation.SuppressLint; import android.content.Context; @@ -7,23 +7,23 @@ import androidx.annotation.NonNull; import android.view.GestureDetector; import android.view.MotionEvent; -class ScrollGestureLayout extends GestureLayout { +import com.otaliastudios.cameraview.CameraLogger; + +/** + * A {@link GestureLayout} that detects {@link Gesture#SCROLL_HORIZONTAL} + * and {@link Gesture#SCROLL_VERTICAL} gestures. + */ +public class ScrollGestureLayout extends GestureLayout { private static final String TAG = ScrollGestureLayout.class.getSimpleName(); private static final CameraLogger LOG = CameraLogger.create(TAG); private GestureDetector mDetector; private boolean mNotify; - /* tests */ float mFactor; + private float mFactor; public ScrollGestureLayout(@NonNull Context context) { - super(context); - } - - @Override - protected void onInitialize(@NonNull Context context) { - super.onInitialize(context); - mPoints = new PointF[]{ new PointF(0, 0), new PointF(0, 0) }; + super(context, 2); mDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override @@ -31,17 +31,17 @@ class ScrollGestureLayout extends GestureLayout { boolean horizontal; LOG.i("onScroll:", "distanceX="+distanceX, "distanceY="+distanceY); if (e1 == null || e2 == null) return false; // Got some crashes about this. - if (e1.getX() != mPoints[0].x || e1.getY() != mPoints[0].y) { + if (e1.getX() != getPoint(0).x || e1.getY() != getPoint(0).y) { // First step. We choose now if it's a vertical or horizontal scroll, and // stick to it for the whole gesture. horizontal = Math.abs(distanceX) >= Math.abs(distanceY); - mType = horizontal ? Gesture.SCROLL_HORIZONTAL : Gesture.SCROLL_VERTICAL; - mPoints[0].set(e1.getX(), e1.getY()); + setGesture(horizontal ? Gesture.SCROLL_HORIZONTAL : Gesture.SCROLL_VERTICAL); + getPoint(0).set(e1.getX(), e1.getY()); } else { // Not the first step. We already defined the type. - horizontal = mType == Gesture.SCROLL_HORIZONTAL; + horizontal = getGesture() == Gesture.SCROLL_HORIZONTAL; } - mPoints[1].set(e2.getX(), e2.getY()); + getPoint(1).set(e2.getX(), e2.getY()); mFactor = horizontal ? (distanceX / getWidth()) : (distanceY / getHeight()); mFactor = horizontal ? -mFactor : mFactor; // When vertical, up = positive mNotify = true; @@ -52,11 +52,8 @@ class ScrollGestureLayout extends GestureLayout { mDetector.setIsLongpressEnabled(false); // Looks important. } - @SuppressLint("ClickableViewAccessibility") @Override - public boolean onTouchEvent(MotionEvent event) { - if (!mEnabled) return false; - + protected boolean handleTouchEvent(@NonNull MotionEvent event) { // Reset the mNotify flag on a new gesture. // This is to ensure that the mNotify flag stays on until the // previous gesture ends. @@ -68,20 +65,23 @@ class ScrollGestureLayout extends GestureLayout { mDetector.onTouchEvent(event); // Keep notifying CameraView as long as the gesture goes. - if (mNotify) LOG.i("Notifying a gesture of type", mType.name()); + if (mNotify) LOG.i("Notifying a gesture of type", getGesture().name()); return mNotify; } @Override - public float scaleValue(float currValue, float minValue, float maxValue) { - float delta = mFactor; // -1 ... 1 + public float getValue(float currValue, float minValue, float maxValue) { + float delta = getFactor(); // -1 ... 1 // ^ This works well if minValue = 0, maxValue = 1. // Account for the different range: delta *= (maxValue - minValue); // -(max-min) ... (max-min) delta *= 2; // Add some sensitivity. - return capValue(currValue, currValue + delta, minValue, maxValue); + return currValue + delta; } + /* for tests */ protected float getFactor() { + return mFactor; + } } diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/TapGestureLayout.java b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/TapGestureLayout.java similarity index 87% rename from cameraview/src/main/views/com/otaliastudios/cameraview/TapGestureLayout.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/gesture/TapGestureLayout.java index f99ec852..6d388d54 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/TapGestureLayout.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/gesture/TapGestureLayout.java @@ -1,4 +1,4 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.gesture; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -14,7 +14,13 @@ import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; -class TapGestureLayout extends GestureLayout { +import com.otaliastudios.cameraview.R; + +/** + * A {@link GestureLayout} that detects {@link Gesture#TAP} + * and {@link Gesture#LONG_TAP} gestures. + */ +public class TapGestureLayout extends GestureLayout { private GestureDetector mDetector; private boolean mNotify; @@ -23,19 +29,13 @@ class TapGestureLayout extends GestureLayout { private ImageView mFocusMarkerFill; public TapGestureLayout(@NonNull Context context) { - super(context); - } - - @Override - protected void onInitialize(@NonNull Context context) { - super.onInitialize(context); - mPoints = new PointF[]{ new PointF(0, 0) }; + super(context, 1); mDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp(MotionEvent e) { mNotify = true; - mType = Gesture.TAP; + setGesture(Gesture.TAP); return true; } @@ -50,7 +50,7 @@ class TapGestureLayout extends GestureLayout { @Override public void onLongPress(MotionEvent e) { mNotify = true; - mType = Gesture.LONG_TAP; + setGesture(Gesture.LONG_TAP); } }); @@ -63,11 +63,8 @@ class TapGestureLayout extends GestureLayout { mFocusMarkerFill = findViewById(R.id.fill); } - @SuppressLint("ClickableViewAccessibility") @Override - public boolean onTouchEvent(MotionEvent event) { - if (!mEnabled) return false; - + protected boolean handleTouchEvent(@NonNull MotionEvent event) { // Reset the mNotify flag on a new gesture. // This is to ensure that the mNotify flag stays on until the // previous gesture ends. @@ -80,15 +77,15 @@ class TapGestureLayout extends GestureLayout { // Keep notifying CameraView as long as the gesture goes. if (mNotify) { - mPoints[0].x = event.getX(); - mPoints[0].y = event.getY(); + getPoint(0).x = event.getX(); + getPoint(0).y = event.getY(); return true; } return false; } @Override - public float scaleValue(float currValue, float minValue, float maxValue) { + public float getValue(float currValue, float minValue, float maxValue) { return 0; } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/AspectRatio.java b/cameraview/src/main/java/com/otaliastudios/cameraview/size/AspectRatio.java similarity index 92% rename from cameraview/src/main/java/com/otaliastudios/cameraview/AspectRatio.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/size/AspectRatio.java index c1de522d..c8f0f11c 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/AspectRatio.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/size/AspectRatio.java @@ -1,4 +1,4 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.size; import androidx.annotation.NonNull; @@ -10,7 +10,7 @@ import java.util.HashMap; */ public class AspectRatio implements Comparable { - final static HashMap sCache = new HashMap<>(16); + private final static HashMap sCache = new HashMap<>(16); /** * Creates an aspect ratio for the given size. @@ -125,9 +125,13 @@ public class AspectRatio implements Comparable { return -1; } + /** + * Returns a flipped aspect ratio, which means inverting its dimensions. + * @return a flipped aspect ratio + */ @SuppressWarnings("SuspiciousNameCombination") @NonNull - public AspectRatio inverse() { + public AspectRatio flip() { return AspectRatio.of(mY, mX); } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Size.java b/cameraview/src/main/java/com/otaliastudios/cameraview/size/Size.java similarity index 81% rename from cameraview/src/main/java/com/otaliastudios/cameraview/Size.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/size/Size.java index 9d413dd2..679fd23f 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Size.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/size/Size.java @@ -1,4 +1,4 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.size; import androidx.annotation.NonNull; @@ -10,7 +10,7 @@ public class Size implements Comparable { private final int mWidth; private final int mHeight; - Size(int width, int height) { + public Size(int width, int height) { mWidth = width; mHeight = height; } @@ -23,8 +23,14 @@ public class Size implements Comparable { return mHeight; } + /** + * Returns a flipped size, with height equal to this size's width + * and width equal to this size's height. + * + * @return a flipped size + */ @SuppressWarnings("SuspiciousNameCombination") - Size flip() { + public Size flip() { return new Size(mHeight, mWidth); } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/SizeSelector.java b/cameraview/src/main/java/com/otaliastudios/cameraview/size/SizeSelector.java similarity index 84% rename from cameraview/src/main/java/com/otaliastudios/cameraview/SizeSelector.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/size/SizeSelector.java index 35e1b9c9..04317735 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/SizeSelector.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/size/SizeSelector.java @@ -1,4 +1,6 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.size; + +import com.otaliastudios.cameraview.size.Size; import androidx.annotation.NonNull; diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/size/SizeSelectorParser.java b/cameraview/src/main/java/com/otaliastudios/cameraview/size/SizeSelectorParser.java new file mode 100644 index 00000000..b24cb2ed --- /dev/null +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/size/SizeSelectorParser.java @@ -0,0 +1,102 @@ +package com.otaliastudios.cameraview.size; + +import android.content.Context; +import android.content.res.TypedArray; + +import com.otaliastudios.cameraview.R; +import com.otaliastudios.cameraview.controls.Audio; +import com.otaliastudios.cameraview.controls.Facing; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.controls.Grid; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.Mode; +import com.otaliastudios.cameraview.controls.Preview; +import com.otaliastudios.cameraview.controls.VideoCodec; +import com.otaliastudios.cameraview.controls.WhiteBalance; + +import java.util.ArrayList; +import java.util.List; + +import androidx.annotation.NonNull; + +/** + * Parses size selectors from XML attributes. + */ +public class SizeSelectorParser { + + private SizeSelector pictureSizeSelector; + private SizeSelector videoSizeSelector; + + public SizeSelectorParser(@NonNull TypedArray array) { + List pictureConstraints = new ArrayList<>(3); + if (array.hasValue(R.styleable.CameraView_cameraPictureSizeMinWidth)) { + pictureConstraints.add(SizeSelectors.minWidth(array.getInteger(R.styleable.CameraView_cameraPictureSizeMinWidth, 0))); + } + if (array.hasValue(R.styleable.CameraView_cameraPictureSizeMaxWidth)) { + pictureConstraints.add(SizeSelectors.maxWidth(array.getInteger(R.styleable.CameraView_cameraPictureSizeMaxWidth, 0))); + } + if (array.hasValue(R.styleable.CameraView_cameraPictureSizeMinHeight)) { + pictureConstraints.add(SizeSelectors.minHeight(array.getInteger(R.styleable.CameraView_cameraPictureSizeMinHeight, 0))); + } + if (array.hasValue(R.styleable.CameraView_cameraPictureSizeMaxHeight)) { + pictureConstraints.add(SizeSelectors.maxHeight(array.getInteger(R.styleable.CameraView_cameraPictureSizeMaxHeight, 0))); + } + if (array.hasValue(R.styleable.CameraView_cameraPictureSizeMinArea)) { + pictureConstraints.add(SizeSelectors.minArea(array.getInteger(R.styleable.CameraView_cameraPictureSizeMinArea, 0))); + } + if (array.hasValue(R.styleable.CameraView_cameraPictureSizeMaxArea)) { + pictureConstraints.add(SizeSelectors.maxArea(array.getInteger(R.styleable.CameraView_cameraPictureSizeMaxArea, 0))); + } + if (array.hasValue(R.styleable.CameraView_cameraPictureSizeAspectRatio)) { + //noinspection ConstantConditions + pictureConstraints.add(SizeSelectors.aspectRatio(AspectRatio.parse(array.getString(R.styleable.CameraView_cameraPictureSizeAspectRatio)), 0)); + } + + if (array.getBoolean(R.styleable.CameraView_cameraPictureSizeSmallest, false)) pictureConstraints.add(SizeSelectors.smallest()); + if (array.getBoolean(R.styleable.CameraView_cameraPictureSizeBiggest, false)) pictureConstraints.add(SizeSelectors.biggest()); + pictureSizeSelector = !pictureConstraints.isEmpty() ? + SizeSelectors.and(pictureConstraints.toArray(new SizeSelector[0])) : + SizeSelectors.biggest(); + + // Video size selector + List videoConstraints = new ArrayList<>(3); + if (array.hasValue(R.styleable.CameraView_cameraVideoSizeMinWidth)) { + videoConstraints.add(SizeSelectors.minWidth(array.getInteger(R.styleable.CameraView_cameraVideoSizeMinWidth, 0))); + } + if (array.hasValue(R.styleable.CameraView_cameraVideoSizeMaxWidth)) { + videoConstraints.add(SizeSelectors.maxWidth(array.getInteger(R.styleable.CameraView_cameraVideoSizeMaxWidth, 0))); + } + if (array.hasValue(R.styleable.CameraView_cameraVideoSizeMinHeight)) { + videoConstraints.add(SizeSelectors.minHeight(array.getInteger(R.styleable.CameraView_cameraVideoSizeMinHeight, 0))); + } + if (array.hasValue(R.styleable.CameraView_cameraVideoSizeMaxHeight)) { + videoConstraints.add(SizeSelectors.maxHeight(array.getInteger(R.styleable.CameraView_cameraVideoSizeMaxHeight, 0))); + } + if (array.hasValue(R.styleable.CameraView_cameraVideoSizeMinArea)) { + videoConstraints.add(SizeSelectors.minArea(array.getInteger(R.styleable.CameraView_cameraVideoSizeMinArea, 0))); + } + if (array.hasValue(R.styleable.CameraView_cameraVideoSizeMaxArea)) { + videoConstraints.add(SizeSelectors.maxArea(array.getInteger(R.styleable.CameraView_cameraVideoSizeMaxArea, 0))); + } + if (array.hasValue(R.styleable.CameraView_cameraVideoSizeAspectRatio)) { + //noinspection ConstantConditions + videoConstraints.add(SizeSelectors.aspectRatio(AspectRatio.parse(array.getString(R.styleable.CameraView_cameraVideoSizeAspectRatio)), 0)); + } + if (array.getBoolean(R.styleable.CameraView_cameraVideoSizeSmallest, false)) videoConstraints.add(SizeSelectors.smallest()); + if (array.getBoolean(R.styleable.CameraView_cameraVideoSizeBiggest, false)) videoConstraints.add(SizeSelectors.biggest()); + videoSizeSelector = !videoConstraints.isEmpty() ? + SizeSelectors.and(videoConstraints.toArray(new SizeSelector[0])) : + SizeSelectors.biggest(); + } + + @NonNull + public SizeSelector getPictureSizeSelector() { + return pictureSizeSelector; + } + + @NonNull + public SizeSelector getVideoSizeSelector() { + return videoSizeSelector; + } + +} diff --git a/cameraview/src/main/utils/com/otaliastudios/cameraview/SizeSelectors.java b/cameraview/src/main/java/com/otaliastudios/cameraview/size/SizeSelectors.java similarity index 97% rename from cameraview/src/main/utils/com/otaliastudios/cameraview/SizeSelectors.java rename to cameraview/src/main/java/com/otaliastudios/cameraview/size/SizeSelectors.java index 138a982a..5289035e 100644 --- a/cameraview/src/main/utils/com/otaliastudios/cameraview/SizeSelectors.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/size/SizeSelectors.java @@ -1,4 +1,8 @@ -package com.otaliastudios.cameraview; +package com.otaliastudios.cameraview.size; + +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; +import com.otaliastudios.cameraview.size.SizeSelector; import androidx.annotation.NonNull; diff --git a/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraLogger.java b/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraLogger.java index 6a75d42c..eaaa8bf2 100644 --- a/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraLogger.java +++ b/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraLogger.java @@ -71,7 +71,14 @@ public final class CameraLogger { }); } - static CameraLogger create(@NonNull String tag) { + /** + * Creates a CameraLogger that will stream logs into the + * internal logs and dispatch them to {@link Logger}s. + * + * @param tag the logger tag + * @return a new CameraLogger + */ + public static CameraLogger create(@NonNull String tag) { return new CameraLogger(tag); } @@ -121,18 +128,34 @@ public final class CameraLogger { return sLevel <= messageLevel && sLoggers.size() > 0; } - void v(@NonNull Object... data) { + /** + * Log to the verbose channel. + * @param data log contents + */ + public void v(@NonNull Object... data) { log(LEVEL_VERBOSE, data); } - void i(@NonNull Object... data) { + /** + * Log to the info channel. + * @param data log contents + */ + public void i(@NonNull Object... data) { log(LEVEL_INFO, data); } + /** + * Log to the warning channel. + * @param data log contents + */ void w(@NonNull Object... data) { log(LEVEL_WARNING, data); } + /** + * Log to the error channel. + * @param data log contents + */ void e(@NonNull Object... data) { log(LEVEL_ERROR, data); } diff --git a/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraUtils.java b/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraUtils.java index 7870d53e..80ccea00 100644 --- a/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraUtils.java +++ b/cameraview/src/main/utils/com/otaliastudios/cameraview/CameraUtils.java @@ -7,6 +7,9 @@ import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.hardware.Camera; import android.os.Handler; + +import com.otaliastudios.cameraview.controls.Facing; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; @@ -15,7 +18,6 @@ import androidx.exifinterface.media.ExifInterface; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; diff --git a/cameraview/src/main/utils/com/otaliastudios/cameraview/CropHelper.java b/cameraview/src/main/utils/com/otaliastudios/cameraview/CropHelper.java index 1d09e023..768a0172 100644 --- a/cameraview/src/main/utils/com/otaliastudios/cameraview/CropHelper.java +++ b/cameraview/src/main/utils/com/otaliastudios/cameraview/CropHelper.java @@ -1,6 +1,10 @@ package com.otaliastudios.cameraview; import android.graphics.Rect; + +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; + import androidx.annotation.NonNull; class CropHelper { diff --git a/cameraview/src/main/utils/com/otaliastudios/cameraview/RotationHelper.java b/cameraview/src/main/utils/com/otaliastudios/cameraview/RotationHelper.java index fb3bb955..311968ff 100644 --- a/cameraview/src/main/utils/com/otaliastudios/cameraview/RotationHelper.java +++ b/cameraview/src/main/utils/com/otaliastudios/cameraview/RotationHelper.java @@ -1,5 +1,7 @@ package com.otaliastudios.cameraview; +import com.otaliastudios.cameraview.size.Size; + import androidx.annotation.NonNull; /** diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java b/cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java index 9469ef61..934d0f7f 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/CameraPreview.java @@ -6,6 +6,8 @@ import androidx.annotation.Nullable; import android.view.View; import android.view.ViewGroup; +import com.otaliastudios.cameraview.size.Size; + /** * A CameraPreview takes in input stream from the {@link CameraController}, and streams it * into an output surface that belongs to the view hierarchy. diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/GestureLayout.java b/cameraview/src/main/views/com/otaliastudios/cameraview/GestureLayout.java deleted file mode 100644 index 403bfdfa..00000000 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/GestureLayout.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.otaliastudios.cameraview; - -import android.content.Context; -import android.graphics.PointF; -import androidx.annotation.NonNull; -import android.view.MotionEvent; -import android.widget.FrameLayout; - -abstract class GestureLayout extends FrameLayout { - - // The number of possible values between minValue and maxValue, for the scaleValue method. - // We could make this non-static (e.g. larger granularity for exposure correction). - private final static int GRANULARITY = 50; - - protected boolean mEnabled; - protected Gesture mType; - protected PointF[] mPoints; - - public GestureLayout(@NonNull Context context) { - super(context); - onInitialize(context); - } - - protected void onInitialize(@NonNull Context context) { - } - - public void enable(boolean enable) { - mEnabled = enable; - } - - public boolean enabled() { - return mEnabled; - } - - public abstract boolean onTouchEvent(MotionEvent event); - - @NonNull - public final Gesture getGestureType() { - return mType; - } - - // For tests. - void setGestureType(@NonNull Gesture type) { - mType = type; - } - - @NonNull - public final PointF[] getPoints() { - return mPoints; - } - - // Implementors should call capValue at the end. - public abstract float scaleValue(float currValue, float minValue, float maxValue); - - // Checks for newValue to be between minValue and maxValue, - // and checks that it is 'far enough' from the oldValue, in order - // to reduce useless updates. - protected static float capValue(float oldValue, float newValue, float minValue, float maxValue) { - if (newValue < minValue) newValue = minValue; - if (newValue > maxValue) newValue = maxValue; - - float distance = (maxValue - minValue) / (float) GRANULARITY; - float half = distance / 2; - if (newValue >= oldValue - half && newValue <= oldValue + half) { - // Too close! Return the oldValue. - return oldValue; - } - return newValue; - } -} diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/GlCameraPreview.java b/cameraview/src/main/views/com/otaliastudios/cameraview/GlCameraPreview.java index 4a454577..883d6237 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/GlCameraPreview.java +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/GlCameraPreview.java @@ -12,6 +12,8 @@ import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; +import com.otaliastudios.cameraview.size.AspectRatio; + import java.util.Collections; import java.util.HashSet; import java.util.Set; diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/GridLinesLayout.java b/cameraview/src/main/views/com/otaliastudios/cameraview/GridLinesLayout.java index be586cce..d7faf122 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/GridLinesLayout.java +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/GridLinesLayout.java @@ -12,6 +12,8 @@ import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; +import com.otaliastudios.cameraview.controls.Grid; + class GridLinesLayout extends View { private final static float GOLDEN_RATIO_INV = 0.61803398874989f; diff --git a/cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java b/cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java index 05b641fc..dfac157b 100644 --- a/cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java +++ b/cameraview/src/main/views/com/otaliastudios/cameraview/TextureCameraPreview.java @@ -10,6 +10,8 @@ import android.view.TextureView; import android.view.View; import android.view.ViewGroup; +import com.otaliastudios.cameraview.size.AspectRatio; + class TextureCameraPreview extends CameraPreview { private View mRootView; diff --git a/cameraview/src/test/java/com/otaliastudios/cameraview/AspectRatioTest.java b/cameraview/src/test/java/com/otaliastudios/cameraview/AspectRatioTest.java index 98af6c1f..75973680 100644 --- a/cameraview/src/test/java/com/otaliastudios/cameraview/AspectRatioTest.java +++ b/cameraview/src/test/java/com/otaliastudios/cameraview/AspectRatioTest.java @@ -1,6 +1,9 @@ package com.otaliastudios.cameraview; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; + import org.junit.Test; import static org.junit.Assert.*; @@ -47,7 +50,7 @@ public class AspectRatioTest { @Test public void testInverse() { AspectRatio ratio = AspectRatio.of(50, 10); - AspectRatio inverse = ratio.inverse(); + AspectRatio inverse = ratio.flip(); assertEquals(inverse.getX(), 1f, 0); assertEquals(inverse.getY(), 5f, 0); } diff --git a/cameraview/src/test/java/com/otaliastudios/cameraview/FrameManagerTest.java b/cameraview/src/test/java/com/otaliastudios/cameraview/FrameManagerTest.java index 6a9dd72d..951d8154 100644 --- a/cameraview/src/test/java/com/otaliastudios/cameraview/FrameManagerTest.java +++ b/cameraview/src/test/java/com/otaliastudios/cameraview/FrameManagerTest.java @@ -1,13 +1,11 @@ package com.otaliastudios.cameraview; -import android.graphics.ImageFormat; +import com.otaliastudios.cameraview.size.Size; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -15,13 +13,11 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; public class FrameManagerTest { diff --git a/cameraview/src/test/java/com/otaliastudios/cameraview/FrameTest.java b/cameraview/src/test/java/com/otaliastudios/cameraview/FrameTest.java index 22f27942..da23e478 100644 --- a/cameraview/src/test/java/com/otaliastudios/cameraview/FrameTest.java +++ b/cameraview/src/test/java/com/otaliastudios/cameraview/FrameTest.java @@ -3,10 +3,11 @@ package com.otaliastudios.cameraview; import android.graphics.ImageFormat; +import com.otaliastudios.cameraview.size.Size; + import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; import static junit.framework.Assert.assertNotNull; import static org.junit.Assert.assertArrayEquals; diff --git a/cameraview/src/test/java/com/otaliastudios/cameraview/SizeSelectorsTest.java b/cameraview/src/test/java/com/otaliastudios/cameraview/SizeSelectorsTest.java index 1366fd0e..f194f69b 100644 --- a/cameraview/src/test/java/com/otaliastudios/cameraview/SizeSelectorsTest.java +++ b/cameraview/src/test/java/com/otaliastudios/cameraview/SizeSelectorsTest.java @@ -1,6 +1,11 @@ package com.otaliastudios.cameraview; +import com.otaliastudios.cameraview.size.AspectRatio; +import com.otaliastudios.cameraview.size.Size; +import com.otaliastudios.cameraview.size.SizeSelector; +import com.otaliastudios.cameraview.size.SizeSelectors; + import androidx.annotation.NonNull; import org.junit.Before; diff --git a/cameraview/src/test/java/com/otaliastudios/cameraview/SizeTest.java b/cameraview/src/test/java/com/otaliastudios/cameraview/SizeTest.java index 67e9892a..9b24854d 100644 --- a/cameraview/src/test/java/com/otaliastudios/cameraview/SizeTest.java +++ b/cameraview/src/test/java/com/otaliastudios/cameraview/SizeTest.java @@ -1,6 +1,8 @@ package com.otaliastudios.cameraview; +import com.otaliastudios.cameraview.size.Size; + import org.junit.Test; import static org.junit.Assert.*; 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 ea80ab64..016a405a 100644 --- a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java @@ -2,13 +2,11 @@ package com.otaliastudios.cameraview.demo; import android.content.Intent; import android.content.pm.PackageManager; -import android.net.Uri; import android.os.Bundle; import androidx.annotation.NonNull; import com.google.android.material.bottomsheet.BottomSheetBehavior; import androidx.appcompat.app.AppCompatActivity; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; @@ -19,10 +17,8 @@ import com.otaliastudios.cameraview.CameraListener; import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraOptions; import com.otaliastudios.cameraview.CameraView; -import com.otaliastudios.cameraview.Frame; -import com.otaliastudios.cameraview.FrameProcessor; import com.otaliastudios.cameraview.PictureResult; -import com.otaliastudios.cameraview.Mode; +import com.otaliastudios.cameraview.controls.Mode; import com.otaliastudios.cameraview.VideoResult; import java.io.File; diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java index f8620dd9..be88a3ee 100644 --- a/demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java @@ -5,17 +5,17 @@ import androidx.annotation.NonNull; import android.view.View; import android.view.ViewGroup; -import com.otaliastudios.cameraview.Audio; +import com.otaliastudios.cameraview.controls.Audio; import com.otaliastudios.cameraview.CameraOptions; import com.otaliastudios.cameraview.CameraView; -import com.otaliastudios.cameraview.Flash; -import com.otaliastudios.cameraview.Gesture; -import com.otaliastudios.cameraview.GestureAction; -import com.otaliastudios.cameraview.Grid; -import com.otaliastudios.cameraview.Hdr; -import com.otaliastudios.cameraview.Mode; -import com.otaliastudios.cameraview.VideoCodec; -import com.otaliastudios.cameraview.WhiteBalance; +import com.otaliastudios.cameraview.controls.Flash; +import com.otaliastudios.cameraview.gesture.Gesture; +import com.otaliastudios.cameraview.gesture.GestureAction; +import com.otaliastudios.cameraview.controls.Grid; +import com.otaliastudios.cameraview.controls.Hdr; +import com.otaliastudios.cameraview.controls.Mode; +import com.otaliastudios.cameraview.controls.VideoCodec; +import com.otaliastudios.cameraview.controls.WhiteBalance; import java.util.ArrayList; import java.util.Collection; @@ -157,7 +157,7 @@ public enum Control { case AUDIO: case VIDEO_CODEC: case HDR: - camera.set((com.otaliastudios.cameraview.Control) value); + camera.set((com.otaliastudios.cameraview.controls.Control) value); break; case PINCH: camera.mapGesture(Gesture.PINCH, (GestureAction) value); diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/PicturePreviewActivity.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/PicturePreviewActivity.java index 97fba873..2df4aaf1 100644 --- a/demo/src/main/java/com/otaliastudios/cameraview/demo/PicturePreviewActivity.java +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/PicturePreviewActivity.java @@ -9,7 +9,7 @@ import androidx.annotation.Nullable; import android.util.Log; import android.widget.ImageView; -import com.otaliastudios.cameraview.AspectRatio; +import com.otaliastudios.cameraview.size.AspectRatio; import com.otaliastudios.cameraview.BitmapCallback; import com.otaliastudios.cameraview.PictureResult;