Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shapes detector #28

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.Toast;

import com.mapbox.android.gestures.AndroidGesturesManager;
import com.mapbox.android.gestures.MoveGestureDetector;
Expand All @@ -23,10 +24,12 @@
import com.mapbox.android.gestures.ShoveGestureDetector;
import com.mapbox.android.gestures.StandardGestureDetector;
import com.mapbox.android.gestures.StandardScaleGestureDetector;
import com.mapbox.android.gestures.shape.ShapeGestureDetector;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;

public class MainActivity extends AppCompatActivity {
Expand Down Expand Up @@ -232,6 +235,23 @@ public boolean onShove(ShoveGestureDetector detector, float deltaPixelsSinceLast
}
});

androidGesturesManager.setShapeGestureListener(new ShapeGestureDetector.OnShapeDetectedListener() {
@Override
public void onShapeDetected(ShapeGestureDetector detector, @ShapeGestureDetector.ShapeType int shape) {
String shapeName = "none";
if (shape == ShapeGestureDetector.SHAPE_DASH) {
shapeName = "dash";
} else if (shape == ShapeGestureDetector.SHAPE_CROSS) {
shapeName = "cross";
} else if (shape == ShapeGestureDetector.SHAPE_CIRCLE) {
shapeName = "circle";
}

Toast.makeText(MainActivity.this, String.format(Locale.getDefault(), "Shape detected - %s", shapeName),
Toast.LENGTH_SHORT).show();
}
});

androidGesturesManager.setMoveGestureListener(onMoveGestureListener);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import android.support.annotation.UiThread;
import android.view.MotionEvent;

