fedilab-Android-App/cropper/src/main/java/com/theartofdev/edmodo/cropper/CropImageActivity.java

368 lines
12 KiB
Java

// "Therefore those skilled at the unorthodox
// are infinite as heaven and earth,
// inexhaustible as the great rivers.
// When they come to an end,
// they begin again,
// like the days and months;
// they die and are reborn,
// like the four seasons."
//
// - Sun Tsu,
// "The Art of War"
package com.theartofdev.edmodo.cropper;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import java.io.File;
import java.io.IOException;
/**
* Built-in activity for image cropping.<br>
* Use {@link CropImage#activity(Uri)} to create a builder to start this activity.
*/
public class CropImageActivity extends AppCompatActivity
implements CropImageView.OnSetImageUriCompleteListener,
CropImageView.OnCropImageCompleteListener {
/**
* The crop image view library widget used in the activity
*/
private CropImageView mCropImageView;
/**
* Persist URI image to crop URI if specific permissions are required
*/
private Uri mCropImageUri;
/**
* the options that were set for the crop image
*/
private CropImageOptions mOptions;
@Override
@SuppressLint("NewApi")
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.crop_image_activity);
mCropImageView = findViewById(R.id.cropImageView);
Bundle bundle = getIntent().getBundleExtra(CropImage.CROP_IMAGE_EXTRA_BUNDLE);
mCropImageUri = bundle.getParcelable(CropImage.CROP_IMAGE_EXTRA_SOURCE);
mOptions = bundle.getParcelable(CropImage.CROP_IMAGE_EXTRA_OPTIONS);
if (savedInstanceState == null) {
if (mCropImageUri == null || mCropImageUri.equals(Uri.EMPTY)) {
if (CropImage.isExplicitCameraPermissionRequired(this)) {
// request permissions and handle the result in onRequestPermissionsResult()
requestPermissions(
new String[]{Manifest.permission.CAMERA},
CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE);
} else {
CropImage.startPickImageActivity(this);
}
} else if (CropImage.isReadExternalStoragePermissionsRequired(this, mCropImageUri)) {
// request permissions and handle the result in onRequestPermissionsResult()
requestPermissions(
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE);
} else {
// no permissions required or already grunted, can start crop image activity
mCropImageView.setImageUriAsync(mCropImageUri);
}
}
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
CharSequence title = mOptions != null &&
mOptions.activityTitle != null && mOptions.activityTitle.length() > 0
? mOptions.activityTitle
: getResources().getString(R.string.crop_image_activity_title);
actionBar.setTitle(title);
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
@Override
protected void onStart() {
super.onStart();
mCropImageView.setOnSetImageUriCompleteListener(this);
mCropImageView.setOnCropImageCompleteListener(this);
}
@Override
protected void onStop() {
super.onStop();
mCropImageView.setOnSetImageUriCompleteListener(null);
mCropImageView.setOnCropImageCompleteListener(null);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.crop_image_menu, menu);
if (!mOptions.allowRotation) {
menu.removeItem(R.id.crop_image_menu_rotate_left);
menu.removeItem(R.id.crop_image_menu_rotate_right);
} else if (mOptions.allowCounterRotation) {
menu.findItem(R.id.crop_image_menu_rotate_left).setVisible(true);
}
if (!mOptions.allowFlipping) {
menu.removeItem(R.id.crop_image_menu_flip);
}
if (mOptions.cropMenuCropButtonTitle != null) {
menu.findItem(R.id.crop_image_menu_crop).setTitle(mOptions.cropMenuCropButtonTitle);
}
Drawable cropIcon = null;
try {
if (mOptions.cropMenuCropButtonIcon != 0) {
cropIcon = ContextCompat.getDrawable(this, mOptions.cropMenuCropButtonIcon);
menu.findItem(R.id.crop_image_menu_crop).setIcon(cropIcon);
}
} catch (Exception e) {
e.printStackTrace();
}
if (mOptions.activityMenuIconColor != 0) {
updateMenuItemIconColor(
menu, R.id.crop_image_menu_rotate_left, mOptions.activityMenuIconColor);
updateMenuItemIconColor(
menu, R.id.crop_image_menu_rotate_right, mOptions.activityMenuIconColor);
updateMenuItemIconColor(menu, R.id.crop_image_menu_flip, mOptions.activityMenuIconColor);
if (cropIcon != null) {
updateMenuItemIconColor(menu, R.id.crop_image_menu_crop, mOptions.activityMenuIconColor);
}
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.crop_image_menu_crop) {
cropImage();
return true;
}
if (item.getItemId() == R.id.crop_image_menu_rotate_left) {
rotateImage(-mOptions.rotationDegrees);
return true;
}
if (item.getItemId() == R.id.crop_image_menu_rotate_right) {
rotateImage(mOptions.rotationDegrees);
return true;
}
if (item.getItemId() == R.id.crop_image_menu_flip_horizontally) {
mCropImageView.flipImageHorizontally();
return true;
}
if (item.getItemId() == R.id.crop_image_menu_flip_vertically) {
mCropImageView.flipImageVertically();
return true;
}
if (item.getItemId() == android.R.id.home) {
setResultCancel();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onBackPressed() {
super.onBackPressed();
setResultCancel();
}
@Override
@SuppressLint("NewApi")
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// handle result of pick image chooser
if (requestCode == CropImage.PICK_IMAGE_CHOOSER_REQUEST_CODE) {
if (resultCode == Activity.RESULT_CANCELED) {
// User cancelled the picker. We don't have anything to crop
setResultCancel();
}
if (resultCode == Activity.RESULT_OK) {
mCropImageUri = CropImage.getPickImageResultUri(this, data);
// For API >= 23 we need to check specifically that we have permissions to read external
// storage.
if (CropImage.isReadExternalStoragePermissionsRequired(this, mCropImageUri)) {
// request permissions and handle the result in onRequestPermissionsResult()
requestPermissions(
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE);
} else {
// no permissions required or already grunted, can start crop image activity
mCropImageView.setImageUriAsync(mCropImageUri);
}
}
}
}
@Override
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == CropImage.PICK_IMAGE_PERMISSIONS_REQUEST_CODE) {
if (mCropImageUri != null
&& grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// required permissions granted, start crop image activity
mCropImageView.setImageUriAsync(mCropImageUri);
} else {
Toast.makeText(this, R.string.crop_image_activity_no_permissions, Toast.LENGTH_LONG).show();
setResultCancel();
}
}
if (requestCode == CropImage.CAMERA_CAPTURE_PERMISSIONS_REQUEST_CODE) {
// Irrespective of whether camera permission was given or not, we show the picker
// The picker will not add the camera intent if permission is not available
CropImage.startPickImageActivity(this);
}
}
@Override
public void onSetImageUriComplete(CropImageView view, Uri uri, Exception error) {
if (error == null) {
if (mOptions.initialCropWindowRectangle != null) {
mCropImageView.setCropRect(mOptions.initialCropWindowRectangle);
}
if (mOptions.initialRotation > -1) {
mCropImageView.setRotatedDegrees(mOptions.initialRotation);
}
} else {
setResult(null, error, 1);
}
}
@Override
public void onCropImageComplete(CropImageView view, CropImageView.CropResult result) {
setResult(result.getUri(), result.getError(), result.getSampleSize());
}
// region: Private methods
/**
* Execute crop image and save the result tou output uri.
*/
protected void cropImage() {
if (mOptions.noOutputImage) {
setResult(null, null, 1);
} else {
Uri outputUri = getOutputUri();
mCropImageView.saveCroppedImageAsync(
outputUri,
mOptions.outputCompressFormat,
mOptions.outputCompressQuality,
mOptions.outputRequestWidth,
mOptions.outputRequestHeight,
mOptions.outputRequestSizeOptions);
}
}
/**
* Rotate the image in the crop image view.
*/
protected void rotateImage(int degrees) {
mCropImageView.rotateImage(degrees);
}
/**
* Get Android uri to save the cropped image into.<br>
* Use the given in options or create a temp file.
*/
protected Uri getOutputUri() {
Uri outputUri = mOptions.outputUri;
if (outputUri == null || outputUri.equals(Uri.EMPTY)) {
try {
String ext =
mOptions.outputCompressFormat == Bitmap.CompressFormat.JPEG
? ".jpg"
: mOptions.outputCompressFormat == Bitmap.CompressFormat.PNG ? ".png" : ".webp";
outputUri = Uri.fromFile(File.createTempFile("cropped", ext, getCacheDir()));
} catch (IOException e) {
throw new RuntimeException("Failed to create temp file for output image", e);
}
}
return outputUri;
}
/**
* Result with cropped image data or error if failed.
*/
protected void setResult(Uri uri, Exception error, int sampleSize) {
int resultCode = error == null ? RESULT_OK : CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE;
setResult(resultCode, getResultIntent(uri, error, sampleSize));
finish();
}
/**
* Cancel of cropping activity.
*/
protected void setResultCancel() {
setResult(RESULT_CANCELED);
finish();
}
/**
* Get intent instance to be used for the result of this activity.
*/
protected Intent getResultIntent(Uri uri, Exception error, int sampleSize) {
CropImage.ActivityResult result =
new CropImage.ActivityResult(
mCropImageView.getImageUri(),
uri,
error,
mCropImageView.getCropPoints(),
mCropImageView.getCropRect(),
mCropImageView.getRotatedDegrees(),
mCropImageView.getWholeImageRect(),
sampleSize);
Intent intent = new Intent();
intent.putExtras(getIntent());
intent.putExtra(CropImage.CROP_IMAGE_EXTRA_RESULT, result);
return intent;
}
/**
* Update the color of a specific menu item to the given color.
*/
private void updateMenuItemIconColor(Menu menu, int itemId, int color) {
MenuItem menuItem = menu.findItem(itemId);
if (menuItem != null) {
Drawable menuItemIcon = menuItem.getIcon();
if (menuItemIcon != null) {
try {
menuItemIcon.mutate();
menuItemIcon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
menuItem.setIcon(menuItemIcon);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}