Skip to content

Commit

Permalink
Merge pull request #65 from icerockdev/#44-bind-for-ios
Browse files Browse the repository at this point in the history
#44 bind for ios
  • Loading branch information
Alex009 authored Apr 11, 2023
2 parents 634942b + ced5cfa commit 802cb7c
Show file tree
Hide file tree
Showing 24 changed files with 326 additions and 79 deletions.
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ buildscript {
classpath(libs.androidGradlePlugin)
classpath(libs.mokoGradlePlugin)
classpath(libs.mobileMultiplatformGradlePlugin)
classpath(libs.composeJetBrainsGradlePlugin)
classpath(libs.detektGradlePlugin)
}
}

Expand Down
7 changes: 5 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ org.gradle.configureondemand=false
org.gradle.parallel=true

kotlin.code.style=official
kotlin.mpp.androidSourceSetLayoutVersion=2

org.jetbrains.compose.experimental.uikit.enabled=true

android.useAndroidX=true

moko.android.targetSdk=31
moko.android.compileSdk=31
moko.android.targetSdk=33
moko.android.compileSdk=33
moko.android.minSdk=19

moko.publish.name=MOKO media
Expand Down
29 changes: 18 additions & 11 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
[versions]
kotlinVersion = "1.6.21"
androidAppCompatVersion = "1.3.1"
materialDesignVersion = "1.4.0"
kotlinVersion = "1.8.10"
androidAppCompatVersion = "1.6.1"
materialDesignVersion = "1.8.0"
androidLifecycleVersion = "2.3.1"
androidExifInterface = "1.3.2"
androidExifInterface = "1.3.6"
androidMediaFilePicker = "1.9.1"
coroutinesVersion = "1.6.0-native-mt"
mokoMvvmVersion = "0.13.0"
mokoPermissionsVersion = "0.11.0"
coroutinesVersion = "1.6.4"
mokoMvvmVersion = "0.16.0"
mokoPermissionsVersion = "0.15.0"
mokoTestVersion = "0.6.1"
mokoMediaVersion = "0.10.0"
mokoMediaVersion = "0.11.0"
composeJetBrainsVersion = "1.3.1"

[libraries]
appCompat = { module = "androidx.appcompat:appcompat", version.ref = "androidAppCompatVersion" }
Expand All @@ -18,14 +19,20 @@ lifecycle = { module = "androidx.lifecycle:lifecycle-extensions", version.ref =
exifInterface = { module = "androidx.exifinterface:exifinterface", version.ref = "androidExifInterface" }
mediaFilePicker = { module = "com.github.icerockdev:MaterialFilePicker", version.ref = "androidMediaFilePicker" }
coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutinesVersion" }

mokoPermissions = { module = "dev.icerock.moko:permissions", version.ref = "mokoPermissionsVersion" }
mokoPermissionsCompose = { module = "dev.icerock.moko:permissions-compose", version.ref = "mokoPermissionsVersion" }

mokoMvvmCore = { module = "dev.icerock.moko:mvvm-core", version.ref = "mokoMvvmVersion" }
mokoMvvmLiveData = { module = "dev.icerock.moko:mvvm-livedata", version.ref = "mokoMvvmVersion" }

mokoTest = { module = "dev.icerock.moko:test-core", version.ref = "mokoTestVersion" }
mokoMvvmTest = { module = "dev.icerock.moko:mvvm-test", version.ref = "mokoMvvmVersion" }
mokoPermissionsTest = { module = "dev.icerock.moko:permissions-test", version.ref = "mokoPermissionsVersion" }

kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinVersion" }
androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7.2.0" }
mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "0.1.0" }
mobileMultiplatformGradlePlugin = { module = "dev.icerock:mobile-multiplatform", version = "0.14.1" }
androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7.4.2" }
mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "0.2.0" }
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.4.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
31 changes: 31 additions & 0 deletions media-compose/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

