Example #1
0
  public Bitmap fullSizeBitmap(
      int minSideLength, int maxNumberOfPixels, boolean rotateAsNeeded, boolean useNative) {
    Uri url = mContainer.contentUri(mId);
    if (url == null) return null;

    Bitmap b = Util.makeBitmap(minSideLength, maxNumberOfPixels, url, mContentResolver, useNative);

    if (b != null && rotateAsNeeded) {
      b = Util.rotate(b, getDegreesRotated());
    }

    return b;
  }
  public void initialize(
      Context context,
      PreferenceGroup group,
      boolean isZoomSupported,
      String[] keys,
      String[] otherSettingKeys) {
    mShutterButtonRadius = IndicatorControlWheelContainer.SHUTTER_BUTTON_RADIUS;
    mStrokeWidth = Util.dpToPixel(IndicatorControlWheelContainer.STROKE_WIDTH);
    mWheelRadius = mShutterButtonRadius + mStrokeWidth * 0.5;

    setPreferenceGroup(group);

    // Add the ZoomControl if supported.
    if (isZoomSupported) {
      mZoomControl = (ZoomControlWheel) findViewById(R.id.zoom_control);
      mZoomControl.setVisibility(View.VISIBLE);
    }

    // Add CameraPicker.
    initializeCameraPicker();

    // Add second-level Indicator Icon.
    mSecondLevelIcon = addImageButton(context, R.drawable.ic_settings_holo_light, true);
    mSecondLevelStartIndex = getChildCount();

    // Add second-level buttons.
    mCloseIcon = addImageButton(context, R.drawable.btn_wheel_close_settings, false);
    addControls(keys, otherSettingKeys);

    // The angle(in radians) of each icon for touch events.
    mChildRadians = new double[getChildCount()];
    presetFirstLevelChildRadians();
    presetSecondLevelChildRadians();
    mInitialized = true;
  }
 @Override
 public void onOrientationChanged(int orientation) {
   if (mOrientation != orientation) {
     mOrientation = orientation;
     Util.setOrientation(mView, mOrientation, true);
   }
 }
 private synchronized void handleShow() {
   if (mView != mNextView) {
     // remove the old view if necessary
     handleHide();
     mView = mNextView;
     // / M: we set hint center_horizontal and bottom in xml.
     // final int gravity = mGravity;
     // mParams.gravity = gravity;
     // if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK)
     // == Gravity.FILL_HORIZONTAL) {
     // mParams.horizontalWeight = 1.0f;
     // }
     // if ((gravity & Gravity.VERTICAL_GRAVITY_MASK)
     // == Gravity.FILL_VERTICAL) {
     // mParams.verticalWeight = 1.0f;
     // }
     // mParams.x = mX;
     // mParams.y = mY;
     // mParams.verticalMargin = mVerticalMargin;
     // mParams.horizontalMargin = mHorizontalMargin;
     mParams.x = 0;
     mParams.y = 0;
     mParams.height = WindowManager.LayoutParams.MATCH_PARENT;
     mParams.width = WindowManager.LayoutParams.MATCH_PARENT;
     try {
       if (mView.getParent() != null) {
         mWM.removeView(mView);
       }
       mWM.addView(mView, mParams);
     } catch (BadTokenException ex) {
       ex.printStackTrace();
     }
     Util.fadeIn(mView);
   }
 }
