kegs-android/src/com/froop/app/kegs/KegsTouch.java
2012-12-21 19:58:50 -08:00

218 lines
7.7 KiB
Java

package com.froop.app.kegs;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import java.util.concurrent.ConcurrentLinkedQueue;
// Translate onTouchEvent calls into mouse pointer movement.
class KegsTouch {
private ConcurrentLinkedQueue mEventQueue;
private int mButton1 = 0;
private int mTouchSlopSquare;
private int mPrimaryId = -1; // mouse movement
private int mSecondaryId = -1; // button presses
private boolean mPrimaryPastSlop = false;
private boolean mPrimaryLongPress = false;
private int mPrimaryX = -1; // original X
private int mPrimaryY = -1; // original Y
private int mPrimaryLastX = -1; // last seen X
private int mPrimaryLastY = -1; // last seen Y
private int mSecondaryX = -1; // in case of promotion to primary
private int mSecondaryY = -1; // in case of promotion to primary
private static final int LONG_PRESS = 1;
private static final int LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
private TouchSpecialZone mSpecialZone = null;
public KegsTouch(Context context, ConcurrentLinkedQueue q) {
mEventQueue = q;
final ViewConfiguration configuration = ViewConfiguration.get(context);
int touchSlop = configuration.getScaledTouchSlop();
mTouchSlopSquare = touchSlop * touchSlop;
}
public void setSpecialZone(TouchSpecialZone zone) {
mSpecialZone = zone;
}
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == LONG_PRESS) {
if (mPrimaryId != -1) {
mPrimaryLongPress = true;
mButton1 = 1;
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
}
}
}
};
// For promotion of secondary to primary. Crazy stuff.
private boolean checkSecondarySlop(MotionEvent newPoint, int index) {
final int deltaX = (int)newPoint.getX(index) - mSecondaryX;
final int deltaY = (int)newPoint.getY(index) - mSecondaryY;
final int distance = (deltaX * deltaX) + (deltaY * deltaY);
if (distance > mTouchSlopSquare) {
return true;
} else {
return false;
}
}
private boolean checkPrimarySlop(MotionEvent newPoint, int index) {
if (mPrimaryPastSlop) {
return true;
}
final int deltaX = (int)newPoint.getX(index) - mPrimaryX;
final int deltaY = (int)newPoint.getY(index) - mPrimaryY;
final int distance = (deltaX * deltaX) + (deltaY * deltaY);
if (distance > mTouchSlopSquare) {
mPrimaryPastSlop = true;
return true;
} else {
return false;
}
}
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getActionMasked();
final int pointerIndex = event.getActionIndex();
final int pointerId = event.getPointerId(pointerIndex);
if (action == MotionEvent.ACTION_DOWN ||
action == MotionEvent.ACTION_POINTER_DOWN) {
if (mPrimaryId == -1 && mSecondaryId != pointerId) {
// First new finger down becomes movement.
mPrimaryId = pointerId;
mPrimaryX = (int)event.getX(pointerIndex);
mPrimaryY = (int)event.getY(pointerIndex);
mPrimaryLastX = mPrimaryX;
mPrimaryLastY = mPrimaryY;
mPrimaryPastSlop = false;
mPrimaryLongPress = false;
// Track for long presses.
mHandler.removeMessages(LONG_PRESS);
mHandler.sendEmptyMessageDelayed(LONG_PRESS, LONG_PRESS_TIMEOUT);
} else {
// Any subequent fingers become the mouse button.
mSecondaryId = pointerId;
mSecondaryX = (int)event.getX(pointerIndex);
mSecondaryY = (int)event.getY(pointerIndex);
mButton1 = 1;
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
}
return true;
} else if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_POINTER_UP) {
if (mPrimaryId == pointerId) {
mHandler.removeMessages(LONG_PRESS);
if (mPrimaryLongPress) {
if (mSecondaryId == -1) {
// If the other finger is down, let it take over the mouse button.
mButton1 = 0;
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
}
} else if (!checkPrimarySlop(event, pointerIndex)) {
// It didn't move while it was down, so send a click event.
if (mSpecialZone != null &&
!mSpecialZone.click(event, pointerIndex)) {
mButton1 = 1;
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
mButton1 = 0;
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
}
}
mPrimaryId = -1;
mPrimaryPastSlop = false;
mPrimaryLongPress = false;
return true;
} else if (mSecondaryId == pointerId) {
// Release mouse button.
mButton1 = 0;
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
mSecondaryId = -1;
mSecondaryX = -1;
mSecondaryY = -1;
return true;
}
} else if (action == MotionEvent.ACTION_CANCEL) {
if (pointerId == mPrimaryId) {
mHandler.removeMessages(LONG_PRESS);
if (mPrimaryLongPress && mSecondaryId == -1 && (mButton1 & 1) == 1) {
mButton1 = 0;
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
}
mPrimaryId = -1;
mPrimaryPastSlop = false;
mPrimaryLongPress = false;
return true;
} else if (pointerId == mSecondaryId) {
mSecondaryId = -1;
mSecondaryX = -1;
mSecondaryY = -1;
if ((mButton1 & 1) == 1) {
mButton1 = 0;
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
}
return true;
}
} else if (action == MotionEvent.ACTION_MOVE) {
// This doesn't use getActionIndex(), we need to check the entire event.
int moveIndex;
int moveId;
for(moveIndex = 0; moveIndex < event.getPointerCount(); moveIndex++) {
moveId = event.getPointerId(moveIndex);
if (moveId == mSecondaryId && mPrimaryId != -1 && !mPrimaryPastSlop && mSecondaryX != -1 && mSecondaryY != -1) {
// If the secondary goes past slop now, swap primary and secondary...
// This allows you to click with one finger, and then drag the next
// and that drag becomes the primary finger.
if (checkSecondarySlop(event, moveIndex)) {
final int oldPrimary = mPrimaryId;
mPrimaryId = mSecondaryId;
mSecondaryId = oldPrimary;
mPrimaryPastSlop = true;
mPrimaryX = mSecondaryX;
mPrimaryY = mSecondaryY;
mPrimaryLastX = mSecondaryX;
mPrimaryLastY = mSecondaryY;
mSecondaryX = -1;
mSecondaryY = -1;
}
// Let this fall through to process the event.
}
if (moveId == mPrimaryId) {
if (checkPrimarySlop(event, moveIndex)) {
mHandler.removeMessages(LONG_PRESS);
final int currentX = (int)event.getX(moveIndex);
final int currentY = (int)event.getY(moveIndex);
final int changeX = currentX - mPrimaryLastX;
final int changeY = currentY - mPrimaryLastY;
mPrimaryLastX = currentX;
mPrimaryLastY = currentY;
mEventQueue.add(new Event.MouseKegsEvent(changeX, changeY, mButton1, 1));
// Once we have had an active primary, don't allow promotions.
mSecondaryX = -1;
mSecondaryY = -1;
}
}
}
return true; // ACTION_MOVE
}
return false;
}
}