plugins {
id("dev.icerock.moko.gradle.multiplatform.mobile")
id("dev.icerock.moko.gradle.publication")
id("dev.icerock.moko.gradle.stub.javadoc")
id("dev.icerock.moko.gradle.detekt")
id("org.jetbrains.compose")
}

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

defaultConfig {
minSdk = 21
}
}

dependencies {
commonMainApi(projects.media)
commonMainApi(compose.runtime)
commonMainApi(compose.ui)
commonMainApi(libs.mokoPermissionsCompose)

androidMainImplementation(libs.appCompat)

// without this i got Could not find "moko-media/media-compose/build/kotlinTransformedMetadataLibraries/commonMain/org.jetbrains.kotlinx-atomicfu-0.17.3-nativeInterop-8G5yng.klib"
commonMainImplementation("org.jetbrains.kotlinx:atomicfu:0.17.3")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.media.compose

import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.LifecycleOwner
import dev.icerock.moko.media.picker.MediaPickerController

@Suppress("FunctionNaming")
@Composable
actual fun BindMediaPickerEffect(mediaPickerController: MediaPickerController) {
val lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current
val context: Context = LocalContext.current

LaunchedEffect(mediaPickerController, lifecycleOwner, context) {
val fragmentManager: FragmentManager = (context as FragmentActivity).supportFragmentManager

mediaPickerController.bind(lifecycleOwner.lifecycle, fragmentManager)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.media.compose

import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import dev.icerock.moko.media.Bitmap

actual fun Bitmap.toImageBitmap(): ImageBitmap {
return this.platformBitmap.asImageBitmap()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.media.compose

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import dev.icerock.moko.media.picker.MediaPickerController
import dev.icerock.moko.permissions.PermissionsController
import dev.icerock.moko.permissions.compose.PermissionsControllerFactory
import dev.icerock.moko.permissions.compose.rememberPermissionsControllerFactory

@Composable
actual fun rememberMediaPickerControllerFactory(): MediaPickerControllerFactory {
val permissionsControllerFactory: PermissionsControllerFactory =
rememberPermissionsControllerFactory()
return remember(permissionsControllerFactory) {
object : MediaPickerControllerFactory {
override fun createMediaPickerController(
permissionsController: PermissionsController
): MediaPickerController {
return MediaPickerController(permissionsController)
}

override fun createMediaPickerController(): MediaPickerController {
return MediaPickerController(
permissionsController = permissionsControllerFactory.createPermissionsController()
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.media.compose

import androidx.compose.runtime.Composable
import dev.icerock.moko.media.picker.MediaPickerController

@Suppress("FunctionNaming")
@Composable
expect fun BindMediaPickerEffect(mediaPickerController: MediaPickerController)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.media.compose

import androidx.compose.ui.graphics.ImageBitmap
import dev.icerock.moko.media.Bitmap

expect fun Bitmap.toImageBitmap(): ImageBitmap
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.media.compose

import androidx.compose.runtime.Composable
import dev.icerock.moko.media.picker.MediaPickerController
import dev.icerock.moko.permissions.PermissionsController

interface MediaPickerControllerFactory {
fun createMediaPickerController(): MediaPickerController
fun createMediaPickerController(permissionsController: PermissionsController): MediaPickerController
}

@Composable
expect fun rememberMediaPickerControllerFactory(): MediaPickerControllerFactory
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright 2019 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.media.compose

import androidx.compose.runtime.Composable
import dev.icerock.moko.media.picker.MediaPickerController
import dev.icerock.moko.permissions.compose.BindEffect

@Suppress("FunctionNaming")
@Composable
actual fun BindMediaPickerEffect(mediaPickerController: MediaPickerController) {
BindEffect(mediaPickerController.permissionsController)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.media.compose

import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.toComposeImageBitmap
import dev.icerock.moko.media.Bitmap
import org.jetbrains.skia.Image

actual fun Bitmap.toImageBitmap(): ImageBitmap {
return Image.makeFromEncoded(this.toByteArray()).toComposeImageBitmap()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2023 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.media.compose

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import dev.icerock.moko.media.picker.ios.MediaPickerController
import dev.icerock.moko.permissions.PermissionsController
import dev.icerock.moko.permissions.compose.PermissionsControllerFactory
import dev.icerock.moko.permissions.compose.rememberPermissionsControllerFactory
import platform.UIKit.UIApplication
import platform.UIKit.UIViewController

@Composable
actual fun rememberMediaPickerControllerFactory(): MediaPickerControllerFactory {
val permissionsControllerFactory: PermissionsControllerFactory =
rememberPermissionsControllerFactory()
return remember(permissionsControllerFactory) {
val getVC: () -> UIViewController = {
// for compose app in common case used one ComposeViewController in one window. so it will works
UIApplication.sharedApplication.keyWindow?.rootViewController
?: error("can't find view controller")
}

object : MediaPickerControllerFactory {
override fun createMediaPickerController(
permissionsController: PermissionsController
): MediaPickerController {
return MediaPickerController(
permissionsController = permissionsController,
getViewController = getVC
)
}

override fun createMediaPickerController(): MediaPickerController {
return MediaPickerController(
permissionsController = permissionsControllerFactory.createPermissionsController(),
getViewController = getVC
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import dev.icerock.moko.media.Media
import dev.icerock.moko.media.picker.MediaPickerController
import dev.icerock.moko.media.picker.MediaSource
import dev.icerock.moko.permissions.PermissionsController
import platform.UIKit.UIViewController

actual open class MediaPickerControllerMock actual constructor(
actual override val permissionsController: PermissionsController
) : MediaPickerController {
override fun bind(viewController: UIViewController) = Unit

actual override suspend fun pickImage(source: MediaSource): Bitmap {
TODO("Not yet implemented")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ object MediaFactory {

return cursorRef.use { cursor ->
if (!cursor.moveToFirst()) {
throw IllegalStateException("cursor should have one element")
error("cursor should have one element")
}

val mimeTypeIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE)
Expand Down Expand Up @@ -68,7 +68,7 @@ object MediaFactory {

return cursorRef.use { cursor ->
if (!cursor.moveToFirst()) {
throw IllegalStateException("not found resource")
error("not found resource")
}

val orientation = contentResolver.openInputStream(uri)?.use {
Expand Down Expand Up @@ -107,7 +107,7 @@ object MediaFactory {

return cursorRef.use { cursor ->
if (!cursor.moveToFirst()) {
throw IllegalStateException("cursor should have one element")
error("cursor should have one element")
}

val titleColumn = cursor.getColumnIndex(MediaStore.Video.VideoColumns.TITLE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ internal class MediaPickerControllerImpl(
*/
override suspend fun pickImage(source: MediaSource, maxWidth: Int, maxHeight: Int): Bitmap {
val fragmentManager =
fragmentManager ?: throw IllegalStateException("can't pick image without active window")
fragmentManager ?: error("can't pick image without active window")

source.requiredPermissions().forEach { permission ->
permissionsController.providePermission(permission)
Expand Down Expand Up @@ -82,7 +82,7 @@ internal class MediaPickerControllerImpl(

override suspend fun pickMedia(): Media {
val fragmentManager =
fragmentManager ?: throw IllegalStateException("can't pick image without active window")
fragmentManager ?: error("can't pick image without active window")

permissionsController.providePermission(Permission.GALLERY)

Expand All @@ -106,7 +106,7 @@ internal class MediaPickerControllerImpl(

override suspend fun pickFiles(): FileMedia {
val fragmentManager =
fragmentManager ?: throw IllegalStateException("can't pick image without active window")
fragmentManager ?: error("can't pick image without active window")

permissionsController.providePermission(Permission.STORAGE)

Expand Down
Loading

0 comments on commit 802cb7c

Please sign in to comment.