Example #5
0
    public void saveImageToDatabase(RequestOperator r) {
      Log.i(TAG, "[saveImageToDatabase]...");
      // Insert into MediaStore.
      ContentValues values = new ContentValues(14);
      values.put(ImageColumns.TITLE, r.mTitle);
      values.put(ImageColumns.DISPLAY_NAME, r.mFileName);
      values.put(ImageColumns.DATE_TAKEN, r.mDateTaken);
      values.put(ImageColumns.MIME_TYPE, r.mMimeType);
      values.put(ImageColumns.DATA, r.mFilePath);
      values.put(ImageColumns.SIZE, r.mDataSize);
      if (r.mLocation != null) {
        values.put(ImageColumns.LATITUDE, r.mLocation.getLatitude());
        values.put(ImageColumns.LONGITUDE, r.mLocation.getLongitude());
      }
      values.put(ImageColumns.ORIENTATION, r.mOrientation);
      // M: ConShots
      values.put(Images.Media.GROUP_ID, r.mGroupId);
      values.put(Images.Media.GROUP_INDEX, r.mGroupIndex);
      values.put(Images.Media.FOCUS_VALUE_HIGH, r.mFocusValueHigh);
      values.put(Images.Media.FOCUS_VALUE_LOW, r.mFocusValueLow);
      // Add for Refocus image database
      values.put(Images.Media.CAMERA_REFOCUS, r.mTag);
      values.put(ImageColumns.WIDTH, r.mWidth);
      values.put(ImageColumns.HEIGHT, r.mHeight);
      try {
        r.mUri = mContentResolver.insert(Images.Media.EXTERNAL_CONTENT_URI, values);
        if (r.mUri != null) {
          mContext.addSecureAlbumItemIfNeeded(false, r.mUri);
          if (mContext.isNonePickIntent()) {
            // picture taken and saved by camera which is launched
            // by 3rd apps will
            // be inserted into DB. But do not broadcast
            // "New_Picture" intent,
            // otherwise, it will not pass camera CTS test.
            Util.broadcastNewPicture(mContext, r.mUri);
          }
          Log.i(TAG, "[saveImageToDatabase]mUri = " + r.mUri);
        }

      } catch (IllegalArgumentException e) { // Here we keep google
        // default, don't
        // follow check style
        // This can happen when the external volume is already mounted,
        // but
        // MediaScanner has not notify MediaProvider to add that volume.
        // The picture is still safe and MediaScanner will find it and
        // insert it into MediaProvider. The only problem is that the
        // user
        // cannot click the thumbnail to review the picture.
        Log.e(TAG, "[saveImageToDatabase]Failed to write MediaStore,IllegalArgumentException:", e);
      } catch (UnsupportedOperationException e) {
        Log.e(
            TAG,
            "[saveImageToDatabase]Failed to write MediaStore," + "UnsupportedOperationException:",
            e);
      }
    }
 // Set the override and/or reload the value from preferences.
 private void updateContent(String override, boolean reloadValue) {
   if (!reloadValue && Util.equals(mOverride, override)) return;
   IconListPreference pref = mPreference;
   mOverride = override;
   int index = pref.findIndexOfValue(override == null ? pref.getValue() : override);
   if (mIndex != index) {
     mIndex = index;
     invalidate();
   }
 }
  @Override
  protected void onFinishInflate() {
    mShutterButton = findViewById(R.id.shutter_button);
    mShutterButtonRadius = Util.dpToPixel(SHUTTER_BUTTON_RADIUS);

    mZoomControlWheel = (ZoomControlWheel) findViewById(R.id.zoom_control);
    mZoomControlWheel.setOnIndicatorEventListener(this);

    mIndicatorControlWheel = (IndicatorControlWheel) findViewById(R.id.indicator_control_wheel);
  }
Example #8
0
  @Override
  protected void onDraw(Canvas canvas) {
    if (!mBlocked && (mFaces != null) && (mFaces.length > 0)) {
      final CameraScreenNail sn = ((CameraActivity) getContext()).getCameraScreenNail();
      int rw = sn.getUncroppedRenderWidth();
      int rh = sn.getUncroppedRenderHeight();
      // Prepare the matrix.
      if (((rh > rw) && ((mDisplayOrientation == 0) || (mDisplayOrientation == 180)))
          || ((rw > rh) && ((mDisplayOrientation == 90) || (mDisplayOrientation == 270)))) {
        int temp = rw;
        rw = rh;
        rh = temp;
      }
      Util.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, rw, rh);
      int dx = (getWidth() - rw) / 2;
      int dy = (getHeight() - rh) / 2;

      // Focus indicator is directional. Rotate the matrix and the canvas
      // so it looks correctly in all orientations.
      canvas.save();
      mMatrix.postRotate(mOrientation); // postRotate is clockwise
      canvas.rotate(-mOrientation); // rotate is counter-clockwise (for canvas)
      for (int i = 0; i < mFaces.length; i++) {
        // Filter out false positives.
        if (mFaces[i].score < 50) continue;

        // Transform the coordinates.
        mRect.set(mFaces[i].rect);
        if (LOGV) Util.dumpRect(mRect, "Original rect");
        mMatrix.mapRect(mRect);
        if (LOGV) Util.dumpRect(mRect, "Transformed rect");
        mPaint.setColor(mColor);
        mRect.offset(dx, dy);
        canvas.drawOval(mRect, mPaint);
      }
      canvas.restore();
    }
    super.onDraw(canvas);
  }
