Example #1
0
 /**
  * Set the display mode of the current pattern. This can be useful, for instance, after detecting
  * a pattern to tell this view whether change the in progress result to correct or wrong.
  *
  * @param displayMode The display mode.
  */
 public void setDisplayMode(final DisplayMode displayMode) {
   patternDisplayMode = displayMode;
   if (displayMode == DisplayMode.Animate) {
     if (mPattern.size() == 0) {
       throw new IllegalStateException(
           "you must have a pattern to "
               + "animate if you want to set the display mode to animate");
     }
     animatingPeriodStart = SystemClock.elapsedRealtime();
     final Cell first = mPattern.get(0);
     inProgressX = getCenterXForColumn(first.getColumn());
     inProgressY = getCenterYForRow(first.getRow());
     clearPatternDrawLookup();
   }
   invalidate();
 }
Example #2
0
  private void handleActionDown(MotionEvent event) {
    resetPattern();
    final float x = event.getX();
    final float y = event.getY();
    final Cell hitCell = detectAndAddHit(x, y);
    if (hitCell != null) {
      patternInProgress = true;
      patternDisplayMode = DisplayMode.Correct;
      notifyPatternStarted();
    } else {
      /*
       * Original source check for patternInProgress == true first before
       * calling this block. But if we do that, there will be nothing
       * happened when the user taps at empty area and releases the
       * finger. We want the pattern to be reset and the message will be
       * updated after the user did that.
       */
      patternInProgress = false;
      notifyPatternCleared();
    }
    if (hitCell != null) {
      final float startX = getCenterXForColumn(hitCell.getColumn());
      final float startY = getCenterYForRow(hitCell.getRow());

      final float widthOffset = squareWidth / 2f;
      final float heightOffset = squareHeight / 2f;

      invalidate(
          (int) (startX - widthOffset),
          (int) (startY - heightOffset),
          (int) (startX + widthOffset),
          (int) (startY + heightOffset));
    }
    inProgressX = x;
    inProgressY = y;
    if (PROFILE_DRAWING) {
      if (!drawingProfilingStarted) {
        Debug.startMethodTracing("LockPatternDrawing");
        drawingProfilingStarted = true;
      }
    }
  }