import com.mapbox.android.gestures.shape.ShapeGestureDetector;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
Expand Down Expand Up @@ -32,7 +34,8 @@ public class AndroidGesturesManager {
GESTURE_TYPE_DOUBLE_TAP,
GESTURE_TYPE_DOUBLE_TAP_EVENT,
GESTURE_TYPE_SINGLE_TAP_CONFIRMED,
GESTURE_TYPE_MOVE
GESTURE_TYPE_MOVE,
GESTURE_TYPE_SHAPE
})
public @interface GestureType {
}
Expand All @@ -51,6 +54,7 @@ public class AndroidGesturesManager {
public static final int GESTURE_TYPE_DOUBLE_TAP_EVENT = 11;
public static final int GESTURE_TYPE_SINGLE_TAP_CONFIRMED = 12;
public static final int GESTURE_TYPE_MOVE = 13;
public static final int GESTURE_TYPE_SHAPE = 14;

private final List<Set<Integer>> mutuallyExclusiveGestures = new ArrayList<>();
private final List<BaseGesture> detectors = new ArrayList<>();
Expand All @@ -61,6 +65,7 @@ public class AndroidGesturesManager {
private final ShoveGestureDetector shoveGestureDetector;
private final MultiFingerTapGestureDetector multiFingerTapGestureDetector;
private final MoveGestureDetector moveGestureDetector;
private final ShapeGestureDetector shapeGestureDetector;

/**
* Creates a new instance of the {@link AndroidGesturesManager}.
Expand Down Expand Up @@ -120,13 +125,15 @@ public AndroidGesturesManager(Context context, List<Set<Integer>> exclusiveGestu
multiFingerTapGestureDetector = new MultiFingerTapGestureDetector(context, this);
moveGestureDetector = new MoveGestureDetector(context, this);
standardGestureDetector = new StandardGestureDetector(context, this);
shapeGestureDetector = new ShapeGestureDetector(context, this);

detectors.add(rotateGestureDetector);
detectors.add(standardScaleGestureDetector);
detectors.add(shoveGestureDetector);
detectors.add(multiFingerTapGestureDetector);
detectors.add(moveGestureDetector);
detectors.add(standardGestureDetector);
detectors.add(shapeGestureDetector);

if (applyDefaultThresholds) {
initDefaultThresholds();
Expand Down Expand Up @@ -157,6 +164,20 @@ private void initDefaultThresholds() {
Constants.DEFAULT_MULTI_TAP_TIME_THRESHOLD);
}

if (detector instanceof ShapeGestureDetector) {
((ShapeGestureDetector) detector).setMinimumMovementThresholdResource(
R.dimen.mapbox_defaultShapeMinimumMovementThreshold);

((ShapeGestureDetector) detector).setDashMovementBoundsResource(
R.dimen.mapbox_defaultShapeDashMovementBounds);

((ShapeGestureDetector) detector).setCrossMovementBoundsResource(
R.dimen.mapbox_defaultShapeCrossMovementBounds);

((ShapeGestureDetector) detector).setCircleMovementBoundsResource(
R.dimen.mapbox_defaultShapeCircleMovementBounds);
}

if (detector instanceof RotateGestureDetector) {
((RotateGestureDetector) detector).setAngleThreshold(Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD);
}
Expand Down Expand Up @@ -248,6 +269,22 @@ public void removeShoveGestureListener() {
shoveGestureDetector.removeListener();
}

/**
* Sets a listener for shape gestures.
*
* @param listener your gestures listener
*/
public void setShapeGestureListener(ShapeGestureDetector.OnShapeDetectedListener listener) {
shapeGestureDetector.setListener(listener);
}

/**
* Removes a listener for shape gestures.
*/
public void removeShapeGestureListener() {
shapeGestureDetector.removeListener();
}

/**
* Sets a listener for multi finger tap gestures.
*
Expand Down Expand Up @@ -351,6 +388,15 @@ public MoveGestureDetector getMoveGestureDetector() {
return moveGestureDetector;
}

/**
* Get shape gesture detector.
*
* @return gesture detector
*/
public ShapeGestureDetector getShapeGestureDetector() {
return shapeGestureDetector;
}

/**
* Sets a number of sets containing mutually exclusive gestures.
*
Expand Down
14 changes: 14 additions & 0 deletions library/src/main/java/com/mapbox/android/gestures/Utils.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mapbox.android.gestures;

import android.graphics.Point;
import android.graphics.PointF;
import android.support.annotation.NonNull;
import android.view.MotionEvent;
Expand Down Expand Up @@ -56,4 +57,17 @@ public static float getRawY(MotionEvent event, int pointerIndex) {
}
return 0.0f;
}

/**
* Returns relative distance in pixels between two points on screen.
*
* @param point1 first point
* @param point2 second point
* @return relative distance in pixels
*/
public static float calcualteRelativeDistance(@NonNull Point point1, @NonNull Point point2) {
float diffX = point2.x - point1.x;
float diffY = point2.y - point1.y;
return (float) Math.sqrt(diffX * diffX + diffY * diffY);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.mapbox.android.gestures.shape;

import android.graphics.Point;
import android.view.MotionEvent;

import com.mapbox.android.gestures.Utils;

import java.util.List;

class CircleDetector implements ShapeDetector {

private float movementBounds;

@Override
public void onDown(MotionEvent motionEvent) {
// do nothing
}

@Override
public int onUp(MotionEvent motionEvent, List<Point> pointerCoords) {
Point centerPoint = determineCenterPoint(pointerCoords);

// calculating distance to the first point which will serve as a reference
float distance = Utils.calcualteRelativeDistance(pointerCoords.get(0), centerPoint);

if (verifyPointsWithinBounds(pointerCoords, centerPoint, distance, movementBounds)
&& isClosed(pointerCoords, movementBounds)) {
return ShapeGestureDetector.SHAPE_CIRCLE;
} else {
return ShapeGestureDetector.SHAPE_NONE;
}
}

private Point determineCenterPoint(List<Point> pointerCoords) {
int x = 0;
int y = 0;
for (Point point : pointerCoords) {
x += point.x;
y += point.y;
}

x /= pointerCoords.size();
y /= pointerCoords.size();

return new Point(x, y);
}

private boolean verifyPointsWithinBounds(List<Point> pointerCoords, Point centerPoint, float desiredDistance,
float bounds) {
for (Point point : pointerCoords) {
float distance = Utils.calcualteRelativeDistance(point, centerPoint);
if (distance < desiredDistance - bounds || distance > desiredDistance + bounds) {
return false;
}
}

return true;
}

private boolean isClosed(List<Point> pointerCoords, float bounds) {
Point firstPoint = pointerCoords.get(0);
Point lastPoint = pointerCoords.get(pointerCoords.size() - 1);

float distance = Utils.calcualteRelativeDistance(firstPoint, lastPoint);
return distance < bounds;
}

@Override
public void cancel() {
// do nothing
}

float getMovementBounds() {
return movementBounds;
}

void setMovementBounds(float movementBounds) {
this.movementBounds = movementBounds;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.mapbox.android.gestures.shape;

import android.graphics.Point;
import android.view.MotionEvent;

import java.util.List;

// TODO: 22.03.18 they need to intersect
class CrossDetector extends DashDetector {

private boolean hasHorizontalDash;
private boolean hasVerticalDash;

@Override
public void onDown(MotionEvent motionEvent) {
// do nothing
}

@Override
public int onUp(MotionEvent motionEvent, List<Point> pointerCoords) {
boolean verticalDash = !isBeyondHorizontalBounds(pointerCoords);
boolean horizontalDash = !isBeyondVerticalBounds(pointerCoords);

// if none fits, abort
if (!verticalDash && !horizontalDash) {
clear();
return ShapeGestureDetector.SHAPE_NONE;
}

// if line is duplicated, clear but don't abort
if ((hasVerticalDash && verticalDash) || (hasHorizontalDash && horizontalDash)) {
clear();
}

if (!hasVerticalDash) {
hasVerticalDash = verticalDash;
}

if (!hasHorizontalDash) {
hasHorizontalDash = horizontalDash;
}

if (hasVerticalDash && hasHorizontalDash) {
// success, deliver event
clear();
return ShapeGestureDetector.SHAPE_CROSS;
} else {
// waiting for the second line
return ShapeGestureDetector.SHAPE_NONE;
}
}

private boolean isBeyondHorizontalBounds(List<Point> pointerCoords) {
Point firstPoint = pointerCoords.get(0);
for (Point point : pointerCoords) {
if (Math.abs(firstPoint.x - point.x) > movementBounds) {
return true;
}
}
return false;
}

@Override
public void cancel() {
super.cancel();
clear();
}

private void clear() {
hasVerticalDash = false;
hasHorizontalDash = false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.mapbox.android.gestures.shape;

import android.graphics.Point;
import android.view.MotionEvent;

import java.util.List;

class DashDetector implements ShapeDetector {

float movementBounds;

@Override
public void onDown(MotionEvent motionEvent) {
// do nothing
}

@Override
public int onUp(MotionEvent motionEvent, List<Point> pointerCoords) {
if (isBeyondVerticalBounds(pointerCoords)) {
return ShapeGestureDetector.SHAPE_NONE;
}

return ShapeGestureDetector.SHAPE_DASH;
}

boolean isBeyondVerticalBounds(List<Point> pointerCoords) {
Point firstPoint = pointerCoords.get(0);
for (Point point : pointerCoords) {
if (Math.abs(firstPoint.y - point.y) > movementBounds) {
return true;
}
}
return false;
}

@Override
public void cancel() {
// do nothing
}

float getMovementBounds() {
return movementBounds;
}

void setMovementBounds(float movementBounds) {
this.movementBounds = movementBounds;
}
}
Loading