@Override
  public List<TouchEvent> getTouchEvents() {
    synchronized (this) {
      // We find out how many events are in the touchEvents arrayList
      int len = touchEvents.size();
      // Then we iterate through each item and add it to the Pool's recyclable Events ArrayList
      for (int i = 0; i < len; i++) {
        touchEventPool.free(touchEvents.get(i));
      }

      // Then we empty the touchEvents ArrayList
      touchEvents.clear();
      // Basically just copy all of the items in touchEventsBuffer
      // ArrayList into touchEvents ArrayList
      touchEvents.addAll(touchEventsBuffer);
      // Then we clear the touchEventsBuffer ArrayList
      touchEventsBuffer.clear();

      return touchEvents;
    }
  }
  /** Called when touch events are fired */
  @Override
  public boolean onTouch(View v, MotionEvent event) {
    synchronized (this) {
      // First we want the event type. We mask the getAction() integer.
      int action = event.getAction() & MotionEvent.ACTION_MASK;
      // Then, we extract the pointer index and fetch the corresponding pointer identifier from the
      // MotionEvent (p.139)
      int pointerIndex =
          (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK)
              >> MotionEvent.ACTION_POINTER_ID_SHIFT;
      // Find out the number of fingers that have coordinates in the motion event. getPointerCount()
      // returns the number of active pointers
      int pointerCount = event.getPointerCount();

      TouchEvent touchEvent;

      for (int i = 0; i < MAX_TOUCHPOINTS; i++) {

        // Reset the data for fingers that are not/no longer touched down.
        if (i >= pointerCount) {
          isTouched[i] = false;
          id[i] = -1;
          continue;
        }

        // getPointerId takes a pointer index, and returns the pointer identifier based on a pointer
        // index
        int pointerId = event.getPointerId(i);

        if (event.getAction() != MotionEvent.ACTION_MOVE && i != pointerIndex) {
          // if it's an up/down/cancel/out event, mask the id to see if we should process it for
          // this touch point
          continue;
        }

        switch (action) {
          case MotionEvent.ACTION_DOWN:
          case MotionEvent.ACTION_POINTER_DOWN:
            {
              touchEvent = touchEventPool.newObject();
              touchEvent.type = TouchEvent.TOUCH_DOWN;
              touchEvent.pointer = pointerId;
              touchEvent.x = touchX[i] = (int) (event.getX(i) * scaleX);
              touchEvent.y = touchY[i] = (int) (event.getY(i) * scaleY);
              isTouched[i] = true;
              id[i] = pointerId;
              touchEventsBuffer.add(touchEvent);
              break;
            }

          case MotionEvent.ACTION_UP:
          case MotionEvent.ACTION_POINTER_UP:
          case MotionEvent.ACTION_CANCEL:
            {
              touchEvent = touchEventPool.newObject();
              touchEvent.type = TouchEvent.TOUCH_UP;
              touchEvent.pointer = pointerId;
              touchEvent.x = touchX[i] = (int) (event.getX(i) * scaleX);
              touchEvent.y = touchY[i] = (int) (event.getY(i) * scaleY);
              isTouched[i] = false;
              id[i] = -1;
              touchEventsBuffer.add(touchEvent);
              break;
            }

          case MotionEvent.ACTION_MOVE:
            {
              touchEvent = touchEventPool.newObject();
              touchEvent.type = TouchEvent.TOUCH_DRAGGED;
              touchEvent.pointer = pointerId;
              touchEvent.x = touchX[i] = (int) (event.getX(i) * scaleX);
              touchEvent.y = touchY[i] = (int) (event.getY(i) * scaleY);
              isTouched[i] = true;
              id[i] = pointerId;
              touchEventsBuffer.add(touchEvent);
              break;
            }
        } // end switch
      } // end for loop

      // Indicate that we processed the touch event ourselves
      return true;
    }
  }