Example #9
0
 private void setupDimension() {
   ParcelFileDescriptor input = null;
   try {
     input = mContentResolver.openFileDescriptor(mUri, "r");
     BitmapFactory.Options options = new BitmapFactory.Options();
     options.inJustDecodeBounds = true;
     BitmapManager.instance().decodeFileDescriptor(input.getFileDescriptor(), options);
     mWidth = options.outWidth;
     mHeight = options.outHeight;
   } catch (FileNotFoundException ex) {
     mWidth = 0;
     mHeight = 0;
   } finally {
     Util.closeSilently(input);
   }
 }
Example #10
0
 private synchronized void handleHide() {
   if (mView != null) {
     // note: checking parent() just to make sure the view has
     // been added... i have seen cases where we get here when
     // the view isn't yet added, so let's try not to crash.
     Util.fadeOut(mView);
     try {
       if (mView.getParent() != null) {
         mWM.removeView(mView);
       }
     } catch (BadTokenException ex) {
       ex.printStackTrace();
     }
     mView = null;
   }
 }
Example #11
0
 public Bitmap miniThumbBitmap() {
   Bitmap b = null;
   try {
     long id = mId;
     b =
         BitmapManager.instance()
             .getThumbnail(mContentResolver, id, Images.Thumbnails.MICRO_KIND, null, false);
   } catch (Throwable ex) {
     Log.e(TAG, "miniThumbBitmap got exception", ex);
     return null;
   }
   if (b != null) {
     b = Util.rotate(b, getDegreesRotated());
   }
   return b;
 }
  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

    // Layout the shutter button.
    int shutterButtonWidth = mShutterButton.getMeasuredWidth();
    int shutterButtonHeight = mShutterButton.getMeasuredHeight();
    mCenterX = right - left - Util.dpToPixel(FULL_WHEEL_RADIUS);
    mCenterY = (bottom - top) / 2;
    mShutterButton.layout(
        right - left - shutterButtonWidth,
        mCenterY - shutterButtonHeight / 2,
        right - left,
        mCenterY + shutterButtonHeight - shutterButtonHeight / 2);
    // Layout the control wheel.
    mIndicatorControlWheel.layout(0, 0, right - left, bottom - top);
    mZoomControlWheel.layout(0, 0, right - left, bottom - top);
  }
