diff --git a/.travis.yml b/.travis.yml index 8a555561..b1406f5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: false android: components: - - build-tools-23.0.1 + - build-tools-23.0.3 - android-23 - extra-android-support - extra-android-m2repository diff --git a/build.gradle b/build.gradle index 9eb04bf7..49c948aa 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,10 @@ buildscript { repositories { - mavenCentral() + google() + jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.0' + classpath 'com.android.tools.build:gradle:3.1.2' } } @@ -13,5 +14,6 @@ allprojects { repositories { mavenCentral() + google() } } diff --git a/example/build.gradle b/example/build.gradle index 33f163f5..5b9decd1 100644 --- a/example/build.gradle +++ b/example/build.gradle @@ -3,17 +3,16 @@ apply plugin: 'com.android.application' archivesBaseName = 'android-crop-example' android { - compileSdkVersion 23 - buildToolsVersion '23.0.1' + compileSdkVersion 27 defaultConfig { - minSdkVersion 10 - targetSdkVersion 22 + minSdkVersion 14 + targetSdkVersion 27 versionCode Integer.parseInt(project.VERSION_CODE) versionName project.VERSION } } dependencies { - compile project(':lib') + implementation project(':lib') } diff --git a/example/src/main/java/com/soundcloud/android/crop/example/MainActivity.java b/example/src/main/java/com/soundcloud/android/crop/example/MainActivity.java index 079775aa..ef28dacf 100644 --- a/example/src/main/java/com/soundcloud/android/crop/example/MainActivity.java +++ b/example/src/main/java/com/soundcloud/android/crop/example/MainActivity.java @@ -1,7 +1,5 @@ package com.soundcloud.android.crop.example; -import com.soundcloud.android.crop.Crop; - import android.app.Activity; import android.content.Intent; import android.net.Uri; @@ -11,6 +9,8 @@ import android.widget.ImageView; import android.widget.Toast; +import com.soundcloud.android.crop.Crop; + import java.io.File; public class MainActivity extends Activity { @@ -51,7 +51,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent result) private void beginCrop(Uri source) { Uri destination = Uri.fromFile(new File(getCacheDir(), "cropped")); - Crop.of(source, destination).asSquare().start(this); + + Crop.of(source, destination).withMinSize(512, (512 / 16)).start(this); } private void handleCrop(int resultCode, Intent result) { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7d0eb9b2..84b3274a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/lib/build.gradle b/lib/build.gradle index 20e1a767..014d9c71 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -6,25 +6,29 @@ apply plugin: 'signing' archivesBaseName = 'android-crop' android { - compileSdkVersion 23 - buildToolsVersion '23.0.1' + compileSdkVersion 27 defaultConfig { - minSdkVersion 10 - targetSdkVersion 22 + minSdkVersion 14 + targetSdkVersion 27 + versionCode 1 + versionName "1.0.1" testApplicationId 'com.soundcloud.android.crop.test' testInstrumentationRunner 'android.test.InstrumentationTestRunner' } + lintOptions { + disable 'Registered', 'IconDensities' + } } dependencies { - compile 'com.android.support:support-annotations:23.0.1' - compile 'com.android.support:support-v4:23.0.1' - androidTestCompile 'com.squareup:fest-android:1.0.7' - androidTestCompile 'com.android.support:support-v4:23.0.1' - androidTestCompile 'org.mockito:mockito-core:1.9.5' - androidTestCompile 'com.google.dexmaker:dexmaker:1.0' - androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.0' + implementation 'com.android.support:support-annotations:27.1.1' + implementation 'com.android.support:support-v4:27.1.1' + androidTestImplementation 'com.squareup:fest-android:1.0.7' + androidTestImplementation 'com.android.support:support-v4:27.1.1' + androidTestImplementation 'org.mockito:mockito-core:1.9.5' + androidTestImplementation 'com.google.dexmaker:dexmaker:1.0' + androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:1.0' } diff --git a/lib/src/main/AndroidManifest.xml b/lib/src/main/AndroidManifest.xml index 0cb62bb6..760b435c 100644 --- a/lib/src/main/AndroidManifest.xml +++ b/lib/src/main/AndroidManifest.xml @@ -1 +1 @@ - + \ No newline at end of file diff --git a/lib/src/main/java/com/soundcloud/android/crop/Crop.java b/lib/src/main/java/com/soundcloud/android/crop/Crop.java index 3ce06d61..a4baa881 100644 --- a/lib/src/main/java/com/soundcloud/android/crop/Crop.java +++ b/lib/src/main/java/com/soundcloud/android/crop/Crop.java @@ -23,10 +23,19 @@ public class Crop { interface Extra { String ASPECT_X = "aspect_x"; String ASPECT_Y = "aspect_y"; + + String MIN_ASPECT_X = "min_aspect_x"; + String MIN_ASPECT_Y = "min_aspect_y"; + String MAX_ASPECT_X = "max_aspect_x"; + String MAX_ASPECT_Y = "max_aspect_y"; + String MAX_X = "max_x"; String MAX_Y = "max_y"; + String MIN_X = "min_x"; + String MIN_Y = "min_y"; String AS_PNG = "as_png"; String ERROR = "error"; + String LAYOUT_ID = "layout_id"; } private Intent cropIntent; @@ -47,6 +56,11 @@ private Crop(Uri source, Uri destination) { cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, destination); } +// public Crop withLayout(int layoutId) { +// cropIntent.putExtra(Extra.LAYOUT_ID, layoutId); +// return this; +// } + /** * Set fixed aspect ratio for crop area * @@ -59,6 +73,16 @@ public Crop withAspect(int x, int y) { return this; } +// public Crop withAspectRange(int minX, int minY, int maxX, int maxY) { +// cropIntent.putExtra(Extra.MIN_ASPECT_X, minX); +// cropIntent.putExtra(Extra.MIN_ASPECT_Y, minY); +// cropIntent.putExtra(Extra.MAX_ASPECT_X, maxX); +// cropIntent.putExtra(Extra.MAX_ASPECT_Y, maxY); +// cropIntent.putExtra(Extra.ASPECT_X, maxX); +// cropIntent.putExtra(Extra.ASPECT_Y, maxY); +// return this; +// } + /** * Crop area with fixed 1:1 aspect ratio */ @@ -80,8 +104,21 @@ public Crop withMaxSize(int width, int height) { return this; } + /** + * Set minimum crop size + * + * @param width Min width + * @param height Min height + */ + public Crop withMinSize(int width, int height) { + cropIntent.putExtra(Extra.MIN_X, width); + cropIntent.putExtra(Extra.MIN_Y, height); + return this; + } + /** * Set whether to save the result as a PNG or not. Helpful to preserve alpha. + * * @param asPng whether to save the result as a PNG or not */ public Crop asPng(boolean asPng) { diff --git a/lib/src/main/java/com/soundcloud/android/crop/CropImageActivity.java b/lib/src/main/java/com/soundcloud/android/crop/CropImageActivity.java index 1cb31d04..9834fcbf 100644 --- a/lib/src/main/java/com/soundcloud/android/crop/CropImageActivity.java +++ b/lib/src/main/java/com/soundcloud/android/crop/CropImageActivity.java @@ -34,6 +34,7 @@ import android.view.Window; import android.view.WindowManager; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -52,7 +53,14 @@ public class CropImageActivity extends MonitoredActivity { private int aspectX; private int aspectY; + private int minAspectX; + private int minAspectY; + private int maxAspectX; + private int maxAspectY; + // Output image + private int minX; + private int minY; private int maxX; private int maxY; private int exifRotation; @@ -91,7 +99,14 @@ private void setupWindowFlags() { } private void setupViews() { - setContentView(R.layout.crop__activity_crop); + Intent intent = getIntent(); + Bundle extras = intent.getExtras(); + + int layoutId = R.layout.crop__activity_crop; + if (extras != null) { + layoutId = extras.getInt(Crop.Extra.LAYOUT_ID, R.layout.crop__activity_crop); + } + setContentView(layoutId); imageView = (CropImageView) findViewById(R.id.crop_image); imageView.context = this; @@ -124,10 +139,19 @@ private void loadInput() { if (extras != null) { aspectX = extras.getInt(Crop.Extra.ASPECT_X); aspectY = extras.getInt(Crop.Extra.ASPECT_Y); + + minAspectX = extras.getInt(Crop.Extra.MIN_ASPECT_X); + minAspectY = extras.getInt(Crop.Extra.MIN_ASPECT_Y); + maxAspectX = extras.getInt(Crop.Extra.MAX_ASPECT_X); + maxAspectX = extras.getInt(Crop.Extra.MAX_ASPECT_Y); + maxX = extras.getInt(Crop.Extra.MAX_X); maxY = extras.getInt(Crop.Extra.MAX_Y); saveAsPng = extras.getBoolean(Crop.Extra.AS_PNG, false); saveUri = extras.getParcelable(MediaStore.EXTRA_OUTPUT); + + minX = extras.getInt(Crop.Extra.MIN_X, 0); + minY = extras.getInt(Crop.Extra.MIN_Y, 0); } sourceUri = intent.getData(); @@ -141,6 +165,19 @@ private void loadInput() { BitmapFactory.Options option = new BitmapFactory.Options(); option.inSampleSize = sampleSize; rotateBitmap = new RotateBitmap(BitmapFactory.decodeStream(is, null, option), exifRotation); + + option.inJustDecodeBounds = true; + BitmapFactory.decodeFile(new File(sourceUri.getPath()).getAbsolutePath(), option); + int imageHeight = option.outHeight; + int imageWidth = option.outWidth; + + if (minX > imageHeight || minY > imageWidth) { + Intent intentError = new Intent(); + intentError.putExtra(Crop.Extra.ERROR, new Throwable("Image is less then minimum size specified.\nRequired size:" + minX + "x" + minY)); + setResult(Crop.RESULT_ERROR, intentError); + finish(); + } + } catch (IOException e) { Log.e("Error reading image: " + e.getMessage(), e); setResultException(e); @@ -246,7 +283,10 @@ private void makeDefault() { int y = (height - cropHeight) / 2; RectF cropRect = new RectF(x, y, x + cropWidth, y + cropHeight); - hv.setup(imageView.getUnrotatedMatrix(), imageRect, cropRect, aspectX != 0 && aspectY != 0); + + boolean maintainAspectRatio = !(minAspectX != 0 && minAspectY != 0 && maxAspectX != 0 && maxAspectY != 0) && (aspectX != 0 && aspectY != 0); + + hv.setup(imageView.getUnrotatedMatrix(), imageRect, cropRect, maintainAspectRatio, minX, minY); imageView.add(hv); } @@ -290,6 +330,12 @@ private void onSaveClicked() { try { croppedImage = decodeRegionCrop(r, outWidth, outHeight); + + if (exifRotation != 0) { + Matrix m = new Matrix(); + m.postRotate(exifRotation); + croppedImage = Bitmap.createBitmap(croppedImage, 0, 0, croppedImage.getWidth(), croppedImage.getHeight(), m, true); + } } catch (IllegalArgumentException e) { setResultException(e); finish(); @@ -297,7 +343,7 @@ private void onSaveClicked() { } if (croppedImage != null) { - imageView.setImageRotateBitmapResetBase(new RotateBitmap(croppedImage, exifRotation), true); +// imageView.setImageRotateBitmapResetBase(new RotateBitmap(croppedImage, exifRotation), true); imageView.center(); imageView.highlightViews.clear(); } @@ -394,11 +440,6 @@ private void saveOutput(Bitmap croppedImage) { CropUtil.closeSilently(outputStream); } - CropUtil.copyExifRotation( - CropUtil.getFromMediaUri(this, getContentResolver(), sourceUri), - CropUtil.getFromMediaUri(this, getContentResolver(), saveUri) - ); - setResultUri(saveUri); } diff --git a/lib/src/main/java/com/soundcloud/android/crop/HighlightView.java b/lib/src/main/java/com/soundcloud/android/crop/HighlightView.java index 27cc10da..92e85b09 100644 --- a/lib/src/main/java/com/soundcloud/android/crop/HighlightView.java +++ b/lib/src/main/java/com/soundcloud/android/crop/HighlightView.java @@ -77,6 +77,9 @@ enum HandleMode { Changing, Always, Never } private float outlineWidth; private boolean isFocused; + private float minWidth = 0f; + private float minHeight = 0f; + public HighlightView(View context) { viewContext = context; initStyles(context.getContext()); @@ -97,6 +100,13 @@ private void initStyles(Context context) { } } + public void setup(Matrix m, Rect imageRect, RectF cropRect, boolean maintainAspectRatio, int minWidth, int minHeight) + { + this.minWidth = minWidth; + this.minHeight = minHeight; + setup(m, imageRect, cropRect, maintainAspectRatio); + } + public void setup(Matrix m, Rect imageRect, RectF cropRect, boolean maintainAspectRatio) { matrix = new Matrix(m); @@ -209,10 +219,8 @@ private void drawThirds(Canvas canvas) { drawRect.left + xThird, drawRect.bottom, outlinePaint); canvas.drawLine(drawRect.left + xThird * 2, drawRect.top, drawRect.left + xThird * 2, drawRect.bottom, outlinePaint); - canvas.drawLine(drawRect.left, drawRect.top + yThird, - drawRect.right, drawRect.top + yThird, outlinePaint); - canvas.drawLine(drawRect.left, drawRect.top + yThird * 2, - drawRect.right, drawRect.top + yThird * 2, outlinePaint); + canvas.drawLine(drawRect.left, drawRect.top + yThird, drawRect.right, drawRect.top + yThird, outlinePaint); + canvas.drawLine(drawRect.left, drawRect.top + yThird * 2, drawRect.right, drawRect.top + yThird * 2, outlinePaint); } private void drawCircle(Canvas canvas) { @@ -348,6 +356,14 @@ void growBy(float dx, float dy) { r.inset(0F, -(heightCap - r.height()) / 2F); } + // Limits the size of the image to the given minimum amount + if (minWidth > 0f || minHeight > 0f) + { + float widthDiff = (Math.abs((r.right-r.left)) < minWidth) ? minWidth - Math.abs((r.right-r.left)) : 0f; + float heightDiff = (Math.abs((r.bottom-r.top)) < minHeight) ? minHeight - Math.abs((r.bottom-r.top)) : 0f; + r.inset((widthDiff/2)*-1, (heightDiff / 2) * -1); + } + // Put the cropping rectangle inside the image rectangle if (r.left < imageRect.left) { r.offset(imageRect.left - r.left, 0F); diff --git a/lib/src/main/res/drawable-hdpi/crop__tile.png b/lib/src/main/res/drawable-hdpi/crop__tile.png new file mode 100644 index 00000000..2f2569f5 Binary files /dev/null and b/lib/src/main/res/drawable-hdpi/crop__tile.png differ diff --git a/lib/src/main/res/drawable-mdpi/crop__divider.9.png b/lib/src/main/res/drawable-mdpi/crop__divider.9.png deleted file mode 100644 index 0279e17a..00000000 Binary files a/lib/src/main/res/drawable-mdpi/crop__divider.9.png and /dev/null differ diff --git a/lib/src/main/res/drawable-mdpi/crop__tile.png b/lib/src/main/res/drawable-mdpi/crop__tile.png new file mode 100644 index 00000000..77a325cd Binary files /dev/null and b/lib/src/main/res/drawable-mdpi/crop__tile.png differ diff --git a/lib/src/main/res/drawable/crop__selectable_background.xml b/lib/src/main/res/drawable-v10/crop__selectable_background.xml similarity index 86% rename from lib/src/main/res/drawable/crop__selectable_background.xml rename to lib/src/main/res/drawable-v10/crop__selectable_background.xml index 83c4a7de..b92a29a2 100644 --- a/lib/src/main/res/drawable/crop__selectable_background.xml +++ b/lib/src/main/res/drawable-v10/crop__selectable_background.xml @@ -1,7 +1,6 @@ - + diff --git a/lib/src/main/res/drawable-v11/crop__selectable_background.xml b/lib/src/main/res/drawable-v11/crop__selectable_background.xml new file mode 100644 index 00000000..9469b59a --- /dev/null +++ b/lib/src/main/res/drawable-v11/crop__selectable_background.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/src/main/res/drawable-xhdpi/crop__divider.9.png b/lib/src/main/res/drawable-xhdpi/crop__divider.9.png index 65061c0f..809f1307 100644 Binary files a/lib/src/main/res/drawable-xhdpi/crop__divider.9.png and b/lib/src/main/res/drawable-xhdpi/crop__divider.9.png differ diff --git a/lib/src/main/res/drawable-xhdpi/crop__tile.png b/lib/src/main/res/drawable-xhdpi/crop__tile.png index b0b00440..43739d28 100644 Binary files a/lib/src/main/res/drawable-xhdpi/crop__tile.png and b/lib/src/main/res/drawable-xhdpi/crop__tile.png differ diff --git a/lib/src/main/res/drawable-xxhdpi/crop__divider.9.png b/lib/src/main/res/drawable-xxhdpi/crop__divider.9.png new file mode 100644 index 00000000..ea71af02 Binary files /dev/null and b/lib/src/main/res/drawable-xxhdpi/crop__divider.9.png differ diff --git a/lib/src/main/res/drawable-xxhdpi/crop__tile.png b/lib/src/main/res/drawable-xxhdpi/crop__tile.png new file mode 100644 index 00000000..7f757d90 Binary files /dev/null and b/lib/src/main/res/drawable-xxhdpi/crop__tile.png differ diff --git a/lib/src/main/res/drawable-xxxhdpi/crop__divider.9.png b/lib/src/main/res/drawable-xxxhdpi/crop__divider.9.png new file mode 100644 index 00000000..d505ff90 Binary files /dev/null and b/lib/src/main/res/drawable-xxxhdpi/crop__divider.9.png differ diff --git a/lib/src/main/res/drawable-xxxhdpi/crop__tile.png b/lib/src/main/res/drawable-xxxhdpi/crop__tile.png new file mode 100644 index 00000000..0833b970 Binary files /dev/null and b/lib/src/main/res/drawable-xxxhdpi/crop__tile.png differ diff --git a/lib/src/main/res/values-fa/strings.xml b/lib/src/main/res/values-fa/strings.xml index a4f3d6cf..962f2f81 100644 --- a/lib/src/main/res/values-fa/strings.xml +++ b/lib/src/main/res/values-fa/strings.xml @@ -1,7 +1,7 @@ در حال ذخیره سازی - لطفاً صبر کنید ... + لطفاً صبر کنید … تصویری در دسترس نیست تأیید