1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.gallery3d.filtershow.crop; 18 19 import android.app.ActionBar; 20 import android.app.Activity; 21 import android.app.WallpaperManager; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.res.Configuration; 25 import android.graphics.Bitmap; 26 import android.graphics.Bitmap.CompressFormat; 27 import android.graphics.BitmapFactory; 28 import android.graphics.BitmapRegionDecoder; 29 import android.graphics.Canvas; 30 import android.graphics.Matrix; 31 import android.graphics.Paint; 32 import android.graphics.Rect; 33 import android.graphics.RectF; 34 import android.net.Uri; 35 import android.os.AsyncTask; 36 import android.os.Bundle; 37 import android.provider.MediaStore; 38 import android.util.DisplayMetrics; 39 import android.util.Log; 40 import android.view.View; 41 import android.view.View.OnClickListener; 42 import android.view.WindowManager; 43 import android.widget.Toast; 44 45 import com.android.gallery3d.R; 46 import com.android.gallery3d.common.Utils; 47 import com.android.gallery3d.filtershow.cache.ImageLoader; 48 import com.android.gallery3d.filtershow.tools.SaveImage; 49 50 import java.io.ByteArrayInputStream; 51 import java.io.ByteArrayOutputStream; 52 import java.io.FileNotFoundException; 53 import java.io.IOException; 54 import java.io.InputStream; 55 import java.io.OutputStream; 56 57 /** 58 * Activity for cropping an image. 59 */ 60 public class CropActivity extends Activity { 61 private static final String LOGTAG = "CropActivity"; 62 public static final String CROP_ACTION = "com.android.camera.action.CROP"; 63 private CropExtras mCropExtras = null; 64 private LoadBitmapTask mLoadBitmapTask = null; 65 66 private int mOutputX = 0; 67 private int mOutputY = 0; 68 private Bitmap mOriginalBitmap = null; 69 private RectF mOriginalBounds = null; 70 private int mOriginalRotation = 0; 71 private Uri mSourceUri = null; 72 private CropView mCropView = null; 73 private View mSaveButton = null; 74 private boolean finalIOGuard = false; 75 76 private static final int SELECT_PICTURE = 1; // request code for picker 77 78 private static final int DEFAULT_COMPRESS_QUALITY = 90; 79 /** 80 * The maximum bitmap size we allow to be returned through the intent. 81 * Intents have a maximum of 1MB in total size. However, the Bitmap seems to 82 * have some overhead to hit so that we go way below the limit here to make 83 * sure the intent stays below 1MB.We should consider just returning a byte 84 * array instead of a Bitmap instance to avoid overhead. 85 */ 86 public static final int MAX_BMAP_IN_INTENT = 750000; 87 88 // Flags 89 private static final int DO_SET_WALLPAPER = 1; 90 private static final int DO_RETURN_DATA = 1 << 1; 91 private static final int DO_EXTRA_OUTPUT = 1 << 2; 92 93 private static final int FLAG_CHECK = DO_SET_WALLPAPER | DO_RETURN_DATA | DO_EXTRA_OUTPUT; 94 95 @Override 96 public void onCreate(Bundle savedInstanceState) { 97 super.onCreate(savedInstanceState); 98 Intent intent = getIntent(); 99 setResult(RESULT_CANCELED, new Intent()); 100 mCropExtras = getExtrasFromIntent(intent); 101 if (mCropExtras != null && mCropExtras.getShowWhenLocked()) { 102 getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); 103 } 104 105 setContentView(R.layout.crop_activity); 106 mCropView = (CropView) findViewById(R.id.cropView); 107 108 ActionBar actionBar = getActionBar(); 109 if (actionBar != null) { 110 actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); 111 actionBar.setCustomView(R.layout.filtershow_actionbar); 112 113 View mSaveButton = actionBar.getCustomView(); 114 mSaveButton.setOnClickListener(new OnClickListener() { 115 @Override 116 public void onClick(View view) { 117 startFinishOutput(); 118 } 119 }); 120 } 121 if (intent.getData() != null) { 122 mSourceUri = intent.getData(); 123 startLoadBitmap(mSourceUri); 124 } else { 125 pickImage(); 126 } 127 } 128 129 private void enableSave(boolean enable) { 130 if (mSaveButton != null) { 131 mSaveButton.setEnabled(enable); 132 } 133 } 134 135 @Override 136 protected void onDestroy() { 137 if (mLoadBitmapTask != null) { 138 mLoadBitmapTask.cancel(false); 139 } 140 super.onDestroy(); 141 } 142 143 @Override 144 public void onConfigurationChanged (Configuration newConfig) { 145 super.onConfigurationChanged(newConfig); 146 mCropView.configChanged(); 147 } 148 149 /** 150 * Opens a selector in Gallery to chose an image for use when none was given 151 * in the CROP intent. 152 */ 153 private void pickImage() { 154 Intent intent = new Intent(); 155 intent.setType("image/*"); 156 intent.setAction(Intent.ACTION_GET_CONTENT); 157 startActivityForResult(Intent.createChooser(intent, getString(R.string.select_image)), 158 SELECT_PICTURE); 159 } 160 161 /** 162 * Callback for pickImage(). 163 */ 164 @Override 165 public void onActivityResult(int requestCode, int resultCode, Intent data) { 166 if (resultCode == RESULT_OK && requestCode == SELECT_PICTURE) { 167 mSourceUri = data.getData(); 168 startLoadBitmap(mSourceUri); 169 } 170 } 171 172 /** 173 * Gets screen size metric. 174 */ 175 private int getScreenImageSize() { 176 DisplayMetrics outMetrics = new DisplayMetrics(); 177 getWindowManager().getDefaultDisplay().getMetrics(outMetrics); 178 return (int) Math.max(outMetrics.heightPixels, outMetrics.widthPixels); 179 } 180 181 /** 182 * Method that loads a bitmap in an async task. 183 */ 184 private void startLoadBitmap(Uri uri) { 185 if (uri != null) { 186 enableSave(false); 187 final View loading = findViewById(R.id.loading); 188 loading.setVisibility(View.VISIBLE); 189 mLoadBitmapTask = new LoadBitmapTask(); 190 mLoadBitmapTask.execute(uri); 191 } else { 192 cannotLoadImage(); 193 done(); 194 } 195 } 196 197 /** 198 * Method called on UI thread with loaded bitmap. 199 */ 200 private void doneLoadBitmap(Bitmap bitmap, RectF bounds, int orientation) { 201 final View loading = findViewById(R.id.loading); 202 loading.setVisibility(View.GONE); 203 mOriginalBitmap = bitmap; 204 mOriginalBounds = bounds; 205 mOriginalRotation = orientation; 206 if (bitmap != null && bitmap.getWidth() != 0 && bitmap.getHeight() != 0) { 207 RectF imgBounds = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight()); 208 mCropView.initialize(bitmap, imgBounds, imgBounds, orientation); 209 if (mCropExtras != null) { 210 int aspectX = mCropExtras.getAspectX(); 211 int aspectY = mCropExtras.getAspectY(); 212 mOutputX = mCropExtras.getOutputX(); 213 mOutputY = mCropExtras.getOutputY(); 214 if (mOutputX > 0 && mOutputY > 0) { 215 mCropView.applyAspect(mOutputX, mOutputY); 216 217 } 218 float spotX = mCropExtras.getSpotlightX(); 219 float spotY = mCropExtras.getSpotlightY(); 220 if (spotX > 0 && spotY > 0) { 221 mCropView.setWallpaperSpotlight(spotX, spotY); 222 } 223 if (aspectX > 0 && aspectY > 0) { 224 mCropView.applyAspect(aspectX, aspectY); 225 } 226 } 227 enableSave(true); 228 } else { 229 Log.w(LOGTAG, "could not load image for cropping"); 230 cannotLoadImage(); 231 setResult(RESULT_CANCELED, new Intent()); 232 done(); 233 } 234 } 235 236 /** 237 * Display toast for image loading failure. 238 */ 239 private void cannotLoadImage() { 240 CharSequence text = getString(R.string.cannot_load_image); 241 Toast toast = Toast.makeText(this, text, Toast.LENGTH_SHORT); 242 toast.show(); 243 } 244 245 /** 246 * AsyncTask for loading a bitmap into memory. 247 * 248 * @see #startLoadBitmap(Uri) 249 * @see #doneLoadBitmap(Bitmap) 250 */ 251 private class LoadBitmapTask extends AsyncTask<Uri, Void, Bitmap> { 252 int mBitmapSize; 253 Context mContext; 254 Rect mOriginalBounds; 255 int mOrientation; 256 257 public LoadBitmapTask() { 258 mBitmapSize = getScreenImageSize(); 259 mContext = getApplicationContext(); 260 mOriginalBounds = new Rect(); 261 mOrientation = 0; 262 } 263 264 @Override 265 protected Bitmap doInBackground(Uri... params) { 266 Uri uri = params[0]; 267 Bitmap bmap = ImageLoader.loadConstrainedBitmap(uri, mContext, mBitmapSize, 268 mOriginalBounds, false); 269 mOrientation = ImageLoader.getMetadataRotation(mContext, uri); 270 return bmap; 271 } 272 273 @Override 274 protected void onPostExecute(Bitmap result) { 275 doneLoadBitmap(result, new RectF(mOriginalBounds), mOrientation); 276 } 277 } 278 279 protected void startFinishOutput() { 280 if (finalIOGuard) { 281 return; 282 } else { 283 finalIOGuard = true; 284 } 285 enableSave(false); 286 Uri destinationUri = null; 287 int flags = 0; 288 if (mOriginalBitmap != null && mCropExtras != null) { 289 if (mCropExtras.getExtraOutput() != null) { 290 destinationUri = mCropExtras.getExtraOutput(); 291 if (destinationUri != null) { 292 flags |= DO_EXTRA_OUTPUT; 293 } 294 } 295 if (mCropExtras.getSetAsWallpaper()) { 296 flags |= DO_SET_WALLPAPER; 297 } 298 if (mCropExtras.getReturnData()) { 299 flags |= DO_RETURN_DATA; 300 } 301 } 302 if (flags == 0) { 303 destinationUri = SaveImage.makeAndInsertUri(this, mSourceUri); 304 if (destinationUri != null) { 305 flags |= DO_EXTRA_OUTPUT; 306 } 307 } 308 if ((flags & FLAG_CHECK) != 0 && mOriginalBitmap != null) { 309 RectF photo = new RectF(0, 0, mOriginalBitmap.getWidth(), mOriginalBitmap.getHeight()); 310 RectF crop = getBitmapCrop(photo); 311 startBitmapIO(flags, mOriginalBitmap, mSourceUri, destinationUri, crop, 312 photo, mOriginalBounds, 313 (mCropExtras == null) ? null : mCropExtras.getOutputFormat(), mOriginalRotation); 314 return; 315 } 316 setResult(RESULT_CANCELED, new Intent()); 317 done(); 318 return; 319 } 320 321 private void startBitmapIO(int flags, Bitmap currentBitmap, Uri sourceUri, Uri destUri, 322 RectF cropBounds, RectF photoBounds, RectF currentBitmapBounds, String format, 323 int rotation) { 324 if (cropBounds == null || photoBounds == null || currentBitmap == null 325 || currentBitmap.getWidth() == 0 || currentBitmap.getHeight() == 0 326 || cropBounds.width() == 0 || cropBounds.height() == 0 || photoBounds.width() == 0 327 || photoBounds.height() == 0) { 328 return; // fail fast 329 } 330 if ((flags & FLAG_CHECK) == 0) { 331 return; // no output options 332 } 333 if ((flags & DO_SET_WALLPAPER) != 0) { 334 Toast.makeText(this, R.string.setting_wallpaper, Toast.LENGTH_LONG).show(); 335 } 336 337 final View loading = findViewById(R.id.loading); 338 loading.setVisibility(View.VISIBLE); 339 BitmapIOTask ioTask = new BitmapIOTask(sourceUri, destUri, format, flags, cropBounds, 340 photoBounds, currentBitmapBounds, rotation, mOutputX, mOutputY); 341 ioTask.execute(currentBitmap); 342 } 343 344 private void doneBitmapIO(boolean success, Intent intent) { 345 final View loading = findViewById(R.id.loading); 346 loading.setVisibility(View.GONE); 347 if (success) { 348 setResult(RESULT_OK, intent); 349 } else { 350 setResult(RESULT_CANCELED, intent); 351 } 352 done(); 353 } 354 355 private class BitmapIOTask extends AsyncTask<Bitmap, Void, Boolean> { 356 357 private final WallpaperManager mWPManager; 358 InputStream mInStream = null; 359 OutputStream mOutStream = null; 360 String mOutputFormat = null; 361 Uri mOutUri = null; 362 Uri mInUri = null; 363 int mFlags = 0; 364 RectF mCrop = null; 365 RectF mPhoto = null; 366 RectF mOrig = null; 367 Intent mResultIntent = null; 368 int mRotation = 0; 369 370 // Helper to setup input stream 371 private void regenerateInputStream() { 372 if (mInUri == null) { 373 Log.w(LOGTAG, "cannot read original file, no input URI given"); 374 } else { 375 Utils.closeSilently(mInStream); 376 try { 377 mInStream = getContentResolver().openInputStream(mInUri); 378 } catch (FileNotFoundException e) { 379 Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e); 380 } 381 } 382 } 383 384 public BitmapIOTask(Uri sourceUri, Uri destUri, String outputFormat, int flags, 385 RectF cropBounds, RectF photoBounds, RectF originalBitmapBounds, int rotation, 386 int outputX, int outputY) { 387 mOutputFormat = outputFormat; 388 mOutStream = null; 389 mOutUri = destUri; 390 mInUri = sourceUri; 391 mFlags = flags; 392 mCrop = cropBounds; 393 mPhoto = photoBounds; 394 mOrig = originalBitmapBounds; 395 mWPManager = WallpaperManager.getInstance(getApplicationContext()); 396 mResultIntent = new Intent(); 397 mRotation = (rotation < 0) ? -rotation : rotation; 398 mRotation %= 360; 399 mRotation = 90 * (int) (mRotation / 90); // now mRotation is a multiple of 90 400 mOutputX = outputX; 401 mOutputY = outputY; 402 403 if ((flags & DO_EXTRA_OUTPUT) != 0) { 404 if (mOutUri == null) { 405 Log.w(LOGTAG, "cannot write file, no output URI given"); 406 } else { 407 try { 408 mOutStream = getContentResolver().openOutputStream(mOutUri); 409 } catch (FileNotFoundException e) { 410 Log.w(LOGTAG, "cannot write file: " + mOutUri.toString(), e); 411 } 412 } 413 } 414 415 if ((flags & (DO_EXTRA_OUTPUT | DO_SET_WALLPAPER)) != 0) { 416 regenerateInputStream(); 417 } 418 } 419 420 @Override 421 protected Boolean doInBackground(Bitmap... params) { 422 boolean failure = false; 423 Bitmap img = params[0]; 424 425 // Set extra for crop bounds 426 if (mCrop != null && mPhoto != null && mOrig != null) { 427 RectF trueCrop = CropMath.getScaledCropBounds(mCrop, mPhoto, mOrig); 428 Matrix m = new Matrix(); 429 m.setRotate(mRotation); 430 m.mapRect(trueCrop); 431 if (trueCrop != null) { 432 Rect rounded = new Rect(); 433 trueCrop.roundOut(rounded); 434 mResultIntent.putExtra(CropExtras.KEY_CROPPED_RECT, rounded); 435 } 436 } 437 438 // Find the small cropped bitmap that is returned in the intent 439 if ((mFlags & DO_RETURN_DATA) != 0) { 440 assert (img != null); 441 Bitmap ret = getCroppedImage(img, mCrop, mPhoto); 442 if (ret != null) { 443 ret = getDownsampledBitmap(ret, MAX_BMAP_IN_INTENT); 444 } 445 if (ret == null) { 446 Log.w(LOGTAG, "could not downsample bitmap to return in data"); 447 failure = true; 448 } else { 449 if (mRotation > 0) { 450 Matrix m = new Matrix(); 451 m.setRotate(mRotation); 452 Bitmap tmp = Bitmap.createBitmap(ret, 0, 0, ret.getWidth(), 453 ret.getHeight(), m, true); 454 if (tmp != null) { 455 ret = tmp; 456 } 457 } 458 mResultIntent.putExtra(CropExtras.KEY_DATA, ret); 459 } 460 } 461 462 // Do the large cropped bitmap and/or set the wallpaper 463 if ((mFlags & (DO_EXTRA_OUTPUT | DO_SET_WALLPAPER)) != 0 && mInStream != null) { 464 // Find crop bounds (scaled to original image size) 465 RectF trueCrop = CropMath.getScaledCropBounds(mCrop, mPhoto, mOrig); 466 if (trueCrop == null) { 467 Log.w(LOGTAG, "cannot find crop for full size image"); 468 failure = true; 469 return false; 470 } 471 Rect roundedTrueCrop = new Rect(); 472 trueCrop.roundOut(roundedTrueCrop); 473 474 if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { 475 Log.w(LOGTAG, "crop has bad values for full size image"); 476 failure = true; 477 return false; 478 } 479 480 // Attempt to open a region decoder 481 BitmapRegionDecoder decoder = null; 482 try { 483 decoder = BitmapRegionDecoder.newInstance(mInStream, true); 484 } catch (IOException e) { 485 Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e); 486 } 487 488 Bitmap crop = null; 489 if (decoder != null) { 490 // Do region decoding to get crop bitmap 491 BitmapFactory.Options options = new BitmapFactory.Options(); 492 options.inMutable = true; 493 crop = decoder.decodeRegion(roundedTrueCrop, options); 494 decoder.recycle(); 495 } 496 497 if (crop == null) { 498 // BitmapRegionDecoder has failed, try to crop in-memory 499 regenerateInputStream(); 500 Bitmap fullSize = null; 501 if (mInStream != null) { 502 fullSize = BitmapFactory.decodeStream(mInStream); 503 } 504 if (fullSize != null) { 505 crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, 506 roundedTrueCrop.top, roundedTrueCrop.width(), 507 roundedTrueCrop.height()); 508 } 509 } 510 511 if (crop == null) { 512 Log.w(LOGTAG, "cannot decode file: " + mInUri.toString()); 513 failure = true; 514 return false; 515 } 516 if (mOutputX > 0 && mOutputY > 0) { 517 Matrix m = new Matrix(); 518 RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight()); 519 if (mRotation > 0) { 520 m.setRotate(mRotation); 521 m.mapRect(cropRect); 522 } 523 RectF returnRect = new RectF(0, 0, mOutputX, mOutputY); 524 m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); 525 m.preRotate(mRotation); 526 Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), 527 (int) returnRect.height(), Bitmap.Config.ARGB_8888); 528 if (tmp != null) { 529 Canvas c = new Canvas(tmp); 530 c.drawBitmap(crop, m, new Paint()); 531 crop = tmp; 532 } 533 } else if (mRotation > 0) { 534 Matrix m = new Matrix(); 535 m.setRotate(mRotation); 536 Bitmap tmp = Bitmap.createBitmap(crop, 0, 0, crop.getWidth(), 537 crop.getHeight(), m, true); 538 if (tmp != null) { 539 crop = tmp; 540 } 541 } 542 // Get output compression format 543 CompressFormat cf = 544 convertExtensionToCompressFormat(getFileExtension(mOutputFormat)); 545 546 // If we only need to output to a URI, compress straight to file 547 if (mFlags == DO_EXTRA_OUTPUT) { 548 if (mOutStream == null 549 || !crop.compress(cf, DEFAULT_COMPRESS_QUALITY, mOutStream)) { 550 Log.w(LOGTAG, "failed to compress bitmap to file: " + mOutUri.toString()); 551 failure = true; 552 } else { 553 mResultIntent.setData(mOutUri); 554 } 555 } else { 556 // Compress to byte array 557 ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048); 558 if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) { 559 560 // If we need to output to a Uri, write compressed 561 // bitmap out 562 if ((mFlags & DO_EXTRA_OUTPUT) != 0) { 563 if (mOutStream == null) { 564 Log.w(LOGTAG, 565 "failed to compress bitmap to file: " + mOutUri.toString()); 566 failure = true; 567 } else { 568 try { 569 mOutStream.write(tmpOut.toByteArray()); 570 mResultIntent.setData(mOutUri); 571 } catch (IOException e) { 572 Log.w(LOGTAG, 573 "failed to compress bitmap to file: " 574 + mOutUri.toString(), e); 575 failure = true; 576 } 577 } 578 } 579 580 // If we need to set to the wallpaper, set it 581 if ((mFlags & DO_SET_WALLPAPER) != 0 && mWPManager != null) { 582 if (mWPManager == null) { 583 Log.w(LOGTAG, "no wallpaper manager"); 584 failure = true; 585 } else { 586 try { 587 mWPManager.setStream(new ByteArrayInputStream(tmpOut 588 .toByteArray())); 589 } catch (IOException e) { 590 Log.w(LOGTAG, "cannot write stream to wallpaper", e); 591 failure = true; 592 } 593 } 594 } 595 } else { 596 Log.w(LOGTAG, "cannot compress bitmap"); 597 failure = true; 598 } 599 } 600 } 601 return !failure; // True if any of the operations failed 602 } 603 604 @Override 605 protected void onPostExecute(Boolean result) { 606 Utils.closeSilently(mOutStream); 607 Utils.closeSilently(mInStream); 608 doneBitmapIO(result.booleanValue(), mResultIntent); 609 } 610 611 } 612 613 private void done() { 614 finish(); 615 } 616 617 protected static Bitmap getCroppedImage(Bitmap image, RectF cropBounds, RectF photoBounds) { 618 RectF imageBounds = new RectF(0, 0, image.getWidth(), image.getHeight()); 619 RectF crop = CropMath.getScaledCropBounds(cropBounds, photoBounds, imageBounds); 620 if (crop == null) { 621 return null; 622 } 623 Rect intCrop = new Rect(); 624 crop.roundOut(intCrop); 625 return Bitmap.createBitmap(image, intCrop.left, intCrop.top, intCrop.width(), 626 intCrop.height()); 627 } 628 629 protected static Bitmap getDownsampledBitmap(Bitmap image, int max_size) { 630 if (image == null || image.getWidth() == 0 || image.getHeight() == 0 || max_size < 16) { 631 throw new IllegalArgumentException("Bad argument to getDownsampledBitmap()"); 632 } 633 int shifts = 0; 634 int size = CropMath.getBitmapSize(image); 635 while (size > max_size) { 636 shifts++; 637 size /= 4; 638 } 639 Bitmap ret = Bitmap.createScaledBitmap(image, image.getWidth() >> shifts, 640 image.getHeight() >> shifts, true); 641 if (ret == null) { 642 return null; 643 } 644 // Handle edge case for rounding. 645 if (CropMath.getBitmapSize(ret) > max_size) { 646 return Bitmap.createScaledBitmap(ret, ret.getWidth() >> 1, ret.getHeight() >> 1, true); 647 } 648 return ret; 649 } 650 651 /** 652 * Gets the crop extras from the intent, or null if none exist. 653 */ 654 protected static CropExtras getExtrasFromIntent(Intent intent) { 655 Bundle extras = intent.getExtras(); 656 if (extras != null) { 657 return new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0), 658 extras.getInt(CropExtras.KEY_OUTPUT_Y, 0), 659 extras.getBoolean(CropExtras.KEY_SCALE, true) && 660 extras.getBoolean(CropExtras.KEY_SCALE_UP_IF_NEEDED, false), 661 extras.getInt(CropExtras.KEY_ASPECT_X, 0), 662 extras.getInt(CropExtras.KEY_ASPECT_Y, 0), 663 extras.getBoolean(CropExtras.KEY_SET_AS_WALLPAPER, false), 664 extras.getBoolean(CropExtras.KEY_RETURN_DATA, false), 665 (Uri) extras.getParcelable(MediaStore.EXTRA_OUTPUT), 666 extras.getString(CropExtras.KEY_OUTPUT_FORMAT), 667 extras.getBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, false), 668 extras.getFloat(CropExtras.KEY_SPOTLIGHT_X), 669 extras.getFloat(CropExtras.KEY_SPOTLIGHT_Y)); 670 } 671 return null; 672 } 673 674 protected static CompressFormat convertExtensionToCompressFormat(String extension) { 675 return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG; 676 } 677 678 protected static String getFileExtension(String requestFormat) { 679 String outputFormat = (requestFormat == null) 680 ? "jpg" 681 : requestFormat; 682 outputFormat = outputFormat.toLowerCase(); 683 return (outputFormat.equals("png") || outputFormat.equals("gif")) 684 ? "png" // We don't support gif compression. 685 : "jpg"; 686 } 687 688 private RectF getBitmapCrop(RectF imageBounds) { 689 RectF crop = mCropView.getCrop(); 690 RectF photo = mCropView.getPhoto(); 691 if (crop == null || photo == null) { 692 Log.w(LOGTAG, "could not get crop"); 693 return null; 694 } 695 RectF scaledCrop = CropMath.getScaledCropBounds(crop, photo, imageBounds); 696 return scaledCrop; 697 } 698 } 699