Skip to content

Commit

Permalink
Merge pull request #11 from icerockdev/develop
Browse files Browse the repository at this point in the history
Release 0.4.2 fixed
  • Loading branch information
Alex009 authored Apr 2, 2020
2 parents 62394be + e3c95ee commit 977193f
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 8 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ TODO
- kotlin 1.3.70
- 0.4.0
- 0.4.1
- 0.4.2

## Installation
root build.gradle
Expand All @@ -45,7 +46,7 @@ allprojects {
project build.gradle
```groovy
dependencies {
commonMainApi("dev.icerock.moko:media:0.4.1")
commonMainApi("dev.icerock.moko:media:0.4.2")
}
```

Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ object Versions {
object MultiPlatform {
const val coroutines = "1.3.4"
const val mokoPermissions = "0.5.0"
const val mokoMedia = "0.4.1"
const val mokoMedia = "0.4.2"
}
}
}
44 changes: 44 additions & 0 deletions media/src/androidMain/kotlin/dev/icerock/moko/media/BitmapUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
package dev.icerock.moko.media

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Matrix
import android.media.ExifInterface
import java.io.IOException
import java.io.InputStream

object BitmapUtils {

Expand Down Expand Up @@ -49,5 +51,47 @@ object BitmapUtils {
return Bitmap.createScaledBitmap(bitmap, width, height, true)
}

fun getBitmapOptionsFromStream(
inputStream: InputStream
): BitmapFactory.Options {
return BitmapFactory.Options().apply {
inJustDecodeBounds = true
BitmapFactory.decodeStream(inputStream, null, this)
}
}

fun getBitmapForStream(
inputStream: InputStream,
sampleSize: Int
): Bitmap? {
val bitmapOptions = BitmapFactory.Options().apply {
inJustDecodeBounds = false
inSampleSize = sampleSize
}

return BitmapFactory.decodeStream(inputStream, null, bitmapOptions)
}

fun calculateInSampleSize(
options: BitmapFactory.Options,
maxWidth: Int,
maxHeight: Int
): Int {
val (height: Int, width: Int) = options.run { outHeight to outWidth }
var inSampleSize = 1

if (height > maxHeight || width > maxWidth) {

val halfHeight: Int = height / 2
val halfWidth: Int = width / 2

while (halfHeight / inSampleSize >= maxHeight && halfWidth / inSampleSize >= maxWidth) {
inSampleSize *= 2
}
}

return inSampleSize
}

private const val DEFAULT_MAX_SIZE = 500
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright 2020 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.media.picker

import java.io.IOException

class BitmapDecodeException(message: String? = null) : IOException(message)
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.app.Activity
import android.content.Intent
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import androidx.fragment.app.Fragment
Expand All @@ -19,6 +20,11 @@ import androidx.lifecycle.OnLifecycleEvent
import com.nbsp.materialfilepicker.ui.FilePickerActivity
import com.nbsp.materialfilepicker.ui.FilePickerActivity.ARG_CLOSEABLE
import dev.icerock.moko.media.*
import dev.icerock.moko.media.BitmapUtils.calculateInSampleSize
import dev.icerock.moko.media.BitmapUtils.getBitmapForStream
import dev.icerock.moko.media.BitmapUtils.getBitmapOptionsFromStream
import dev.icerock.moko.media.picker.MediaPickerController.PickerFragment.Companion.ARG_IMG_MAX_HEIGHT
import dev.icerock.moko.media.picker.MediaPickerController.PickerFragment.Companion.ARG_IMG_MAX_WIDTH
import dev.icerock.moko.permissions.Permission
import dev.icerock.moko.permissions.PermissionsController
import java.io.File
Expand Down Expand Up @@ -49,6 +55,15 @@ actual class MediaPickerController(
}

actual suspend fun pickImage(source: MediaSource): Bitmap {
return pickImage(source, DEFAULT_MAX_IMAGE_WIDTH, DEFAULT_MAX_IMAGE_HEIGHT)
}

/**
* A default values for [maxWidth] and [maxHeight] arguments are not used because bug of kotlin
* compiler. Default values for suspend functions don't work correctly.
* (Look here: https://youtrack.jetbrains.com/issue/KT-37331)
*/
actual suspend fun pickImage(source: MediaSource, maxWidth: Int, maxHeight: Int): Bitmap {
val fragmentManager =
fragmentManager ?: throw IllegalStateException("can't pick image without active window")

Expand All @@ -61,6 +76,10 @@ actual class MediaPickerController(
currentFragment as PickerFragment
} else {
PickerFragment().apply {
arguments = Bundle().apply {
putInt(ARG_IMG_MAX_WIDTH, maxWidth)
putInt(ARG_IMG_MAX_HEIGHT, maxHeight)
}
fragmentManager
.beginTransaction()
.add(this, pickerFragmentTag)
Expand Down Expand Up @@ -144,6 +163,17 @@ actual class MediaPickerController(

private val codeCallbackMap = mutableMapOf<Int, CallbackData>()

private var maxImageWidth = DEFAULT_MAX_IMAGE_WIDTH
private var maxImageHeight = DEFAULT_MAX_IMAGE_HEIGHT

override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
maxImageWidth = arguments?.getInt(ARG_IMG_MAX_WIDTH, DEFAULT_MAX_IMAGE_WIDTH)
?: DEFAULT_MAX_IMAGE_WIDTH
maxImageHeight = arguments?.getInt(ARG_IMG_MAX_HEIGHT, DEFAULT_MAX_IMAGE_HEIGHT)
?: DEFAULT_MAX_IMAGE_HEIGHT
}

fun pickGalleryImage(callback: (Result<android.graphics.Bitmap>) -> Unit) {
val requestCode = codeCallbackMap.keys.sorted().lastOrNull() ?: 0

Expand Down Expand Up @@ -218,13 +248,32 @@ actual class MediaPickerController(
}

val contentResolver = requireContext().contentResolver
val inputStream = contentResolver.openInputStream(uri)
var inputStream = contentResolver.openInputStream(uri)
if (inputStream == null) {
callback.invoke(Result.failure(NoAccessToFileException(uri.toString())))
return
}
val bitmap = BitmapFactory.decodeStream(inputStream)
callback.invoke(Result.success(bitmap))

val bitmapOptions = getBitmapOptionsFromStream(inputStream)
inputStream.close()

inputStream = contentResolver.openInputStream(uri)
if (inputStream == null) {
callback.invoke(Result.failure(NoAccessToFileException(uri.toString())))
return
}

val sampleSize = calculateInSampleSize(bitmapOptions, maxImageWidth, maxImageHeight)
val bitmap = getBitmapForStream(inputStream, sampleSize)
inputStream.close()

if (bitmap != null) {
callback.invoke(Result.success(bitmap))
} else {
callback.invoke(
Result.failure(BitmapDecodeException("The image data could not be decoded."))
)
}
}

private fun processCameraResult(
Expand Down Expand Up @@ -260,8 +309,11 @@ actual class MediaPickerController(
) : CallbackData(callback)
}

private companion object {
const val DEFAULT_FILE_NAME = "image.png"
companion object {
private const val DEFAULT_FILE_NAME = "image.png"

internal const val ARG_IMG_MAX_WIDTH = "args_img_max_width"
internal const val ARG_IMG_MAX_HEIGHT = "args_img_max_height"
}
}

Expand Down Expand Up @@ -396,4 +448,4 @@ actual class MediaPickerController(

class CallbackData(val callback: (Result<FileMedia>) -> Unit)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import dev.icerock.moko.media.Bitmap
import dev.icerock.moko.media.FileMedia
import dev.icerock.moko.media.Media

internal const val DEFAULT_MAX_IMAGE_WIDTH = 1024
internal const val DEFAULT_MAX_IMAGE_HEIGHT = 1024

expect class MediaPickerController {
suspend fun pickImage(source: MediaSource): Bitmap
suspend fun pickImage(source: MediaSource, maxWidth: Int, maxHeight: Int): Bitmap
suspend fun pickMedia(): Media
suspend fun pickFiles(): FileMedia
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ actual class MediaPickerController(
)

actual suspend fun pickImage(source: MediaSource): Bitmap {
return pickImage(source, DEFAULT_MAX_IMAGE_WIDTH, DEFAULT_MAX_IMAGE_HEIGHT)
}

actual suspend fun pickImage(source: MediaSource, maxWidth: Int, maxHeight: Int): Bitmap {
source.requiredPermissions().forEach { permission ->
permissionsController.providePermission(permission)
}
Expand Down

0 comments on commit 977193f

Please sign in to comment.