Example #13
0
    @Override
    public void saveRequest() {
      // title, file path, temp file path is ready
      FileOutputStream out = null;
      try {
        // Write to a temporary file and rename it to the final name.
        // This
        // avoids other apps reading incomplete data.
        out = new FileOutputStream(mTempFilePath);
        out.write(mData);
        out.close();
        new File(mTempFilePath).renameTo(new File(mFilePath));
      } catch (IOException e) {
        Log.e(TAG, "[saveRequest]PanoOperator,Failed to write image", e);
      } finally {
        if (out != null) {
          try {
            out.close();
          } catch (IOException e) {
            Log.e(TAG, "[saveRequest]PanoOperator,exception:", e);
          }
        }
      }
      mDataSize = new File(mFilePath).length();
      try {
        ExifInterface exif = new ExifInterface(mFilePath);
        int orientation = Util.getExifOrientation(exif);
        int width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
        int height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
        mWidth = width;
        mHeight = height;
        mOrientation = orientation;
      } catch (IOException ex) {
        Log.e(TAG, "[saveRequest]PanoOperator,cannot read exif:", ex);
      }
      if (null == mFileName) {
        mTitle = createName(mFileType, mDateTaken, mGroupIndex);
        mFileName = Storage.generateFileName(mTitle, mTempPictureType);
        Log.i(TAG, "[saveRequest]PhotoOperator,mFileName = " + mFileName);
      }
      mMimeType = Storage.generateMimetype(mTitle, mTempPictureType);

      saveImageToDatabase(this);
    }
  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);
  }
  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    if (!mInitialized) return;
    if (mInAnimation) {
      rotateWheel();
      mHandler.post(mRunnable);
    }
    mCenterX = right - left - Util.dpToPixel(IndicatorControlWheelContainer.FULL_WHEEL_RADIUS);
    mCenterY = (bottom - top) / 2;

    // Layout the indicators based on the current level.
    // The icons are spreaded on the left side of the shutter button.
    for (int i = 0; i < getChildCount(); ++i) {
      View view = getChildAt(i);
      // We still need to show the disabled indicators in the second level.
      double radian = mChildRadians[i];
      double startVisibleRadians =
          mInAnimation ? mStartVisibleRadians[1] : mStartVisibleRadians[mCurrentLevel];
      double endVisibleRadians =
          mInAnimation ? mEndVisibleRadians[1] : mEndVisibleRadians[mCurrentLevel];
      if ((!view.isEnabled() && (mCurrentLevel == 0))
          || (radian < (startVisibleRadians - HIGHLIGHT_RADIANS / 2))
          || (radian > (endVisibleRadians + HIGHLIGHT_RADIANS / 2))) {
        view.setVisibility(View.GONE);
        continue;
      }
      view.setVisibility(View.VISIBLE);
      int x = mCenterX + (int) (mWheelRadius * Math.cos(radian));
      int y = mCenterY - (int) (mWheelRadius * Math.sin(radian));
      int width = view.getMeasuredWidth();
      int height = view.getMeasuredHeight();
      if (view == mZoomControl) {
        // ZoomControlWheel matches the size of its parent view.
        view.layout(0, 0, right - left, bottom - top);
      } else {
        view.layout(x - width / 2, y - height / 2, x + width / 2, y + height / 2);
      }
    }
  }
 public void onClick(View v) {
   switch (v.getId()) {
     case R.id.discard:
       MenuHelper.deletePhoto(this, mDeletePhotoRunnable);
       break;
     case R.id.play:
       startPlayVideoActivity();
       break;
     case R.id.share:
       {
         IImage image = mAllImages.getImageAt(mCurrentPosition);
         if (!MenuHelper.isWhiteListUri(image.fullSizeImageUri())) {
           return;
         }
         startShareMediaActivity(image);
         break;
       }
     case R.id.setas:
       {
         IImage image = mAllImages.getImageAt(mCurrentPosition);
         Intent intent = Util.createSetAsIntent(image);
         try {
           startActivity(Intent.createChooser(intent, getText(R.string.setImage)));
         } catch (android.content.ActivityNotFoundException ex) {
           Toast.makeText(this, R.string.no_way_to_share_video, Toast.LENGTH_SHORT).show();
         }
         break;
       }
     case R.id.next_image:
       moveNextOrPrevious(1);
       break;
     case R.id.prev_image:
       moveNextOrPrevious(-1);
       break;
   }
 }
  private void saveOutput(Bitmap croppedImage) {
    if (mSaveUri != null) {
      OutputStream outputStream = null;
      try {
        outputStream = mContentResolver.openOutputStream(mSaveUri);
        if (outputStream != null) {
          croppedImage.compress(mOutputFormat, mOutputQuality, outputStream);
        }
      } catch (IOException ex) {
        // TODO: report error to caller
        Log.e(TAG, "Cannot open file: " + mSaveUri, ex);
      } finally {
        Util.closeSilently(outputStream);
      }
      Bundle extras = new Bundle();
      setResult(RESULT_OK, new Intent(mSaveUri.toString()).putExtras(extras));
    } else if (mSetWallpaper) {
      try {
        WallpaperManager.getInstance(this).setBitmap(croppedImage);
        setResult(RESULT_OK);
      } catch (IOException e) {
        Log.e(TAG, "Failed to set wallpaper.", e);
        setResult(RESULT_CANCELED);
      }
    } else {
      Bundle extras = new Bundle();
      extras.putString("rect", mCrop.getCropRect().toString());

      File oldPath = new File(mImage.getDataPath());
      File directory = new File(oldPath.getParent());

      int x = 0;
      String fileName = oldPath.getName();
      fileName = fileName.substring(0, fileName.lastIndexOf("."));

      // Try file-1.jpg, file-2.jpg, ... until we find a filename which
      // does not exist yet.
      while (true) {
        x += 1;
        String candidate = directory.toString() + "/" + fileName + "-" + x + ".jpg";
        boolean exists = (new File(candidate)).exists();
        if (!exists) {
          break;
        }
      }

      try {
        int[] degree = new int[1];
        Uri newUri =
            ImageManager.addImage(
                mContentResolver,
                mImage.getTitle(),
                mImage.getDateTaken(),
                null, // TODO this null is going to cause us to lose
                // the location (gps).
                directory.toString(),
                fileName + "-" + x + ".jpg",
                croppedImage,
                null,
                degree);

        setResult(RESULT_OK, new Intent().setAction(newUri.toString()).putExtras(extras));
      } catch (Exception ex) {
        // basically ignore this or put up
        // some ui saying we failed
        Log.e(TAG, "store image fail, continue anyway", ex);
      }
    }

    final Bitmap b = croppedImage;
    mHandler.post(
        new Runnable() {
          public void run() {
            mImageView.clear();
            b.recycle();
          }
        });

    finish();
  }
