Compare commits
1 Commits
master
...
exif-same-
| Author | SHA1 | Date |
|---|---|---|
|
|
bd544eb7d2 |
18
.travis.yml
18
.travis.yml
|
|
@ -1,13 +1,21 @@
|
|||
language: android
|
||||
sudo: false
|
||||
|
||||
android:
|
||||
components:
|
||||
- build-tools-23.0.1
|
||||
- android-23
|
||||
- build-tools-21.1.1
|
||||
- android-21
|
||||
- extra-android-support
|
||||
- extra-android-m2repository
|
||||
- sys-img-armeabi-v7a-android-21
|
||||
|
||||
install:
|
||||
- ./gradlew :lib:build
|
||||
|
||||
before_script:
|
||||
- echo no | android create avd --force -n test -t android-21 --abi armeabi-v7a
|
||||
- emulator -avd test -no-skin -no-audio -no-window &
|
||||
- android-wait-for-emulator
|
||||
- adb shell input keyevent 82 &
|
||||
|
||||
script:
|
||||
- ./gradlew clean build
|
||||
- ./gradlew :lib:connectedAndroidTest
|
||||
|
||||
27
CHANGELOG.md
27
CHANGELOG.md
|
|
@ -1,28 +1,3 @@
|
|||
## Next
|
||||
* Fix max size crash when input cannot be decoded
|
||||
* Translations: German, Chinese (simplified & traditional)
|
||||
|
||||
## 1.0.1
|
||||
|
||||
* Support image picker helper from Fragments
|
||||
* Restore support for SDK level 10
|
||||
* Fix translucent status bar set via app theme
|
||||
* Fix wrong result code when crop results in IOException
|
||||
* Fix image "twitching" on zoom out to max bounds
|
||||
* Translations: Italian, Turkish, Catalan, Swedish
|
||||
|
||||
## 1.0.0
|
||||
|
||||
* Improved builder interface: `Crop.of(in, out).start(activity)`
|
||||
* Material styling
|
||||
* Drop support for SDK level 9
|
||||
* Start crop from support Fragment
|
||||
* Fix max size
|
||||
* Fix issue cropping images from Google Drive
|
||||
* Optional circle crop guide
|
||||
* Optional custom request code
|
||||
* Translations: French, Korean, Chinese, Spanish, Japanese, Arabic, Portuguese, Indonesian, Russian
|
||||
|
||||
## 0.9.10
|
||||
|
||||
* Fix bug on some devices where image was displayed with 0 size
|
||||
|
|
@ -30,5 +5,5 @@
|
|||
## 0.9.9
|
||||
|
||||
* Downscale source images that are too big to load
|
||||
* Optional always show crop handles
|
||||
* Fix shading outside crop area on some API levels
|
||||
* Add option to always show crop handles
|
||||
|
|
|
|||
62
README.md
62
README.md
|
|
@ -1,65 +1,52 @@
|
|||
> I guess people are just cropping out all the sadness
|
||||
|
||||
An Android library project that provides a simple image cropping `Activity`, based on code from AOSP.
|
||||
An Android library project to provide a simple image cropping `Activity`, based on code from AOSP.
|
||||
|
||||
[](https://travis-ci.org/jdamcd/android-crop)
|
||||
[](http://search.maven.org/#artifactdetails%7Ccom.soundcloud.android%7Candroid-crop%7C1.0.1%7Caar.asc)
|
||||
[](CHANGELOG.md)
|
||||
[](https://travis-ci.org/jdamcd/android-crop)
|
||||
|
||||
## Features
|
||||
## Goals
|
||||
|
||||
* Gradle build & AAR
|
||||
* Gradle build with AAR
|
||||
* Modern UI
|
||||
* Backwards compatible to SDK 10
|
||||
* Backwards compatible to Gingerbread
|
||||
* Simple builder for configuration
|
||||
* Example project
|
||||
* More tests, less unused complexity
|
||||
|
||||
## Usage
|
||||
|
||||
First, declare `CropImageActivity` in your manifest file:
|
||||
|
||||
```xml
|
||||
<activity android:name="com.soundcloud.android.crop.CropImageActivity" />
|
||||
```
|
||||
`<activity android:name="com.soundcloud.android.crop.CropImageActivity" />`
|
||||
|
||||
#### Crop
|
||||
|
||||
```java
|
||||
Crop.of(inputUri, outputUri).asSquare().start(activity)
|
||||
```
|
||||
`new Crop(inputUri).output(outputUri).asSquare().start(activity)`
|
||||
|
||||
Listen for the result of the crop (see example project if you want to do some error handling):
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent result) {
|
||||
if (requestCode == Crop.REQUEST_CROP && resultCode == RESULT_OK) {
|
||||
doSomethingWithCroppedImage(outputUri);
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent result) {
|
||||
if (requestCode == Crop.REQUEST_CROP && resultCode == RESULT_OK) {
|
||||
doSomethingWithCroppedImage(outputUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Some attributes are provided to customise the crop screen. See the example project [theme](https://github.com/jdamcd/android-crop/blob/master/example/src/main/res/values/theme.xml).
|
||||
|
||||
#### Pick
|
||||
|
||||
The library provides a utility method to start an image picker:
|
||||
|
||||
```java
|
||||
Crop.pickImage(activity)
|
||||
```
|
||||
`Crop.pickImage(activity)`
|
||||
|
||||
#### Dependency
|
||||
|
||||
The AAR is published on Maven Central:
|
||||
|
||||
```groovy
|
||||
compile 'com.soundcloud.android:android-crop:1.0.1@aar'
|
||||
```
|
||||
`compile 'com.soundcloud.android:android-crop:0.9.10@aar'`
|
||||
|
||||
#### Users
|
||||
#### Apps
|
||||
|
||||
Apps that use this library include: [SoundCloud](https://play.google.com/store/apps/details?id=com.soundcloud.android), [Depop](https://play.google.com/store/apps/details?id=com.depop), [Polyvore](https://play.google.com/store/apps/details?id=com.polyvore), [TextSecure](https://play.google.com/store/apps/details?id=org.thoughtcrime.securesms)
|
||||
Apps that use this library include: [SoundCloud](https://play.google.com/store/apps/details?id=com.soundcloud.android), [Depop](https://play.google.com/store/apps/details?id=com.depop)
|
||||
|
||||
## How does it look?
|
||||
|
||||
|
|
@ -69,16 +56,5 @@ Apps that use this library include: [SoundCloud](https://play.google.com/store/a
|
|||
|
||||
This project is based on the [AOSP](https://source.android.com) camera image cropper via [android-cropimage](https://github.com/lvillani/android-cropimage).
|
||||
|
||||
Copyright 2015 SoundCloud
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Copyright 2014 [SoundCloud](https://soundcloud.com)
|
||||
Apache License, Version 2.0
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.2.3'
|
||||
classpath 'com.android.tools.build:gradle:1.0.0'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ apply plugin: 'com.android.application'
|
|||
archivesBaseName = 'android-crop-example'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion '23.0.1'
|
||||
compileSdkVersion 21
|
||||
buildToolsVersion '21.1.2'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 10
|
||||
targetSdkVersion 22
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 21
|
||||
versionCode Integer.parseInt(project.VERSION_CODE)
|
||||
versionName project.VERSION
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
package com.soundcloud.android.crop.example;
|
||||
|
||||
import com.soundcloud.android.crop.Crop;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.soundcloud.android.crop.Crop;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
|
|
@ -50,8 +51,8 @@ public class MainActivity extends Activity {
|
|||
}
|
||||
|
||||
private void beginCrop(Uri source) {
|
||||
Uri destination = Uri.fromFile(new File(getCacheDir(), "cropped"));
|
||||
Crop.of(source, destination).asSquare().start(this);
|
||||
Uri outputUri = Uri.fromFile(new File(getCacheDir(), "cropped"));
|
||||
new Crop(source).output(outputUri).asSquare().start(this);
|
||||
}
|
||||
|
||||
private void handleCrop(int resultCode, Intent result) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
<style name="Widget.CropImageView" parent="android:Widget">
|
||||
<item name="showThirds">true</item>
|
||||
<item name="showCircle">false</item>
|
||||
<item name="showHandles">always</item>
|
||||
<item name="highlightColor">@color/highlight</item>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
VERSION=1.0.1
|
||||
VERSION=0.9.10
|
||||
VERSION_CODE=1
|
||||
|
||||
signing.keyId=63A46540
|
||||
|
|
@ -6,4 +6,4 @@ signing.secretKeyRingFile=
|
|||
signing.password=
|
||||
|
||||
sonatypeUsername=jdamcd
|
||||
sonatypePassword=
|
||||
sonatypePassword=
|
||||
|
|
@ -6,12 +6,12 @@ apply plugin: 'signing'
|
|||
archivesBaseName = 'android-crop'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion '23.0.1'
|
||||
compileSdkVersion 21
|
||||
buildToolsVersion '21.1.1'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 10
|
||||
targetSdkVersion 22
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 21
|
||||
|
||||
testApplicationId 'com.soundcloud.android.crop.test'
|
||||
testInstrumentationRunner 'android.test.InstrumentationTestRunner'
|
||||
|
|
@ -19,10 +19,9 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-annotations:23.0.1'
|
||||
compile 'com.android.support:support-v4:23.0.1'
|
||||
compile 'com.android.support:support-annotations:21.0.0'
|
||||
androidTestCompile 'com.squareup:fest-android:1.0.7'
|
||||
androidTestCompile 'com.android.support:support-v4:23.0.1'
|
||||
androidTestCompile 'com.android.support:support-v4:21.0.0'
|
||||
androidTestCompile 'org.mockito:mockito-core:1.9.5'
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker:1.0'
|
||||
androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.0'
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
package com.soundcloud.android.crop;
|
||||
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.fest.assertions.api.ANDROID;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import org.fest.assertions.api.ANDROID;
|
||||
|
||||
import static org.fest.assertions.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class CropBuilderTest extends BaseTestCase {
|
||||
|
||||
private Activity activity;
|
||||
|
|
@ -22,7 +22,7 @@ public class CropBuilderTest extends BaseTestCase {
|
|||
activity = mock(Activity.class);
|
||||
when(activity.getPackageName()).thenReturn("com.example");
|
||||
|
||||
builder = Crop.of(Uri.parse("image:input"), Uri.parse("image:output"));
|
||||
builder = new Crop(Uri.parse("image:input"));
|
||||
}
|
||||
|
||||
public void testInputUriSetAsData() {
|
||||
|
|
@ -30,6 +30,8 @@ public class CropBuilderTest extends BaseTestCase {
|
|||
}
|
||||
|
||||
public void testOutputUriSetAsExtra() {
|
||||
builder.output(Uri.parse("image:output"));
|
||||
|
||||
Intent intent = builder.getIntent(activity);
|
||||
Uri output = intent.getParcelableExtra(MediaStore.EXTRA_OUTPUT);
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1,7 @@
|
|||
<manifest package="com.soundcloud.android.crop" />
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.soundcloud.android.crop">
|
||||
|
||||
<application
|
||||
android:allowBackup="true" />
|
||||
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public class Crop {
|
|||
public static final int REQUEST_PICK = 9162;
|
||||
public static final int RESULT_ERROR = 404;
|
||||
|
||||
interface Extra {
|
||||
static interface Extra {
|
||||
String ASPECT_X = "aspect_x";
|
||||
String ASPECT_Y = "aspect_y";
|
||||
String MAX_X = "max_x";
|
||||
|
|
@ -31,19 +31,23 @@ public class Crop {
|
|||
private Intent cropIntent;
|
||||
|
||||
/**
|
||||
* Create a crop Intent builder with source and destination image Uris
|
||||
* Create a crop Intent builder with source image
|
||||
*
|
||||
* @param source Uri for image to crop
|
||||
* @param destination Uri for saving the cropped image
|
||||
* @param source Source image URI
|
||||
*/
|
||||
public static Crop of(Uri source, Uri destination) {
|
||||
return new Crop(source, destination);
|
||||
}
|
||||
|
||||
private Crop(Uri source, Uri destination) {
|
||||
public Crop(Uri source) {
|
||||
cropIntent = new Intent();
|
||||
cropIntent.setData(source);
|
||||
cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, destination);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set output URI where the cropped image will be saved
|
||||
*
|
||||
* @param output Output image URI
|
||||
*/
|
||||
public Crop output(Uri output) {
|
||||
cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, output);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -70,7 +74,7 @@ public class Crop {
|
|||
/**
|
||||
* Set maximum crop size
|
||||
*
|
||||
* @param width Max width
|
||||
* @param width Max width
|
||||
* @param height Max height
|
||||
*/
|
||||
public Crop withMaxSize(int width, int height) {
|
||||
|
|
@ -80,7 +84,7 @@ public class Crop {
|
|||
}
|
||||
|
||||
/**
|
||||
* Send the crop Intent from an Activity
|
||||
* Send the crop Intent!
|
||||
*
|
||||
* @param activity Activity to receive result
|
||||
*/
|
||||
|
|
@ -89,9 +93,9 @@ public class Crop {
|
|||
}
|
||||
|
||||
/**
|
||||
* Send the crop Intent from an Activity with a custom request code
|
||||
* Send the crop Intent with a custom requestCode
|
||||
*
|
||||
* @param activity Activity to receive result
|
||||
* @param activity Activity to receive result
|
||||
* @param requestCode requestCode for result
|
||||
*/
|
||||
public void start(Activity activity, int requestCode) {
|
||||
|
|
@ -99,30 +103,21 @@ public class Crop {
|
|||
}
|
||||
|
||||
/**
|
||||
* Send the crop Intent from a Fragment
|
||||
* Send the crop Intent!
|
||||
*
|
||||
* @param context Context
|
||||
* @param context Context
|
||||
* @param fragment Fragment to receive result
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public void start(Context context, Fragment fragment) {
|
||||
start(context, fragment, REQUEST_CROP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the crop Intent from a support library Fragment
|
||||
* Send the crop Intent with a custom requestCode
|
||||
*
|
||||
* @param context Context
|
||||
* @param context Context
|
||||
* @param fragment Fragment to receive result
|
||||
*/
|
||||
public void start(Context context, android.support.v4.app.Fragment fragment) {
|
||||
start(context, fragment, REQUEST_CROP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the crop Intent with a custom request code
|
||||
*
|
||||
* @param context Context
|
||||
* @param fragment Fragment to receive result
|
||||
* @param requestCode requestCode for result
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
|
|
@ -130,17 +125,6 @@ public class Crop {
|
|||
fragment.startActivityForResult(getIntent(context), requestCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the crop Intent with a custom request code
|
||||
*
|
||||
* @param context Context
|
||||
* @param fragment Fragment to receive result
|
||||
* @param requestCode requestCode for result
|
||||
*/
|
||||
public void start(Context context, android.support.v4.app.Fragment fragment, int requestCode) {
|
||||
fragment.startActivityForResult(getIntent(context), requestCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Intent to start crop Activity
|
||||
*
|
||||
|
|
@ -172,85 +156,27 @@ public class Crop {
|
|||
}
|
||||
|
||||
/**
|
||||
* Pick image from an Activity
|
||||
* Utility to start an image picker
|
||||
*
|
||||
* @param activity Activity to receive result
|
||||
* @param activity Activity that will receive result
|
||||
*/
|
||||
public static void pickImage(Activity activity) {
|
||||
pickImage(activity, REQUEST_PICK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick image from a Fragment
|
||||
* Utility to start an image picker with request code
|
||||
*
|
||||
* @param context Context
|
||||
* @param fragment Fragment to receive result
|
||||
*/
|
||||
public static void pickImage(Context context, Fragment fragment) {
|
||||
pickImage(context, fragment, REQUEST_PICK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick image from a support library Fragment
|
||||
*
|
||||
* @param context Context
|
||||
* @param fragment Fragment to receive result
|
||||
*/
|
||||
public static void pickImage(Context context, android.support.v4.app.Fragment fragment) {
|
||||
pickImage(context, fragment, REQUEST_PICK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick image from an Activity with a custom request code
|
||||
*
|
||||
* @param activity Activity to receive result
|
||||
* @param activity Activity that will receive result
|
||||
* @param requestCode requestCode for result
|
||||
*/
|
||||
public static void pickImage(Activity activity, int requestCode) {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT).setType("image/*");
|
||||
try {
|
||||
activity.startActivityForResult(getImagePicker(), requestCode);
|
||||
activity.startActivityForResult(intent, requestCode);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
showImagePickerError(activity);
|
||||
Toast.makeText(activity, R.string.crop__pick_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick image from a Fragment with a custom request code
|
||||
*
|
||||
* @param context Context
|
||||
* @param fragment Fragment to receive result
|
||||
* @param requestCode requestCode for result
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static void pickImage(Context context, Fragment fragment, int requestCode) {
|
||||
try {
|
||||
fragment.startActivityForResult(getImagePicker(), requestCode);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
showImagePickerError(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick image from a support library Fragment with a custom request code
|
||||
*
|
||||
* @param context Context
|
||||
* @param fragment Fragment to receive result
|
||||
* @param requestCode requestCode for result
|
||||
*/
|
||||
public static void pickImage(Context context, android.support.v4.app.Fragment fragment, int requestCode) {
|
||||
try {
|
||||
fragment.startActivityForResult(getImagePicker(), requestCode);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
showImagePickerError(context);
|
||||
}
|
||||
}
|
||||
|
||||
private static Intent getImagePicker() {
|
||||
return new Intent(Intent.ACTION_GET_CONTENT).setType("image/*");
|
||||
}
|
||||
|
||||
private static void showImagePickerError(Context context) {
|
||||
Toast.makeText(context, R.string.crop__pick_error, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import android.content.Intent;
|
|||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.BitmapRegionDecoder;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
|
|
@ -32,8 +33,8 @@ import android.os.Handler;
|
|||
import android.provider.MediaStore;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
|
@ -44,6 +45,7 @@ import java.util.concurrent.CountDownLatch;
|
|||
*/
|
||||
public class CropImageActivity extends MonitoredActivity {
|
||||
|
||||
private static final boolean IN_MEMORY_CROP = Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD_MR1;
|
||||
private static final int SIZE_DEFAULT = 2048;
|
||||
private static final int SIZE_LIMIT = 4096;
|
||||
|
||||
|
|
@ -70,10 +72,11 @@ public class CropImageActivity extends MonitoredActivity {
|
|||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
setupWindowFlags();
|
||||
setupViews();
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.crop__activity_crop);
|
||||
initViews();
|
||||
|
||||
loadInput();
|
||||
setupFromIntent();
|
||||
if (rotateBitmap == null) {
|
||||
finish();
|
||||
return;
|
||||
|
|
@ -81,17 +84,7 @@ public class CropImageActivity extends MonitoredActivity {
|
|||
startCrop();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private void setupWindowFlags() {
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupViews() {
|
||||
setContentView(R.layout.crop__activity_crop);
|
||||
|
||||
private void initViews() {
|
||||
imageView = (CropImageView) findViewById(R.id.crop_image);
|
||||
imageView.context = this;
|
||||
imageView.setRecycler(new ImageViewTouchBase.Recycler() {
|
||||
|
|
@ -116,7 +109,7 @@ public class CropImageActivity extends MonitoredActivity {
|
|||
});
|
||||
}
|
||||
|
||||
private void loadInput() {
|
||||
private void setupFromIntent() {
|
||||
Intent intent = getIntent();
|
||||
Bundle extras = intent.getExtras();
|
||||
|
||||
|
|
@ -198,7 +191,7 @@ public class CropImageActivity extends MonitoredActivity {
|
|||
handler.post(new Runnable() {
|
||||
public void run() {
|
||||
if (imageView.getScale() == 1F) {
|
||||
imageView.center();
|
||||
imageView.center(true, true);
|
||||
}
|
||||
latch.countDown();
|
||||
}
|
||||
|
|
@ -262,19 +255,23 @@ public class CropImageActivity extends MonitoredActivity {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO
|
||||
* This should use the decode/crop/encode single step API so that the whole
|
||||
* (possibly large) Bitmap doesn't need to be read into memory
|
||||
*/
|
||||
private void onSaveClicked() {
|
||||
if (cropView == null || isSaving) {
|
||||
return;
|
||||
}
|
||||
isSaving = true;
|
||||
|
||||
Bitmap croppedImage;
|
||||
Bitmap croppedImage = null;
|
||||
Rect r = cropView.getScaledCropRect(sampleSize);
|
||||
int width = r.width();
|
||||
int height = r.height();
|
||||
|
||||
int outWidth = width;
|
||||
int outHeight = height;
|
||||
int outWidth = width, outHeight = height;
|
||||
if (maxX > 0 && maxY > 0 && (width > maxX || height > maxY)) {
|
||||
float ratio = (float) width / (float) height;
|
||||
if ((float) maxX / (float) maxY > ratio) {
|
||||
|
|
@ -286,18 +283,27 @@ public class CropImageActivity extends MonitoredActivity {
|
|||
}
|
||||
}
|
||||
|
||||
try {
|
||||
croppedImage = decodeRegionCrop(r, outWidth, outHeight);
|
||||
} catch (IllegalArgumentException e) {
|
||||
setResultException(e);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (IN_MEMORY_CROP && rotateBitmap != null) {
|
||||
croppedImage = inMemoryCrop(rotateBitmap, r, width, height, outWidth, outHeight);
|
||||
if (croppedImage != null) {
|
||||
imageView.setImageBitmapResetBase(croppedImage, true);
|
||||
imageView.center(true, true);
|
||||
imageView.highlightViews.clear();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
croppedImage = decodeRegionCrop(r);
|
||||
} catch (IllegalArgumentException e) {
|
||||
setResultException(e);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (croppedImage != null) {
|
||||
imageView.setImageRotateBitmapResetBase(new RotateBitmap(croppedImage, exifRotation), true);
|
||||
imageView.center();
|
||||
imageView.highlightViews.clear();
|
||||
if (croppedImage != null) {
|
||||
imageView.setImageRotateBitmapResetBase(new RotateBitmap(croppedImage, exifRotation), true);
|
||||
imageView.center(true, true);
|
||||
imageView.highlightViews.clear();
|
||||
}
|
||||
}
|
||||
saveImage(croppedImage);
|
||||
}
|
||||
|
|
@ -317,7 +323,8 @@ public class CropImageActivity extends MonitoredActivity {
|
|||
}
|
||||
}
|
||||
|
||||
private Bitmap decodeRegionCrop(Rect rect, int outWidth, int outHeight) {
|
||||
@TargetApi(10)
|
||||
private Bitmap decodeRegionCrop(Rect rect) {
|
||||
// Release memory now
|
||||
clearImageView();
|
||||
|
||||
|
|
@ -344,11 +351,6 @@ public class CropImageActivity extends MonitoredActivity {
|
|||
|
||||
try {
|
||||
croppedImage = decoder.decodeRegion(rect, new BitmapFactory.Options());
|
||||
if (croppedImage != null && (rect.width() > outWidth || rect.height() > outHeight)) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.postScale((float) outWidth / rect.width(), (float) outHeight / rect.height());
|
||||
croppedImage = Bitmap.createBitmap(croppedImage, 0, 0, croppedImage.getWidth(), croppedImage.getHeight(), matrix, true);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Rethrow with some extra information
|
||||
throw new IllegalArgumentException("Rectangle " + rect + " is outside of the image ("
|
||||
|
|
@ -357,7 +359,7 @@ public class CropImageActivity extends MonitoredActivity {
|
|||
|
||||
} catch (IOException e) {
|
||||
Log.e("Error cropping image: " + e.getMessage(), e);
|
||||
setResultException(e);
|
||||
finish();
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e("OOM cropping image: " + e.getMessage(), e);
|
||||
setResultException(e);
|
||||
|
|
@ -367,6 +369,33 @@ public class CropImageActivity extends MonitoredActivity {
|
|||
return croppedImage;
|
||||
}
|
||||
|
||||
private Bitmap inMemoryCrop(RotateBitmap rotateBitmap, Rect r, int width, int height, int outWidth, int outHeight) {
|
||||
// In-memory crop means potential OOM errors,
|
||||
// but we have no choice as we can't selectively decode a bitmap with this API level
|
||||
System.gc();
|
||||
|
||||
Bitmap croppedImage = null;
|
||||
try {
|
||||
croppedImage = Bitmap.createBitmap(outWidth, outHeight, Bitmap.Config.RGB_565);
|
||||
|
||||
Canvas canvas = new Canvas(croppedImage);
|
||||
RectF dstRect = new RectF(0, 0, width, height);
|
||||
|
||||
Matrix m = new Matrix();
|
||||
m.setRectToRect(new RectF(r), dstRect, Matrix.ScaleToFit.FILL);
|
||||
m.preConcat(rotateBitmap.getRotateMatrix());
|
||||
canvas.drawBitmap(rotateBitmap.getBitmap(), m, null);
|
||||
} catch (OutOfMemoryError e) {
|
||||
Log.e("OOM cropping image: " + e.getMessage(), e);
|
||||
setResultException(e);
|
||||
System.gc();
|
||||
}
|
||||
|
||||
// Release Bitmap memory as soon as possible
|
||||
clearImageView();
|
||||
return croppedImage;
|
||||
}
|
||||
|
||||
private void clearImageView() {
|
||||
imageView.clear();
|
||||
if (rotateBitmap != null) {
|
||||
|
|
@ -390,10 +419,11 @@ public class CropImageActivity extends MonitoredActivity {
|
|||
CropUtil.closeSilently(outputStream);
|
||||
}
|
||||
|
||||
CropUtil.copyExifRotation(
|
||||
CropUtil.getFromMediaUri(this, getContentResolver(), sourceUri),
|
||||
CropUtil.getFromMediaUri(this, getContentResolver(), saveUri)
|
||||
);
|
||||
if (!IN_MEMORY_CROP) {
|
||||
// In-memory crop negates the rotation
|
||||
File saveFile = CropUtil.getFromMediaUri(this, getContentResolver(), saveUri);
|
||||
CropUtil.saveExifRotation(saveFile, exifRotation);
|
||||
}
|
||||
|
||||
setResultUri(saveUri);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package com.soundcloud.android.crop;
|
|||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
|
|
@ -18,16 +17,18 @@ public class CropImageView extends ImageViewTouchBase {
|
|||
private float lastX;
|
||||
private float lastY;
|
||||
private int motionEdge;
|
||||
private int validPointerId;
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public CropImageView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public CropImageView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
public CropImageView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
|
@ -84,7 +85,7 @@ public class CropImageView extends ImageViewTouchBase {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(@NonNull MotionEvent event) {
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
CropImageActivity cropImageActivity = (CropImageActivity) context;
|
||||
if (cropImageActivity.isSaving()) {
|
||||
return false;
|
||||
|
|
@ -99,8 +100,6 @@ public class CropImageView extends ImageViewTouchBase {
|
|||
motionHighlightView = hv;
|
||||
lastX = event.getX();
|
||||
lastY = event.getY();
|
||||
// Prevent multiple touches from interfering with crop area re-sizing
|
||||
validPointerId = event.getPointerId(event.getActionIndex());
|
||||
motionHighlightView.setMode((edge == HighlightView.MOVE)
|
||||
? HighlightView.ModifyMode.Move
|
||||
: HighlightView.ModifyMode.Grow);
|
||||
|
|
@ -114,20 +113,29 @@ public class CropImageView extends ImageViewTouchBase {
|
|||
motionHighlightView.setMode(HighlightView.ModifyMode.None);
|
||||
}
|
||||
motionHighlightView = null;
|
||||
center();
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (motionHighlightView != null && event.getPointerId(event.getActionIndex()) == validPointerId) {
|
||||
if (motionHighlightView != null) {
|
||||
motionHighlightView.handleMotion(motionEdge, event.getX()
|
||||
- lastX, event.getY() - lastY);
|
||||
lastX = event.getX();
|
||||
lastY = event.getY();
|
||||
ensureVisible(motionHighlightView);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// If we're not zoomed then there's no point in even allowing the user to move the image around.
|
||||
// This call to center puts it back to the normalized location.
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_UP:
|
||||
center(true, true);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// if we're not zoomed then there's no point in even allowing
|
||||
// the user to move the image around. This call to center puts
|
||||
// it back to the normalized location (with false meaning don't
|
||||
// animate).
|
||||
if (getScale() == 1F) {
|
||||
center();
|
||||
center(true, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -181,10 +189,10 @@ public class CropImageView extends ImageViewTouchBase {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(@NonNull Canvas canvas) {
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
for (HighlightView highlightView : highlightViews) {
|
||||
highlightView.draw(canvas);
|
||||
for (HighlightView mHighlightView : highlightViews) {
|
||||
mHighlightView.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,21 +68,20 @@ class CropUtil {
|
|||
return ExifInterface.ORIENTATION_UNDEFINED;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("Error getting Exif data", e);
|
||||
Log.e("Error reading Exif rotation data", e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean copyExifRotation(File sourceFile, File destFile) {
|
||||
if (sourceFile == null || destFile == null) return false;
|
||||
public static boolean saveExifRotation(File file, int exifRotation) {
|
||||
if (file == null) return false;
|
||||
try {
|
||||
ExifInterface exifSource = new ExifInterface(sourceFile.getAbsolutePath());
|
||||
ExifInterface exifDest = new ExifInterface(destFile.getAbsolutePath());
|
||||
exifDest.setAttribute(ExifInterface.TAG_ORIENTATION, exifSource.getAttribute(ExifInterface.TAG_ORIENTATION));
|
||||
ExifInterface exifDest = new ExifInterface(file.getAbsolutePath());
|
||||
exifDest.setAttribute(ExifInterface.TAG_ORIENTATION, String.valueOf(exifRotation));
|
||||
exifDest.saveAttributes();
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
Log.e("Error copying Exif data", e);
|
||||
Log.e("Error saving Exif rotation data", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -159,7 +158,7 @@ class CropUtil {
|
|||
|
||||
public static void startBackgroundJob(MonitoredActivity activity,
|
||||
String title, String message, Runnable job, Handler handler) {
|
||||
// Make the progress dialog uncancelable, so that we can guarantee
|
||||
// Make the progress dialog uncancelable, so that we can gurantee
|
||||
// the thread will be done before the activity getting destroyed
|
||||
ProgressDialog dialog = ProgressDialog.show(
|
||||
activity, title, message, true, false);
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ class HighlightView {
|
|||
|
||||
private View viewContext; // View displaying image
|
||||
private boolean showThirds;
|
||||
private boolean showCircle;
|
||||
private int highlightColor;
|
||||
|
||||
private ModifyMode modifyMode = ModifyMode.None;
|
||||
|
|
@ -88,7 +87,6 @@ class HighlightView {
|
|||
TypedArray attributes = context.obtainStyledAttributes(outValue.resourceId, R.styleable.CropImageView);
|
||||
try {
|
||||
showThirds = attributes.getBoolean(R.styleable.CropImageView_showThirds, false);
|
||||
showCircle = attributes.getBoolean(R.styleable.CropImageView_showCircle, false);
|
||||
highlightColor = attributes.getColor(R.styleable.CropImageView_highlightColor,
|
||||
DEFAULT_HIGHLIGHT_COLOR);
|
||||
handleMode = HandleMode.values()[attributes.getInt(R.styleable.CropImageView_showHandles, 0)];
|
||||
|
|
@ -152,10 +150,6 @@ class HighlightView {
|
|||
drawThirds(canvas);
|
||||
}
|
||||
|
||||
if (showCircle) {
|
||||
drawCircle(canvas);
|
||||
}
|
||||
|
||||
if (handleMode == HandleMode.Always ||
|
||||
(handleMode == HandleMode.Changing && modifyMode == ModifyMode.Grow)) {
|
||||
drawHandles(canvas);
|
||||
|
|
@ -215,11 +209,6 @@ class HighlightView {
|
|||
drawRect.right, drawRect.top + yThird * 2, outlinePaint);
|
||||
}
|
||||
|
||||
private void drawCircle(Canvas canvas) {
|
||||
outlinePaint.setStrokeWidth(1);
|
||||
canvas.drawOval(new RectF(drawRect), outlinePaint);
|
||||
}
|
||||
|
||||
public void setMode(ModifyMode mode) {
|
||||
if (mode != modifyMode) {
|
||||
modifyMode = mode;
|
||||
|
|
|
|||
|
|
@ -192,10 +192,12 @@ abstract class ImageViewTouchBase extends ImageView {
|
|||
maxZoom = calculateMaxZoom();
|
||||
}
|
||||
|
||||
// Center as much as possible in one or both axis. Centering is defined as follows:
|
||||
// * If the image is scaled down below the view's dimensions then center it.
|
||||
// * If the image is scaled larger than the view and is translated out of view then translate it back into view.
|
||||
protected void center() {
|
||||
// Center as much as possible in one or both axis. Centering is
|
||||
// defined as follows: if the image is scaled down below the
|
||||
// view's dimensions then center it (literally). If the image
|
||||
// is scaled larger than the view and is translated out of view
|
||||
// then translate it back into view (i.e. eliminate black bars).
|
||||
protected void center(boolean horizontal, boolean vertical) {
|
||||
final Bitmap bitmap = bitmapDisplayed.getBitmap();
|
||||
if (bitmap == null) {
|
||||
return;
|
||||
|
|
@ -210,37 +212,32 @@ abstract class ImageViewTouchBase extends ImageView {
|
|||
|
||||
float deltaX = 0, deltaY = 0;
|
||||
|
||||
deltaY = centerVertical(rect, height, deltaY);
|
||||
deltaX = centerHorizontal(rect, width, deltaX);
|
||||
if (vertical) {
|
||||
int viewHeight = getHeight();
|
||||
if (height < viewHeight) {
|
||||
deltaY = (viewHeight - height) / 2 - rect.top;
|
||||
} else if (rect.top > 0) {
|
||||
deltaY = -rect.top;
|
||||
} else if (rect.bottom < viewHeight) {
|
||||
deltaY = getHeight() - rect.bottom;
|
||||
}
|
||||
}
|
||||
|
||||
if (horizontal) {
|
||||
int viewWidth = getWidth();
|
||||
if (width < viewWidth) {
|
||||
deltaX = (viewWidth - width) / 2 - rect.left;
|
||||
} else if (rect.left > 0) {
|
||||
deltaX = -rect.left;
|
||||
} else if (rect.right < viewWidth) {
|
||||
deltaX = viewWidth - rect.right;
|
||||
}
|
||||
}
|
||||
|
||||
postTranslate(deltaX, deltaY);
|
||||
setImageMatrix(getImageViewMatrix());
|
||||
}
|
||||
|
||||
private float centerVertical(RectF rect, float height, float deltaY) {
|
||||
int viewHeight = getHeight();
|
||||
if (height < viewHeight) {
|
||||
deltaY = (viewHeight - height) / 2 - rect.top;
|
||||
} else if (rect.top > 0) {
|
||||
deltaY = -rect.top;
|
||||
} else if (rect.bottom < viewHeight) {
|
||||
deltaY = getHeight() - rect.bottom;
|
||||
}
|
||||
return deltaY;
|
||||
}
|
||||
|
||||
private float centerHorizontal(RectF rect, float width, float deltaX) {
|
||||
int viewWidth = getWidth();
|
||||
if (width < viewWidth) {
|
||||
deltaX = (viewWidth - width) / 2 - rect.left;
|
||||
} else if (rect.left > 0) {
|
||||
deltaX = -rect.left;
|
||||
} else if (rect.right < viewWidth) {
|
||||
deltaX = viewWidth - rect.right;
|
||||
}
|
||||
return deltaX;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setScaleType(ImageView.ScaleType.MATRIX);
|
||||
}
|
||||
|
|
@ -316,7 +313,7 @@ abstract class ImageViewTouchBase extends ImageView {
|
|||
|
||||
suppMatrix.postScale(deltaScale, deltaScale, centerX, centerY);
|
||||
setImageMatrix(getImageViewMatrix());
|
||||
center();
|
||||
center(true, true);
|
||||
}
|
||||
|
||||
protected void zoomTo(final float scale, final float centerX,
|
||||
|
|
@ -386,7 +383,7 @@ abstract class ImageViewTouchBase extends ImageView {
|
|||
suppMatrix.postScale(1F / rate, 1F / rate, cx, cy);
|
||||
}
|
||||
setImageMatrix(getImageViewMatrix());
|
||||
center();
|
||||
center(true, true);
|
||||
}
|
||||
|
||||
protected void postTranslate(float dx, float dy) {
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ class Log {
|
|||
|
||||
private static final String TAG = "android-crop";
|
||||
|
||||
public static void e(String msg) {
|
||||
public static final void e(String msg) {
|
||||
android.util.Log.e(TAG, msg);
|
||||
}
|
||||
|
||||
public static void e(String msg, Throwable e) {
|
||||
public static final void e(String msg, Throwable e) {
|
||||
android.util.Log.e(TAG, msg, e);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="crop__saving">جارى حفظ الصورة …</string>
|
||||
<string name="crop__wait">رجاء الأنتظار …</string>
|
||||
<string name="crop__pick_error">الصورة غير متاحة</string>
|
||||
|
||||
<string name="crop__done">تم</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">الغاء</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="crop__saving">Guardant imatge…</string>
|
||||
<string name="crop__wait">Si us plau esperi…</string>
|
||||
<string name="crop__pick_error">No hi ha imatges disponibles</string>
|
||||
|
||||
<string name="crop__done">ACCEPTAR</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">CANCEL·LAR</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="crop__saving">Bild speichern…</string>
|
||||
<string name="crop__wait">Bitte warten…</string>
|
||||
<string name="crop__pick_error">Keine Bildquellen verfügbar</string>
|
||||
|
||||
<string name="crop__done">übernehmen</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">abbrechen</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<string name="crop__saving">Guardando imagen…</string>
|
||||
<string name="crop__wait">Por favor espere…</string>
|
||||
<string name="crop__pick_error">No hay imágenes disponibles</string>
|
||||
<string name="crop__pick_error">No hay imagenes disponibles</string>
|
||||
|
||||
<string name="crop__done">ACEPTAR</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">CANCELAR</string>
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="crop__saving">Enregistrement de l\'image…</string>
|
||||
<string name="crop__wait">Veuillez patienter…</string>
|
||||
<string name="crop__pick_error">Aucune image disponible</string>
|
||||
|
||||
<string name="crop__done">ACCEPTER</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">ANNULER</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="crop__saving">Menyimpan gambar…</string>
|
||||
<string name="crop__wait">Silakan tunggu…</string>
|
||||
<string name="crop__pick_error">Tidak ada sumber gambar yang tersedia</string>
|
||||
|
||||
<string name="crop__done">SELESAI</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">BATAL</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="crop__saving">Salvataggio immagine…</string>
|
||||
<string name="crop__wait">Attendere prego…</string>
|
||||
<string name="crop__pick_error">Nessuna immagine disponibile</string>
|
||||
|
||||
<string name="crop__done">ACCETTA</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">ANNULLA</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="crop__saving">사진을 저장중입니다…</string>
|
||||
<string name="crop__wait">잠시만 기다려주세요…</string>
|
||||
<string name="crop__pick_error">이미지가 존재하지 않습니다.</string>
|
||||
|
||||
<string name="crop__done">확인</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">취소</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="crop__saving">Salvando imagem…</string>
|
||||
<string name="crop__wait">Por favor, aguarde…</string>
|
||||
<string name="crop__pick_error">Sem fontes de imagem disponíveis</string>
|
||||
|
||||
<string name="crop__done">FINALIZADO</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">CANCELAR</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="crop__saving">Изображение сохраняется…</string>
|
||||
<string name="crop__wait">Пожалуйста, подождите…</string>
|
||||
<string name="crop__pick_error">Нет доступных изображений</string>
|
||||
|
||||
<string name="crop__done">ГОТОВО</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">ОТМЕНА</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="crop__saving">Sparar bild…</string>
|
||||
<string name="crop__wait">Var god vänta…</string>
|
||||
<string name="crop__pick_error">Inga bildkällor tillgängliga</string>
|
||||
|
||||
<string name="crop__done">KLAR</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">AVBRYT</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="crop__saving">Fotoğraf kaydediliyor…</string>
|
||||
<string name="crop__wait">Lütfen bekleyin…</string>
|
||||
<string name="crop__pick_error">Fotoğraf bulunamadı</string>
|
||||
|
||||
<string name="crop__done">TAMAM</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">ÇIKIŞ</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<string name="crop__saving">正在儲存相片…</string>
|
||||
<string name="crop__wait">請稍候…</string>
|
||||
<string name="crop__pick_error">沒有可用的圖片來源</string>
|
||||
|
||||
<string name="crop__done">完成</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">取消</string>
|
||||
|
||||
</resources>
|
||||
|
|
@ -3,8 +3,7 @@
|
|||
<string name="crop__saving">正在保存照片…</string>
|
||||
<string name="crop__wait">请等待…</string>
|
||||
<string name="crop__pick_error">无效的图片</string>
|
||||
|
||||
<string name="crop__done">完成</string>
|
||||
<string name="crop__cancel" tools:ignore="ButtonCase">取消</string>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
@ -5,7 +5,6 @@
|
|||
<declare-styleable name="CropImageView">
|
||||
<attr name="highlightColor" format="reference|color" />
|
||||
<attr name="showThirds" format="boolean" />
|
||||
<attr name="showCircle" format="boolean" />
|
||||
<attr name="showHandles">
|
||||
<enum name="changing" value="0" />
|
||||
<enum name="always" value="1" />
|
||||
|
|
|
|||
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
|
Before Width: | Height: | Size: 299 KiB After Width: | Height: | Size: 179 KiB |
Loading…
Reference in New Issue