├── .gitattributes ├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── mavenpush.gradle ├── preview.gif ├── preview.png ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── yalantis │ │ └── ucrop │ │ └── sample │ │ ├── BaseActivity.java │ │ ├── ResultActivity.java │ │ ├── SampleActivity.java │ │ └── SampleApp.java │ └── res │ ├── drawable │ ├── bg_rounded_rectangle.xml │ ├── ic_done.xml │ └── ic_file_download.xml │ ├── layout │ ├── activity_result.xml │ ├── activity_sample.xml │ └── include_settings.xml │ ├── menu │ └── menu_result.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── themes.xml │ └── xml │ └── file_provider_paths.xml ├── settings.gradle └── ucrop ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── com │ └── yalantis │ └── ucrop │ ├── UCrop.java │ ├── UCropActivity.java │ ├── UCropFragment.java │ ├── UCropFragmentCallback.java │ ├── UCropHttpClientStore.java │ ├── callback │ ├── BitmapCropCallback.java │ ├── BitmapLoadCallback.java │ ├── CropBoundsChangeListener.java │ └── OverlayViewChangeListener.java │ ├── model │ ├── AspectRatio.java │ ├── CropParameters.java │ ├── ExifInfo.java │ └── ImageState.java │ ├── task │ ├── BitmapCropTask.java │ └── BitmapLoadTask.java │ ├── util │ ├── BitmapLoadUtils.java │ ├── CubicEasing.java │ ├── EglUtils.java │ ├── FastBitmapDrawable.java │ ├── FileUtils.java │ ├── ImageHeaderParser.java │ ├── RectUtils.java │ ├── RotationGestureDetector.java │ └── SelectedStateListDrawable.java │ └── view │ ├── CropImageView.java │ ├── GestureCropImageView.java │ ├── OverlayView.java │ ├── TransformImageView.java │ ├── UCropView.java │ └── widget │ ├── AspectRatioTextView.java │ └── HorizontalProgressWheelView.java ├── jni ├── Android.mk ├── Application.mk ├── CImg.h ├── com_yalantis_ucrop_task_BitmapCropTask.h └── uCrop.cpp ├── jniLibs ├── arm64-v8a │ └── libucrop.so ├── armeabi-v7a │ └── libucrop.so ├── armeabi │ └── libucrop.so ├── x86 │ └── libucrop.so └── x86_64 │ └── libucrop.so └── res ├── anim ├── ucrop_loader_circle_path.xml └── ucrop_loader_circle_scale.xml ├── color └── ucrop_scale_text_view_selector.xml ├── drawable-hdpi ├── ucrop_ic_angle.png └── ucrop_ic_done.png ├── drawable-ldpi ├── ucrop_ic_angle.png └── ucrop_ic_done.png ├── drawable-mdpi ├── ucrop_ic_angle.png └── ucrop_ic_done.png ├── drawable-xhdpi ├── ucrop_ic_angle.png └── ucrop_ic_done.png ├── drawable-xxhdpi ├── ucrop_ic_angle.png └── ucrop_ic_done.png ├── drawable-xxxhdpi ├── ucrop_ic_angle.png └── ucrop_ic_done.png ├── drawable ├── ucrop_crop.xml ├── ucrop_ic_crop.xml ├── ucrop_ic_crop_unselected.xml ├── ucrop_ic_cross.xml ├── ucrop_ic_next.xml ├── ucrop_ic_reset.xml ├── ucrop_ic_rotate.xml ├── ucrop_ic_rotate_unselected.xml ├── ucrop_ic_scale.xml ├── ucrop_ic_scale_unselected.xml ├── ucrop_rotate.xml ├── ucrop_scale.xml ├── ucrop_shadow_upside.xml ├── ucrop_vector_ic_crop.xml ├── ucrop_vector_loader.xml ├── ucrop_vector_loader_animated.xml └── ucrop_wrapper_controls_shape.xml ├── layout ├── ucrop_activity_photobox.xml ├── ucrop_aspect_ratio.xml ├── ucrop_controls.xml ├── ucrop_fragment_photobox.xml ├── ucrop_layout_rotate_wheel.xml ├── ucrop_layout_scale_wheel.xml └── ucrop_view.xml ├── menu └── ucrop_menu_activity.xml ├── values-de └── strings.xml ├── values-es └── strings.xml ├── values-fa └── strings.xml ├── values-fi └── strings.xml ├── values-fr └── strings.xml ├── values-in └── strings.xml ├── values-it └── strings.xml ├── values-ja └── strings.xml ├── values-ko └── strings.xml ├── values-nl └── strings.xml ├── values-pt └── strings.xml ├── values-ru └── strings.xml ├── values-sk └── strings.xml ├── values-th └── strings.xml ├── values-tr └── strings.xml ├── values-zh-rTW └── strings.xml ├── values-zh └── strings.xml └── values ├── attrs.xml ├── colors.xml ├── dimens.xml ├── public.xml ├── strings.xml ├── styles.xml └── values.xml /.gitattributes: -------------------------------------------------------------------------------- 1 | ucrop/src/main/jni/CImg.h linguist-vendored 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Do you want to request a *feature* or report a *bug*?** 2 | 3 | **What is the current behavior?** 4 | 5 | **What is the expected behavior?** 6 | 7 | **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.** 8 | 9 | **Please attach any *image files*, *URL* and `stack trace` that can be used to reproduce the *bug*.** 10 | 11 | **Which versions of uCrop, and which Android API versions are affected by this issue? Did this work in previous versions of uCrop?** -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # svn 2 | *.svn* 3 | 4 | # built application files 5 | *.apk 6 | *.ap_ 7 | 8 | # files for the dex VM 9 | *.dex 10 | 11 | # Java class files 12 | *.class 13 | 14 | # generated GUI files 15 | */R.java 16 | 17 | # generated folder 18 | bin 19 | gen 20 | 21 | # local 22 | local.properties 23 | 24 | proguard_logs/ 25 | 26 | # log files 27 | log*.txt 28 | 29 | # archives 30 | *.gz 31 | *.tar 32 | *.zip 33 | 34 | # eclipse 35 | *.metadata 36 | *.settings 37 | *.prefs 38 | 39 | #idea 40 | *.idea 41 | *.iml 42 | out/ 43 | 44 | build/ 45 | captures/ 46 | .gradle/ 47 | 48 | .DS_Store 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uCrop - Image Cropping Library for Android 2 | 3 | #### This project aims to provide an ultimate and flexible image cropping experience. Made in [Yalantis](https://yalantis.com/?utm_source=github) 4 | 5 | #### [How We Created uCrop](https://yalantis.com/blog/how-we-created-ucrop-our-own-image-cropping-library-for-android/) 6 | #### Check this [project on Dribbble](https://dribbble.com/shots/2484752-uCrop-Image-Cropping-Library) 7 | 8 | 9 | 10 | # Usage 11 | 12 | *For a working implementation, please have a look at the Sample Project - sample* 13 | 14 | Get it on Google Play 15 | 16 | 1. Include the library as a local library project. 17 | 18 | ``` 19 | allprojects { 20 | repositories { 21 | jcenter() 22 | maven { url "https://jitpack.io" } 23 | } 24 | } 25 | ``` 26 | 27 | ``` implementation 'com.github.yalantis:ucrop:2.2.10' ``` - lightweight general solution 28 | 29 | ``` implementation 'com.github.yalantis:ucrop:2.2.9-native' ``` - get power of the native code to preserve image quality (+ about 1.5 MB to an apk size) 30 | 31 | 2. Add UCropActivity into your AndroidManifest.xml 32 | 33 | ``` 34 | 38 | ``` 39 | 40 | 3. The uCrop configuration is created using the builder pattern. 41 | 42 | ```java 43 | UCrop.of(sourceUri, destinationUri) 44 | .withAspectRatio(16, 9) 45 | .withMaxResultSize(maxWidth, maxHeight) 46 | .start(context); 47 | ``` 48 | 49 | 4. Override `onActivityResult` method and handle uCrop result. 50 | 51 | ```java 52 | @Override 53 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 54 | if (resultCode == RESULT_OK && requestCode == UCrop.REQUEST_CROP) { 55 | final Uri resultUri = UCrop.getOutput(data); 56 | } else if (resultCode == UCrop.RESULT_ERROR) { 57 | final Throwable cropError = UCrop.getError(data); 58 | } 59 | } 60 | ``` 61 | 62 | 5. You may want to add this to your PROGUARD config: 63 | 64 | ``` 65 | -dontwarn com.yalantis.ucrop** 66 | -keep class com.yalantis.ucrop** { *; } 67 | -keep interface com.yalantis.ucrop** { *; } 68 | ``` 69 | 70 | # Customization 71 | 72 | If you want to let your users choose crop ratio dynamically, just do not call `withAspectRatio(x, y)`. 73 | 74 | uCrop builder class has method `withOptions(UCrop.Options options)` which extends library configurations. 75 | 76 | Currently, you can change: 77 | 78 | * image compression format (e.g. PNG, JPEG), compression 79 | * image compression quality [0 - 100]. PNG which is lossless, will ignore the quality setting. 80 | * whether all gestures are enabled simultaneously 81 | * maximum size for Bitmap that is decoded from source Uri and used within crop view. If you want to override the default behaviour. 82 | * toggle whether to show crop frame/guidelines 83 | * setup color/width/count of crop frame/rows/columns 84 | * choose whether you want rectangle or oval(`options.setCircleDimmedLayer(true)`) crop area 85 | * the UI colors (Toolbar, StatusBar, active widget state) 86 | * and more... 87 | 88 | # Compatibility 89 | 90 | * Library - Android ICS 4.0+ (API 14) (Android GINGERBREAD 2.3+ (API 10) for versions <= 1.3.2) 91 | * Sample - Android ICS 4.0+ (API 14) 92 | * CPU - armeabi armeabi-v7a x86 x86_64 arm64-v8a (for versions >= 2.1.2) 93 | 94 | # Changelog 95 | 96 | ### Version: 2.2.10 97 | 98 | * Fixed [#926](https://github.com/Yalantis/uCrop/issues/926) 99 | 100 | ### Version: 2.2.9 101 | 102 | * Update compileSdk and targetSdk versions up to 33 103 | * Fixed [#867](https://github.com/Yalantis/uCrop/issues/867) 104 | * Fixed [#873](https://github.com/Yalantis/uCrop/issues/873) 105 | * And other improvements 106 | 107 | ### Version: 2.2.8 108 | 109 | * Merged pending pull requests with improvements and bugfixes 110 | * Update compileSdk and targetSdk versions up to 31 111 | * Add localizations 112 | * Fixed [#609](https://github.com/Yalantis/uCrop/issues/609) 113 | * Fixed [#794](https://github.com/Yalantis/uCrop/issues/794) 114 | 115 | 116 | ### Version: 2.2.5 117 | 118 | * Fixed [#584](https://github.com/Yalantis/uCrop/issues/584) 119 | * Fixed [#598](https://github.com/Yalantis/uCrop/issues/598) 120 | * Fixed [#543](https://github.com/Yalantis/uCrop/issues/543) 121 | * Fixed [#602](https://github.com/Yalantis/uCrop/issues/602) 122 | * And other improvements 123 | 124 | ### Version: 2.2.4 125 | 126 | * **AndroidX migration** 127 | * Redesign 128 | * Several fixes including [#550](https://github.com/Yalantis/uCrop/issues/550) 129 | 130 | ### Version: 2.2.3 131 | 132 | * Several fixes including [#445](https://github.com/Yalantis/uCrop/issues/445), [#465](https://github.com/Yalantis/uCrop/issues/465) and more! 133 | * Material design support 134 | * uCrop fragment as child fragment 135 | * Added the Italian language 136 | 137 | ### Version: 2.2.2 138 | 139 | * uCrop fragment added 140 | * bugfix 141 | 142 | ### Version: 2.2.1 143 | 144 | * Fix including [#285](https://github.com/Yalantis/uCrop/issues/285) 145 | 146 | ### Version: 2.2 147 | 148 | * Several fixes including [#121](https://github.com/Yalantis/uCrop/issues/121), [#173](https://github.com/Yalantis/uCrop/issues/173), [#184](https://github.com/Yalantis/uCrop/issues/184) and more! 149 | * New APIs introduced [#149](https://github.com/Yalantis/uCrop/issues/149), [#186](https://github.com/Yalantis/uCrop/issues/186) and [#156](https://github.com/Yalantis/uCrop/issues/156) 150 | 151 | ### Version: 2.1 152 | 153 | * 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) 154 | * Added API to set custom set of aspect ratio options for the user. [#131](https://github.com/Yalantis/uCrop/issues/131) 155 | * Added API to set all configs via UCrop.Options class. [#126](https://github.com/Yalantis/uCrop/issues/126) 156 | * Added ABI x86_64 support. [#105](https://github.com/Yalantis/uCrop/issues/105) 157 | 158 | ### Version: 2.0 159 | 160 | * Native image crop (able to crop high-resolution images, e.g. 16MP & 32MP images on Nexus 5X). 161 | * WebP compression format is not supported at the moment (choose JPEG or PNG). 162 | * Now library copies EXIF data to cropped image (size and orientation are updated). 163 | 164 | ### Version: 1.5 165 | 166 | * Introduced "Freestyle" crop (you can resize crop rectangle by dragging it corners) [#32](https://github.com/Yalantis/uCrop/issues/32) 167 | * Now image & crop view paddings are not associated [#68](https://github.com/Yalantis/uCrop/issues/68) 168 | * Updated API 169 | 170 | ### Version: 1.4 171 | 172 | * Introduced HTTP(s) Uri support! 173 | * Image is cropped in a background thread. 174 | * Showing loader while Bitmap is processed (both loading and cropping). 175 | * Several bug fixes. 176 | * Couple new things to configure. 177 | * Updated minSdkVersion to Android ICS 4.0 (no reason to support couple percents of old phones). 178 | 179 | ### Version: 1.3 180 | 181 | * Image is loaded in a background thread. Better error-handling for image decoding. 182 | * Improved EXIF data support (rotation and mirror). 183 | * Small UI updates. 184 | * Couple new things to configure. 185 | 186 | * Sample updated with the possibility to choose custom aspect ratio. 187 | 188 | ### Version: 1.2 189 | 190 | * Updated core logic so an image corrects its position smoothly and obviously. 191 | 192 | ### Version: 1.1 193 | 194 | * UCrop builder was updated and now UCrop.Options class has even more values to setup. 195 | 196 | ### Version: 1.0 197 | 198 | * Initial Build 199 | 200 | ### Let us know! 201 | 202 | 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. 203 | 204 | #### Apps using uCrop 205 | 206 | - [Thirty](https://play.google.com/store/apps/details?id=com.twominds.thirty). 207 | - [Light Smart HD](https://play.google.com/store/apps/details?id=com.SmartCamera.simple). 208 | - [BCReader](https://play.google.com/store/apps/details?id=com.iac.bcreader). 209 | - [Xprezia: Share Your Passion](https://play.google.com/store/apps/details?id=com.xprezzia.cnj). 210 | 211 | ## License 212 | 213 | Copyright 2017, Yalantis 214 | 215 | Software doesn't collect, store or transfer data to Yalantis or third parties. 216 | Emplacement of this Software is carried out locally at device. 217 | 218 | Licensed under the Apache License, Version 2.0 (the "License"); 219 | you may not use this file except in compliance with the License. 220 | You may obtain a copy of the License at 221 | 222 | http://www.apache.org/licenses/LICENSE-2.0 223 | 224 | Unless required by applicable law or agreed to in writing, software 225 | distributed under the License is distributed on an "AS IS" BASIS, 226 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 227 | See the License for the specific language governing permissions and 228 | limitations under the License. 229 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | androidx_appcompat_version = "1.6.1" 4 | androidx_core_version = "1.9.0" 5 | androidx_exifinterface_version = "1.3.6" 6 | androidx_transition_version = "1.4.1" 7 | constraintlayout_version = "2.1.4" 8 | } 9 | 10 | repositories { 11 | mavenCentral() 12 | maven { 13 | url 'https://maven.google.com/' 14 | name 'Google' 15 | } 16 | } 17 | dependencies { 18 | classpath 'com.android.tools.build:gradle:7.4.2' 19 | } 20 | } 21 | 22 | def isReleaseBuild() { 23 | return version.contains("SNAPSHOT") == false 24 | } 25 | 26 | allprojects { 27 | version = VERSION_NAME 28 | group = GROUP 29 | 30 | repositories { 31 | mavenCentral() 32 | maven { 33 | url 'https://maven.google.com/' 34 | name 'Google' 35 | } 36 | } 37 | } 38 | 39 | task clean(type: Delete) { 40 | delete rootProject.buildDir 41 | } 42 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | VERSION_NAME=2.2.9-native 21 | VERSION_CODE=28 22 | GROUP=com.yalantis 23 | 24 | POM_DESCRIPTION=Android Library for cropping images 25 | POM_URL=https://github.com/Yalantis/uCrop 26 | POM_SCM_URL=https://github.com/Yalantis/uCrop 27 | POM_SCM_CONNECTION=scm:git@github.com/Yalantis/uCrop.git 28 | POM_SCM_DEV_CONNECTION=scm:git@github.com/Yalantis/uCrop.git 29 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 30 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0 31 | POM_LICENCE_DIST=repo 32 | POM_DEVELOPER_ID=yalantis 33 | POM_DEVELOPER_NAME=Yalantis 34 | android.useAndroidX=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yalantis/uCrop/016672137e413a9462313b5d73ca6f736b8ad99f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Sep 06 16:37:21 EEST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | # configuration file for building snapshots and releases with jitpack.io 2 | jdk: 3 | - openjdk11 4 | before_install: 5 | - sdk install java 11.0.10-open 6 | - sdk use java 11.0.10-open -------------------------------------------------------------------------------- /mavenpush.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven-publish' 2 | apply plugin: 'signing' 3 | 4 | def sonatypeRepositoryUrl 5 | if (isReleaseBuild()) { 6 | println 'RELEASE BUILD' 7 | sonatypeRepositoryUrl = hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL 8 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 9 | } else { 10 | println 'DEBUG BUILD' 11 | sonatypeRepositoryUrl = hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL 12 | : "https://oss.sonatype.org/content/repositories/snapshots/" 13 | } 14 | 15 | def getRepositoryUsername() { 16 | return hasProperty('nexusUsername') ? nexusUsername : "" 17 | } 18 | 19 | def getRepositoryPassword() { 20 | return hasProperty('nexusPassword') ? nexusPassword : "" 21 | } 22 | 23 | 24 | tasks.register('androidJavadocs', Javadoc) { 25 | source = android.sourceSets.main.java.sourceFiles 26 | } 27 | 28 | tasks.register('androidJavadocsJar', Jar) { 29 | classifier = 'javadoc' 30 | //basename = artifact_id 31 | from androidJavadocs.destinationDir 32 | } 33 | 34 | tasks.register('androidSourcesJar', Jar) { 35 | classifier = 'sources' 36 | //basename = artifact_id 37 | from android.sourceSets.main.java.sourceFiles 38 | } 39 | 40 | publishing { 41 | repositories { 42 | maven { 43 | url = sonatypeRepositoryUrl 44 | credentials { 45 | username = getRepositoryUsername() 46 | password = getRepositoryPassword() 47 | } 48 | } 49 | } 50 | publications { 51 | maven(MavenPublication) { 52 | afterEvaluate { project -> 53 | from components.release 54 | artifact androidSourcesJar 55 | artifact androidJavadocsJar 56 | version = project.version 57 | } 58 | 59 | pom { 60 | name = POM_NAME 61 | packaging = POM_PACKAGING 62 | description = POM_DESCRIPTION 63 | url = POM_URL 64 | scm { 65 | url = POM_SCM_URL 66 | connection = POM_SCM_CONNECTION 67 | developerConnection = POM_SCM_DEV_CONNECTION 68 | } 69 | 70 | licenses { 71 | license { 72 | name = POM_LICENCE_NAME 73 | url = POM_LICENCE_URL 74 | distribution = POM_LICENCE_DIST 75 | } 76 | } 77 | 78 | developers { 79 | developer { 80 | id = POM_DEVELOPER_ID 81 | name = POM_DEVELOPER_NAME 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | 90 | signing { 91 | required { isReleaseBuild() && gradle.taskGraph.hasTask("publishing") } 92 | sign publishing.publications.maven 93 | sign configurations.archives 94 | } 95 | 96 | -------------------------------------------------------------------------------- /preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yalantis/uCrop/016672137e413a9462313b5d73ca6f736b8ad99f/preview.gif -------------------------------------------------------------------------------- /preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yalantis/uCrop/016672137e413a9462313b5d73ca6f736b8ad99f/preview.png -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdk 33 5 | defaultConfig { 6 | applicationId "com.yalantis.ucrop.sample" 7 | minSdkVersion 14 8 | targetSdkVersion 33 9 | versionCode 13 10 | versionName "1.2.4" 11 | } 12 | flavorDimensions "default" 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | compileOptions { 20 | sourceCompatibility JavaVersion.VERSION_1_8 21 | targetCompatibility JavaVersion.VERSION_1_8 22 | } 23 | lintOptions { 24 | abortOnError false 25 | } 26 | productFlavors { 27 | activity { 28 | buildConfigField("int","RequestMode", "1") 29 | } 30 | fragment { 31 | buildConfigField("int","RequestMode", "2") 32 | } 33 | } 34 | } 35 | 36 | dependencies { 37 | implementation "androidx.appcompat:appcompat:${androidx_appcompat_version}" 38 | implementation "androidx.core:core:${androidx_core_version}" 39 | implementation "androidx.constraintlayout:constraintlayout:${constraintlayout_version}" 40 | implementation "com.squareup.okhttp3:okhttp:3.12.13" 41 | implementation project(':ucrop') 42 | } -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/oleksii/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 16 | 21 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /sample/src/main/java/com/yalantis/ucrop/sample/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.yalantis.ucrop.sample; 2 | 3 | import android.content.DialogInterface; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.annotation.Nullable; 7 | import androidx.appcompat.app.AlertDialog; 8 | import androidx.appcompat.app.AppCompatActivity; 9 | import androidx.core.app.ActivityCompat; 10 | 11 | /** 12 | * Created by Oleksii Shliama (https://github.com/shliama). 13 | */ 14 | public class BaseActivity extends AppCompatActivity { 15 | 16 | protected static final int REQUEST_STORAGE_READ_ACCESS_PERMISSION = 101; 17 | protected static final int REQUEST_STORAGE_WRITE_ACCESS_PERMISSION = 102; 18 | 19 | private AlertDialog mAlertDialog; 20 | 21 | /** 22 | * Hide alert dialog if any. 23 | */ 24 | @Override 25 | protected void onStop() { 26 | super.onStop(); 27 | if (mAlertDialog != null && mAlertDialog.isShowing()) { 28 | mAlertDialog.dismiss(); 29 | } 30 | } 31 | 32 | 33 | /** 34 | * Requests given permission. 35 | * If the permission has been denied previously, a Dialog will prompt the user to grant the 36 | * permission, otherwise it is requested directly. 37 | */ 38 | protected void requestPermission(final String permission, String rationale, final int requestCode) { 39 | if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { 40 | showAlertDialog(getString(R.string.permission_title_rationale), rationale, 41 | new DialogInterface.OnClickListener() { 42 | @Override 43 | public void onClick(DialogInterface dialog, int which) { 44 | ActivityCompat.requestPermissions(BaseActivity.this, 45 | new String[]{permission}, requestCode); 46 | } 47 | }, getString(R.string.label_ok), null, getString(R.string.label_cancel)); 48 | } else { 49 | ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode); 50 | } 51 | } 52 | 53 | /** 54 | * This method shows dialog with given title & message. 55 | * Also there is an option to pass onClickListener for positive & negative button. 56 | * 57 | * @param title - dialog title 58 | * @param message - dialog message 59 | * @param onPositiveButtonClickListener - listener for positive button 60 | * @param positiveText - positive button text 61 | * @param onNegativeButtonClickListener - listener for negative button 62 | * @param negativeText - negative button text 63 | */ 64 | protected void showAlertDialog(@Nullable String title, @Nullable String message, 65 | @Nullable DialogInterface.OnClickListener onPositiveButtonClickListener, 66 | @NonNull String positiveText, 67 | @Nullable DialogInterface.OnClickListener onNegativeButtonClickListener, 68 | @NonNull String negativeText) { 69 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 70 | builder.setTitle(title); 71 | builder.setMessage(message); 72 | builder.setPositiveButton(positiveText, onPositiveButtonClickListener); 73 | builder.setNegativeButton(negativeText, onNegativeButtonClickListener); 74 | mAlertDialog = builder.show(); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /sample/src/main/java/com/yalantis/ucrop/sample/ResultActivity.java: -------------------------------------------------------------------------------- 1 | package com.yalantis.ucrop.sample; 2 | 3 | import android.Manifest; 4 | import android.annotation.TargetApi; 5 | import android.app.NotificationChannel; 6 | import android.app.NotificationManager; 7 | import android.app.PendingIntent; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.content.pm.PackageManager; 11 | import android.content.pm.ResolveInfo; 12 | import android.graphics.BitmapFactory; 13 | import android.graphics.Color; 14 | import android.net.Uri; 15 | import android.os.Build; 16 | import android.os.Bundle; 17 | import android.os.Environment; 18 | import android.util.Log; 19 | import android.view.Menu; 20 | import android.view.MenuItem; 21 | import android.widget.Toast; 22 | 23 | import com.yalantis.ucrop.view.UCropView; 24 | 25 | import java.io.File; 26 | import java.io.FileInputStream; 27 | import java.io.FileOutputStream; 28 | import java.nio.channels.FileChannel; 29 | import java.util.Calendar; 30 | import java.util.List; 31 | 32 | import androidx.annotation.NonNull; 33 | import androidx.appcompat.app.ActionBar; 34 | import androidx.appcompat.widget.Toolbar; 35 | import androidx.core.app.ActivityCompat; 36 | import androidx.core.app.NotificationCompat; 37 | import androidx.core.content.FileProvider; 38 | 39 | import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; 40 | import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION; 41 | 42 | /** 43 | * Created by Oleksii Shliama (https://github.com/shliama). 44 | */ 45 | public class ResultActivity extends BaseActivity { 46 | 47 | private static final String TAG = "ResultActivity"; 48 | private static final String CHANNEL_ID = "3000"; 49 | private static final int DOWNLOAD_NOTIFICATION_ID_DONE = 911; 50 | 51 | public static void startWithUri(@NonNull Context context, @NonNull Uri uri) { 52 | Intent intent = new Intent(context, ResultActivity.class); 53 | intent.setData(uri); 54 | context.startActivity(intent); 55 | } 56 | 57 | @Override 58 | protected void onCreate(Bundle savedInstanceState) { 59 | super.onCreate(savedInstanceState); 60 | setContentView(R.layout.activity_result); 61 | Uri uri = getIntent().getData(); 62 | if (uri != null) { 63 | try { 64 | UCropView uCropView = findViewById(R.id.ucrop); 65 | uCropView.getCropImageView().setImageUri(uri, null); 66 | uCropView.getOverlayView().setShowCropFrame(false); 67 | uCropView.getOverlayView().setShowCropGrid(false); 68 | uCropView.getOverlayView().setDimmedColor(Color.TRANSPARENT); 69 | } catch (Exception e) { 70 | Log.e(TAG, "setImageUri", e); 71 | Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show(); 72 | } 73 | } 74 | final BitmapFactory.Options options = new BitmapFactory.Options(); 75 | options.inJustDecodeBounds = true; 76 | BitmapFactory.decodeFile(new File(getIntent().getData().getPath()).getAbsolutePath(), options); 77 | 78 | setSupportActionBar((Toolbar) findViewById(R.id.toolbar)); 79 | final ActionBar actionBar = getSupportActionBar(); 80 | if (actionBar != null) { 81 | actionBar.setDisplayHomeAsUpEnabled(true); 82 | actionBar.setTitle(getString(R.string.format_crop_result_d_d, options.outWidth, options.outHeight)); 83 | } 84 | } 85 | 86 | @Override 87 | public boolean onCreateOptionsMenu(final Menu menu) { 88 | getMenuInflater().inflate(R.menu.menu_result, menu); 89 | return true; 90 | } 91 | 92 | @Override 93 | public boolean onOptionsItemSelected(MenuItem item) { 94 | if (item.getItemId() == R.id.menu_download) { 95 | saveCroppedImage(); 96 | } else if (item.getItemId() == android.R.id.home) { 97 | onBackPressed(); 98 | } 99 | return super.onOptionsItemSelected(item); 100 | } 101 | 102 | 103 | /** 104 | * Callback received when a permissions request has been completed. 105 | */ 106 | @Override 107 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 108 | switch (requestCode) { 109 | case REQUEST_STORAGE_WRITE_ACCESS_PERMISSION: 110 | if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { 111 | saveCroppedImage(); 112 | } 113 | break; 114 | default: 115 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 116 | } 117 | } 118 | 119 | private void saveCroppedImage() { 120 | if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) 121 | != PackageManager.PERMISSION_GRANTED) { 122 | requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, 123 | getString(R.string.permission_write_storage_rationale), 124 | REQUEST_STORAGE_WRITE_ACCESS_PERMISSION); 125 | } else { 126 | Uri imageUri = getIntent().getData(); 127 | if (imageUri != null && imageUri.getScheme().equals("file")) { 128 | try { 129 | copyFileToDownloads(getIntent().getData()); 130 | } catch (Exception e) { 131 | Toast.makeText(ResultActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); 132 | Log.e(TAG, imageUri.toString(), e); 133 | } 134 | } else { 135 | Toast.makeText(ResultActivity.this, getString(R.string.toast_unexpected_error), Toast.LENGTH_SHORT).show(); 136 | } 137 | } 138 | } 139 | 140 | private void copyFileToDownloads(Uri croppedFileUri) throws Exception { 141 | String downloadsDirectoryPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); 142 | String filename = String.format("%d_%s", Calendar.getInstance().getTimeInMillis(), croppedFileUri.getLastPathSegment()); 143 | 144 | File saveFile = new File(downloadsDirectoryPath, filename); 145 | 146 | FileInputStream inStream = new FileInputStream(new File(croppedFileUri.getPath())); 147 | FileOutputStream outStream = new FileOutputStream(saveFile); 148 | FileChannel inChannel = inStream.getChannel(); 149 | FileChannel outChannel = outStream.getChannel(); 150 | inChannel.transferTo(0, inChannel.size(), outChannel); 151 | inStream.close(); 152 | outStream.close(); 153 | 154 | showNotification(saveFile); 155 | Toast.makeText(this, R.string.notification_image_saved, Toast.LENGTH_SHORT).show(); 156 | finish(); 157 | } 158 | 159 | private void showNotification(@NonNull File file) { 160 | Intent intent = new Intent(Intent.ACTION_VIEW); 161 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 162 | Uri fileUri = FileProvider.getUriForFile( 163 | this, 164 | getString(R.string.file_provider_authorities), 165 | file); 166 | 167 | intent.setDataAndType(fileUri, "image/*"); 168 | 169 | List resInfoList = getPackageManager().queryIntentActivities( 170 | intent, 171 | PackageManager.MATCH_DEFAULT_ONLY); 172 | for (ResolveInfo info : resInfoList) { 173 | grantUriPermission( 174 | info.activityInfo.packageName, 175 | fileUri, FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_READ_URI_PERMISSION); 176 | } 177 | 178 | NotificationCompat.Builder notificationBuilder; 179 | NotificationManager notificationManager = (NotificationManager) this 180 | .getSystemService(Context.NOTIFICATION_SERVICE); 181 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 182 | if (notificationManager != null) { 183 | notificationManager.createNotificationChannel(createChannel()); 184 | } 185 | notificationBuilder = new NotificationCompat.Builder(this, CHANNEL_ID); 186 | } else { 187 | notificationBuilder = new NotificationCompat.Builder(this); 188 | } 189 | 190 | notificationBuilder 191 | .setContentTitle(getString(R.string.app_name)) 192 | .setContentText(getString(R.string.notification_image_saved_click_to_preview)) 193 | .setTicker(getString(R.string.notification_image_saved)) 194 | .setSmallIcon(R.drawable.ic_done) 195 | .setOngoing(false) 196 | .setContentIntent(PendingIntent.getActivity(this, 0, intent, 0)) 197 | .setAutoCancel(true); 198 | if (notificationManager != null) { 199 | notificationManager.notify(DOWNLOAD_NOTIFICATION_ID_DONE, notificationBuilder.build()); 200 | } 201 | } 202 | 203 | @TargetApi(Build.VERSION_CODES.O) 204 | public NotificationChannel createChannel() { 205 | int importance = NotificationManager.IMPORTANCE_LOW; 206 | NotificationChannel channel = new NotificationChannel(CHANNEL_ID, getString(R.string.channel_name), importance); 207 | channel.setDescription(getString(R.string.channel_description)); 208 | channel.enableLights(true); 209 | channel.setLightColor(Color.YELLOW); 210 | return channel; 211 | } 212 | 213 | } 214 | -------------------------------------------------------------------------------- /sample/src/main/java/com/yalantis/ucrop/sample/SampleApp.java: -------------------------------------------------------------------------------- 1 | package com.yalantis.ucrop.sample; 2 | 3 | import android.app.Application; 4 | 5 | import com.yalantis.ucrop.UCropHttpClientStore; 6 | 7 | import java.util.Collections; 8 | 9 | import okhttp3.ConnectionSpec; 10 | import okhttp3.OkHttpClient; 11 | 12 | public class SampleApp extends Application { 13 | 14 | @Override 15 | public void onCreate() { 16 | super.onCreate(); 17 | setUcropHttpClient(); 18 | } 19 | 20 | private void setUcropHttpClient() { 21 | ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) 22 | .allEnabledCipherSuites() 23 | .allEnabledTlsVersions() 24 | .build(); 25 | 26 | OkHttpClient client = new OkHttpClient.Builder() 27 | .connectionSpecs(Collections.singletonList(cs)) 28 | .build(); 29 | 30 | UCropHttpClientStore.INSTANCE.setClient(client); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/bg_rounded_rectangle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_done.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_file_download.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_result.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_sample.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 22 | 23 | 24 | 25 | 33 | 34 | 37 | 38 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/include_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 27 | 28 | 37 | 38 |