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
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
6 changes: 3 additions & 3 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 @@ -10,7 +10,7 @@ mokoMvvmVersion = "0.16.0"
mokoPermissionsVersion = "0.15.0"
mokoTestVersion = "0.6.1"
mokoMediaVersion = "0.11.0"
composeJetBrainsVersion = "1.3.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 @@ -132,7 +132,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
8 changes: 5 additions & 3 deletions media/src/iosMain/kotlin/dev/icerock/moko/media/Bitmap.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package dev.icerock.moko.media

import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.get
import kotlinx.cinterop.reinterpret
import kotlinx.cinterop.useContents
Expand All @@ -18,6 +19,7 @@ import platform.UIKit.UIGraphicsGetImageFromCurrentImageContext
import platform.UIKit.UIImage
import platform.UIKit.UIImageJPEGRepresentation

@OptIn(ExperimentalForeignApi::class)
actual class Bitmap(val image: UIImage) {

actual fun toByteArray(): ByteArray {
Expand All @@ -34,7 +36,7 @@ actual class Bitmap(val image: UIImage) {
val imageData = UIImageJPEGRepresentation(image, COMPRESSION_QUALITY)
?: throw IllegalArgumentException("image data is null")

return imageData.base64EncodedStringWithOptions(0)
return imageData.base64EncodedStringWithOptions(0u)
}

actual fun toBase64WithCompress(maxSize: Int): String {
Expand All @@ -49,12 +51,12 @@ actual class Bitmap(val image: UIImage) {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(newWidth, newHeight), false, 0.0)
image.drawInRect(CGRectMake(0.0, 0.0, newWidth, newHeight))
val newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
UIGraphicsEndImageContext()

val imageData = UIImageJPEGRepresentation(newImage!!, COMPRESSION_QUALITY)
?: throw IllegalArgumentException("image data is null")

return imageData.base64EncodedStringWithOptions(0)
return imageData.base64EncodedStringWithOptions(0u)
}

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

import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.get
import kotlinx.cinterop.reinterpret
import platform.Foundation.NSData
Expand All @@ -18,6 +19,7 @@ fun FileMedia.toNSData(): NSData {
return NSData.dataWithContentsOfURL(url) ?: throw IllegalArgumentException("invalid file data")
}

@OptIn(ExperimentalForeignApi::class)
actual fun FileMedia.toByteArray(): ByteArray {
val data = toNSData()
val bytes = data.bytes ?: throw IllegalArgumentException("file bytes is null")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import dev.icerock.moko.media.Bitmap
import dev.icerock.moko.media.Media
import dev.icerock.moko.media.MediaType
import dev.icerock.moko.media.picker.ios.MediaPickerController
import kotlinx.cinterop.ExperimentalForeignApi
import platform.AVFoundation.AVAsset
import platform.AVFoundation.AVAssetImageGenerator
import platform.AVFoundation.AVURLAsset
Expand All @@ -26,6 +27,7 @@ import platform.darwin.NSObject
import kotlin.coroutines.Continuation
import kotlin.random.Random

@OptIn(ExperimentalForeignApi::class)
internal class ImagePickerDelegateToContinuation constructor(
private val continuation: Continuation<Media>
) : NSObject(), UINavigationControllerDelegateProtocol, UIImagePickerControllerDelegateProtocol {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import dev.icerock.moko.media.picker.ImagePickerDelegateToContinuation
import dev.icerock.moko.media.picker.MediaSource
import dev.icerock.moko.permissions.Permission
import dev.icerock.moko.permissions.PermissionsController
import kotlinx.cinterop.ExperimentalForeignApi
import platform.CoreFoundation.CFTypeRef
import platform.CoreServices.kUTTypeData
import platform.CoreServices.kUTTypeImage
import platform.CoreServices.kUTTypeMovie
Expand All @@ -27,8 +29,10 @@ import platform.UIKit.UIImagePickerControllerSourceType
import platform.UIKit.UIViewController
import platform.UIKit.presentationController
import kotlin.coroutines.suspendCoroutine
import kotlin.experimental.ExperimentalNativeApi
import kotlin.native.ref.WeakReference

@OptIn(ExperimentalNativeApi::class)
class MediaPickerController(
override val permissionsController: PermissionsController
) : MediaPickerControllerProtocol {
Expand Down Expand Up @@ -155,10 +159,11 @@ class MediaPickerController(
return media
}

@OptIn(ExperimentalForeignApi::class)
internal companion object {
val kVideoType = CFBridgingRelease(kUTTypeVideo) as String
val kMovieType = CFBridgingRelease(kUTTypeMovie) as String
val kImageType = CFBridgingRelease(kUTTypeImage) as String
val kStandardFileTypesId = CFBridgingRelease(kUTTypeData) as String
val kVideoType = CFBridgingRelease(kUTTypeVideo as CFTypeRef?) as String
val kMovieType = CFBridgingRelease(kUTTypeMovie as CFTypeRef?) as String
val kImageType = CFBridgingRelease(kUTTypeImage as CFTypeRef?) as String
val kStandardFileTypesId = CFBridgingRelease(kUTTypeData as CFTypeRef?) as String
}
}
Loading
Loading