Example #3
0
  @Override
  protected void onDraw(Canvas canvas) {
    final ArrayList<Cell> pattern = mPattern;
    final int count = pattern.size();

    if (patternDisplayMode == DisplayMode.Animate) {

      final int oneCycle = (count + 1) * MILLIS_PER_CIRCLE_ANIMATING;
      final int spotInCycle =
          (int) (SystemClock.elapsedRealtime() - animatingPeriodStart) % oneCycle;
      final int numCircles = spotInCycle / MILLIS_PER_CIRCLE_ANIMATING;

      clearPatternDrawLookup();
      for (int i = 0; i < numCircles; i++) {
        final Cell cell = pattern.get(i);
        cellManager.draw(cell, true);
      }

      final boolean needToUpdateInProgressPoint = numCircles > 0 && numCircles < count;

      if (needToUpdateInProgressPoint) {
        final float percentageOfNextCircle =
            ((float) (spotInCycle % MILLIS_PER_CIRCLE_ANIMATING)) / MILLIS_PER_CIRCLE_ANIMATING;

        final Cell currentCell = pattern.get(numCircles - 1);
        final float centerX = getCenterXForColumn(currentCell.getColumn());
        final float centerY = getCenterYForRow(currentCell.getRow());

        final Cell nextCell = pattern.get(numCircles);
        final float dx =
            percentageOfNextCircle * (getCenterXForColumn(nextCell.getColumn()) - centerX);
        final float dy = percentageOfNextCircle * (getCenterYForRow(nextCell.getRow()) - centerY);
        inProgressX = centerX + dx;
        inProgressY = centerY + dy;
      }
      invalidate();
    }

    final float squareWidth = this.squareWidth;
    final float squareHeight = this.squareHeight;

    float radius = (squareWidth * diameterFactor * 0.5f);
    pathPaint.setStrokeWidth(radius);

    final Path currentPath = this.currentPath;
    currentPath.rewind();

    // draw the circles
    final int paddingTop = this.paddingTop;
    final int paddingLeft = this.paddingLeft;

    for (int i = 0; i < gridSize; i++) {
      float topY = paddingTop + i * squareHeight;
      for (int j = 0; j < gridSize; j++) {
        float leftX = paddingLeft + j * squareWidth;
        drawCircle(canvas, (int) leftX, (int) topY, cellManager.isDrawn(i, j));
      }
    }

    // only the last segment of the path should be computed here
    // draw the path of the pattern (unless the user is in progress, and
    // we are in stealth mode)
    final boolean drawPath =
        (!inStealthMode && patternDisplayMode == DisplayMode.Correct
            || !inErrorStealthMode && patternDisplayMode == DisplayMode.Wrong);

    // draw the arrows associated with the path (unless the user is in
    // progress, and
    // we are in stealth mode)
    boolean oldFlag = (circlePaint.getFlags() & Paint.FILTER_BITMAP_FLAG) != 0;
    circlePaint.setFilterBitmap(true);

    if (drawPath) {
      boolean anyCircles = false;
      for (int i = 0; i < count; i++) {
        Cell cell = pattern.get(i);

        // only draw the part of the pattern stored in
        // the lookup table (this is only different in the case
        // of animation).
        if (!cellManager.isDrawn(cell)) {
          break;
        }
        anyCircles = true;

        float centerX = getCenterXForColumn(cell.getColumn());
        float centerY = getCenterYForRow(cell.getRow());
        if (i == 0) {
          currentPath.moveTo(centerX, centerY);
        } else {
          currentPath.lineTo(centerX, centerY);
        }
      }

      // add last in progress section
      if ((patternInProgress || patternDisplayMode == DisplayMode.Animate)
          && anyCircles
          && count > 1) {
        currentPath.lineTo(inProgressX, inProgressY);
      }
      canvas.drawPath(currentPath, pathPaint);
    }

    circlePaint.setFilterBitmap(oldFlag); // restore default flag
  }