/** A view that contains camera zoom control and its layout. */
public class ZoomControlBar extends ZoomControl {
  @SuppressWarnings("unused")
  private static final String TAG = "ZoomControlBar";

  private static final int THRESHOLD_FIRST_MOVE = Util.dpToPixel(10); // pixels
  // Space between indicator icon and the zoom-in/out icon.
  private static final int ICON_SPACING = Util.dpToPixel(12);

  private View mBar;
  private boolean mStartChanging;
  private int mSliderPosition = 0;
  private int mSliderLength;
  // The width of the zoom control bar (including the '+', '-' icons and the
  // slider bar) for phone in portrait orientation, or the height of that
  // for phone in landscape orientation.
  private int mSize;
  // The width of the '+' icon (the same as '-' icon) for phone in portrait
  // orientation, or the height of that for phone in landscape orientation.
  private int mIconSize;
  // mIconSize + padding
  private int mTotalIconSize;

  public ZoomControlBar(Context context, AttributeSet attrs) {
    super(context, attrs);
    mBar = new View(context);
    mBar.setBackgroundResource(R.drawable.zoom_slider_bar);
    addView(mBar);
  }

  @Override
  public void setActivated(boolean activated) {
    super.setActivated(activated);
    mBar.setActivated(activated);
  }

  private int getSliderPosition(int offset) {
    // Calculate the absolute offset of the slider in the zoom control bar.
    // For left-hand users, as the device is rotated for 180 degree for
    // landscape mode, the zoom-in bottom should be on the top, so the
    // position should be reversed.
    int pos; // the relative position in the zoom slider bar
    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
      if (mOrientation == 180) {
        pos = offset - mTotalIconSize;
      } else {
        pos = mSize - mTotalIconSize - offset;
      }
    } else {
      if (mOrientation == 90) {
        pos = mSize - mTotalIconSize - offset;
      } else {
        pos = offset - mTotalIconSize;
      }
    }
    if (pos < 0) pos = 0;
    if (pos > mSliderLength) pos = mSliderLength;
    return pos;
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
      mSize = h;
      mIconSize = mZoomIn.getMeasuredHeight();
    } else {
      mSize = w;
      mIconSize = mZoomIn.getMeasuredWidth();
    }
    mTotalIconSize = mIconSize + ICON_SPACING;
    mSliderLength = mSize - (2 * mTotalIconSize);
  }

  @Override
  public boolean dispatchTouchEvent(MotionEvent event) {
    if (!isEnabled() || (mSize == 0)) return false;
    int action = event.getAction();

    switch (action) {
      case MotionEvent.ACTION_OUTSIDE:
      case MotionEvent.ACTION_CANCEL:
      case MotionEvent.ACTION_UP:
        setActivated(false);
        closeZoomControl();
        break;

      case MotionEvent.ACTION_DOWN:
        setActivated(true);
        mStartChanging = false;
      case MotionEvent.ACTION_MOVE:
        boolean isLandscape =
            (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
        int pos = getSliderPosition((int) (isLandscape ? event.getY() : event.getX()));
        if (!mStartChanging) {
          // Make sure the movement is large enough before we start
          // changing the zoom.
          int delta = mSliderPosition - pos;
          if ((delta > THRESHOLD_FIRST_MOVE) || (delta < -THRESHOLD_FIRST_MOVE)) {
            mStartChanging = true;
          }
        }
        if (mStartChanging) {
          performZoom(1.0d * pos / mSliderLength);
          mSliderPosition = pos;
        }
        requestLayout();
    }
    return true;
  }

  @Override
  public void setOrientation(int orientation, boolean animation) {
    // layout for the left-hand camera control
    if ((orientation == 180) || (mOrientation == 180)) requestLayout();
    super.setOrientation(orientation, animation);
  }

  @Override
  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    boolean isLandscape =
        (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
    if (mZoomMax == 0) return;
    int size = 0;
    if (isLandscape) {
      size = right - left;
      mBar.layout(0, mTotalIconSize, size, mSize - mTotalIconSize);
    } else {
      size = bottom - top;
      mBar.layout(mTotalIconSize, 0, mSize - mTotalIconSize, size);
    }
    // For left-hand users, as the device is rotated for 180 degree,
    // the zoom-in button should be on the top.
    int pos; // slider position
    int sliderPosition;
    if (mSliderPosition != -1) { // -1 means invalid
      sliderPosition = mSliderPosition;
    } else {
      sliderPosition = (int) ((double) mSliderLength * mZoomIndex / mZoomMax);
    }

    if (isLandscape) {
      if (mOrientation == 180) {
        mZoomOut.layout(0, 0, size, mIconSize);
        mZoomIn.layout(0, mSize - mIconSize, size, mSize);
        pos = mBar.getTop() + sliderPosition;
      } else {
        mZoomIn.layout(0, 0, size, mIconSize);
        mZoomOut.layout(0, mSize - mIconSize, size, mSize);
        pos = mBar.getBottom() - sliderPosition;
      }
      int sliderHeight = mZoomSlider.getMeasuredHeight();
      mZoomSlider.layout(0, (pos - sliderHeight / 2), size, (pos + sliderHeight / 2));
    } else {
      if (mOrientation == 90) {
        mZoomIn.layout(0, 0, mIconSize, size);
        mZoomOut.layout(mSize - mIconSize, 0, mSize, size);
        pos = mBar.getRight() - sliderPosition;
      } else {
        mZoomOut.layout(0, 0, mIconSize, size);
        mZoomIn.layout(mSize - mIconSize, 0, mSize, size);
        pos = mBar.getLeft() + sliderPosition;
      }
      int sliderWidth = mZoomSlider.getMeasuredWidth();
      mZoomSlider.layout((pos - sliderWidth / 2), 0, (pos + sliderWidth / 2), size);
    }
  }

  @Override
  public void setZoomIndex(int index) {
    super.setZoomIndex(index);
    mSliderPosition = -1; // -1 means invalid
  }
}
  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);
    }
  }