Skip to content

Commit

Permalink
Merge pull request #66 from icerockdev/develop
Browse files Browse the repository at this point in the history
Release 0.11.0
  • Loading branch information
Alex009 authored Apr 11, 2023
2 parents df68164 + e899ae1 commit 40c777b
Show file tree
Hide file tree
Showing 25 changed files with 442 additions and 91 deletions.
128 changes: 116 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
[![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) [![Download](https://img.shields.io/maven-central/v/dev.icerock.moko/media) ](https://repo1.maven.org/maven2/dev/icerock/moko/media) ![kotlin-version](https://kotlin-version.aws.icerock.dev/kotlin-version?group=dev.icerock.moko&name=media)

# Mobile Kotlin media access
This is a Kotlin MultiPlatform library that provides media picking in common code (photo/video) and video player controls.

This is a Kotlin MultiPlatform library that provides media picking in common code (photo/video) and
video player controls.

## Table of Contents

- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
Expand All @@ -15,15 +18,22 @@ This is a Kotlin MultiPlatform library that provides media picking in common cod
- [License](#license)

## Features
TODO

- Capture camera photo
- Pick image from gallery
- Control video player
- **Compose Multiplatform** support

## Requirements

- Gradle version 6.8+
- Android API 16+
- iOS version 11.0+

## Installation
root build.gradle

root build.gradle

```groovy
allprojects {
repositories {
Expand All @@ -34,64 +44,158 @@ allprojects {
```

project build.gradle

```groovy
dependencies {
commonMainApi("dev.icerock.moko:media:0.10.0")
commonMainApi("dev.icerock.moko:media:0.11.0")
// Compose Multiplatform
commonMainApi("dev.icerock.moko:media-compose:0.11.0")
commonTestImplementation("dev.icerock.moko:media-test:0.11.0")
}
```

## Usage
TODO

```kotlin
class ViewModel(val mediaController: MediaPickerController): ViewModel() {
class ViewModel(val mediaController: MediaPickerController) : ViewModel() {
fun onSelectPhotoPressed() {
launch {
try {
val bitmap = mediaController.pickImage(MediaControllerSource.CAMERA)
// captured photo in bitmap
} catch(_: CanceledException) {
} catch (_: CanceledException) {
// cancel capture
} catch(error: Throwable) {
} catch (error: Throwable) {
// denied permission or file read error
}
}
}
}
```

android:

```kotlin
val viewModel = getViewModel {
val permissionsController = PermissionsController()
val mediaController = MediaPickerController(permissionsController)
ViewModel(mediaController)
}

viewModel.mediaController.bind(lifecycle, supportFragmentManager) // permissioncController bind automatically
viewModel.mediaController.bind(
lifecycle,
supportFragmentManager
) // permissioncController bind automatically
```

iOS:

```swift
let permissionsController = PermissionsController()
let mediaController = MediaPickerController(permissionsController: permissionsController, viewController: self)
let viewModel = ViewModel(mediaController: mediaController)
```

### Compose Multiplatform

```kotlin
@Composable
fun Sample() {
val factory = rememberMediaPickerControllerFactory()
val picker = remember(factory) { factory.createMediaPickerController() }
val coroutineScope = rememberCoroutineScope()

BindMediaPickerEffect(picker)

var image: ImageBitmap? by remember { mutableStateOf(null) }

image?.let {
Image(bitmap = it, contentDescription = null)
}

Button(
onClick = {
coroutineScope.launch {
val result = picker.pickImage(MediaSource.GALLERY)
image = result.toImageBitmap()
}
}
) {
Text(text = "Click on me")
}
}
```

or with moko-mvvm (with correct configuration change on android):

```kotlin
@Composable
fun Sample() {
val factory = rememberMediaPickerControllerFactory()
val viewModel: SampleViewModel = getViewModel(
key = "sample",
factory = viewModelFactory {
val picker = factory.createMediaPickerController()
SampleViewModel(picker)
}
)

BindMediaPickerEffect(viewModel.mediaPickerController)

val image: Bitmap? by viewModel.image.collectAsState()
val imageBitmap: ImageBitmap? = remember(image) { image?.toImageBitmap() }

imageBitmap?.let {
Image(bitmap = it, contentDescription = null)
}

Button(onClick = viewModel::onButtonClick) {
Text(text = "Click on me")
}
}

class SampleViewModel(
val mediaPickerController: MediaPickerController
) : ViewModel() {
private val _image: MutableStateFlow<Bitmap?> = MutableStateFlow(null)
val image: StateFlow<Bitmap?> get() = _image

fun onButtonClick() {
viewModelScope.launch {
try {
_image.value = mediaPickerController.pickImage(MediaSource.GALLERY)
} catch (exc: Exception) {
println("error $exc")
}
}
}
}
```

## Samples

More examples can be found in the [sample directory](sample).

## Set Up Locally
## Set Up Locally

- In [media directory](media) contains `media` library;
- In [sample directory](sample) contains samples on android, ios & mpp-library connected to apps.

## Contributing
All development (both new features and bug fixes) is performed in `develop` branch. This way `master` sources always contain sources of the most recently released version. Please send PRs with bug fixes to `develop` branch. Fixes to documentation in markdown files are an exception to this rule. They are updated directly in `master`.

All development (both new features and bug fixes) is performed in `develop` branch. This
way `master` sources always contain sources of the most recently released version. Please send PRs
with bug fixes to `develop` branch. Fixes to documentation in markdown files are an exception to
this rule. They are updated directly in `master`.

The `develop` branch is pushed to `master` during release.

More detailed guide for contributers see in [contributing guide](CONTRIBUTING.md).

## License

Copyright 2019 IceRock MAG Inc

Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
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()
)
}
}
}
}
Loading

0 comments on commit 40c777b

Please sign in to comment.