Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/compilation-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
java-version: 17
- name: Build and test
run: ./gradlew build publishToMavenLocal syncMultiPlatformLibraryDebugFrameworkIosX64
- name: Install pods
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11
java-version: 17
- name: Publish
run: ./gradlew publish

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ project build.gradle

```groovy
dependencies {
commonMainApi("dev.icerock.moko:media:0.11.0")
commonMainApi("dev.icerock.moko:media:0.11.1")

// Compose Multiplatform
commonMainApi("dev.icerock.moko:media-compose:0.11.0")
commonMainApi("dev.icerock.moko:media-compose:0.11.1")

commonTestImplementation("dev.icerock.moko:media-test:0.11.0")
commonTestImplementation("dev.icerock.moko:media-test:0.11.1")
}
```

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ moko.publish.description=Media selection & presenting for mobile (android & ios)
moko.publish.repo.org=icerockdev
moko.publish.repo.name=moko-media
moko.publish.license=Apache-2.0
moko.publish.developers=alex009|Aleksey Mikhailov|Aleksey.Mikhailov@icerockdev.com,Tetraquark|Vladislav Areshkin|vareshkin@icerockdev.com,RezMike|Mikhail Reznichenko|mreznichenko@icerockdev.com
moko.publish.developers=alex009|Aleksey Mikhailov|Aleksey.Mikhailov@icerockdev.com,Tetraquark|Vladislav Areshkin|vareshkin@icerockdev.com,RezMike|Mikhail Reznichenko|mreznichenko@icerockdev.com
8 changes: 4 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
kotlinVersion = "1.8.10"
kotlinVersion = "1.9.10"
androidAppCompatVersion = "1.6.1"
materialDesignVersion = "1.8.0"
androidLifecycleVersion = "2.3.1"
Expand All @@ -9,8 +9,8 @@ coroutinesVersion = "1.6.4"
mokoMvvmVersion = "0.16.0"
mokoPermissionsVersion = "0.15.0"
mokoTestVersion = "0.6.1"
mokoMediaVersion = "0.11.0"
composeJetBrainsVersion = "1.3.1"
mokoMediaVersion = "0.11.1"
composeJetBrainsVersion = "1.7.1"

[libraries]
appCompat = { module = "androidx.appcompat:appcompat", version.ref = "androidAppCompatVersion" }
Expand All @@ -32,7 +32,7 @@ mokoPermissionsTest = { module = "dev.icerock.moko:permissions-test", version.re

kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinVersion" }
androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7.4.2" }
mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "0.2.0" }
mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "0.4.2" }
mobileMultiplatformGradlePlugin = { module = "dev.icerock:mobile-multiplatform", version = "0.14.2" }
composeJetBrainsGradlePlugin = { module = "org.jetbrains.compose:compose-gradle-plugin", version.ref = "composeJetBrainsVersion" }
detektGradlePlugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version = "1.22.0" }
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
4 changes: 4 additions & 0 deletions media-test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ plugins {
id("dev.icerock.moko.gradle.detekt")
}

android {
namespace = "dev.icerock.moko.media.test"
}

