Skip to content

Commit

Permalink
Merge pull request #185 from CanHub/always_return_uri
Browse files Browse the repository at this point in the history
Crop always return uri (Gradle 7.0, Java 11, Android 12)
  • Loading branch information
Canato authored Sep 1, 2021
2 parents cf46f2c + 5e8f32b commit 72078ed
Show file tree
Hide file tree
Showing 17 changed files with 827 additions and 611 deletions.
6 changes: 6 additions & 0 deletions .idea/kotlinc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- `Fixed` for any bug fixes.
- `Security` in case of vulnerabilities.

## [unreleased x.x.x] -
## [3.3.0] -
### Changed
- Update to Android 12
- Update library to gradle 7.0.1 and Java 11 [#191](https://github.com/CanHub/Android-Image-Cropper/issues/191)
- Any crop action should return uri content [#180](https://github.com/CanHub/Android-Image-Cropper/issues/180)

### Fixed
- Implement onBackPressed() in sample code for handle backButton pressed [#174](https://github.com/CanHub/Android-Image-Cropper/issues/174)

## [3.2.2] - 31/07/21
### Fixed
- After cropping a camera image, cancelling library picker shows again the last cropped image [#162](https://github.com/CanHub/Android-Image-Cropper/issues/162)
- implement onBackPressed() in sample code for handle backButton pressed [#174](https://github.com/CanHub/Android-Image-Cropper/issues/174)

## [3.2.1] - 14/07/21
### Fixed
Expand Down
290 changes: 290 additions & 0 deletions build.gradle

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions cropper/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'kotlin-parcelize'

group='com.github.Canato'
Expand All @@ -15,8 +14,8 @@ android {
versionName rootProject.libVersion
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
lintOptions {
abortOnError false
Expand All @@ -25,7 +24,7 @@ android {
viewBinding true
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = JavaVersion.VERSION_11.toString()
}
testOptions {
unitTests {
Expand All @@ -35,7 +34,7 @@ android {
}

dependencies {
api "androidx.appcompat:appcompat:$androidXAppCompatVersionCropper"
implementation "androidx.appcompat:appcompat:$androidXAppCompatVersion"

implementation "androidx.activity:activity-ktx:$androidXActivity"

Expand Down
30 changes: 29 additions & 1 deletion cropper/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,34 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/library_file_paths" />
</provider>
<activity android:name=".CropImageActivity" />
<activity
android:name=".CropImageActivity"
android:exported="true" />

<!-- This is here because the library did not update yet to Android 12 so we force the exported value when merging manifests -->
<activity
android:name="androidx.test.core.app.InstrumentationActivityInvoker$BootstrapActivity"
android:exported="true"
android:theme="@android:style/Theme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity"
android:exported="true"
android:theme="@android:style/Theme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity
android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyFloatingActivity"
android:exported="true"
android:theme="@android:style/Theme.Dialog">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
</application>
</manifest>
37 changes: 16 additions & 21 deletions cropper/src/main/java/com/canhub/cropper/BitmapCroppingWorkerJob.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ class BitmapCroppingWorkerJob(
private val flipHorizontally: Boolean,
private val flipVertically: Boolean,
private val options: CropImageView.RequestSizeOptions,
private val saveUri: Uri?,
private val saveCompressFormat: Bitmap.CompressFormat? = Bitmap.CompressFormat.JPEG,
private val saveCompressFormat: Bitmap.CompressFormat,
private val saveCompressQuality: Int
) : CoroutineScope {

Expand Down Expand Up @@ -81,28 +80,24 @@ class BitmapCroppingWorkerJob(
val resizedBitmap =
BitmapUtils.resizeBitmap(bitmapSampled.bitmap, reqWidth, reqHeight, options)

if (saveUri == null)
onPostExecute(Result(resizedBitmap, bitmapSampled.sampleSize))
else
launch(Dispatchers.IO) {
BitmapUtils.writeBitmapToUri(
context,
resizedBitmap,
saveUri,
saveCompressFormat ?: Bitmap.CompressFormat.JPEG,
saveCompressQuality
)
resizedBitmap.recycle()
onPostExecute(
Result(
saveUri,
bitmapSampled.sampleSize
)
launch(Dispatchers.IO) {
val newUri = BitmapUtils.writeBitmapToUri(
context,
resizedBitmap,
saveCompressFormat,
saveCompressQuality
)
resizedBitmap.recycle()
onPostExecute(
Result(
newUri, // saveUri,
bitmapSampled.sampleSize
)
}
)
}
}
} catch (e: Exception) {
onPostExecute(Result(e, saveUri != null))
onPostExecute(Result(e, false))
}
}
}
Expand Down
90 changes: 47 additions & 43 deletions cropper/src/main/java/com/canhub/cropper/BitmapUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.RectF
import android.net.Uri
import android.os.Environment
import android.util.Log
import android.util.Pair
import androidx.core.content.FileProvider
import androidx.exifinterface.media.ExifInterface
import com.canhub.cropper.CropImageView.RequestSizeOptions
import com.canhub.cropper.common.CommonValues
import com.canhub.cropper.common.CommonVersionCheck.isAtLeastQ29
import com.canhub.cropper.utils.getUriForFile
import java.io.Closeable
import java.io.File
import java.io.FileNotFoundException
Expand All @@ -41,8 +41,8 @@ internal object BitmapUtils {

val EMPTY_RECT = Rect()
val EMPTY_RECT_F = RectF()

private const val IMAGE_MAX_BITMAP_DIMENSION = 2048
private const val WRITE_AND_TRUNCATE = "wt"

/**
* Reusable rectangle for general internal usage
Expand Down Expand Up @@ -391,34 +391,12 @@ internal object BitmapUtils {
* Write given bitmap to a temp file. If file already exists no-op as we already saved the file in
* this session. Uses JPEG 95% compression.
*
* @param uri the uri to write the bitmap to, if null
* @return the uri where the image was saved in, either the given uri or new pointing to temp
* file.
*/
fun writeTempStateStoreBitmap(context: Context, bitmap: Bitmap?, uri: Uri?): Uri? {
var tempUri = uri
return try {
var needSave = true
if (tempUri == null) {
// We have this because of a HUAWEI path bug when we use getUriForFile
tempUri = if (isAtLeastQ29()) {
FileProvider.getUriForFile(
context,
context.packageName + CommonValues.authority,
File.createTempFile("aic_state_store_temp", ".jpg", context.cacheDir)
)
} else {
Uri.fromFile(
File.createTempFile("aic_state_store_temp", ".jpg", context.cacheDir)
)
}
} else if (tempUri.path?.let { File(it).exists() } == true) {
needSave = false
}
if (needSave) {
writeBitmapToUri(context, bitmap!!, tempUri, CompressFormat.JPEG, 95)
}
tempUri
fun writeTempStateStoreBitmap(context: Context, bitmap: Bitmap?): Uri? =
try {
writeBitmapToUri(context, bitmap!!, CompressFormat.JPEG, 95)
} catch (e: Exception) {
Log.w(
"AIC",
Expand All @@ -427,7 +405,6 @@ internal object BitmapUtils {
)
null
}
}

/**
* Write the given bitmap to the given uri using the given compression.
Expand All @@ -436,20 +413,50 @@ internal object BitmapUtils {
fun writeBitmapToUri(
context: Context,
bitmap: Bitmap,
uri: Uri?,
compressFormat: CompressFormat?,
compressFormat: CompressFormat,
compressQuality: Int
) {
): Uri? {
val newUri = buildUri(context, compressFormat)
var outputStream: OutputStream? = null
try {
outputStream = context.contentResolver.openOutputStream(uri!!)
outputStream = context.contentResolver.openOutputStream(newUri!!, WRITE_AND_TRUNCATE)

bitmap.compress(compressFormat ?: CompressFormat.JPEG, compressQuality, outputStream)
bitmap.compress(compressFormat, compressQuality, outputStream)
} finally {
closeSafe(outputStream)
}
return newUri
}

private fun buildUri(
context: Context,
compressFormat: CompressFormat
): Uri? =
try {
val ext = when (compressFormat) {
CompressFormat.JPEG -> ".jpg"
CompressFormat.PNG -> ".png"
else -> ".webp"
}
// We have this because of a HUAWEI path bug when we use getUriForFile
if (isAtLeastQ29()) {
try {
val file = File.createTempFile(
"cropped",
ext,
context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
)
getUriForFile(context, file)
} catch (e: Exception) {
Log.e("AIC", "${e.message}")
val file = File.createTempFile("cropped", ext, context.cacheDir)
getUriForFile(context, file)
}
} else Uri.fromFile(File.createTempFile("cropped", ext, context.cacheDir))
} catch (e: IOException) {
throw RuntimeException("Failed to create temp file for output image", e)
}

/**
* Resize the given bitmap to the given width/height by the given option.<br></br>
*/
Expand Down Expand Up @@ -703,8 +710,6 @@ internal object BitmapUtils {
reqHeight: Int,
sampleMulti: Int
): BitmapSampled {
var stream: InputStream? = null
var decoder: BitmapRegionDecoder? = null
try {
val options = BitmapFactory.Options()
options.inSampleSize = (
Expand All @@ -713,23 +718,23 @@ internal object BitmapUtils {
rect.width(), rect.height(), reqWidth, reqHeight
)
)
stream = context.contentResolver.openInputStream(uri)
decoder = BitmapRegionDecoder.newInstance(stream, false)
val stream = context.contentResolver.openInputStream(uri)
val decoder = BitmapRegionDecoder.newInstance(stream!!, false)
do {
try {
return BitmapSampled(decoder.decodeRegion(rect, options), options.inSampleSize)
return BitmapSampled(decoder!!.decodeRegion(rect, options), options.inSampleSize)
} catch (e: OutOfMemoryError) {
options.inSampleSize *= 2
}
} while (options.inSampleSize <= 512)

closeSafe(stream)
decoder?.recycle()
} catch (e: Exception) {
throw RuntimeException(
"Failed to load sampled bitmap: $uri\r\n${e.message}",
e
)
} finally {
closeSafe(stream)
decoder?.recycle()
}
return BitmapSampled(null, 1)
}
Expand Down Expand Up @@ -878,7 +883,6 @@ internal object BitmapUtils {
private val maxTextureSize: Int
get() {
// Safe minimum default size

return try {
// Get EGL Display
val egl = EGLContext.getEGL() as EGL10
Expand Down
Loading

0 comments on commit 72078ed

Please sign in to comment.