private void startFaceDetection() {
    if (isFinishing()) {
      return;
    }

    mImageView.setImageBitmapResetBase(mBitmap, true);

    Util.startBackgroundJob(
        this,
        this,
        null,
        getResources().getString(R.string.runningFaceDetection),
        new Runnable() {
          public void run() {
            final CountDownLatch latch = new CountDownLatch(1);
            final Bitmap b =
                (mImage != null)
                    ? mImage.fullSizeBitmap(IImage.UNCONSTRAINED, 1024 * 1024)
                    : mBitmap;
            mHandler.post(
                new Runnable() {
                  public void run() {
                    if (b != mBitmap && b != null) {
                      mImageView.setImageBitmapResetBase(b, true);
                      mBitmap.recycle();
                      mBitmap = b;
                    }
                    if (mImageView.getScale() == 1F) {
                      mImageView.center(true, true);
                    }
                    latch.countDown();
                  }
                });
            try {
              latch.await();
            } catch (InterruptedException e) {
              throw new RuntimeException(e);
            }
            mRunFaceDetection.run();
          }
        },
        mHandler);
  }
  private void onSaveClicked() {
    // TODO this code needs to change to use the decode/crop/encode single
    // step api so that we don't require that the whole (possibly large)
    // bitmap doesn't have to be read into memory
    if (mCrop == null) {
      return;
    }

    if (mSaving) return;
    mSaving = true;

    Bitmap croppedImage;

    // If the output is required to a specific size, create an new image
    // with the cropped image in the center and the extra space filled.
    if (mOutputX != 0 && mOutputY != 0 && !mScale) {
      // Don't scale the image but instead fill it so it's the
      // required dimension
      croppedImage = Bitmap.createBitmap(mOutputX, mOutputY, Bitmap.Config.RGB_565);
      Canvas canvas = new Canvas(croppedImage);

      Rect srcRect = mCrop.getCropRect();
      Rect dstRect = new Rect(0, 0, mOutputX, mOutputY);

      int dx = (srcRect.width() - dstRect.width()) / 2;
      int dy = (srcRect.height() - dstRect.height()) / 2;

      // If the srcRect is too big, use the center part of it.
      srcRect.inset(Math.max(0, dx), Math.max(0, dy));

      // If the dstRect is too big, use the center part of it.
      dstRect.inset(Math.max(0, -dx), Math.max(0, -dy));

      // Draw the cropped bitmap in the center
      canvas.drawBitmap(mBitmap, srcRect, dstRect, null);

      // Release bitmap memory as soon as possible
      mImageView.clear();
      mBitmap.recycle();
    } else {
      Rect r = mCrop.getCropRect();

      int width = r.width();
      int height = r.height();

      // If we are circle cropping, we want alpha channel, which is the
      // third param here.
      croppedImage =
          Bitmap.createBitmap(
              width, height, mCircleCrop ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);

      Canvas canvas = new Canvas(croppedImage);
      Rect dstRect = new Rect(0, 0, width, height);
      canvas.drawBitmap(mBitmap, r, dstRect, null);

      // Release bitmap memory as soon as possible
      mImageView.clear();
      mBitmap.recycle();

      if (mCircleCrop) {
        // OK, so what's all this about?
        // Bitmaps are inherently rectangular but we want to return
        // something that's basically a circle.  So we fill in the
        // area around the circle with alpha.  Note the all important
        // PortDuff.Mode.CLEAR.
        Canvas c = new Canvas(croppedImage);
        Path p = new Path();
        p.addCircle(width / 2F, height / 2F, width / 2F, Path.Direction.CW);
        c.clipPath(p, Region.Op.DIFFERENCE);
        c.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
      }

      // If the required dimension is specified, scale the image.
      if (mOutputX != 0 && mOutputY != 0 && mScale) {
        croppedImage =
            Util.transform(
                new Matrix(), croppedImage, mOutputX, mOutputY, mScaleUp, Util.RECYCLE_INPUT);
      }
    }

    mImageView.setImageBitmapResetBase(croppedImage, true);
    mImageView.center(true, true);
    mImageView.mHighlightViews.clear();

    // Return the cropped image directly or save it to the specified URI.
    Bundle myExtras = getIntent().getExtras();
    if (myExtras != null
        && (myExtras.getParcelable("data") != null || myExtras.getBoolean("return-data"))) {
      Bundle extras = new Bundle();
      extras.putParcelable("data", croppedImage);
      setResult(RESULT_OK, (new Intent()).setAction("inline-data").putExtras(extras));
      finish();
    } else {
      final Bitmap b = croppedImage;
      final int msdId = mSetWallpaper ? R.string.wallpaper : R.string.savingImage;
      Util.startBackgroundJob(
          this,
          this,
          null,
          getResources().getString(msdId),
          new Runnable() {
            public void run() {
              saveOutput(b);
            }
          },
          mHandler);
    }
  }