This repository has been archived by the owner on Aug 8, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Leith Bade
committed
Nov 21, 2014
1 parent
201ab76
commit abd2b5c
Showing
13 changed files
with
923 additions
and
1,044 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
150 changes: 150 additions & 0 deletions
150
android/java/lib/src/main/java/com/almeros/android/multitouch/BaseGestureDetector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package com.almeros.android.multitouch; | ||
|
||
import android.content.Context; | ||
import android.view.MotionEvent; | ||
|
||
/** | ||
* @author Almer Thie (code.almeros.com) | ||
* Copyright (c) 2013, Almer Thie (code.almeros.com) | ||
* | ||
* All rights reserved. | ||
* | ||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: | ||
* | ||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. | ||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer | ||
* in the documentation and/or other materials provided with the distribution. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, | ||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | ||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY | ||
* OF SUCH DAMAGE. | ||
*/ | ||
public abstract class BaseGestureDetector { | ||
protected final Context mContext; | ||
protected boolean mGestureInProgress; | ||
|
||
protected MotionEvent mPrevEvent; | ||
protected MotionEvent mCurrEvent; | ||
|
||
protected float mCurrPressure; | ||
protected float mPrevPressure; | ||
protected long mTimeDelta; | ||
|
||
|
||
/** | ||
* This value is the threshold ratio between the previous combined pressure | ||
* and the current combined pressure. When pressure decreases rapidly | ||
* between events the position values can often be imprecise, as it usually | ||
* indicates that the user is in the process of lifting a pointer off of the | ||
* device. This value was tuned experimentally. | ||
*/ | ||
protected static final float PRESSURE_THRESHOLD = 0.67f; | ||
|
||
|
||
public BaseGestureDetector(Context context) { | ||
mContext = context; | ||
} | ||
|
||
/** | ||
* All gesture detectors need to be called through this method to be able to | ||
* detect gestures. This method delegates work to handler methods | ||
* (handleStartProgressEvent, handleInProgressEvent) implemented in | ||
* extending classes. | ||
* | ||
* @param event | ||
* @return | ||
*/ | ||
public boolean onTouchEvent(MotionEvent event){ | ||
final int actionCode = event.getAction() & MotionEvent.ACTION_MASK; | ||
if (!mGestureInProgress) { | ||
handleStartProgressEvent(actionCode, event); | ||
} else { | ||
handleInProgressEvent(actionCode, event); | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* Called when the current event occurred when NO gesture is in progress | ||
* yet. The handling in this implementation may set the gesture in progress | ||
* (via mGestureInProgress) or out of progress | ||
* @param actionCode | ||
* @param event | ||
*/ | ||
protected abstract void handleStartProgressEvent(int actionCode, MotionEvent event); | ||
|
||
/** | ||
* Called when the current event occurred when a gesture IS in progress. The | ||
* handling in this implementation may set the gesture out of progress (via | ||
* mGestureInProgress). | ||
* @param actionCode | ||
* @param event | ||
*/ | ||
protected abstract void handleInProgressEvent(int actionCode, MotionEvent event); | ||
|
||
|
||
protected void updateStateByEvent(MotionEvent curr){ | ||
final MotionEvent prev = mPrevEvent; | ||
|
||
// Reset mCurrEvent | ||
if (mCurrEvent != null) { | ||
mCurrEvent.recycle(); | ||
mCurrEvent = null; | ||
} | ||
mCurrEvent = MotionEvent.obtain(curr); | ||
|
||
|
||
// Delta time | ||
mTimeDelta = curr.getEventTime() - prev.getEventTime(); | ||
|
||
// Pressure | ||
mCurrPressure = curr.getPressure(curr.getActionIndex()); | ||
mPrevPressure = prev.getPressure(prev.getActionIndex()); | ||
} | ||
|
||
protected void resetState() { | ||
if (mPrevEvent != null) { | ||
mPrevEvent.recycle(); | ||
mPrevEvent = null; | ||
} | ||
if (mCurrEvent != null) { | ||
mCurrEvent.recycle(); | ||
mCurrEvent = null; | ||
} | ||
mGestureInProgress = false; | ||
} | ||
|
||
|
||
/** | ||
* Returns {@code true} if a gesture is currently in progress. | ||
* @return {@code true} if a gesture is currently in progress, {@code false} otherwise. | ||
*/ | ||
public boolean isInProgress() { | ||
return mGestureInProgress; | ||
} | ||
|
||
/** | ||
* Return the time difference in milliseconds between the previous accepted | ||
* GestureDetector event and the current GestureDetector event. | ||
* | ||
* @return Time difference since the last move event in milliseconds. | ||
*/ | ||
public long getTimeDelta() { | ||
return mTimeDelta; | ||
} | ||
|
||
/** | ||
* Return the event time of the current GestureDetector event being | ||
* processed. | ||
* | ||
* @return Current GestureDetector event time in milliseconds. | ||
*/ | ||
public long getEventTime() { | ||
return mCurrEvent.getEventTime(); | ||
} | ||
|
||
} |
174 changes: 174 additions & 0 deletions
174
android/java/lib/src/main/java/com/almeros/android/multitouch/MoveGestureDetector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
package com.almeros.android.multitouch; | ||
|
||
import android.content.Context; | ||
import android.graphics.PointF; | ||
import android.view.MotionEvent; | ||
import com.almeros.android.multitouch.BaseGestureDetector; | ||
|
||
/** | ||
* @author Almer Thie (code.almeros.com) | ||
* Copyright (c) 2013, Almer Thie (code.almeros.com) | ||
* | ||
* All rights reserved. | ||
* | ||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: | ||
* | ||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. | ||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer | ||
* in the documentation and/or other materials provided with the distribution. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, | ||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, | ||
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | ||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY | ||
* OF SUCH DAMAGE. | ||
*/ | ||
public class MoveGestureDetector extends BaseGestureDetector { | ||
|
||
/** | ||
* Listener which must be implemented which is used by MoveGestureDetector | ||
* to perform callbacks to any implementing class which is registered to a | ||
* MoveGestureDetector via the constructor. | ||
* | ||
* @see MoveGestureDetector.SimpleOnMoveGestureListener | ||
*/ | ||
public interface OnMoveGestureListener { | ||
public boolean onMove(MoveGestureDetector detector); | ||
public boolean onMoveBegin(MoveGestureDetector detector); | ||
public void onMoveEnd(MoveGestureDetector detector); | ||
} | ||
|
||
/** | ||
* Helper class which may be extended and where the methods may be | ||
* implemented. This way it is not necessary to implement all methods | ||
* of OnMoveGestureListener. | ||
*/ | ||
public static class SimpleOnMoveGestureListener implements OnMoveGestureListener { | ||
public boolean onMove(MoveGestureDetector detector) { | ||
return false; | ||
} | ||
|
||
public boolean onMoveBegin(MoveGestureDetector detector) { | ||
return true; | ||
} | ||
|
||
public void onMoveEnd(MoveGestureDetector detector) { | ||
// Do nothing, overridden implementation may be used | ||
} | ||
} | ||
|
||
private static final PointF FOCUS_DELTA_ZERO = new PointF(); | ||
|
||
private final OnMoveGestureListener mListener; | ||
|
||
private PointF mCurrFocusInternal; | ||
private PointF mPrevFocusInternal; | ||
private PointF mFocusExternal = new PointF(); | ||
private PointF mFocusDeltaExternal = new PointF(); | ||
|
||
|
||
public MoveGestureDetector(Context context, OnMoveGestureListener listener) { | ||
super(context); | ||
mListener = listener; | ||
} | ||
|
||
@Override | ||
protected void handleStartProgressEvent(int actionCode, MotionEvent event){ | ||
switch (actionCode) { | ||
case MotionEvent.ACTION_DOWN: | ||
resetState(); // In case we missed an UP/CANCEL event | ||
|
||
mPrevEvent = MotionEvent.obtain(event); | ||
mTimeDelta = 0; | ||
|
||
updateStateByEvent(event); | ||
break; | ||
|
||
case MotionEvent.ACTION_MOVE: | ||
mGestureInProgress = mListener.onMoveBegin(this); | ||
break; | ||
} | ||
} | ||
|
||
@Override | ||
protected void handleInProgressEvent(int actionCode, MotionEvent event){ | ||
switch (actionCode) { | ||
case MotionEvent.ACTION_UP: | ||
case MotionEvent.ACTION_CANCEL: | ||
mListener.onMoveEnd(this); | ||
resetState(); | ||
break; | ||
|
||
case MotionEvent.ACTION_MOVE: | ||
updateStateByEvent(event); | ||
|
||
// Only accept the event if our relative pressure is within | ||
// a certain limit. This can help filter shaky data as a | ||
// finger is lifted. | ||
if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) { | ||
final boolean updatePrevious = mListener.onMove(this); | ||
if (updatePrevious) { | ||
mPrevEvent.recycle(); | ||
mPrevEvent = MotionEvent.obtain(event); | ||
} | ||
} | ||
break; | ||
} | ||
} | ||
|
||
protected void updateStateByEvent(MotionEvent curr) { | ||
super.updateStateByEvent(curr); | ||
|
||
final MotionEvent prev = mPrevEvent; | ||
|
||
// Focus intenal | ||
mCurrFocusInternal = determineFocalPoint(curr); | ||
mPrevFocusInternal = determineFocalPoint(prev); | ||
|
||
// Focus external | ||
// - Prevent skipping of focus delta when a finger is added or removed | ||
boolean mSkipNextMoveEvent = prev.getPointerCount() != curr.getPointerCount(); | ||
mFocusDeltaExternal = mSkipNextMoveEvent ? FOCUS_DELTA_ZERO : new PointF(mCurrFocusInternal.x - mPrevFocusInternal.x, mCurrFocusInternal.y - mPrevFocusInternal.y); | ||
|
||
// - Don't directly use mFocusInternal (or skipping will occur). Add | ||
// unskipped delta values to mFocusExternal instead. | ||
mFocusExternal.x += mFocusDeltaExternal.x; | ||
mFocusExternal.y += mFocusDeltaExternal.y; | ||
} | ||
|
||
/** | ||
* Determine (multi)finger focal point (a.k.a. center point between all | ||
* fingers) | ||
* | ||
* @param MotionEvent e | ||
* @return PointF focal point | ||
*/ | ||
private PointF determineFocalPoint(MotionEvent e){ | ||
// Number of fingers on screen | ||
final int pCount = e.getPointerCount(); | ||
float x = 0f; | ||
float y = 0f; | ||
|
||
for(int i = 0; i < pCount; i++){ | ||
x += e.getX(i); | ||
y += e.getY(i); | ||
} | ||
|
||
return new PointF(x/pCount, y/pCount); | ||
} | ||
|
||
public float getFocusX() { | ||
return mFocusExternal.x; | ||
} | ||
|
||
public float getFocusY() { | ||
return mFocusExternal.y; | ||
} | ||
|
||
public PointF getFocusDelta() { | ||
return mFocusDeltaExternal; | ||
} | ||
|
||
} |
Oops, something went wrong.