Example #4
0
  private void handleActionMove(MotionEvent event) {
    // Handle all recent motion events so we don't skip any cells even when
    // the device
    // is busy...
    final int historySize = event.getHistorySize();
    for (int i = 0; i < historySize + 1; i++) {
      final float x = i < historySize ? event.getHistoricalX(i) : event.getX();
      final float y = i < historySize ? event.getHistoricalY(i) : event.getY();
      final int patternSizePreHitDetect = mPattern.size();
      Cell hitCell = detectAndAddHit(x, y);
      final int patternSize = mPattern.size();
      if (hitCell != null && patternSize == 1) {
        patternInProgress = true;
        notifyPatternStarted();
      }
      // note current x and y for rubber banding of in progress patterns
      final float dx = Math.abs(x - inProgressX);
      final float dy = Math.abs(y - inProgressY);
      if (dx + dy > squareWidth * 0.01f) {
        float oldX = inProgressX;
        float oldY = inProgressY;

        inProgressX = x;
        inProgressY = y;

        if (patternInProgress && patternSize > 0) {
          final ArrayList<Cell> pattern = mPattern;
          final float radius = squareWidth * diameterFactor * 0.5f;

          final Cell lastCell = pattern.get(patternSize - 1);

          float startX = getCenterXForColumn(lastCell.getColumn());
          float startY = getCenterYForRow(lastCell.getRow());

          float left;
          float top;
          float right;
          float bottom;

          final Rect invalidateRect = invalidate;

          if (startX < x) {
            left = startX;
            right = x;
          } else {
            left = x;
            right = startX;
          }

          if (startY < y) {
            top = startY;
            bottom = y;
          } else {
            top = y;
            bottom = startY;
          }

          // Invalidate between the pattern's last cell and the
          // current location
          invalidateRect.set(
              (int) (left - radius),
              (int) (top - radius),
              (int) (right + radius),
              (int) (bottom + radius));

          if (startX < oldX) {
            left = startX;
            right = oldX;
          } else {
            left = oldX;
            right = startX;
          }

          if (startY < oldY) {
            top = startY;
            bottom = oldY;
          } else {
            top = oldY;
            bottom = startY;
          }

          // Invalidate between the pattern's last cell and the
          // previous location
          invalidateRect.union(
              (int) (left - radius),
              (int) (top - radius),
              (int) (right + radius),
              (int) (bottom + radius));

          // Invalidate between the pattern's new cell and the
          // pattern's previous cell
          if (hitCell != null) {
            startX = getCenterXForColumn(hitCell.getColumn());
            startY = getCenterYForRow(hitCell.getRow());

            if (patternSize >= 2) {
              // (re-using hitcell for old cell)
              hitCell = pattern.get(patternSize - 1 - (patternSize - patternSizePreHitDetect));
              oldX = getCenterXForColumn(hitCell.getColumn());
              oldY = getCenterYForRow(hitCell.getRow());

              if (startX < oldX) {
                left = startX;
                right = oldX;
              } else {
                left = oldX;
                right = startX;
              }

              if (startY < oldY) {
                top = startY;
                bottom = oldY;
              } else {
                top = oldY;
                bottom = startY;
              }
            } else {
              left = right = startX;
              top = bottom = startY;
            }

            final float widthOffset = squareWidth / 2f;
            final float heightOffset = squareHeight / 2f;

            invalidateRect.set(
                (int) (left - widthOffset),
                (int) (top - heightOffset),
                (int) (right + widthOffset),
                (int) (bottom + heightOffset));
          }

          invalidate(invalidateRect);
        } else {
          invalidate();
        }
      }
    }
    invalidate();
  }
Example #5
0
  /**
   * Determines whether the point x, y will add a new point to the current pattern (in addition to
   * finding the cell, also makes heuristic choices such as filling in gaps based on current
   * pattern).
   *
   * @param x The x coordinate.
   * @param y The y coordinate.
   */
  private Cell detectAndAddHit(float x, float y) {
    final Cell cell = checkForNewHit(x, y);
    if (cell != null) {

      // check for gaps in existing pattern
      // Cell fillInGapCelal = null;
      final ArrayList<Cell> newCells = new ArrayList<>();
      if (!mPattern.isEmpty()) {
        final Cell lastCell = mPattern.get(mPattern.size() - 1);
        int dRow = cell.getRow() - lastCell.getRow();
        int dCol = cell.getColumn() - lastCell.getColumn();
        int rsign = dRow > 0 ? 1 : -1;
        int csign = dCol > 0 ? 1 : -1;

        if (dRow == 0) {
          for (int i = 1; i < Math.abs(dCol); i++) {
            newCells.add(new Cell(lastCell.getRow(), lastCell.getColumn() + i * csign));
          }
        } else if (dCol == 0) {
          for (int i = 1; i < Math.abs(dRow); i++) {
            newCells.add(new Cell(lastCell.getRow() + i * rsign, lastCell.getColumn()));
          }
        } else if (Math.abs(dCol) == Math.abs(dRow)) {
          for (int i = 1; i < Math.abs(dRow); i++) {
            newCells.add(new Cell(lastCell.getRow() + i * rsign, lastCell.getColumn() + i * csign));
          }
        }
      }
      for (Cell fillInGapCell : newCells) {
        if (fillInGapCell != null && !cellManager.isDrawn(fillInGapCell)) {
          addCellToPattern(fillInGapCell);
        }
      }
      addCellToPattern(cell);
      if (enableHapticFeedback) {
        performHapticFeedback(
            HapticFeedbackConstants.VIRTUAL_KEY,
            HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING
                | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
      }
      return cell;
    }
    return null;
  }