Program: 20625
Project: 20625
Buggy source:
@Override public boolean onTouchEvent(MotionEvent event){
  if (touchable) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
      for (int i=0; i < getChildCount(); i++) {
        View c=getChildAt(i);
        if (c instanceof GestureLockView) {
          ((GestureLockView)c).setLockerState(GestureLockView.LockerState.LOCKER_STATE_NORMAL);
          ((GestureLockView)c).setArrow(-1);
        }
      }
    gesturePath=null;
  lastX=(int)event.getX();
lastY=(int)event.getY();
lastPathX=lastX;
lastPathY=lastY;
paint.setColor(0x66FFFFFF);
break;
case MotionEvent.ACTION_MOVE:
lastX=(int)event.getX();
lastY=(int)event.getY();
int cId=calculateChildIdByCoords(lastX,lastY);
View child=findViewById(cId + 1);
boolean checked=false;
for (int id : gesturesContainer) {
if (id == cId) {
checked=true;
break;
}
}
if (child != null && child instanceof GestureLockView && checkChildInCoords(lastX,lastY,child)) {
((GestureLockView)child).setLockerState(GestureLockView.LockerState.LOCKER_STATE_SELECTED);
if (!checked) {
int checkedX=child.getLeft() + child.getWidth() / 2;
int checkedY=child.getTop() + child.getHeight() / 2;
if (gesturePath == null) {
gesturePath=new Path();
gesturePath.moveTo(checkedX,checkedY);
}
 else {
gesturePath.lineTo(checkedX,checkedY);
}
gesturesContainer[gestureCursor]=cId;
gestureCursor++;
lastPathX=checkedX;
lastPathY=checkedY;
if (onGestureEventListener != null) onGestureEventListener.onBlockSelected(cId);
}
}
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (gesturesContainer[0] != -1) {
boolean matched=false;
int length=0;
for (int i : gesturesContainer) {
if (i != -1) length++;
 else break;
}
if (length != defaultGestures.length) matched=false;
 else for (int j=0; j < defaultGestures.length; j++) {
if (gesturesContainer[j] == defaultGestures[j]) {
matched=true;
}
 else {
matched=false;
break;
}
}
if (!matched && mode != MODE_EDIT) {
unmatchedCount++;
paint.setColor(0x66FF0000);
for (int k=0; k < gesturesContainer.length; k++) {
View selectedChild=findViewById(gesturesContainer[k] + 1);
if (selectedChild != null && selectedChild instanceof GestureLockView) {
((GestureLockView)selectedChild).setLockerState(GestureLockView.LockerState.LOCKER_STATE_ERROR);
if (k < gesturesContainer.length - 1 && gesturesContainer[k + 1] != -1) {
View nextChild=findViewById(gesturesContainer[k + 1] + 1);
if (nextChild != null) {
int dx=nextChild.getLeft() - selectedChild.getLeft();
int dy=nextChild.getTop() - selectedChild.getTop();
int angle=(int)Math.toDegrees(Math.atan2(dy,dx)) + 90;
((GestureLockView)selectedChild).setArrow(angle);
}
}
}
}
}
 else {
unmatchedCount=0;
}
if (onGestureEventListener != null) {
onGestureEventListener.onGestureEvent(matched);
if (unmatchedCount >= unmatchedBoundary) {
onGestureEventListener.onUnmatchedExceedBoundary();
unmatchedCount=0;
}
}
}
gestureCursor=0;
gesturesContainer=negativeGestures.clone();
lastX=lastPathX;
lastY=lastPathY;
invalidate();
break;
}
}
return true;
}

Fixed version (ground truth):
@Override public boolean onTouchEvent(MotionEvent event){
  if (touchable) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
      for (int i=0; i < getChildCount(); i++) {
        View c=getChildAt(i);
        if (c instanceof GestureLockView) {
          ((GestureLockView)c).setLockerState(GestureLockView.LockerState.LOCKER_STATE_NORMAL);
          ((GestureLockView)c).setArrow(-1);
        }
      }
    gesturePath=null;
  lastX=(int)event.getX();
lastY=(int)event.getY();
lastPathX=lastX;
lastPathY=lastY;
paint.setColor(0x66FFFFFF);
break;
case MotionEvent.ACTION_MOVE:
lastX=(int)event.getX();
lastY=(int)event.getY();
int cId=calculateChildIdByCoords(lastX,lastY);
View child=findViewById(cId + 1);
boolean checked=false;
for (int id : gesturesContainer) {
if (id == cId) {
checked=true;
break;
}
}
if (child != null && child instanceof GestureLockView && checkChildInCoords(lastX,lastY,child)) {
((GestureLockView)child).setLockerState(GestureLockView.LockerState.LOCKER_STATE_SELECTED);
if (!checked) {
int checkedX=child.getLeft() + child.getWidth() / 2;
int checkedY=child.getTop() + child.getHeight() / 2;
if (gesturePath == null) {
gesturePath=new Path();
gesturePath.moveTo(checkedX,checkedY);
}
 else {
gesturePath.lineTo(checkedX,checkedY);
}
gesturesContainer[gestureCursor]=cId;
gestureCursor++;
lastPathX=checkedX;
lastPathY=checkedY;
if (onGestureEventListener != null) onGestureEventListener.onBlockSelected(cId);
}
}
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if (gesturesContainer[0] != -1) {
boolean matched=false;
if (gesturesContainer.length > defaultGestures.length && gesturesContainer[defaultGestures.length] != -1) {
matched=false;
}
 else {
for (int j=0; j < defaultGestures.length; j++) {
if (gesturesContainer[j] == defaultGestures[j]) {
matched=true;
}
 else {
matched=false;
break;
}
}
}
if (!matched && mode != MODE_EDIT) {
unmatchedCount++;
paint.setColor(0x66FF0000);
for (int k=0; k < gesturesContainer.length; k++) {
View selectedChild=findViewById(gesturesContainer[k] + 1);
if (selectedChild != null && selectedChild instanceof GestureLockView) {
((GestureLockView)selectedChild).setLockerState(GestureLockView.LockerState.LOCKER_STATE_ERROR);
if (k < gesturesContainer.length - 1 && gesturesContainer[k + 1] != -1) {
View nextChild=findViewById(gesturesContainer[k + 1] + 1);
if (nextChild != null) {
int dx=nextChild.getLeft() - selectedChild.getLeft();
int dy=nextChild.getTop() - selectedChild.getTop();
int angle=(int)Math.toDegrees(Math.atan2(dy,dx)) + 90;
((GestureLockView)selectedChild).setArrow(angle);
}
}
}
}
}
 else {
unmatchedCount=0;
}
if (onGestureEventListener != null) {
onGestureEventListener.onGestureEvent(matched);
if (unmatchedCount >= unmatchedBoundary) {
onGestureEventListener.onUnmatchedExceedBoundary();
unmatchedCount=0;
}
}
}
gestureCursor=0;
gesturesContainer=negativeGestures.clone();
lastX=lastPathX;
lastY=lastPathY;
invalidate();
break;
}
}
return true;
}