Simplify GestureLayouts, now simple classes

pull/494/head
Mattia Iavarone 6 years ago
parent 64714017b8
commit c617a7da2a
  1. 6
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewCallbacksTest.java
  2. 26
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
  3. 43
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/gesture/GestureFinderTest.java
  4. 31
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/gesture/PinchGestureFinderTest.java
  5. 39
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/gesture/ScrollGestureFinderTest.java
  6. 47
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/gesture/TapGestureFinderTest.java
  7. 94
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  8. 19
      cameraview/src/main/java/com/otaliastudios/cameraview/gesture/GestureFinder.java
  9. 13
      cameraview/src/main/java/com/otaliastudios/cameraview/gesture/PinchGestureFinder.java
  10. 15
      cameraview/src/main/java/com/otaliastudios/cameraview/gesture/ScrollGestureFinder.java
  11. 22
      cameraview/src/main/java/com/otaliastudios/cameraview/gesture/TapGestureFinder.java

@ -195,7 +195,7 @@ public class CameraViewCallbacksTest extends BaseTest {
@Test @Test
public void testDispatchOnFocusStart() { public void testDispatchOnFocusStart() {
// Enable tap gesture. // Enable tap gesture.
// Can't mock package protected. camera.mTapGestureLayout = mock(TapGestureLayout.class); // Can't mock package protected. camera.mTapGestureFinder = mock(TapGestureLayout.class);
camera.mapGesture(Gesture.TAP, GestureAction.AUTO_FOCUS); camera.mapGesture(Gesture.TAP, GestureAction.AUTO_FOCUS);
AutoFocusMarker marker = mock(AutoFocusMarker.class); AutoFocusMarker marker = mock(AutoFocusMarker.class);
MarkerLayout markerLayout = mock(MarkerLayout.class); MarkerLayout markerLayout = mock(MarkerLayout.class);
@ -215,7 +215,7 @@ public class CameraViewCallbacksTest extends BaseTest {
@Test @Test
public void testDispatchOnFocusEnd() { public void testDispatchOnFocusEnd() {
// Enable tap gesture. // Enable tap gesture.
// Can't mock package protected. camera.mTapGestureLayout = mock(TapGestureLayout.class); // Can't mock package protected. camera.mTapGestureFinder = mock(TapGestureLayout.class);
camera.mapGesture(Gesture.TAP, GestureAction.AUTO_FOCUS); camera.mapGesture(Gesture.TAP, GestureAction.AUTO_FOCUS);
AutoFocusMarker marker = mock(AutoFocusMarker.class); AutoFocusMarker marker = mock(AutoFocusMarker.class);
camera.setAutoFocusMarker(marker); camera.setAutoFocusMarker(marker);
@ -229,7 +229,7 @@ public class CameraViewCallbacksTest extends BaseTest {
verify(listener, times(1)).onAutoFocusEnd(success, point); verify(listener, times(1)).onAutoFocusEnd(success, point);
verify(marker, times(1)).onAutoFocusEnd(AutoFocusTrigger.GESTURE, success, point); verify(marker, times(1)).onAutoFocusEnd(AutoFocusTrigger.GESTURE, success, point);
// Can't mock package protected. verify(camera.mTapGestureLayout, times(1)).onAutoFocusEnd(success); // Can't mock package protected. verify(camera.mTapGestureFinder, times(1)).onAutoFocusEnd(success);
} }
@Test @Test

@ -28,9 +28,9 @@ import com.otaliastudios.cameraview.controls.Mode;
import com.otaliastudios.cameraview.controls.VideoCodec; import com.otaliastudios.cameraview.controls.VideoCodec;
import com.otaliastudios.cameraview.controls.WhiteBalance; import com.otaliastudios.cameraview.controls.WhiteBalance;
import com.otaliastudios.cameraview.gesture.GestureParser; import com.otaliastudios.cameraview.gesture.GestureParser;
import com.otaliastudios.cameraview.gesture.PinchGestureLayout; import com.otaliastudios.cameraview.gesture.PinchGestureFinder;
import com.otaliastudios.cameraview.gesture.ScrollGestureLayout; import com.otaliastudios.cameraview.gesture.ScrollGestureFinder;
import com.otaliastudios.cameraview.gesture.TapGestureLayout; import com.otaliastudios.cameraview.gesture.TapGestureFinder;
import com.otaliastudios.cameraview.engine.MockCameraEngine; import com.otaliastudios.cameraview.engine.MockCameraEngine;
import com.otaliastudios.cameraview.internal.utils.Op; import com.otaliastudios.cameraview.internal.utils.Op;
import com.otaliastudios.cameraview.markers.AutoFocusMarker; import com.otaliastudios.cameraview.markers.AutoFocusMarker;
@ -201,21 +201,21 @@ public class CameraViewTest extends BaseTest {
// PinchGestureLayout // PinchGestureLayout
cameraView.mapGesture(Gesture.PINCH, GestureAction.ZOOM); cameraView.mapGesture(Gesture.PINCH, GestureAction.ZOOM);
assertTrue(cameraView.mPinchGestureLayout.isActive()); assertTrue(cameraView.mPinchGestureFinder.isActive());
cameraView.clearGesture(Gesture.PINCH); cameraView.clearGesture(Gesture.PINCH);
assertFalse(cameraView.mPinchGestureLayout.isActive()); assertFalse(cameraView.mPinchGestureFinder.isActive());
// TapGestureLayout // TapGestureLayout
cameraView.mapGesture(Gesture.TAP, GestureAction.TAKE_PICTURE); cameraView.mapGesture(Gesture.TAP, GestureAction.TAKE_PICTURE);
assertTrue(cameraView.mTapGestureLayout.isActive()); assertTrue(cameraView.mTapGestureFinder.isActive());
cameraView.clearGesture(Gesture.TAP); cameraView.clearGesture(Gesture.TAP);
assertFalse(cameraView.mPinchGestureLayout.isActive()); assertFalse(cameraView.mPinchGestureFinder.isActive());
// ScrollGestureLayout // ScrollGestureLayout
cameraView.mapGesture(Gesture.SCROLL_HORIZONTAL, GestureAction.ZOOM); cameraView.mapGesture(Gesture.SCROLL_HORIZONTAL, GestureAction.ZOOM);
assertTrue(cameraView.mScrollGestureLayout.isActive()); assertTrue(cameraView.mScrollGestureFinder.isActive());
cameraView.clearGesture(Gesture.SCROLL_HORIZONTAL); cameraView.clearGesture(Gesture.SCROLL_HORIZONTAL);
assertFalse(cameraView.mScrollGestureLayout.isActive()); assertFalse(cameraView.mScrollGestureFinder.isActive());
} }
//endregion //endregion
@ -231,7 +231,7 @@ public class CameraViewTest extends BaseTest {
ui(new Runnable() { ui(new Runnable() {
@Override @Override
public void run() { public void run() {
cameraView.mTapGestureLayout = new TapGestureLayout(cameraView.getContext()) { cameraView.mTapGestureFinder = new TapGestureFinder(cameraView.getContext()) {
protected boolean handleTouchEvent(@NonNull MotionEvent event) { protected boolean handleTouchEvent(@NonNull MotionEvent event) {
setGesture(Gesture.TAP); setGesture(Gesture.TAP);
return true; return true;
@ -253,7 +253,7 @@ public class CameraViewTest extends BaseTest {
ui(new Runnable() { ui(new Runnable() {
@Override @Override
public void run() { public void run() {
cameraView.mTapGestureLayout = new TapGestureLayout(cameraView.getContext()) { cameraView.mTapGestureFinder = new TapGestureFinder(cameraView.getContext()) {
protected boolean handleTouchEvent(@NonNull MotionEvent event) { protected boolean handleTouchEvent(@NonNull MotionEvent event) {
setGesture(Gesture.TAP); setGesture(Gesture.TAP);
return true; return true;
@ -280,7 +280,7 @@ public class CameraViewTest extends BaseTest {
ui(new Runnable() { ui(new Runnable() {
@Override @Override
public void run() { public void run() {
cameraView.mPinchGestureLayout = new PinchGestureLayout(cameraView.getContext()) { cameraView.mPinchGestureFinder = new PinchGestureFinder(cameraView.getContext()) {
@Override @Override
protected boolean handleTouchEvent(@NonNull MotionEvent event) { protected boolean handleTouchEvent(@NonNull MotionEvent event) {
setGesture(Gesture.PINCH); setGesture(Gesture.PINCH);
@ -321,7 +321,7 @@ public class CameraViewTest extends BaseTest {
ui(new Runnable() { ui(new Runnable() {
@Override @Override
public void run() { public void run() {
cameraView.mScrollGestureLayout = new ScrollGestureLayout(cameraView.getContext()) { cameraView.mScrollGestureFinder = new ScrollGestureFinder(cameraView.getContext()) {
@Override @Override
protected boolean handleTouchEvent(@NonNull MotionEvent event) { protected boolean handleTouchEvent(@NonNull MotionEvent event) {
setGesture(Gesture.SCROLL_HORIZONTAL); setGesture(Gesture.SCROLL_HORIZONTAL);

@ -4,11 +4,14 @@ package com.otaliastudios.cameraview.gesture;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull;
import androidx.test.espresso.ViewInteraction; import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.matcher.RootMatchers; import androidx.test.espresso.matcher.RootMatchers;
import androidx.test.rule.ActivityTestRule; import androidx.test.rule.ActivityTestRule;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.otaliastudios.cameraview.BaseTest; import com.otaliastudios.cameraview.BaseTest;
import com.otaliastudios.cameraview.TestActivity; import com.otaliastudios.cameraview.TestActivity;
@ -21,17 +24,19 @@ import org.junit.Rule;
import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.Espresso.onView;
@TargetApi(17) @TargetApi(17)
public abstract class GestureLayoutTest<T extends GestureLayout> extends BaseTest { public abstract class GestureFinderTest<T extends GestureFinder> extends BaseTest {
protected abstract T create(Context context); protected abstract T createFinder(@NonNull GestureFinder.Controller controller);
@Rule @Rule
public ActivityTestRule<TestActivity> rule = new ActivityTestRule<>(TestActivity.class); public ActivityTestRule<TestActivity> rule = new ActivityTestRule<>(TestActivity.class);
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected T layout; protected T finder;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
protected Op<Gesture> touch; protected Op<Gesture> touchOp;
@SuppressWarnings("WeakerAccess")
protected ViewGroup layout;
@Before @Before
public void setUp() { public void setUp() {
@ -39,16 +44,17 @@ public abstract class GestureLayoutTest<T extends GestureLayout> extends BaseTes
@Override @Override
public void run() { public void run() {
TestActivity a = rule.getActivity(); TestActivity a = rule.getActivity();
layout = create(a); layout = new FrameLayout(a);
layout.setActive(true); finder = createFinder(new Controller());
finder.setActive(true);
a.inflate(layout); a.inflate(layout);
touch = new Op<>(); touchOp = new Op<>();
layout.setOnTouchListener(new View.OnTouchListener() { layout.setOnTouchListener(new View.OnTouchListener() {
@Override @Override
public boolean onTouch(View view, MotionEvent motionEvent) { public boolean onTouch(View view, MotionEvent motionEvent) {
boolean found = layout.onTouchEvent(motionEvent); boolean found = finder.onTouchEvent(motionEvent);
if (found) touch.end(layout.getGesture()); if (found) touchOp.end(finder.getGesture());
return true; return true;
} }
}); });
@ -62,4 +68,23 @@ public abstract class GestureLayoutTest<T extends GestureLayout> extends BaseTes
.inRoot(RootMatchers.withDecorView( .inRoot(RootMatchers.withDecorView(
Matchers.is(rule.getActivity().getWindow().getDecorView()))); Matchers.is(rule.getActivity().getWindow().getDecorView())));
} }
private class Controller implements GestureFinder.Controller {
@NonNull
@Override
public Context getContext() {
return layout.getContext();
}
@Override
public int getWidth() {
return layout.getWidth();
}
@Override
public int getHeight() {
return layout.getHeight();
}
}
} }

@ -3,10 +3,7 @@ package com.otaliastudios.cameraview.gesture;
import android.content.Context; import android.content.Context;
import com.otaliastudios.cameraview.gesture.Gesture; import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.gesture.GestureLayoutTest;
import com.otaliastudios.cameraview.gesture.PinchGestureLayout;
import androidx.test.espresso.ViewAction; import androidx.test.espresso.ViewAction;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
@ -22,21 +19,21 @@ import static org.junit.Assert.assertTrue;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@SmallTest @SmallTest
public class PinchGestureLayoutTest extends GestureLayoutTest<PinchGestureLayout> { public class PinchGestureFinderTest extends GestureFinderTest<PinchGestureFinder> {
@Override @Override
protected PinchGestureLayout create(Context context) { protected PinchGestureFinder createFinder(@NonNull GestureFinder.Controller controller) {
return new PinchGestureLayout(context); return new PinchGestureFinder(controller);
} }
@Test @Test
public void testDefaults() { public void testDefaults() {
assertEquals(layout.getGesture(), Gesture.PINCH); assertEquals(finder.getGesture(), Gesture.PINCH);
assertEquals(layout.getPoints().length, 2); assertEquals(finder.getPoints().length, 2);
assertEquals(layout.getPoints()[0].x, 0, 0); assertEquals(finder.getPoints()[0].x, 0, 0);
assertEquals(layout.getPoints()[0].y, 0, 0); assertEquals(finder.getPoints()[0].y, 0, 0);
assertEquals(layout.getPoints()[1].x, 0, 0); assertEquals(finder.getPoints()[1].x, 0, 0);
assertEquals(layout.getPoints()[1].y, 0, 0); assertEquals(finder.getPoints()[1].y, 0, 0);
} }
// TODO: test pinch open // TODO: test pinch open
@ -51,15 +48,15 @@ public class PinchGestureLayoutTest extends GestureLayoutTest<PinchGestureLayout
} }
private void testPinch(ViewAction action, boolean increasing) { private void testPinch(ViewAction action, boolean increasing) {
touch.listen(); touchOp.listen();
touch.start(); touchOp.start();
onLayout().perform(action); onLayout().perform(action);
Gesture found = touch.await(10000); Gesture found = touchOp.await(10000);
assertNotNull(found); assertNotNull(found);
// How will this move our parameter? // How will this move our parameter?
float curr = 0.5f, min = 0f, max = 1f; float curr = 0.5f, min = 0f, max = 1f;
float newValue = layout.computeValue(curr, min, max); float newValue = finder.computeValue(curr, min, max);
if (increasing) { if (increasing) {
assertTrue(newValue > curr); assertTrue(newValue > curr);
assertTrue(newValue <= max); assertTrue(newValue <= max);

@ -3,10 +3,7 @@ package com.otaliastudios.cameraview.gesture;
import android.content.Context; import android.content.Context;
import com.otaliastudios.cameraview.gesture.Gesture; import androidx.annotation.NonNull;
import com.otaliastudios.cameraview.gesture.GestureLayoutTest;
import com.otaliastudios.cameraview.gesture.ScrollGestureLayout;
import androidx.test.espresso.ViewAction; import androidx.test.espresso.ViewAction;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
@ -27,43 +24,43 @@ import static org.junit.Assert.assertTrue;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@SmallTest @SmallTest
public class ScrollGestureLayoutTest extends GestureLayoutTest<ScrollGestureLayout> { public class ScrollGestureFinderTest extends GestureFinderTest<ScrollGestureFinder> {
@Override @Override
protected ScrollGestureLayout create(Context context) { protected ScrollGestureFinder createFinder(@NonNull GestureFinder.Controller controller) {
return new ScrollGestureLayout(context); return new ScrollGestureFinder(controller);
} }
@Test @Test
public void testDefaults() { public void testDefaults() {
assertNull(layout.getGesture()); assertNull(finder.getGesture());
assertEquals(layout.getPoints().length, 2); assertEquals(finder.getPoints().length, 2);
assertEquals(layout.getPoints()[0].x, 0, 0); assertEquals(finder.getPoints()[0].x, 0, 0);
assertEquals(layout.getPoints()[0].y, 0, 0); assertEquals(finder.getPoints()[0].y, 0, 0);
assertEquals(layout.getPoints()[1].x, 0, 0); assertEquals(finder.getPoints()[1].x, 0, 0);
assertEquals(layout.getPoints()[1].y, 0, 0); assertEquals(finder.getPoints()[1].y, 0, 0);
} }
@Test @Test
public void testScrollDisabled() { public void testScrollDisabled() {
layout.setActive(false); finder.setActive(false);
touch.listen(); touchOp.listen();
touch.start(); touchOp.start();
onLayout().perform(swipeUp()); onLayout().perform(swipeUp());
Gesture found = touch.await(500); Gesture found = touchOp.await(500);
assertNull(found); assertNull(found);
} }
private void testScroll(ViewAction scroll, Gesture expected, boolean increasing) { private void testScroll(ViewAction scroll, Gesture expected, boolean increasing) {
touch.listen(); touchOp.listen();
touch.start(); touchOp.start();
onLayout().perform(scroll); onLayout().perform(scroll);
Gesture found = touch.await(500); Gesture found = touchOp.await(500);
assertEquals(found, expected); assertEquals(found, expected);
// How will this move our parameter? // How will this move our parameter?
float curr = 0.5f, min = 0f, max = 1f; float curr = 0.5f, min = 0f, max = 1f;
float newValue = layout.computeValue(curr, min, max); float newValue = finder.computeValue(curr, min, max);
if (increasing) { if (increasing) {
assertTrue(newValue >= curr); assertTrue(newValue >= curr);
assertTrue(newValue <= max); assertTrue(newValue <= max);

@ -2,6 +2,8 @@ package com.otaliastudios.cameraview.gesture;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull;
import androidx.test.espresso.action.GeneralClickAction; import androidx.test.espresso.action.GeneralClickAction;
import androidx.test.espresso.action.GeneralLocation; import androidx.test.espresso.action.GeneralLocation;
import androidx.test.espresso.action.Press; import androidx.test.espresso.action.Press;
@ -11,9 +13,6 @@ import androidx.test.filters.SmallTest;
import android.view.InputDevice; import android.view.InputDevice;
import android.view.MotionEvent; import android.view.MotionEvent;
import com.otaliastudios.cameraview.gesture.Gesture;
import com.otaliastudios.cameraview.gesture.GestureLayoutTest;
import com.otaliastudios.cameraview.gesture.TapGestureLayout;
import com.otaliastudios.cameraview.size.Size; import com.otaliastudios.cameraview.size.Size;
import org.junit.Test; import org.junit.Test;
@ -24,59 +23,59 @@ import static org.junit.Assert.*;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@SmallTest @SmallTest
public class TapGestureLayoutTest extends GestureLayoutTest<TapGestureLayout> { public class TapGestureFinderTest extends GestureFinderTest<TapGestureFinder> {
@Override @Override
protected TapGestureLayout create(Context context) { protected TapGestureFinder createFinder(@NonNull GestureFinder.Controller controller) {
return new TapGestureLayout(context); return new TapGestureFinder(controller);
} }
@Test @Test
public void testDefaults() { public void testDefaults() {
assertNull(layout.getGesture()); assertNull(finder.getGesture());
assertEquals(layout.getPoints().length, 1); assertEquals(finder.getPoints().length, 1);
assertEquals(layout.getPoints()[0].x, 0, 0); assertEquals(finder.getPoints()[0].x, 0, 0);
assertEquals(layout.getPoints()[0].y, 0, 0); assertEquals(finder.getPoints()[0].y, 0, 0);
} }
@Test @Test
public void testTap() { public void testTap() {
touch.listen(); touchOp.listen();
touch.start(); touchOp.start();
GeneralClickAction a = new GeneralClickAction( GeneralClickAction a = new GeneralClickAction(
Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER, Tap.SINGLE, GeneralLocation.CENTER, Press.FINGER,
InputDevice.SOURCE_UNKNOWN, MotionEvent.BUTTON_PRIMARY); InputDevice.SOURCE_UNKNOWN, MotionEvent.BUTTON_PRIMARY);
onLayout().perform(a); onLayout().perform(a);
Gesture found = touch.await(500); Gesture found = touchOp.await(500);
assertEquals(found, Gesture.TAP); assertEquals(found, Gesture.TAP);
Size size = rule.getActivity().getContentSize(); Size size = rule.getActivity().getContentSize();
assertEquals(layout.getPoints()[0].x, (size.getWidth() / 2f), 1f); assertEquals(finder.getPoints()[0].x, (size.getWidth() / 2f), 1f);
assertEquals(layout.getPoints()[0].y, (size.getHeight() / 2f), 1f); assertEquals(finder.getPoints()[0].y, (size.getHeight() / 2f), 1f);
} }
@Test @Test
public void testTapWhileDisabled() { public void testTapWhileDisabled() {
layout.setActive(false); finder.setActive(false);
touch.listen(); touchOp.listen();
touch.start(); touchOp.start();
onLayout().perform(click()); onLayout().perform(click());
Gesture found = touch.await(500); Gesture found = touchOp.await(500);
assertNull(found); assertNull(found);
} }
@Test @Test
public void testLongTap() { public void testLongTap() {
touch.listen(); touchOp.listen();
touch.start(); touchOp.start();
GeneralClickAction a = new GeneralClickAction( GeneralClickAction a = new GeneralClickAction(
Tap.LONG, GeneralLocation.CENTER, Press.FINGER, Tap.LONG, GeneralLocation.CENTER, Press.FINGER,
InputDevice.SOURCE_UNKNOWN, MotionEvent.BUTTON_PRIMARY); InputDevice.SOURCE_UNKNOWN, MotionEvent.BUTTON_PRIMARY);
onLayout().perform(a); onLayout().perform(a);
Gesture found = touch.await(500); Gesture found = touchOp.await(500);
assertEquals(found, Gesture.LONG_TAP); assertEquals(found, Gesture.LONG_TAP);
Size size = rule.getActivity().getContentSize(); Size size = rule.getActivity().getContentSize();
assertEquals(layout.getPoints()[0].x, (size.getWidth() / 2f), 1f); assertEquals(finder.getPoints()[0].x, (size.getWidth() / 2f), 1f);
assertEquals(layout.getPoints()[0].y, (size.getHeight() / 2f), 1f); assertEquals(finder.getPoints()[0].y, (size.getHeight() / 2f), 1f);
} }
} }

@ -50,11 +50,11 @@ import com.otaliastudios.cameraview.controls.Mode;
import com.otaliastudios.cameraview.controls.Preview; import com.otaliastudios.cameraview.controls.Preview;
import com.otaliastudios.cameraview.controls.VideoCodec; import com.otaliastudios.cameraview.controls.VideoCodec;
import com.otaliastudios.cameraview.controls.WhiteBalance; import com.otaliastudios.cameraview.controls.WhiteBalance;
import com.otaliastudios.cameraview.gesture.GestureLayout; import com.otaliastudios.cameraview.gesture.GestureFinder;
import com.otaliastudios.cameraview.gesture.GestureParser; import com.otaliastudios.cameraview.gesture.GestureParser;
import com.otaliastudios.cameraview.gesture.PinchGestureLayout; import com.otaliastudios.cameraview.gesture.PinchGestureFinder;
import com.otaliastudios.cameraview.gesture.ScrollGestureLayout; import com.otaliastudios.cameraview.gesture.ScrollGestureFinder;
import com.otaliastudios.cameraview.gesture.TapGestureLayout; import com.otaliastudios.cameraview.gesture.TapGestureFinder;
import com.otaliastudios.cameraview.internal.GridLinesLayout; import com.otaliastudios.cameraview.internal.GridLinesLayout;
import com.otaliastudios.cameraview.internal.utils.CropHelper; import com.otaliastudios.cameraview.internal.utils.CropHelper;
import com.otaliastudios.cameraview.internal.utils.OrientationHelper; import com.otaliastudios.cameraview.internal.utils.OrientationHelper;
@ -115,11 +115,13 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
@VisibleForTesting List<FrameProcessor> mFrameProcessors = new CopyOnWriteArrayList<>(); @VisibleForTesting List<FrameProcessor> mFrameProcessors = new CopyOnWriteArrayList<>();
private Lifecycle mLifecycle; private Lifecycle mLifecycle;
// Gestures
@VisibleForTesting PinchGestureFinder mPinchGestureFinder;
@VisibleForTesting TapGestureFinder mTapGestureFinder;
@VisibleForTesting ScrollGestureFinder mScrollGestureFinder;
// Views // Views
GridLinesLayout mGridLinesLayout; GridLinesLayout mGridLinesLayout;
PinchGestureLayout mPinchGestureLayout;
TapGestureLayout mTapGestureLayout;
ScrollGestureLayout mScrollGestureLayout;
MarkerLayout mMarkerLayout; MarkerLayout mMarkerLayout;
private boolean mKeepScreenOn; private boolean mKeepScreenOn;
@SuppressWarnings({"FieldCanBeLocal", "unused"}) @SuppressWarnings({"FieldCanBeLocal", "unused"})
@ -173,16 +175,15 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mUiHandler = new Handler(Looper.getMainLooper()); mUiHandler = new Handler(Looper.getMainLooper());
mFrameProcessorsHandler = WorkerHandler.get("FrameProcessorsWorker"); mFrameProcessorsHandler = WorkerHandler.get("FrameProcessorsWorker");
// Gestures
mPinchGestureFinder = new PinchGestureFinder(mCameraCallbacks);
mTapGestureFinder = new TapGestureFinder(mCameraCallbacks);
mScrollGestureFinder = new ScrollGestureFinder(mCameraCallbacks);
// Views // Views
mGridLinesLayout = new GridLinesLayout(context); mGridLinesLayout = new GridLinesLayout(context);
mPinchGestureLayout = new PinchGestureLayout(context);
mTapGestureLayout = new TapGestureLayout(context);
mScrollGestureLayout = new ScrollGestureLayout(context);
mMarkerLayout = new MarkerLayout(context); mMarkerLayout = new MarkerLayout(context);
addView(mGridLinesLayout); addView(mGridLinesLayout);
addView(mPinchGestureLayout);
addView(mTapGestureLayout);
addView(mScrollGestureLayout);
addView(mMarkerLayout); addView(mMarkerLayout);
// Create the engine // Create the engine
@ -225,6 +226,26 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
} }
} }
/**
* Engine is instantiated on creation and anytime
* {@link #setEngine(Engine)} is called.
*/
private void doInstantiateEngine() {
mCameraEngine = instantiateCameraEngine(mEngine, mCameraCallbacks);
}
/**
* Preview is instantiated {@link #onAttachedToWindow()}, because
* we want to know if we're hardware accelerated or not.
* However, in tests, we might want to create the preview right after constructor.
*/
@VisibleForTesting
void doInstantiatePreview() {
mCameraPreview = instantiatePreview(mPreview, getContext(), this);
mCameraEngine.setPreview(mCameraPreview);
}
/** /**
* Instantiates the camera engine. * Instantiates the camera engine.
* *
@ -269,16 +290,6 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
} }
} }
@VisibleForTesting
void doInstantiatePreview() {
mCameraPreview = instantiatePreview(mPreview, getContext(), this);
mCameraEngine.setPreview(mCameraPreview);
}
private void doInstantiateEngine() {
mCameraEngine = instantiateCameraEngine(mEngine, mCameraCallbacks);
}
@Override @Override
protected void onAttachedToWindow() { protected void onAttachedToWindow() {
super.onAttachedToWindow(); super.onAttachedToWindow();
@ -474,19 +485,19 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mGestureMap.put(gesture, action); mGestureMap.put(gesture, action);
switch (gesture) { switch (gesture) {
case PINCH: case PINCH:
mPinchGestureLayout.setActive(mGestureMap.get(Gesture.PINCH) != none); mPinchGestureFinder.setActive(mGestureMap.get(Gesture.PINCH) != none);
break; break;
case TAP: case TAP:
// case DOUBLE_TAP: // case DOUBLE_TAP:
case LONG_TAP: case LONG_TAP:
mTapGestureLayout.setActive( mTapGestureFinder.setActive(
mGestureMap.get(Gesture.TAP) != none || mGestureMap.get(Gesture.TAP) != none ||
// mGestureMap.get(Gesture.DOUBLE_TAP) != none || // mGestureMap.get(Gesture.DOUBLE_TAP) != none ||
mGestureMap.get(Gesture.LONG_TAP) != none); mGestureMap.get(Gesture.LONG_TAP) != none);
break; break;
case SCROLL_HORIZONTAL: case SCROLL_HORIZONTAL:
case SCROLL_VERTICAL: case SCROLL_VERTICAL:
mScrollGestureLayout.setActive( mScrollGestureFinder.setActive(
mGestureMap.get(Gesture.SCROLL_HORIZONTAL) != none || mGestureMap.get(Gesture.SCROLL_HORIZONTAL) != none ||
mGestureMap.get(Gesture.SCROLL_VERTICAL) != none); mGestureMap.get(Gesture.SCROLL_VERTICAL) != none);
break; break;
@ -534,15 +545,15 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
// Pass to our own GestureLayouts // Pass to our own GestureLayouts
CameraOptions options = mCameraEngine.getCameraOptions(); // Non null CameraOptions options = mCameraEngine.getCameraOptions(); // Non null
if (options == null) throw new IllegalStateException("Options should not be null here."); if (options == null) throw new IllegalStateException("Options should not be null here.");
if (mPinchGestureLayout.onTouchEvent(event)) { if (mPinchGestureFinder.onTouchEvent(event)) {
LOG.i("onTouchEvent", "pinch!"); LOG.i("onTouchEvent", "pinch!");
onGesture(mPinchGestureLayout, options); onGesture(mPinchGestureFinder, options);
} else if (mScrollGestureLayout.onTouchEvent(event)) { } else if (mScrollGestureFinder.onTouchEvent(event)) {
LOG.i("onTouchEvent", "scroll!"); LOG.i("onTouchEvent", "scroll!");
onGesture(mScrollGestureLayout, options); onGesture(mScrollGestureFinder, options);
} else if (mTapGestureLayout.onTouchEvent(event)) { } else if (mTapGestureFinder.onTouchEvent(event)) {
LOG.i("onTouchEvent", "tap!"); LOG.i("onTouchEvent", "tap!");
onGesture(mTapGestureLayout, options); onGesture(mTapGestureFinder, options);
} }
return true; return true;
} }
@ -551,7 +562,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
// Some gesture layout detected a gesture. It's not known at this moment: // Some gesture layout detected a gesture. It's not known at this moment:
// (1) if it was mapped to some action (we check here) // (1) if it was mapped to some action (we check here)
// (2) if it's supported by the camera (CameraEngine checks) // (2) if it's supported by the camera (CameraEngine checks)
private void onGesture(GestureLayout source, @NonNull CameraOptions options) { private void onGesture(GestureFinder source, @NonNull CameraOptions options) {
Gesture gesture = source.getGesture(); Gesture gesture = source.getGesture();
GestureAction action = mGestureMap.get(gesture); GestureAction action = mGestureMap.get(gesture);
PointF[] points = source.getPoints(); PointF[] points = source.getPoints();
@ -1732,7 +1743,10 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
//region Callbacks and dispatching //region Callbacks and dispatching
@VisibleForTesting @VisibleForTesting
class CameraCallbacks implements CameraEngine.Callback, OrientationHelper.Callback { class CameraCallbacks implements
CameraEngine.Callback,
OrientationHelper.Callback,
GestureFinder.Controller {
private CameraLogger mLogger = CameraLogger.create(CameraCallbacks.class.getSimpleName()); private CameraLogger mLogger = CameraLogger.create(CameraCallbacks.class.getSimpleName());
@ -1742,6 +1756,16 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
return CameraView.this.getContext(); return CameraView.this.getContext();
} }
@Override
public int getWidth() {
return CameraView.this.getWidth();
}
@Override
public int getHeight() {
return CameraView.this.getHeight();
}
@Override @Override
public void dispatchOnCameraOpened(final CameraOptions options) { public void dispatchOnCameraOpened(final CameraOptions options) {
mLogger.i("dispatchOnCameraOpened", options); mLogger.i("dispatchOnCameraOpened", options);
@ -1786,7 +1810,6 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
@Override @Override
public void onShutter(boolean shouldPlaySound) { public void onShutter(boolean shouldPlaySound) {
if (shouldPlaySound && mPlaySounds) { if (shouldPlaySound && mPlaySounds) {
//noinspection all
playSound(MediaActionSound.SHUTTER_CLICK); playSound(MediaActionSound.SHUTTER_CLICK);
} }
} }
@ -1847,7 +1870,6 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
@Override @Override
public void run() { public void run() {
if (success && mPlaySounds) { if (success && mPlaySounds) {
//noinspection all
playSound(MediaActionSound.FOCUS_COMPLETE); playSound(MediaActionSound.FOCUS_COMPLETE);
} }

@ -7,20 +7,28 @@ import android.view.MotionEvent;
import android.widget.FrameLayout; import android.widget.FrameLayout;
/** /**
* Base class for gesture layouts, that is, layouts that will capture * Base class for gesture finders.
* gestures. * Gesture finders are passed down touch events to detect gestures.
*/ */
public abstract class GestureLayout extends FrameLayout { public abstract class GestureFinder {
public interface Controller {
@NonNull Context getContext();
int getWidth();
int getHeight();
}
// The number of possible values between minValue and maxValue, for the getValue method. // 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). // We could make this non-static (e.g. larger granularity for exposure correction).
private final static int GRANULARITY = 50; private final static int GRANULARITY = 50;
private boolean mActive; private boolean mActive;
private Gesture mType; private Gesture mType;
private PointF[] mPoints; private PointF[] mPoints;
private Controller mController;
GestureLayout(@NonNull Context context, int points) { GestureFinder(@NonNull Controller controller, int points) {
super(context); mController = controller;
mPoints = new PointF[points]; mPoints = new PointF[points];
for (int i = 0; i < points; i++) { for (int i = 0; i < points; i++) {
mPoints[i] = new PointF(0, 0); mPoints[i] = new PointF(0, 0);
@ -108,6 +116,7 @@ public abstract class GestureLayout extends FrameLayout {
* @param which the array position * @param which the array position
* @return the point * @return the point
*/ */
@SuppressWarnings("WeakerAccess")
@NonNull @NonNull
protected final PointF getPoint(int which) { protected final PointF getPoint(int which) {
return mPoints[which]; return mPoints[which];

@ -1,26 +1,25 @@
package com.otaliastudios.cameraview.gesture; package com.otaliastudios.cameraview.gesture;
import android.content.Context;
import android.os.Build; import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector;
/** /**
* A {@link GestureLayout} that detects {@link Gesture#PINCH} gestures. * A {@link GestureFinder} that detects {@link Gesture#PINCH} gestures.
*/ */
public class PinchGestureLayout extends GestureLayout { public class PinchGestureFinder extends GestureFinder {
private final static float ADD_SENSITIVITY = 2f; private final static float ADD_SENSITIVITY = 2f;
ScaleGestureDetector mDetector; private ScaleGestureDetector mDetector;
private boolean mNotify; private boolean mNotify;
private float mFactor = 0; private float mFactor = 0;
public PinchGestureLayout(@NonNull Context context) { public PinchGestureFinder(@NonNull Controller controller) {
super(context, 2); super(controller, 2);
setGesture(Gesture.PINCH); setGesture(Gesture.PINCH);
mDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() { mDetector = new ScaleGestureDetector(controller.getContext(), new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override @Override
public boolean onScale(ScaleGestureDetector detector) { public boolean onScale(ScaleGestureDetector detector) {
mNotify = true; mNotify = true;

@ -1,6 +1,5 @@
package com.otaliastudios.cameraview.gesture; package com.otaliastudios.cameraview.gesture;
import android.content.Context;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.MotionEvent; import android.view.MotionEvent;
@ -8,21 +7,21 @@ import android.view.MotionEvent;
import com.otaliastudios.cameraview.CameraLogger; import com.otaliastudios.cameraview.CameraLogger;
/** /**
* A {@link GestureLayout} that detects {@link Gesture#SCROLL_HORIZONTAL} * A {@link GestureFinder} that detects {@link Gesture#SCROLL_HORIZONTAL}
* and {@link Gesture#SCROLL_VERTICAL} gestures. * and {@link Gesture#SCROLL_VERTICAL} gestures.
*/ */
public class ScrollGestureLayout extends GestureLayout { public class ScrollGestureFinder extends GestureFinder {
private static final String TAG = ScrollGestureLayout.class.getSimpleName(); private static final String TAG = ScrollGestureFinder.class.getSimpleName();
private static final CameraLogger LOG = CameraLogger.create(TAG); private static final CameraLogger LOG = CameraLogger.create(TAG);
private GestureDetector mDetector; private GestureDetector mDetector;
private boolean mNotify; private boolean mNotify;
private float mFactor; private float mFactor;
public ScrollGestureLayout(@NonNull Context context) { public ScrollGestureFinder(final @NonNull Controller controller) {
super(context, 2); super(controller, 2);
mDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { mDetector = new GestureDetector(controller.getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override @Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
@ -40,7 +39,7 @@ public class ScrollGestureLayout extends GestureLayout {
horizontal = getGesture() == Gesture.SCROLL_HORIZONTAL; horizontal = getGesture() == Gesture.SCROLL_HORIZONTAL;
} }
getPoint(1).set(e2.getX(), e2.getY()); getPoint(1).set(e2.getX(), e2.getY());
mFactor = horizontal ? (distanceX / getWidth()) : (distanceY / getHeight()); mFactor = horizontal ? (distanceX / controller.getWidth()) : (distanceY / controller.getHeight());
mFactor = horizontal ? -mFactor : mFactor; // When vertical, up = positive mFactor = horizontal ? -mFactor : mFactor; // When vertical, up = positive
mNotify = true; mNotify = true;
return true; return true;

@ -1,32 +1,22 @@
package com.otaliastudios.cameraview.gesture; package com.otaliastudios.cameraview.gesture;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.graphics.PointF;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.otaliastudios.cameraview.R;
/** /**
* A {@link GestureLayout} that detects {@link Gesture#TAP} * A {@link GestureFinder} that detects {@link Gesture#TAP}
* and {@link Gesture#LONG_TAP} gestures. * and {@link Gesture#LONG_TAP} gestures.
*/ */
public class TapGestureLayout extends GestureLayout { public class TapGestureFinder extends GestureFinder {
private GestureDetector mDetector; private GestureDetector mDetector;
private boolean mNotify; private boolean mNotify;
public TapGestureLayout(@NonNull Context context) { public TapGestureFinder(@NonNull Controller controller) {
super(context, 1); super(controller, 1);
mDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { mDetector = new GestureDetector(controller.getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override @Override
public boolean onSingleTapUp(MotionEvent e) { public boolean onSingleTapUp(MotionEvent e) {
Loading…
Cancel
Save