@Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
   super.onSizeChanged(w, h, oldw, oldh);
   mViewInfo.globalRect = new Rect(0, 0, w, h);
   mViewInfo.timeRect = new Rect(0, 0, w, h / 10);
   mViewInfo.blockRect = new Rect(0, mViewInfo.timeRect.bottom, w, h);
   if (mGameState.isBeforeStart()) {
     initState();
   }
 }
  private void showBlocks(Canvas canvas) {
    int timeWidth = mViewInfo.timeRect.width() * 2 / 3;
    int timeHeight = Math.min(mViewInfo.timeRect.height() * 2 / 3, 50);
    Rect timeR = new Rect();
    timeR.left = mViewInfo.timeRect.left + (mViewInfo.timeRect.width() - timeWidth) / 2;
    timeR.right = timeR.left + timeWidth;
    timeR.top = mViewInfo.timeRect.top + (mViewInfo.timeRect.height() - timeHeight) / 2;
    timeR.bottom = timeR.top + timeHeight;
    canvas.drawRect(timeR, mBlackPaint);
    timeR.right = timeR.left + (int) (timeR.width() * mGameState.getLeftTimePercent());
    canvas.drawRect(timeR, mGreenPaint);

    Block[][] blocks = mGameState.getBlocks();
    int rows = blocks.length;
    int cols = blocks[0].length;
    int blockSize =
        Math.min(mViewInfo.blockRect.width() / cols, mViewInfo.blockRect.height() / rows);
    mViewInfo.blockHeight = mViewInfo.blockWidth = blockSize;
    Log.d(
        LOG_TAG,
        "r "
            + rows
            + ", c "
            + cols
            + ", w "
            + mViewInfo.blockRect.width()
            + ", h "
            + mViewInfo.blockRect.height()
            + "block "
            + blockSize);

    Rect src = new Rect(0, 0, ANIMAL_BITMAP_WIDTH, ANIMAL_BITMAP_HEIGHT);
    Rect dst = new Rect();
    for (int r = 0; r < rows; ++r) {
      for (int c = 0; c < cols; ++c) {
        if (blocks[r][c].isUnselected()) {
          dst.set(0, 0, mViewInfo.blockWidth, mViewInfo.blockHeight);
          dst.offset(
              mViewInfo.blockRect.left + c * mViewInfo.blockWidth,
              mViewInfo.blockRect.top + r * mViewInfo.blockHeight);
          canvas.drawBitmap(mPictureInfo.animalBitmaps.get(blocks[r][c].imgIndex), src, dst, null);
        } else if (blocks[r][c].isSelected()) {
          dst.set(0, 0, mViewInfo.blockWidth, mViewInfo.blockHeight);
          dst.offset(
              mViewInfo.blockRect.left + c * mViewInfo.blockWidth,
              mViewInfo.blockRect.top + r * mViewInfo.blockHeight);
          canvas.drawBitmap(
              mPictureInfo.animalBitmaps.get(blocks[r][c].imgIndex), src, dst, mSelectedImgPaint);
        }
      }
    }

    ArrayList<Point> linkPath = mGameState.getLinkPath();
    if (linkPath != null) {
      int lineCount = linkPath.size() - 1;
      float[] points = new float[lineCount * 4];
      for (int i = 0, j = 0; i < linkPath.size(); ++i, j += 2) {
        int r = linkPath.get(i).row;
        int c = linkPath.get(i).col;
        if (c >= 0 && c < cols) {
          points[j] = c * mViewInfo.blockWidth + mViewInfo.blockWidth / 2;
        } else if (c == -1) {
          points[j] = 0;
        } else if (c == cols) {
          points[j] = cols * mViewInfo.blockWidth;
        }
        points[j] += mViewInfo.blockRect.left;

        if (r >= 0 && r < rows) {
          points[j + 1] = r * mViewInfo.blockHeight + mViewInfo.blockHeight / 2;
        } else if (r == -1) {
          points[j + 1] = 0;
        } else if (r == rows) {
          points[j + 1] = rows * mViewInfo.blockHeight;
        }
        points[j + 1] += mViewInfo.blockRect.top;
        if (i > 0 && i < linkPath.size() - 1) {
          j += 2;
          points[j] = points[j - 2];
          points[j + 1] = points[j - 1];
        }
      }
      canvas.drawLines(points, mLinePaint);
    }
    ArrayList<Point> hintPoints = mGameState.getHintPoints();
    if (hintPoints != null) {
      Log.d(
          LOG_TAG,
          "draw hintPath r1 "
              + hintPoints.get(0).row
              + ", c1 "
              + hintPoints.get(0).col
              + ", r2 "
              + hintPoints.get(1).row
              + ", c2 "
              + hintPoints.get(1).col);
      for (int i = 0; i < 2; i++) {
        int r = hintPoints.get(i).row;
        int c = hintPoints.get(i).col;
        dst.set(0, 0, mViewInfo.blockWidth, mViewInfo.blockHeight);
        dst.offset(
            mViewInfo.blockRect.left + c * mViewInfo.blockWidth,
            mViewInfo.blockRect.top + r * mViewInfo.blockHeight);
        canvas.drawRect(dst, mHintPaint);
      }
    }
  }
  private ViewInfo generateViewInfo(Context context, int selectedId) {
    Bitmap bitmap = null;
    float focusX = -1, focusY = -1;
    final Resources res = context.getResources();

    // Open the database
    SQLiteDatabase db = getDatabase(context, true);
    if (db == null) {
      // We don't have a valid database reference
      return null;
    }

    // Extract original image size
    Cursor c = db.rawQuery("select value from info where key = ?;", new String[] {"orig_size"});
    if (c == null || !c.moveToFirst()) {
      // We don't have a valid cursor reference
      return null;
    }
    int osize = c.getInt(0);
    c.close();

    // Query the metadata table to extract all the commits information
    c = db.rawQuery("select id, name, x, y, r, fs from metadata;", null);
    if (c == null) {
      // We don't have a valid cursor reference
      return null;
    }
    try {
      int colorForeground = res.getColor(R.color.contributors_cloud_fg_color);
      int colorSelected = res.getColor(R.color.contributors_cloud_selected_color);
      Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);

      // Create a bitmap large enough to hold the cloud (use large bitmap when available)
      int bsize = hasLargeHeap() ? 2048 : 1024;
      bitmap = Bitmap.createBitmap(bsize, bsize, Bitmap.Config.ARGB_8888);
      Canvas canvas = new Canvas(bitmap);

      // Draw every contributor name
      while (c.moveToNext()) {
        int id = c.getInt(c.getColumnIndexOrThrow("id"));

        String name = c.getString(c.getColumnIndexOrThrow("name"));
        float x = translate(c.getFloat(c.getColumnIndexOrThrow("x")), osize, bsize);
        float y = translate(c.getFloat(c.getColumnIndexOrThrow("y")), osize, bsize);
        int r = c.getInt(c.getColumnIndexOrThrow("r"));
        float fs = translate(c.getFloat(c.getColumnIndexOrThrow("fs")), osize, bsize);
        if (id < 0) {
          y -= translate(fs, osize, bsize);
        }

        // Choose the correct paint
        paint.setColor(selectedId == id ? colorSelected : colorForeground);
        paint.setTextSize(fs);

        // Check text rotation
        float w = 0f, h = 0f;
        if (selectedId == id || r != 0) {
          Rect bounds = new Rect();
          paint.getTextBounds(name, 0, name.length(), bounds);
          h = bounds.height();
        }
        if (selectedId == id || r == -1) {
          w = paint.measureText(name);
        }
        if (r == 0) {
          // Horizontal
          canvas.drawText(name, x, y, paint);
        } else {
          if (r == -1) {
            // Vertical (-90 rotation)
            canvas.save();
            canvas.translate(h, w - h);
            canvas.rotate(-90, x, y);
            canvas.drawText(name, x, y, paint);
            canvas.restore();
          } else {
            // Vertical (+90 rotation)
            canvas.save();
            canvas.translate(h / 2, -h);
            canvas.rotate(90, x, y);
            canvas.drawText(name, x, y, paint);
            canvas.restore();
          }
        }

        // Calculate focus
        if (selectedId == id) {
          int iw = mImageView.getWidth();
          int ih = mImageView.getHeight();
          int cx = iw / 2;
          int cy = ih / 2;
          int cbx = bsize / 2;
          int cby = bsize / 2;
          float cw = 0f;
          float ch = 0f;
          if (r == 0) {
            cw = translate(w, bsize, Math.min(iw, ih)) / 2;
            ch = translate(h, bsize, Math.min(iw, ih)) / 2;
          } else {
            cw = translate(h, bsize, Math.min(iw, ih)) / 2;
            ch = translate(w, bsize, Math.min(iw, ih)) / 2;
          }

          focusX = cx + translate(x - cbx, bsize, iw) + cw;
          focusY = cy + translate(y - cby, bsize, ih) + ch;
        }
      }

    } finally {
      c.close();
    }

    // Return the bitmap
    ViewInfo viewInfo = new ViewInfo();
    viewInfo.mBitmap = bitmap;
    viewInfo.mFocusX = focusX;
    viewInfo.mFocusY = focusY;
    return viewInfo;
  }