dependencies {
commonMainApi(projects.media)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
package dev.icerock.moko.media.test

import dev.icerock.moko.media.Bitmap
import kotlinx.cinterop.ExperimentalForeignApi
import platform.CoreGraphics.CGRectMake
import platform.CoreImage.CIImage
import platform.UIKit.UIImage

@OptIn(ExperimentalForeignApi::class)
actual fun createBitmapMock(): Bitmap {
val image = CIImage.clearImage.imageByCroppingToRect(CGRectMake(0.0, 0.0, 1.0, 1.0))
return Bitmap(UIImage(image))
Expand Down
4 changes: 4 additions & 0 deletions media/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ plugins {
id("dev.icerock.moko.gradle.detekt")
}

android {
namespace = "dev.icerock.moko.media"
}

dependencies {
commonMainImplementation(libs.coroutines)
commonMainApi(libs.mokoPermissions)
Expand Down
38 changes: 8 additions & 30 deletions media/src/androidMain/kotlin/dev/icerock/moko/media/BitmapUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,16 @@ object BitmapUtils {
fun getNormalizedBitmap(
bitmapStream: InputStream,
orientation: Int,
sampleSize: Int? = null
sampleSize: Int? = null,
): Bitmap {
val bitmapOptions = if (sampleSize != null) {
BitmapFactory.Options().apply {
inJustDecodeBounds = false
inSampleSize = sampleSize
}
} else null
} else {
null
}

val bitmap = BitmapFactory.decodeStream(bitmapStream, null, bitmapOptions)
?: throw IOException("Can't decode bitmap stream")
Expand All @@ -64,28 +66,19 @@ object BitmapUtils {
matrix.setRotate(90f)
matrix.postScale(-1f, 1f)
}

ExifInterface.ORIENTATION_TRANSVERSE -> {
matrix.setRotate(-90f)
matrix.postScale(-1f, 1f)
}

else -> return bitmap
}
val result = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
bitmap.recycle()
return result
}

@Suppress("ForbiddenComment")
// TODO: unused
fun cloneRotated(bitmap: Bitmap, angle: Int): Bitmap {
if (angle != 0) {
val matrix = Matrix()
matrix.postRotate(angle.toFloat())
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}
return bitmap
}

fun getResizedBitmap(bitmap: Bitmap, maxSize: Int = DEFAULT_MAX_SIZE): Bitmap {
var width = bitmap.width
var height = bitmap.height
Expand All @@ -102,39 +95,24 @@ object BitmapUtils {
}

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

@Suppress("ForbiddenComment")
// TODO: unused
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
maxHeight: Int,
): Int {
val height: Int = options.outHeight
val width: Int = options.outWidth
var inSampleSize = 1

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

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class FilePickerFragment : Fragment() {
.start()
}

@Suppress("UnreachableCode")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package dev.icerock.moko.media.picker

import android.app.Activity
import android.content.Intent
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.os.Environment
Expand Down Expand Up @@ -46,7 +47,7 @@ class ImagePickerFragment : Fragment() {
photoFilePath = savedInstanceState?.getString(PHOTO_FILE_PATH_KEY)
}

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

codeCallbackMap[requestCode] =
Expand All @@ -61,7 +62,7 @@ class ImagePickerFragment : Fragment() {
startActivityForResult(intent, requestCode)
}

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

val outputUri = createPhotoUri()
Expand Down Expand Up @@ -92,7 +93,9 @@ class ImagePickerFragment : Fragment() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

val callbackData = codeCallbackMap[requestCode] ?: return
val callbackData: CallbackData? = codeCallbackMap[requestCode]
if (callbackData == null) return

codeCallbackMap.remove(requestCode)

val callback = callbackData.callback
Expand All @@ -111,6 +114,7 @@ class ImagePickerFragment : Fragment() {
callback.invoke(Result.failure(IllegalArgumentException(data?.toString())))
}
}

is CallbackData.Camera -> {
processResult(callback, callbackData.outputUri)
}
Expand All @@ -119,7 +123,7 @@ class ImagePickerFragment : Fragment() {

@Suppress("ReturnCount")
private fun processResult(
callback: (Result<android.graphics.Bitmap>) -> Unit,
callback: (Result<Bitmap>) -> Unit,
uri: Uri
) {
val contentResolver = requireContext().contentResolver
Expand Down Expand Up @@ -151,12 +155,12 @@ class ImagePickerFragment : Fragment() {
callback.invoke(Result.success(bitmap))
}

sealed class CallbackData(val callback: (Result<android.graphics.Bitmap>) -> Unit) {
class Gallery(callback: (Result<android.graphics.Bitmap>) -> Unit) :
sealed class CallbackData(val callback: (Result<Bitmap>) -> Unit) {
class Gallery(callback: (Result<Bitmap>) -> Unit) :
CallbackData(callback)

class Camera(
callback: (Result<android.graphics.Bitmap>) -> Unit,
callback: (Result<Bitmap>) -> Unit,
val outputUri: Uri
) : CallbackData(callback)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ actual interface MediaPickerController {
companion object {
operator fun invoke(
permissionsController: PermissionsController,
pickerFragmentTag: String = "MediaControllerPicker",
mediaPickerFragmentTag: String = "MediaControllerMediaPicker",
imagePickerFragmentTag: String = "MediaControllerImagePicker",
filePickerFragmentTag: String = "FileMediaControllerPicker"
): MediaPickerController {
return MediaPickerControllerImpl(
permissionsController = permissionsController,
pickerFragmentTag = pickerFragmentTag,
mediaPickerFragmentTag = mediaPickerFragmentTag,
imagePickerFragmentTag = imagePickerFragmentTag,
filePickerFragmentTag = filePickerFragmentTag
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import kotlin.coroutines.suspendCoroutine

internal class MediaPickerControllerImpl(
override val permissionsController: PermissionsController,
private val pickerFragmentTag: String,
private val mediaPickerFragmentTag: String,
private val imagePickerFragmentTag: String,
private val filePickerFragmentTag: String
) : MediaPickerController {
var fragmentManager: FragmentManager? = null
Expand Down Expand Up @@ -57,14 +58,14 @@ internal class MediaPickerControllerImpl(
permissionsController.providePermission(permission)
}

val currentFragment: Fragment? = fragmentManager.findFragmentByTag(pickerFragmentTag)
val currentFragment: Fragment? = fragmentManager.findFragmentByTag(imagePickerFragmentTag)
val imagePickerFragment: ImagePickerFragment = if (currentFragment != null) {
currentFragment as ImagePickerFragment
} else {
ImagePickerFragment.newInstance(maxWidth, maxHeight).also {
fragmentManager
.beginTransaction()
.add(it, pickerFragmentTag)
.add(it, imagePickerFragmentTag)
.commitNow()
}
}
Expand All @@ -86,14 +87,14 @@ internal class MediaPickerControllerImpl(

permissionsController.providePermission(Permission.GALLERY)

val currentFragment: Fragment? = fragmentManager.findFragmentByTag(pickerFragmentTag)
val currentFragment: Fragment? = fragmentManager.findFragmentByTag(mediaPickerFragmentTag)
val pickerFragment: MediaPickerFragment = if (currentFragment != null) {
currentFragment as MediaPickerFragment
} else {
MediaPickerFragment().apply {
fragmentManager
.beginTransaction()
.add(this, pickerFragmentTag)
.add(this, mediaPickerFragmentTag)
.commitNow()
}
}
Expand All @@ -117,7 +118,7 @@ internal class MediaPickerControllerImpl(
FilePickerFragment().apply {
fragmentManager
.beginTransaction()
.add(this, pickerFragmentTag)
.add(this, filePickerFragmentTag)
.commitNow()
}
}
Expand All @@ -132,7 +133,7 @@ internal class MediaPickerControllerImpl(

private fun MediaSource.requiredPermissions(): List<Permission> {
return when (this) {
MediaSource.GALLERY -> listOf(Permission.GALLERY)
MediaSource.GALLERY -> listOf()
MediaSource.CAMERA -> listOf(Permission.CAMERA)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ class MediaPickerFragment : Fragment() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

val callbackData = codeCallbackMap[requestCode] ?: return
val callbackData: CallbackData? = codeCallbackMap[requestCode]
if (callbackData == null) return

codeCallbackMap.remove(requestCode)

val callback = callbackData.callback
Expand Down Expand Up @@ -81,7 +83,7 @@ class MediaPickerFragment : Fragment() {
return
}

val result = kotlin.runCatching {
val result = runCatching {
MediaFactory.create(context, intentData)
}
callback.invoke(result)
Expand Down
Loading
Loading