Repository: Yalantis/uCrop Branch: develop Commit: f788b534b48c Files: 125 Total size: 3.6 MB Directory structure: gitextract_w2fudyhs/ ├── .gitattributes ├── .github/ │ └── ISSUE_TEMPLATE.md ├── .gitignore ├── README.md ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── mavenpush.gradle ├── 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 │ ├── 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 └── res/ ├── anim/ │ ├── ucrop_loader_circle_path.xml │ └── ucrop_loader_circle_scale.xml ├── color/ │ └── ucrop_scale_text_view_selector.xml ├── 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/ │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── public.xml │ ├── strings.xml │ ├── styles.xml │ └── values.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-sk/ │ └── strings.xml ├── values-th/ │ └── strings.xml ├── values-tr/ │ └── strings.xml ├── values-zh/ │ └── strings.xml └── values-zh-rTW/ └── strings.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ ucrop/src/main/jni/CImg.h linguist-vendored ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ **Do you want to request a *feature* or report a *bug*?** **What is the current behavior?** **What is the expected behavior?** **If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.** **Please attach any *image files*, *URL* and `stack trace` that can be used to reproduce the *bug*.** **Which versions of uCrop, and which Android API versions are affected by this issue? Did this work in previous versions of uCrop?** ================================================ FILE: .gitignore ================================================ # svn *.svn* # built application files *.apk *.ap_ # files for the dex VM *.dex # Java class files *.class # generated GUI files */R.java # generated folder bin gen # local local.properties proguard_logs/ # log files log*.txt # archives *.gz *.tar *.zip # eclipse *.metadata *.settings *.prefs #idea *.idea *.iml out/ build/ captures/ .gradle/ .DS_Store ================================================ FILE: README.md ================================================ # uCrop - Image Cropping Library for Android #### This project aims to provide an ultimate and flexible image cropping experience. Made in [Yalantis](https://yalantis.com/?utm_source=github) #### [How We Created uCrop](https://yalantis.com/blog/how-we-created-ucrop-our-own-image-cropping-library-for-android/) #### Check this [project on Dribbble](https://dribbble.com/shots/2484752-uCrop-Image-Cropping-Library) # Usage *For a working implementation, please have a look at the Sample Project - sample* Get it on Google Play 1. Include the library as a local library project. ``` allprojects { repositories { maven { url "https://jitpack.io" } } } ``` ``` implementation 'com.github.yalantis:ucrop:2.2.11' ``` - lightweight general solution ``` implementation 'com.github.yalantis:ucrop:2.2.11-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 ``` ``` 3. Register a callback for the uCrop result. ```java private ActivityResultLauncher activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == RESULT_OK) { final Uri resultUri = UCrop.getOutput(result.getData()); } else if (result.getResultCode() == UCrop.RESULT_ERROR) { final Throwable cropError = UCrop.getError(result.getData()); } }); ``` 4. Create the uCrop configuration using the builder pattern. ```java UCrop.of(sourceUri, destinationUri) .withAspectRatio(16, 9) .withMaxResultSize(maxWidth, maxHeight) .start(context, activityResultLauncher); ``` 5. You may want to add this to your PROGUARD config: ``` -dontwarn com.yalantis.ucrop** -keep class com.yalantis.ucrop** { *; } -keep interface com.yalantis.ucrop** { *; } ``` # Customization If you want to let your users choose crop ratio dynamically, just do not call `withAspectRatio(x, y)`. uCrop builder class has method `withOptions(UCrop.Options options)` which extends library configurations. Currently, you can change: * image compression format (e.g. PNG, JPEG), 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 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(`options.setCircleDimmedLayer(true)`) crop area * the UI colors (Toolbar, StatusBar, active widget state) * and more... # Compatibility * Library - Android Lollipop 5.0+ (API 21) (Android ICS 4.0+ (API 14) for versions < 2.2.11) * Sample - Android Lollipop 5.0+ (API 21) * CPU - armeabi-v7a x86 x86_64 arm64-v8a (for versions >= 2.2.11) # Changelog ### Version: 2.2.11 * Updated compileSdk and targetSdk to 36 * Added support for 16 KB page sizes * Enabled edge-to-edge UI support * And other improvements ### Version: 2.2.10 * Fixed [#926](https://github.com/Yalantis/uCrop/issues/926) ### Version: 2.2.9 * Update compileSdk and targetSdk versions up to 33 * Fixed [#867](https://github.com/Yalantis/uCrop/issues/867) * Fixed [#873](https://github.com/Yalantis/uCrop/issues/873) * And other improvements ### 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.5 * Fixed [#584](https://github.com/Yalantis/uCrop/issues/584) * Fixed [#598](https://github.com/Yalantis/uCrop/issues/598) * Fixed [#543](https://github.com/Yalantis/uCrop/issues/543) * Fixed [#602](https://github.com/Yalantis/uCrop/issues/602) * And other improvements ### Version: 2.2.4 * **AndroidX migration** * Redesign * Several fixes including [#550](https://github.com/Yalantis/uCrop/issues/550) ### 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 * uCrop fragment added * bugfix ### Version: 2.2.1 * Fix including [#285](https://github.com/Yalantis/uCrop/issues/285) ### Version: 2.2 * 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! * 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) ### 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 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) ### Version: 2.0 * 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) * Now image & crop view paddings are not associated [#68](https://github.com/Yalantis/uCrop/issues/68) * Updated API ### Version: 1.4 * 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. * Updated minSdkVersion to Android ICS 4.0 (no reason to support couple percents of old phones). ### Version: 1.3 * 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 the possibility to choose custom aspect ratio. ### Version: 1.2 * Updated core logic so an image corrects its position smoothly and obviously. ### Version: 1.1 * UCrop builder was updated and now UCrop.Options class has even more values to setup. ### Version: 1.0 * Initial Build ### 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. #### Apps using uCrop - [Thirty](https://play.google.com/store/apps/details?id=com.twominds.thirty). - [Light Smart HD](https://play.google.com/store/apps/details?id=com.SmartCamera.simple). - [BCReader](https://play.google.com/store/apps/details?id=com.iac.bcreader). - [Xprezia: Share Your Passion](https://play.google.com/store/apps/details?id=com.xprezzia.cnj). ## License Copyright 2017, Yalantis Software doesn't collect, store or transfer data to Yalantis or third parties. Emplacement of this Software is carried out locally at device. 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. ================================================ FILE: build.gradle ================================================ buildscript { ext { androidx_activity_version = "1.10.1" androidx_appcompat_version = "1.7.1" androidx_core_version = "1.16.0" androidx_exifinterface_version = "1.4.1" androidx_transition_version = "1.6.0" constraintlayout_version = "2.2.1" okhttp_version = "5.1.0" } repositories { mavenCentral() maven { url 'https://maven.google.com/' name 'Google' } } dependencies { classpath 'com.android.tools.build:gradle:8.11.1' } } def isReleaseBuild() { return version.contains("SNAPSHOT") == false } allprojects { version = VERSION_NAME group = GROUP repositories { mavenCentral() maven { url 'https://maven.google.com/' name 'Google' } } } task clean(type: Delete) { delete rootProject.buildDir } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Mon Jul 21 21:21:40 EEST 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists ================================================ FILE: gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx10248m -XX:MaxPermSize=256m # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true VERSION_NAME=2.2.11-native VERSION_CODE=29 GROUP=com.yalantis POM_DESCRIPTION=Android Library for cropping images POM_URL=https://github.com/Yalantis/uCrop POM_SCM_URL=https://github.com/Yalantis/uCrop POM_SCM_CONNECTION=scm:git@github.com/Yalantis/uCrop.git POM_SCM_DEV_CONNECTION=scm:git@github.com/Yalantis/uCrop.git POM_LICENCE_NAME=The Apache Software License, Version 2.0 POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0 POM_LICENCE_DIST=repo POM_DEVELOPER_ID=yalantis POM_DEVELOPER_NAME=Yalantis android.useAndroidX=true ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # For Cygwin, ensure paths are in UNIX format before anything is touched. if $cygwin ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` fi # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >&- APP_HOME="`pwd -P`" cd "$SAVED" >&- CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: jitpack.yml ================================================ # configuration file for building snapshots and releases with jitpack.io jdk: - openjdk11 before_install: - sdk install java 11.0.10-open - sdk use java 11.0.10-open ================================================ FILE: mavenpush.gradle ================================================ apply plugin: 'maven-publish' apply plugin: 'signing' def sonatypeRepositoryUrl if (isReleaseBuild()) { println 'RELEASE BUILD' sonatypeRepositoryUrl = hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" } else { println 'DEBUG BUILD' sonatypeRepositoryUrl = hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL : "https://oss.sonatype.org/content/repositories/snapshots/" } def getRepositoryUsername() { return hasProperty('nexusUsername') ? nexusUsername : "" } def getRepositoryPassword() { return hasProperty('nexusPassword') ? nexusPassword : "" } tasks.register('androidJavadocs', Javadoc) { source = android.sourceSets.main.java.sourceFiles classpath += project.files(android.getBootClasspath()) } tasks.register('androidJavadocsJar', Jar) { archiveClassifier.set("javadoc") //basename = artifact_id from tasks.named('androidJavadocs').get().destinationDir } tasks.register('androidSourcesJar', Jar) { archiveClassifier.set("sources") //basename = artifact_id from android.sourceSets.main.java.sourceFiles } publishing { repositories { maven { url = sonatypeRepositoryUrl credentials { username = getRepositoryUsername() password = getRepositoryPassword() } } } publications { maven(MavenPublication) { afterEvaluate { project -> def releaseComponent = components.findByName('release') if (releaseComponent) { from releaseComponent } artifact tasks.named("bundleReleaseAar") artifact tasks.named('androidSourcesJar').get() artifact tasks.named('androidJavadocsJar').get() version = project.version } pom { name = POM_NAME packaging = POM_PACKAGING description = POM_DESCRIPTION url = POM_URL scm { url = POM_SCM_URL connection = POM_SCM_CONNECTION developerConnection = POM_SCM_DEV_CONNECTION } licenses { license { name = POM_LICENCE_NAME url = POM_LICENCE_URL distribution = POM_LICENCE_DIST } } developers { developer { id = POM_DEVELOPER_ID name = POM_DEVELOPER_NAME } } } } } } signing { required { isReleaseBuild() && gradle.taskGraph.hasTask("publishing") } sign publishing.publications.maven } ================================================ FILE: sample/.gitignore ================================================ /build ================================================ FILE: sample/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdk 36 namespace = "com.yalantis.ucrop.sample" defaultConfig { applicationId "com.yalantis.ucrop.sample" minSdkVersion 21 targetSdkVersion 36 versionCode 13 versionName "1.2.4" } flavorDimensions "default" buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } buildFeatures { buildConfig true } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } productFlavors { activity { buildConfigField("int","RequestMode", "1") } fragment { buildConfigField("int","RequestMode", "2") } } lint { abortOnError false } } dependencies { implementation "androidx.activity:activity:${androidx_activity_version}" implementation "androidx.appcompat:appcompat:${androidx_appcompat_version}" implementation "androidx.core:core:${androidx_core_version}" implementation "androidx.constraintlayout:constraintlayout:${constraintlayout_version}" implementation "com.squareup.okhttp3:okhttp:${okhttp_version}" implementation project(':ucrop') } ================================================ FILE: sample/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/oleksii/Library/Android/sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: sample/src/main/AndroidManifest.xml ================================================ ================================================ FILE: sample/src/main/java/com/yalantis/ucrop/sample/BaseActivity.java ================================================ package com.yalantis.ucrop.sample; import android.content.DialogInterface; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; /** * Created by Oleksii Shliama (https://github.com/shliama). */ public class BaseActivity extends AppCompatActivity { protected static final int REQUEST_STORAGE_READ_ACCESS_PERMISSION = 101; protected static final int REQUEST_STORAGE_WRITE_ACCESS_PERMISSION = 102; private AlertDialog mAlertDialog; /** * Hide alert dialog if any. */ @Override protected void onStop() { super.onStop(); if (mAlertDialog != null && mAlertDialog.isShowing()) { mAlertDialog.dismiss(); } } /** * Requests given permission. * If the permission has been denied previously, a Dialog will prompt the user to grant the * permission, otherwise it is requested directly. */ protected void requestPermission(final String permission, String rationale, final int requestCode) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { showAlertDialog(getString(R.string.permission_title_rationale), rationale, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCompat.requestPermissions(BaseActivity.this, new String[]{permission}, requestCode); } }, getString(R.string.label_ok), null, getString(R.string.label_cancel)); } else { ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode); } } /** * This method shows dialog with given title & message. * Also there is an option to pass onClickListener for positive & negative button. * * @param title - dialog title * @param message - dialog message * @param onPositiveButtonClickListener - listener for positive button * @param positiveText - positive button text * @param onNegativeButtonClickListener - listener for negative button * @param negativeText - negative button text */ protected void showAlertDialog(@Nullable String title, @Nullable String message, @Nullable DialogInterface.OnClickListener onPositiveButtonClickListener, @NonNull String positiveText, @Nullable DialogInterface.OnClickListener onNegativeButtonClickListener, @NonNull String negativeText) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(title); builder.setMessage(message); builder.setPositiveButton(positiveText, onPositiveButtonClickListener); builder.setNegativeButton(negativeText, onNegativeButtonClickListener); mAlertDialog = builder.show(); } } ================================================ FILE: sample/src/main/java/com/yalantis/ucrop/sample/ResultActivity.java ================================================ package com.yalantis.ucrop.sample; import android.Manifest; import android.annotation.TargetApi; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.BitmapFactory; import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; import com.yalantis.ucrop.view.UCropView; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.channels.FileChannel; import java.util.Calendar; import java.util.List; import androidx.activity.EdgeToEdge; import androidx.activity.SystemBarStyle; 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 androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION; /** * Created by Oleksii Shliama (https://github.com/shliama). */ public class ResultActivity extends BaseActivity { private static final String TAG = "ResultActivity"; private static final String CHANNEL_ID = "3000"; private static final int DOWNLOAD_NOTIFICATION_ID_DONE = 911; public static void startWithUri(@NonNull Context context, @NonNull Uri uri) { Intent intent = new Intent(context, ResultActivity.class); intent.setData(uri); context.startActivity(intent); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this, SystemBarStyle.dark(Color.TRANSPARENT), SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT)); setContentView(R.layout.activity_result); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.toolbar), (view, windowInsets) -> { Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); view.setPaddingRelative(insets.left, insets.top, insets.right, 0); return windowInsets; }); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.ucrop), (view, windowInsets) -> { Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); view.setPaddingRelative(insets.left, 0, insets.right, insets.bottom); return windowInsets; }); Uri uri = getIntent().getData(); if (uri != null) { try { UCropView uCropView = findViewById(R.id.ucrop); uCropView.getCropImageView().setImageUri(uri, null); uCropView.getOverlayView().setShowCropFrame(false); uCropView.getOverlayView().setShowCropGrid(false); uCropView.getOverlayView().setDimmedColor(Color.TRANSPARENT); } 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; 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)); } } @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.menu_result, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.menu_download) { saveCroppedImage(); } else if (item.getItemId() == android.R.id.home) { onBackPressed(); } return super.onOptionsItemSelected(item); } /** * Callback received when a permissions request has been completed. */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_STORAGE_WRITE_ACCESS_PERMISSION: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { saveCroppedImage(); } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private void saveCroppedImage() { if (ActivityCompat.checkSelfPermission(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 { Uri imageUri = getIntent().getData(); if (imageUri != null && imageUri.getScheme().equals("file")) { try { copyFileToDownloads(getIntent().getData()); } catch (Exception e) { Toast.makeText(ResultActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); Log.e(TAG, imageUri.toString(), e); } } else { Toast.makeText(ResultActivity.this, getString(R.string.toast_unexpected_error), Toast.LENGTH_SHORT).show(); } } } private void copyFileToDownloads(Uri croppedFileUri) throws Exception { String downloadsDirectoryPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(); String filename = String.format("%d_%s", Calendar.getInstance().getTimeInMillis(), croppedFileUri.getLastPathSegment()); File saveFile = new File(downloadsDirectoryPath, filename); FileInputStream inStream = new FileInputStream(new File(croppedFileUri.getPath())); FileOutputStream outStream = new FileOutputStream(saveFile); FileChannel inChannel = inStream.getChannel(); FileChannel outChannel = outStream.getChannel(); inChannel.transferTo(0, inChannel.size(), outChannel); inStream.close(); outStream.close(); showNotification(saveFile); Toast.makeText(this, R.string.notification_image_saved, Toast.LENGTH_SHORT).show(); finish(); } private void showNotification(@NonNull File file) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); Uri fileUri = FileProvider.getUriForFile( this, getString(R.string.file_provider_authorities), file); intent.setDataAndType(fileUri, "image/*"); List resInfoList = getPackageManager().queryIntentActivities( intent, PackageManager.MATCH_DEFAULT_ONLY); for (ResolveInfo info : resInfoList) { grantUriPermission( info.activityInfo.packageName, fileUri, FLAG_GRANT_WRITE_URI_PERMISSION | FLAG_GRANT_READ_URI_PERMISSION); } NotificationCompat.Builder notificationBuilder; NotificationManager notificationManager = (NotificationManager) this .getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (notificationManager != null) { notificationManager.createNotificationChannel(createChannel()); } notificationBuilder = new NotificationCompat.Builder(this, CHANNEL_ID); } else { notificationBuilder = new NotificationCompat.Builder(this); } notificationBuilder .setContentTitle(getString(R.string.app_name)) .setContentText(getString(R.string.notification_image_saved_click_to_preview)) .setTicker(getString(R.string.notification_image_saved)) .setSmallIcon(R.drawable.ic_done) .setOngoing(false) .setContentIntent(PendingIntent.getActivity(this, 0, intent, 0)) .setAutoCancel(true); if (notificationManager != null) { notificationManager.notify(DOWNLOAD_NOTIFICATION_ID_DONE, notificationBuilder.build()); } } @TargetApi(Build.VERSION_CODES.O) public NotificationChannel createChannel() { int importance = NotificationManager.IMPORTANCE_LOW; NotificationChannel channel = new NotificationChannel(CHANNEL_ID, getString(R.string.channel_name), importance); channel.setDescription(getString(R.string.channel_description)); channel.enableLights(true); channel.setLightColor(Color.YELLOW); return channel; } } ================================================ FILE: sample/src/main/java/com/yalantis/ucrop/sample/SampleActivity.java ================================================ package com.yalantis.ucrop.sample; import android.annotation.TargetApi; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.CheckBox; import android.widget.EditText; import android.widget.RadioGroup; import android.widget.ScrollView; import android.widget.SeekBar; import android.widget.TextView; import android.widget.Toast; import androidx.activity.EdgeToEdge; import androidx.activity.SystemBarStyle; import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.widget.Toolbar; import androidx.core.content.ContextCompat; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; import com.yalantis.ucrop.UCrop; import com.yalantis.ucrop.UCropActivity; import com.yalantis.ucrop.UCropFragment; import com.yalantis.ucrop.UCropFragmentCallback; import java.io.File; import java.util.Locale; import java.util.Random; /** * Created by Oleksii Shliama (https://github.com/shliama). */ public class SampleActivity extends BaseActivity implements UCropFragmentCallback { private static final String TAG = "SampleActivity"; private static final int REQUEST_SELECT_PICTURE = 0x01; private static final int REQUEST_SELECT_PICTURE_FOR_FRAGMENT = 0x02; private static final String SAMPLE_CROPPED_IMAGE_NAME = "SampleCropImage"; private RadioGroup mRadioGroupAspectRatio, mRadioGroupCompressionSettings; private EditText mEditTextMaxWidth, mEditTextMaxHeight; private EditText mEditTextRatioX, mEditTextRatioY; private CheckBox mCheckBoxMaxSize; private SeekBar mSeekBarQuality; private TextView mTextViewQuality; private CheckBox mCheckBoxHideBottomControls; private CheckBox mCheckBoxFreeStyleCrop; private Toolbar toolbar; private ScrollView settingsView; private int requestMode = BuildConfig.RequestMode; private UCropFragment fragment; private boolean mShowLoader; private String mToolbarTitle; @DrawableRes private int mToolbarCancelDrawable; @DrawableRes private int mToolbarCropDrawable; // Enables dynamic coloring private int mToolbarColor; private int mToolbarWidgetColor; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EdgeToEdge.enable(this, SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT), SystemBarStyle.light(Color.TRANSPARENT, Color.TRANSPARENT)); setContentView(R.layout.activity_sample); ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.settings), (view, windowInsets) -> { Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); view.setPaddingRelative(insets.left, insets.top, insets.right, insets.bottom); return windowInsets; }); setupUI(); } @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(); if (selectedUri != null) { startCrop(selectedUri); } else { Toast.makeText(SampleActivity.this, R.string.toast_cannot_retrieve_selected_image, Toast.LENGTH_SHORT).show(); } } else if (requestCode == UCrop.REQUEST_CROP) { handleCropResult(data); } } if (resultCode == UCrop.RESULT_ERROR) { handleCropError(data); } } 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(); } } } }; @SuppressWarnings("ConstantConditions") private void setupUI() { findViewById(R.id.button_crop).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { pickFromGallery(); } }); findViewById(R.id.button_random_image).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Random random = new Random(); int minSizePixels = 800; int maxSizePixels = 2400; Uri uri = Uri.parse(String.format(Locale.getDefault(), "https://unsplash.it/%d/%d/?random", minSizePixels + random.nextInt(maxSizePixels - minSizePixels), minSizePixels + random.nextInt(maxSizePixels - minSizePixels))); startCrop(uri); } }); settingsView = findViewById(R.id.settings); mRadioGroupAspectRatio = findViewById(R.id.radio_group_aspect_ratio); mRadioGroupCompressionSettings = findViewById(R.id.radio_group_compression_settings); mCheckBoxMaxSize = findViewById(R.id.checkbox_max_size); mEditTextRatioX = findViewById(R.id.edit_text_ratio_x); mEditTextRatioY = findViewById(R.id.edit_text_ratio_y); mEditTextMaxWidth = findViewById(R.id.edit_text_max_width); mEditTextMaxHeight = findViewById(R.id.edit_text_max_height); mSeekBarQuality = findViewById(R.id.seekbar_quality); mTextViewQuality = findViewById(R.id.text_view_quality); mCheckBoxHideBottomControls = findViewById(R.id.checkbox_hide_bottom_controls); mCheckBoxFreeStyleCrop = findViewById(R.id.checkbox_freestyle_crop); mRadioGroupAspectRatio.check(R.id.radio_dynamic); mEditTextRatioX.addTextChangedListener(mAspectRatioTextWatcher); mEditTextRatioY.addTextChangedListener(mAspectRatioTextWatcher); mRadioGroupCompressionSettings.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { mSeekBarQuality.setEnabled(checkedId == R.id.radio_jpeg); } }); mRadioGroupCompressionSettings.check(R.id.radio_jpeg); mSeekBarQuality.setProgress(UCropActivity.DEFAULT_COMPRESS_QUALITY); mTextViewQuality.setText(String.format(getString(R.string.format_quality_d), mSeekBarQuality.getProgress())); mSeekBarQuality.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { mTextViewQuality.setText(String.format(getString(R.string.format_quality_d), progress)); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); mEditTextMaxHeight.addTextChangedListener(mMaxSizeTextWatcher); mEditTextMaxWidth.addTextChangedListener(mMaxSizeTextWatcher); } private void pickFromGallery() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT) .setType("image/*") .addCategory(Intent.CATEGORY_OPENABLE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { String[] mimeTypes = {"image/jpeg", "image/png"}; intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); } startActivityForResult(Intent.createChooser(intent, getString(R.string.label_select_picture)), requestMode); } private void startCrop(@NonNull Uri uri) { String destinationFileName = SAMPLE_CROPPED_IMAGE_NAME; int checkedId = mRadioGroupCompressionSettings.getCheckedRadioButtonId(); if (checkedId == R.id.radio_png) { destinationFileName += ".png"; } else if (checkedId == R.id.radio_jpeg) { destinationFileName += ".jpg"; } UCrop uCrop = UCrop.of(uri, Uri.fromFile(new File(getCacheDir(), destinationFileName))); uCrop = basisConfig(uCrop); uCrop = advancedConfig(uCrop); if (requestMode == REQUEST_SELECT_PICTURE_FOR_FRAGMENT) { //if build variant = fragment setupFragment(uCrop); } else { // else start uCrop Activity uCrop.start(SampleActivity.this); } } /** * In most cases you need only to set crop aspect ration and max size for resulting image. * * @param uCrop - ucrop builder instance * @return - ucrop builder instance */ private UCrop basisConfig(@NonNull UCrop uCrop) { int checkedId = mRadioGroupAspectRatio.getCheckedRadioButtonId(); if (checkedId == R.id.radio_origin) { uCrop = uCrop.useSourceImageAspectRatio(); } else if (checkedId == R.id.radio_square) { uCrop = uCrop.withAspectRatio(1, 1); } else if (checkedId == R.id.radio_dynamic) { // do nothing } else { try { float ratioX = Float.valueOf(mEditTextRatioX.getText().toString().trim()); float ratioY = Float.valueOf(mEditTextRatioY.getText().toString().trim()); if (ratioX > 0 && ratioY > 0) { uCrop = uCrop.withAspectRatio(ratioX, ratioY); } } catch (NumberFormatException e) { Log.i(TAG, String.format("Number please: %s", e.getMessage())); } } if (mCheckBoxMaxSize.isChecked()) { try { int maxWidth = Integer.valueOf(mEditTextMaxWidth.getText().toString().trim()); int maxHeight = Integer.valueOf(mEditTextMaxHeight.getText().toString().trim()); if (maxWidth > UCrop.MIN_SIZE && maxHeight > UCrop.MIN_SIZE) { uCrop = uCrop.withMaxResultSize(maxWidth, maxHeight); } } catch (NumberFormatException e) { Log.e(TAG, "Number please", e); } } return uCrop; } /** * Sometimes you want to adjust more options, it's done via {@link com.yalantis.ucrop.UCrop.Options} class. * * @param uCrop - ucrop builder instance * @return - ucrop builder instance */ private UCrop advancedConfig(@NonNull UCrop uCrop) { UCrop.Options options = new UCrop.Options(); int checkedId = mRadioGroupCompressionSettings.getCheckedRadioButtonId(); if (checkedId == R.id.radio_png) { options.setCompressionFormat(Bitmap.CompressFormat.PNG); } else { options.setCompressionFormat(Bitmap.CompressFormat.JPEG); } options.setCompressionQuality(mSeekBarQuality.getProgress()); options.setHideBottomControls(mCheckBoxHideBottomControls.isChecked()); options.setFreeStyleCropEnabled(mCheckBoxFreeStyleCrop.isChecked()); /* If you want to configure how gestures work for all UCropActivity tabs options.setAllowedGestures(UCropActivity.SCALE, UCropActivity.ROTATE, UCropActivity.ALL); * */ /* This sets max size for bitmap that will be decoded from source Uri. More size - more memory allocation, default implementation uses screen diagonal. options.setMaxBitmapSize(640); * */ /* Tune everything (ノ◕ヮ◕)ノ*:・゚✧ options.setMaxScaleMultiplier(5); options.setImageToCropBoundsAnimDuration(666); options.setDimmedLayerColor(Color.CYAN); options.setCircleDimmedLayer(true); options.setShowCropFrame(false); options.setCropGridStrokeWidth(20); options.setCropGridColor(Color.GREEN); options.setCropGridColumnCount(2); options.setCropGridRowCount(1); options.setToolbarCropDrawable(R.drawable.your_crop_icon); options.setToolbarCancelDrawable(R.drawable.your_cancel_icon); // System bars appearance options.setStatusBarLight(true); options.setNavigationBarLight(false); // Color palette options.setToolbarColor(ContextCompat.getColor(this, R.color.your_color_res)); options.setToolbarWidgetColor(ContextCompat.getColor(this, R.color.your_color_res)); options.setRootViewBackgroundColor(ContextCompat.getColor(this, R.color.your_color_res)); options.setActiveControlsWidgetColor(ContextCompat.getColor(this, R.color.your_color_res)); // Aspect ratio options 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(); */ return uCrop.withOptions(options); } private void handleCropResult(@NonNull Intent result) { final Uri resultUri = UCrop.getOutput(result); if (resultUri != null) { ResultActivity.startWithUri(SampleActivity.this, resultUri); } else { Toast.makeText(SampleActivity.this, R.string.toast_cannot_retrieve_cropped_image, Toast.LENGTH_SHORT).show(); } } @SuppressWarnings("ThrowableResultOfMethodCallIgnored") private void handleCropError(@NonNull Intent result) { final Throwable cropError = UCrop.getError(result); if (cropError != null) { Log.e(TAG, "handleCropError: ", cropError); Toast.makeText(SampleActivity.this, cropError.getMessage(), Toast.LENGTH_LONG).show(); } else { Toast.makeText(SampleActivity.this, R.string.toast_unexpected_error, Toast.LENGTH_SHORT).show(); } } @Override public void loadingProgress(boolean showLoader) { mShowLoader = showLoader; supportInvalidateOptionsMenu(); } @Override public void onCropFinish(UCropFragment.UCropResult result) { switch (result.mResultCode) { case RESULT_OK: handleCropResult(result.mResultData); break; case UCrop.RESULT_ERROR: handleCropError(result.mResultData); break; } removeFragmentFromScreen(); } public void removeFragmentFromScreen() { getSupportFragmentManager().beginTransaction() .remove(fragment) .commit(); toolbar.setVisibility(View.GONE); settingsView.setVisibility(View.VISIBLE); } public void setupFragment(UCrop uCrop) { fragment = uCrop.getFragment(uCrop.getIntent(this).getExtras()); getSupportFragmentManager().beginTransaction() .add(R.id.fragment_container, fragment, UCropFragment.TAG) .commitAllowingStateLoss(); setupViews(uCrop.getIntent(this).getExtras()); } public void setupViews(Bundle args) { settingsView.setVisibility(View.GONE); mToolbarColor = args.getInt(UCrop.Options.EXTRA_TOOL_BAR_COLOR, ContextCompat.getColor(this, com.yalantis.ucrop.R.color.ucrop_color_toolbar)); mToolbarCancelDrawable = args.getInt(UCrop.Options.EXTRA_UCROP_WIDGET_CANCEL_DRAWABLE, com.yalantis.ucrop.R.drawable.ucrop_ic_cross); mToolbarCropDrawable = args.getInt(UCrop.Options.EXTRA_UCROP_WIDGET_CROP_DRAWABLE, com.yalantis.ucrop.R.drawable.ucrop_ic_done); mToolbarWidgetColor = args.getInt(UCrop.Options.EXTRA_UCROP_WIDGET_COLOR_TOOLBAR, ContextCompat.getColor(this, com.yalantis.ucrop.R.color.ucrop_color_toolbar_widget)); mToolbarTitle = args.getString(UCrop.Options.EXTRA_UCROP_TITLE_TEXT_TOOLBAR); mToolbarTitle = mToolbarTitle != null ? mToolbarTitle : getResources().getString(com.yalantis.ucrop.R.string.ucrop_label_edit_photo); setupAppBar(); } /** * Configures and styles both status bar and toolbar. */ private void setupAppBar() { toolbar = findViewById(R.id.toolbar); // Set all of the Toolbar coloring toolbar.setBackgroundColor(mToolbarColor); toolbar.setTitleTextColor(mToolbarWidgetColor); toolbar.setVisibility(View.VISIBLE); final TextView toolbarTitle = toolbar.findViewById(R.id.toolbar_title); toolbarTitle.setTextColor(mToolbarWidgetColor); toolbarTitle.setText(mToolbarTitle); // Color buttons inside the Toolbar Drawable stateButtonDrawable = ContextCompat.getDrawable(getBaseContext(), mToolbarCancelDrawable); if (stateButtonDrawable != null) { stateButtonDrawable.mutate(); stateButtonDrawable.setColorFilter(mToolbarWidgetColor, PorterDuff.Mode.SRC_ATOP); toolbar.setNavigationIcon(stateButtonDrawable); } setSupportActionBar(toolbar); final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayShowTitleEnabled(false); } } /** * Sets status-bar color for L devices. * * @param color - status-bar color */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void setStatusBarColor(@ColorInt int color) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { final Window window = getWindow(); if (window != null) { window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(color); } } } @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(com.yalantis.ucrop.R.menu.ucrop_menu_activity, menu); // Change crop & loader menu icons color to match the rest of the UI colors MenuItem menuItemLoader = menu.findItem(com.yalantis.ucrop.R.id.menu_loader); Drawable menuItemLoaderIcon = menuItemLoader.getIcon(); if (menuItemLoaderIcon != null) { try { menuItemLoaderIcon.mutate(); menuItemLoaderIcon.setColorFilter(mToolbarWidgetColor, PorterDuff.Mode.SRC_ATOP); menuItemLoader.setIcon(menuItemLoaderIcon); } catch (IllegalStateException e) { Log.i(this.getClass().getName(), String.format("%s - %s", e.getMessage(), getString(com.yalantis.ucrop.R.string.ucrop_mutate_exception_hint))); } ((Animatable) menuItemLoader.getIcon()).start(); } MenuItem menuItemCrop = menu.findItem(com.yalantis.ucrop.R.id.menu_crop); Drawable menuItemCropIcon = ContextCompat.getDrawable(this, mToolbarCropDrawable == 0 ? com.yalantis.ucrop.R.drawable.ucrop_ic_done : mToolbarCropDrawable); if (menuItemCropIcon != null) { menuItemCropIcon.mutate(); menuItemCropIcon.setColorFilter(mToolbarWidgetColor, PorterDuff.Mode.SRC_ATOP); menuItemCrop.setIcon(menuItemCropIcon); } return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { menu.findItem(com.yalantis.ucrop.R.id.menu_crop).setVisible(!mShowLoader); menu.findItem(com.yalantis.ucrop.R.id.menu_loader).setVisible(mShowLoader); return super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == com.yalantis.ucrop.R.id.menu_crop) { if (fragment != null && fragment.isAdded()) fragment.cropAndSaveImage(); } else if (item.getItemId() == android.R.id.home) { removeFragmentFromScreen(); } return super.onOptionsItemSelected(item); } } ================================================ FILE: sample/src/main/java/com/yalantis/ucrop/sample/SampleApp.java ================================================ package com.yalantis.ucrop.sample; import android.app.Application; import com.yalantis.ucrop.UCropHttpClientStore; 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(); UCropHttpClientStore.INSTANCE.setClient(client); } } ================================================ FILE: sample/src/main/res/drawable/bg_rounded_rectangle.xml ================================================ ================================================ FILE: sample/src/main/res/drawable/ic_done.xml ================================================ ================================================ FILE: sample/src/main/res/drawable/ic_file_download.xml ================================================ ================================================ FILE: sample/src/main/res/layout/activity_result.xml ================================================ ================================================ FILE: sample/src/main/res/layout/activity_sample.xml ================================================ ================================================ FILE: sample/src/main/res/layout/include_settings.xml ================================================