diff --git a/README.md b/README.md index 5bbfd1f30..65c01e91b 100644 --- a/README.md +++ b/README.md @@ -13,21 +13,21 @@ Get it on Google Play -1. Include the library as local library project. +1. Include the library as a local library project. ``` allprojects { repositories { - jcenter() + ... maven { url "https://jitpack.io" } } } ``` - ``` compile 'com.github.yalantis:ucrop:2.2.2' ``` - lightweight general solution - - ``` compile 'com.github.yalantis:ucrop:2.2.2-native' ``` - get power of the native code to preserve image quality (+ about 1.5 MB to an apk size) - + ``` implementation 'com.github.yalantis:ucrop:2.2.3' ``` - lightweight general solution + + ``` implementation 'com.github.yalantis:ucrop:2.2.3-native' ``` - get power of the native code to preserve image quality (+ about 1.5 MB to an apk size) + 2. Add UCropActivity into your AndroidManifest.xml ``` @@ -73,29 +73,46 @@ If you want to let your users choose crop ratio dynamically, just do not call `w uCrop builder class has method `withOptions(UCrop.Options options)` which extends library configurations. -Currently you can change: +Currently, you can change: * image compression format (e.g. PNG, JPEG, WEBP), compression * image compression quality [0 - 100]. PNG which is lossless, will ignore the quality setting. * whether all gestures are enabled simultaneously - * maximum size for Bitmap that is decoded from source Uri and used within crop view. If you want to override default behaviour. + * maximum size for Bitmap that is decoded from source Uri and used within crop view. If you want to override the default behaviour. * toggle whether to show crop frame/guidelines * setup color/width/count of crop frame/rows/columns - * choose whether you want rectangle or oval crop area + * choose whether you want rectangle or oval(`options.setCircleDimmedLayer(true)`) crop area * the UI colors (Toolbar, StatusBar, active widget state) * and more... - + +Since version 2.2.7 in case if you need to change transport protocol, setup timeout etc. You may set your `OkHttpClient` next way: + + ```java + new UCropInitializer().setOkHttpClient(client); + ``` + # Compatibility - + * Library - Android ICS 4.0+ (API 14) (Android GINGERBREAD 2.3+ (API 10) for versions <= 1.3.2) * Sample - Android ICS 4.0+ (API 14) * CPU - armeabi armeabi-v7a x86 x86_64 arm64-v8a (for versions >= 2.1.2) - + # Changelog -### Version: 2.2.2 -* uCrop fragment added -* bugfix +### Version: 2.2.8 + +* Merged pending pull requests with improvements and bugfixes +* Update compileSdk and targetSdk versions up to 31 +* Add localizations +* Fixed [#609](https://github.com/Yalantis/uCrop/issues/609) +* Fixed [#794](https://github.com/Yalantis/uCrop/issues/794) + +### Version: 2.2.3 + + * Several fixes including [#445](https://github.com/Yalantis/uCrop/issues/445), [#465](https://github.com/Yalantis/uCrop/issues/465) and more! + * Material design support + * uCrop fragment as child fragment + * Added the Italian language ### Version: 2.2.2 @@ -114,7 +131,7 @@ Currently you can change: ### Version: 2.1 * Fixes issue with EXIF data (images taken on front camera with Samsung devices mostly) [#130](https://github.com/Yalantis/uCrop/issues/130) [#111](https://github.com/Yalantis/uCrop/issues/111) - * Added API to set custom set of aspect ratio options for user. [#131](https://github.com/Yalantis/uCrop/issues/131) + * Added API to set custom set of aspect ratio options for the user. [#131](https://github.com/Yalantis/uCrop/issues/131) * Added API to set all configs via UCrop.Options class. [#126](https://github.com/Yalantis/uCrop/issues/126) * Added ABI x86_64 support. [#105](https://github.com/Yalantis/uCrop/issues/105) @@ -123,7 +140,7 @@ Currently you can change: * Native image crop (able to crop high-resolution images, e.g. 16MP & 32MP images on Nexus 5X). * WebP compression format is not supported at the moment (choose JPEG or PNG). * Now library copies EXIF data to cropped image (size and orientation are updated). - + ### Version: 1.5 * Introduced "Freestyle" crop (you can resize crop rectangle by dragging it corners) [#32](https://github.com/Yalantis/uCrop/issues/32) @@ -132,8 +149,8 @@ Currently you can change: ### Version: 1.4 - * Introduced http(s) Uri support! - * Image is cropped in background thread. + * Introduced HTTP(s) Uri support! + * Image is cropped in a background thread. * Showing loader while Bitmap is processed (both loading and cropping). * Several bug fixes. * Couple new things to configure. @@ -141,12 +158,12 @@ Currently you can change: ### Version: 1.3 - * Image is loaded in background thread. Better error-handling for image decoding. + * Image is loaded in a background thread. Better error-handling for image decoding. * Improved EXIF data support (rotation and mirror). * Small UI updates. * Couple new things to configure. - - * Sample updated with possibility to choose custom aspect ratio. + + * Sample updated with the possibility to choose custom aspect ratio. ### Version: 1.2 @@ -162,7 +179,7 @@ Currently you can change: ### Let us know! -We’d be really happy if you sent us links to your projects where you use our component. Just send an email to github@yalantis.com And do let us know if you have any questions or suggestion regarding the library. +We’d be really happy if you sent us links to your projects where you use our component. Just send an email to github@yalantis.com And do let us know if you have any questions or suggestion regarding the library. #### Apps using uCrop diff --git a/build.gradle b/build.gradle index cbd5640a7..1a4d907ab 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,14 @@ buildscript { ext { - androidx_appcompat_version = "1.1.0" + androidx_appcompat_version = "1.3.0" androidx_core_version = "1.1.0" - androidx_exifinterface_version = "1.1.0-beta01" - androidx_transition_version = "1.2.0-rc01" + androidx_exifinterface_version = "1.3.2" + androidx_transition_version = "1.4.1" constraintlayout_version = "1.1.3" } repositories { - jcenter() + mavenCentral() maven { url 'https://maven.google.com/' name 'Google' @@ -28,7 +28,7 @@ allprojects { group = GROUP repositories { - jcenter() + mavenCentral() maven { url 'https://maven.google.com/' name 'Google' diff --git a/gradle.properties b/gradle.properties index d13941087..1278a05e1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,8 +17,8 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -VERSION_NAME=2.2.2-non-native -VERSION_CODE=24 +VERSION_NAME=2.2.8-native +VERSION_CODE=26 GROUP=com.yalantis POM_DESCRIPTION=Android Library for cropping images diff --git a/sample/build.gradle b/sample/build.gradle index fe59af377..95fb140fe 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 29 - buildToolsVersion '28.0.3' + compileSdkVersion 31 + buildToolsVersion '30.0.3' defaultConfig { applicationId "com.yalantis.ucrop.sample" minSdkVersion 14 - targetSdkVersion 29 + targetSdkVersion 31 versionCode 13 versionName "1.2.4" } @@ -38,5 +38,6 @@ dependencies { implementation "androidx.appcompat:appcompat:1.1.0" implementation "androidx.core:core:1.1.0" implementation "androidx.constraintlayout:constraintlayout:1.1.3" + implementation "com.squareup.okhttp3:okhttp:3.12.13" implementation project(':ucrop') } \ No newline at end of file diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 940116d88..414a1dcd0 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + android:screenOrientation="portrait" + android:exported="true"> diff --git a/sample/src/main/java/com/yalantis/ucrop/sample/ResultActivity.java b/sample/src/main/java/com/yalantis/ucrop/sample/ResultActivity.java index 7d7d935f2..55fb1f832 100755 --- a/sample/src/main/java/com/yalantis/ucrop/sample/ResultActivity.java +++ b/sample/src/main/java/com/yalantis/ucrop/sample/ResultActivity.java @@ -18,24 +18,30 @@ import android.util.Log; import android.view.Menu; import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.widget.Toolbar; +import androidx.core.app.ActivityCompat; +import androidx.core.app.NotificationCompat; +import androidx.core.content.FileProvider; + +import com.yalantis.ucrop.util.BitmapLoadUtils; import com.yalantis.ucrop.view.UCropView; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.nio.channels.FileChannel; import java.util.Calendar; import java.util.List; -import androidx.annotation.NonNull; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.widget.Toolbar; -import androidx.core.app.ActivityCompat; -import androidx.core.app.NotificationCompat; -import androidx.core.content.FileProvider; - import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION; @@ -59,6 +65,8 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_result); Uri uri = getIntent().getData(); + int width = 0; + int height = 0; if (uri != null) { try { UCropView uCropView = findViewById(R.id.ucrop); @@ -66,20 +74,49 @@ protected void onCreate(Bundle savedInstanceState) { uCropView.getOverlayView().setShowCropFrame(false); uCropView.getOverlayView().setShowCropGrid(false); uCropView.getOverlayView().setDimmedColor(Color.TRANSPARENT); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && "content".equals(uri.getScheme())) { + TextView textViewExifWarning = findViewById(R.id.text_view_content_warning); + textViewExifWarning.setVisibility(View.VISIBLE); + } } catch (Exception e) { Log.e(TAG, "setImageUri", e); Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); } + + final BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + + if ("content".equals(uri.getScheme())) { + InputStream is = null; + try { + is = getContentResolver().openInputStream(uri); + BitmapFactory.decodeStream(is, null, options); + } catch (FileNotFoundException e) { + Log.d(TAG, e.getMessage(), e); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + Log.e(TAG, e.getMessage(), e); + } + } + } + } else { + File file = new File(getIntent().getData().getPath()); + BitmapFactory.decodeFile(file.getAbsolutePath(), options); + } + + width = options.outWidth; + height = options.outHeight; } - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(new File(getIntent().getData().getPath()).getAbsolutePath(), options); setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setTitle(getString(R.string.format_crop_result_d_d, options.outWidth, options.outHeight)); + actionBar.setTitle(getString(R.string.format_crop_result_d_d, width, height)); } } @@ -131,6 +168,9 @@ private void saveCroppedImage() { Toast.makeText(ResultActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); Log.e(TAG, imageUri.toString(), e); } + } else if (BitmapLoadUtils.hasContentScheme(imageUri)){ + Toast.makeText(ResultActivity.this, getString(R.string.toast_already_saved), Toast.LENGTH_SHORT).show(); + finish(); } else { Toast.makeText(ResultActivity.this, getString(R.string.toast_unexpected_error), Toast.LENGTH_SHORT).show(); } diff --git a/sample/src/main/java/com/yalantis/ucrop/sample/SampleActivity.java b/sample/src/main/java/com/yalantis/ucrop/sample/SampleActivity.java index 0eca122f4..e3f4cf030 100644 --- a/sample/src/main/java/com/yalantis/ucrop/sample/SampleActivity.java +++ b/sample/src/main/java/com/yalantis/ucrop/sample/SampleActivity.java @@ -2,6 +2,7 @@ import android.Manifest; import android.annotation.TargetApi; +import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; @@ -11,6 +12,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.Environment; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -19,8 +21,11 @@ import android.view.View; import android.view.Window; import android.view.WindowManager; +import android.widget.Button; import android.widget.CheckBox; +import android.widget.CompoundButton; import android.widget.EditText; +import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.ScrollView; import android.widget.SeekBar; @@ -40,9 +45,11 @@ import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import androidx.core.content.FileProvider; /** * Created by Oleksii Shliama (https://github.com/shliama). @@ -53,9 +60,10 @@ public class SampleActivity extends BaseActivity implements UCropFragmentCallbac private static final int REQUEST_SELECT_PICTURE = 0x01; private static final int REQUEST_SELECT_PICTURE_FOR_FRAGMENT = 0x02; + private static final int DESTINATION_IMAGE_FILE_REQUEST_CODE = 0x03; private static final String SAMPLE_CROPPED_IMAGE_NAME = "SampleCropImage"; - private RadioGroup mRadioGroupAspectRatio, mRadioGroupCompressionSettings; + private RadioGroup mRadioGroupAspectRatio, mRadioGroupCompressionSettings, mRadioGroupChooseDestination; private EditText mEditTextMaxWidth, mEditTextMaxHeight; private EditText mEditTextRatioX, mEditTextRatioY; private CheckBox mCheckBoxMaxSize; @@ -80,6 +88,11 @@ public class SampleActivity extends BaseActivity implements UCropFragmentCallbac private int mStatusBarColor; private int mToolbarWidgetColor; + private TextView mFileDestinationTextView; + private CheckBox mCheckBoxUseDocumentProvider; + private CheckBox mCheckBoxUseFileProvider; + private Uri destinationUri; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -89,6 +102,7 @@ protected void onCreate(Bundle savedInstanceState) { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { if (requestCode == requestMode) { final Uri selectedUri = data.getData(); @@ -99,6 +113,9 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } } else if (requestCode == UCrop.REQUEST_CROP) { handleCropResult(data); + } else if (requestCode == DESTINATION_IMAGE_FILE_REQUEST_CODE) { + destinationUri = data.getData(); + mFileDestinationTextView.setText(destinationUri.toString()); } } if (resultCode == UCrop.RESULT_ERROR) { @@ -117,22 +134,134 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis pickFromGallery(); } break; + case REQUEST_STORAGE_WRITE_ACCESS_PERMISSION: + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + showChooseFileDestinationAlertDialog(); + } + break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } + + private TextWatcher mAspectRatioTextWatcher = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + mRadioGroupAspectRatio.clearCheck(); + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + + } + }; + private TextWatcher mMaxSizeTextWatcher = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + + } + + @Override + public void afterTextChanged(Editable s) { + if (s != null && !s.toString().trim().isEmpty()) { + if (Integer.valueOf(s.toString()) < UCrop.MIN_SIZE) { + Toast.makeText(SampleActivity.this, String.format(getString(R.string.format_max_cropped_image_size), UCrop.MIN_SIZE), Toast.LENGTH_SHORT).show(); + } + } + } + }; + + private abstract class OcCropButtonClickListener implements View.OnClickListener { + + @Override + public void onClick(View v) { + if (mRadioGroupChooseDestination.getCheckedRadioButtonId() == R.id.radio_choose_destination && destinationUri == null) { + Toast.makeText(SampleActivity.this, + "Please, select a destination file or set Tmp Files destination option", + Toast.LENGTH_LONG).show(); + } else { + onValidatedClick(v); + } + } + + abstract void onValidatedClick(View v); + } + + private class ButtonFileDestinationClickListener implements View.OnClickListener { + + @Override + public void onClick(View v) { + String destinationFileName = SAMPLE_CROPPED_IMAGE_NAME; + String mimeType = "image/jpeg"; + switch (mRadioGroupCompressionSettings.getCheckedRadioButtonId()) { + case R.id.radio_png: + mimeType = "image/png"; + destinationFileName += ".png"; + break; + case R.id.radio_jpeg: + mimeType = "image/jpeg"; + destinationFileName += ".jpg"; + break; + } + + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + + if (mCheckBoxUseDocumentProvider.isChecked()) { + + Intent createDocumentIntent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + createDocumentIntent.addCategory(Intent.CATEGORY_OPENABLE); + createDocumentIntent.setType(mimeType); + createDocumentIntent.putExtra(Intent.EXTRA_TITLE, destinationFileName); + + if (createDocumentIntent.resolveActivity(getPackageManager()) != null) { + + startActivityForResult(createDocumentIntent, DESTINATION_IMAGE_FILE_REQUEST_CODE); + } else { + Toast.makeText(SampleActivity.this, + R.string.no_file_chooser_error, + Toast.LENGTH_LONG).show(); + } + } else if(ActivityCompat.checkSelfPermission(SampleActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, + getString(R.string.permission_write_storage_rationale), + REQUEST_STORAGE_WRITE_ACCESS_PERMISSION); + } else { + showChooseFileDestinationAlertDialog(); + } + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN + && ActivityCompat.checkSelfPermission(SampleActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, + getString(R.string.permission_write_storage_rationale), + REQUEST_STORAGE_WRITE_ACCESS_PERMISSION); + } else { + showChooseFileDestinationAlertDialog(); + } + } + } + @SuppressWarnings("ConstantConditions") private void setupUI() { - findViewById(R.id.button_crop).setOnClickListener(new View.OnClickListener() { + findViewById(R.id.button_crop).setOnClickListener(new OcCropButtonClickListener() { @Override - public void onClick(View v) { + public void onValidatedClick(View v) { pickFromGallery(); } }); - findViewById(R.id.button_random_image).setOnClickListener(new View.OnClickListener() { + findViewById(R.id.button_random_image).setOnClickListener(new OcCropButtonClickListener() { @Override - public void onClick(View v) { + public void onValidatedClick(View v) { Random random = new Random(); int minSizePixels = 800; int maxSizePixels = 2400; @@ -143,6 +272,47 @@ public void onClick(View v) { startCrop(uri); } }); + mFileDestinationTextView = findViewById(R.id.file_destination_label); + mCheckBoxUseDocumentProvider = findViewById(R.id.checkbox_use_document_provider); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + mCheckBoxUseDocumentProvider.setVisibility(View.VISIBLE); + } + mCheckBoxUseFileProvider = findViewById(R.id.checkbox_use_file_provider); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + mCheckBoxUseFileProvider.setVisibility(View.VISIBLE); + } + mCheckBoxUseDocumentProvider.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + mCheckBoxUseFileProvider.setEnabled(!isChecked); + } + }); + final Button buttonFileDestination = findViewById(R.id.button_file_destination); + buttonFileDestination.setOnClickListener(new ButtonFileDestinationClickListener()); + mRadioGroupChooseDestination = findViewById(R.id.radio_group_choose_destination); + mRadioGroupChooseDestination.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + boolean isEnabledChoseDestination = R.id.radio_choose_destination == checkedId; + buttonFileDestination.setEnabled(isEnabledChoseDestination); + mCheckBoxUseDocumentProvider.setEnabled(isEnabledChoseDestination); + + if (isEnabledChoseDestination) { + mFileDestinationTextView.setVisibility(View.VISIBLE); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + mCheckBoxUseFileProvider.setEnabled(true); + } else { + mCheckBoxUseFileProvider.setEnabled(!mCheckBoxUseDocumentProvider.isChecked()); + } + } else { + mCheckBoxUseFileProvider.setEnabled(false); + mFileDestinationTextView.setVisibility(View.GONE); + mFileDestinationTextView.setText(""); + destinationUri = null; + } + } + }); + mRadioGroupChooseDestination.check(R.id.radio_tmp_destination); settingsView = findViewById(R.id.settings); mRadioGroupAspectRatio = findViewById(R.id.radio_group_aspect_ratio); mRadioGroupCompressionSettings = findViewById(R.id.radio_group_compression_settings); @@ -189,43 +359,96 @@ public void onStopTrackingTouch(SeekBar seekBar) { mEditTextMaxWidth.addTextChangedListener(mMaxSizeTextWatcher); } + private void showChooseFileDestinationAlertDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Destination File"); - private TextWatcher mAspectRatioTextWatcher = new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - mRadioGroupAspectRatio.clearCheck(); - } + builder.setView(R.layout.dialog_file_destination); + builder.setPositiveButton("Ok", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + AlertDialog thisDialog = ((AlertDialog) dialog); + RadioGroup radioGroupDirectory = thisDialog.findViewById(R.id.radio_group_directory); + EditText editTextFilename = thisDialog.findViewById(R.id.edit_text_file_name); + + String fileName = editTextFilename.getText().toString(); + if (!fileName.endsWith("jpg")) { + fileName = fileName.concat(".jpg"); + } - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { + File directory = null; + + switch (radioGroupDirectory.getCheckedRadioButtonId()) { + case R.id.radio_external_storage_dcim: + directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM); + break; + case R.id.radio_external_storage_pictures: + directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); + break; + case R.id.radio_app_external_storage_dcim: + directory = SampleActivity.this.getExternalFilesDir(Environment.DIRECTORY_DCIM); + break; + case R.id.radio_app_external_storage_pictures: + directory = SampleActivity.this.getExternalFilesDir(Environment.DIRECTORY_PICTURES); + break; + } - } + File file = new File(directory, fileName); + if (mCheckBoxUseFileProvider.isChecked()) { + destinationUri = FileProvider.getUriForFile(SampleActivity.this, + getString(R.string.file_provider_authorities), + file); + } else { + destinationUri = Uri.fromFile(file); + } + mFileDestinationTextView.setText(destinationUri.toString()); + } + }); + builder.setNeutralButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // Do nothing + } + }); - @Override - public void afterTextChanged(Editable s) { + // create and show the alert dialog + final AlertDialog dialog = builder.create(); + dialog.show(); - } - }; + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); + EditText editTextFilename = dialog.findViewById(R.id.edit_text_file_name); + if(editTextFilename != null) { + editTextFilename.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { - private TextWatcher mMaxSizeTextWatcher = new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } + } - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { - } + } - @Override - public void afterTextChanged(Editable s) { - if (s != null && !s.toString().trim().isEmpty()) { - if (Integer.valueOf(s.toString()) < UCrop.MIN_SIZE) { - Toast.makeText(SampleActivity.this, String.format(getString(R.string.format_max_cropped_image_size), UCrop.MIN_SIZE), Toast.LENGTH_SHORT).show(); + @Override + public void afterTextChanged(Editable s) { + if (s != null && !s.toString().trim().isEmpty() && s.toString().trim().length() >= 3) { + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true); + } else { + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); + } } - } + }); } - }; + + if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { + RadioButton externalDcim = dialog.findViewById(R.id.radio_external_storage_dcim); + RadioButton externalPictures = dialog.findViewById(R.id.radio_external_storage_pictures); + RadioButton appDcim = dialog.findViewById(R.id.radio_app_external_storage_dcim); + externalDcim.setEnabled(false); + externalPictures.setEnabled(false); + appDcim.setChecked(true); + } + } private void pickFromGallery() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN @@ -259,7 +482,11 @@ private void startCrop(@NonNull Uri uri) { break; } - UCrop uCrop = UCrop.of(uri, Uri.fromFile(new File(getCacheDir(), destinationFileName))); + if (destinationUri == null) { + destinationUri = Uri.fromFile(new File(getCacheDir(), destinationFileName)); + } + + UCrop uCrop = UCrop.of(uri, destinationUri); uCrop = basisConfig(uCrop); uCrop = advancedConfig(uCrop); @@ -378,12 +605,14 @@ Tune everything (ノ◕ヮ◕)ノ*:・゚✧ options.setActiveControlsWidgetColor(ContextCompat.getColor(this, R.color.your_color_res)); // Aspect ratio options - options.setAspectRatioOptions(1, + options.setAspectRatioOptions(2, new AspectRatio("WOW", 1, 2), new AspectRatio("MUCH", 3, 4), new AspectRatio("RATIO", CropImageView.DEFAULT_ASPECT_RATIO, CropImageView.DEFAULT_ASPECT_RATIO), new AspectRatio("SO", 16, 9), new AspectRatio("ASPECT", 1, 1)); + options.withAspectRatio(CropImageView.DEFAULT_ASPECT_RATIO, CropImageView.DEFAULT_ASPECT_RATIO); + options.useSourceImageAspectRatio(); */ @@ -526,7 +755,7 @@ public boolean onCreateOptionsMenu(final Menu menu) { } MenuItem menuItemCrop = menu.findItem(R.id.menu_crop); - Drawable menuItemCropIcon = ContextCompat.getDrawable(this, mToolbarCropDrawable); + Drawable menuItemCropIcon = ContextCompat.getDrawable(this, mToolbarCropDrawable == 0 ? R.drawable.ucrop_ic_done : mToolbarCropDrawable); if (menuItemCropIcon != null) { menuItemCropIcon.mutate(); menuItemCropIcon.setColorFilter(mToolbarWidgetColor, PorterDuff.Mode.SRC_ATOP); @@ -546,7 +775,7 @@ public boolean onPrepareOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.menu_crop) { - if (fragment.isAdded()) + if (fragment != null && fragment.isAdded()) fragment.cropAndSaveImage(); } else if (item.getItemId() == android.R.id.home) { removeFragmentFromScreen(); diff --git a/sample/src/main/java/com/yalantis/ucrop/sample/SampleApp.java b/sample/src/main/java/com/yalantis/ucrop/sample/SampleApp.java new file mode 100644 index 000000000..80e746cff --- /dev/null +++ b/sample/src/main/java/com/yalantis/ucrop/sample/SampleApp.java @@ -0,0 +1,33 @@ +package com.yalantis.ucrop.sample; + +import android.app.Application; + +import com.yalantis.ucrop.UCropInitializer; + +import java.util.Collections; + +import okhttp3.ConnectionSpec; +import okhttp3.OkHttpClient; + +public class SampleApp extends Application { + + @Override + public void onCreate() { + super.onCreate(); + setUcropHttpClient(); + } + + private void setUcropHttpClient() { + ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .allEnabledCipherSuites() + .allEnabledTlsVersions() + .build(); + + OkHttpClient client = new OkHttpClient.Builder() + .connectionSpecs(Collections.singletonList(cs)) + .build(); + + new UCropInitializer() + .setOkHttpClient(client); + } +} diff --git a/sample/src/main/res/drawable/bg_button.xml b/sample/src/main/res/drawable/bg_button.xml new file mode 100644 index 000000000..d35c3fcc0 --- /dev/null +++ b/sample/src/main/res/drawable/bg_button.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample/src/main/res/drawable/bg_text_border.xml b/sample/src/main/res/drawable/bg_text_border.xml new file mode 100644 index 000000000..842888763 --- /dev/null +++ b/sample/src/main/res/drawable/bg_text_border.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/sample/src/main/res/layout/activity_result.xml b/sample/src/main/res/layout/activity_result.xml index 27d854983..c033f67b5 100755 --- a/sample/src/main/res/layout/activity_result.xml +++ b/sample/src/main/res/layout/activity_result.xml @@ -15,6 +15,16 @@ app:title="@string/format_crop_result_d_d" app:titleTextColor="@android:color/white"/> + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sample/src/main/res/layout/include_settings.xml b/sample/src/main/res/layout/include_settings.xml index abdaae8bf..c5340e792 100644 --- a/sample/src/main/res/layout/include_settings.xml +++ b/sample/src/main/res/layout/include_settings.xml @@ -55,6 +55,85 @@ android:textColor="@android:color/white" android:textStyle="bold" /> + + + + + + + + + + + + + + + + + +