Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: File and absolute path support #50

Merged
merged 8 commits into from
Nov 29, 2023
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
30 changes: 19 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,15 @@ You can checkout a web demo at [demo.kmpalette.com](https://demo.kmpalette.com/)

This library is written for Compose Multiplatform, and can be used on the following platforms:

- Android
- iOS / MacOS
- JVM (Desktop)
- JavaScript (Browser)
| Artifact | Android | Desktop | iOS | macOS | Browser |
|------------------------|:-------:|:-------:|:---:|:-----:|:-------:|
| `core` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `extensions-base64` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `extensions-bytearray` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `extensions-libres` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `extensions-network` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `extensions-resources` | ✅ | ✅ | ✅ | ✅ | ✅ |
| `extensions-file` | ✅ | ✅ | ✅ | ✅ | ❌ |

## Inspiration

Expand Down Expand Up @@ -87,6 +92,7 @@ kmpalette-extensions-bytearray = { module = "com.kmpalette:extensions-bytearray"
kmpalette-extensions-libres = { module = "com.kmpalette:extensions-libres", version.ref = "kmpalette" }
kmpalette-extensions-network = { module = "com.kmpalette:extensions-network", version.ref = "kmpalette" }
kmpalette-extensions-resources = { module = "com.kmpalette:extensions-resources", version.ref = "kmpalette" }
kmpalette-extensions-file = { module = "com.kmpalette:extensions-file", version.ref = "kmpalette" }
```

To add to a multiplatform project, add the dependency to the common source-set:
Expand All @@ -105,6 +111,7 @@ kotlin {
implementation(libs.kmpalette.extensions.libres)
implementation(libs.kmpalette.extensions.network)
implementation(libs.kmpalette.extensions.resources)
implementation(libs.kmpalette.extensions.file)
}
}
}
Expand Down Expand Up @@ -215,13 +222,14 @@ the [demo app](demo/composeApp/src/commonMain/kotlin/com/kmpalette/demo/palette)
In order to generate a color palette, you must first have an `ImageBitmap` object. This library
provides some extensions artifacts for some popular sources.

| Artifact | Library | Loader | Input Class | Demo |
|:--------------------------------------------------------:|------------------------------------------------------------------------------------------------------------------------|-------------------|-------------|------------------------------------------------------------------------------------------------------------------------|
| [`extensions-base64`](extensions-base64/README.md) | N/A | `Base64Loader` | `String` | [`Base64DemoScreen`](demo/composeApp/src/commonMain/kotlin/com/kmpalette/demo/dominant/Base64DemoScreen.kt) |
| [`extensions-bytearray`](extensions-bytearray/README.md) | N/A | `ByteArrayLoader` | `ByteArray` | N/A |
| [`extensions-libres`](extensions-libres/README.md) | [libres](https://github.com/Skeptick/libres) | `LibresLoader` | `Image` | [`LibresPaletteScreen`](demo/composeApp/src/commonMain/kotlin/com/kmpalette/demo/palette/LibresPaletteScreen.kt) |
| [`extensions-network`](extensions-network/README.md) | [ktor](https://github.com/ktorio/ktor) | `NetworkLoader` | `Url` | [`NetworkDemoScreen`](demo/composeApp/src/commonMain/kotlin/com/kmpalette/demo/dominant/NetworkDemoScreen.kt) |
| [`extensions-resources`](extensions-resources/README.md) | [Compose Multiplatform Resources](https://github.com/JetBrains/compose-multiplatform/tree/master/components/resources) | `ResourceLoader` | `Resource` | [`ResourcesPaletteScreen`](demo/composeApp/src/commonMain/kotlin/com/kmpalette/demo/palette/ResourcesPaletteScreen.kt) |
| Artifact | Library | Loader | Input Class | Demo |
|----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|--------------------------------|------------------|------------------------------------------------------------------------------------------------------------------------|
| [`extensions-base64`](extensions-base64/README.md) | N/A | `Base64Loader` | `String` | [`Base64DemoScreen`](demo/composeApp/src/commonMain/kotlin/com/kmpalette/demo/dominant/Base64DemoScreen.kt) |
| [`extensions-bytearray`](extensions-bytearray/README.md) | N/A | `ByteArrayLoader` | `ByteArray` | N/A |
| [`extensions-libres`](extensions-libres/README.md) | [libres](https://github.com/Skeptick/libres) | `LibresLoader` | `Image` | [`LibresPaletteScreen`](demo/composeApp/src/commonMain/kotlin/com/kmpalette/demo/palette/LibresPaletteScreen.kt) |
| [`extensions-network`](extensions-network/README.md) | [ktor](https://github.com/ktorio/ktor) | `NetworkLoader` | `Url` | [`NetworkDemoScreen`](demo/composeApp/src/commonMain/kotlin/com/kmpalette/demo/dominant/NetworkDemoScreen.kt) |
| [`extensions-resources`](extensions-resources/README.md) | [Compose Multiplatform Resources](https://github.com/JetBrains/compose-multiplatform/tree/master/components/resources) | `ResourceLoader` | `Resource` | [`ResourcesPaletteScreen`](demo/composeApp/src/commonMain/kotlin/com/kmpalette/demo/palette/ResourcesPaletteScreen.kt) |
| [`extensions-file`](extensions-file/README.md) | [okio](https://square.github.io) | `PathLoader`, `FilePathLoader` | `Path`, `String` | N/A |

Each of these extensions provides a `ImageBitmapLoader` object that can be used to generate
an `ImageBitmap` from the input class. For example, the `NetworkLoader` can be used to generate
Expand Down
4 changes: 1 addition & 3 deletions androidx-palette/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@file:Suppress("OPT_IN_USAGE")

plugins {
alias(libs.plugins.multiplatform)
alias(libs.plugins.android.library)
Expand All @@ -13,7 +11,7 @@ plugins {
kotlin {
explicitApi()

targetHierarchy.default()
applyDefaultHierarchyTemplate()

androidTarget {
publishAllLibraryVariants()
Expand Down
3 changes: 2 additions & 1 deletion demo/composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ plugins {

@OptIn(ExperimentalKotlinGradlePluginApi::class)
kotlin {
targetHierarchy.default()
applyDefaultHierarchyTemplate()

androidTarget()

Expand Down Expand Up @@ -78,6 +78,7 @@ kotlin {

val androidMain by getting {
dependencies {
implementation(project(":extensions-file"))
implementation(libs.androidx.appcompat)
implementation(libs.androidx.activityCompose)
implementation(libs.compose.uitooling)
Expand Down
11 changes: 11 additions & 0 deletions demo/composeApp/composeApp.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ Pod::Spec.new do |spec|
spec.ios.deployment_target = '11.0'


if !Dir.exist?('build/cocoapods/framework/ComposeApp.framework') || Dir.empty?('build/cocoapods/framework/ComposeApp.framework')
raise "

Kotlin framework 'ComposeApp' doesn't exist yet, so a proper Xcode project can't be generated.
'pod install' should be executed after running ':generateDummyFramework' Gradle task:

./gradlew :demo:composeApp:generateDummyFramework

Alternatively, proper pod installation is performed during Gradle sync in the IDE (if Podfile location is set)"
end

spec.pod_target_xcconfig = {
'KOTLIN_PROJECT_PATH' => ':demo:composeApp',
'PRODUCT_MODULE_NAME' => 'ComposeApp',
Expand Down
1 change: 1 addition & 0 deletions demo/composeApp/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
android:icon="@android:drawable/ic_menu_compass"
android:label="kmpalette"
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"
android:name=".AndroidApp"
>
<activity
android:name=".AppActivity"
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,36 @@ package com.kmpalette.demo

import android.app.Activity
import android.app.Application
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
import androidx.fragment.app.FragmentActivity
import java.io.File

class AndroidApp : Application() {

companion object {

lateinit var INSTANCE: AndroidApp

fun sampleFile(): File = File(INSTANCE.cacheDir, "sample_image.jpg")
}

override fun onCreate() {
super.onCreate()
INSTANCE = this

assets.openFd("sample_image.jpg").use { assetFd ->
sampleFile().outputStream().use { fileOut ->
assetFd.createInputStream().use { fileIn ->
fileIn.copyTo(fileOut)
}
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.kmpalette.demo

import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.ImageBitmap
import cafe.adriel.voyager.core.screen.Screen
import com.kmpalette.demo.dominant.DominantDemoContent
import com.kmpalette.loader.FilePathLoader
import com.kmpalette.rememberDominantColorState

actual class FileDemoScreen : Screen {

@Composable
override fun Content() {
val filePath = remember { sampleFile() }
val dominantColorState = rememberDominantColorState(loader = FilePathLoader) {
clearFilters()
}
var errorMessage: String? by remember { mutableStateOf(null) }
var image: ImageBitmap? by remember { mutableStateOf(null) }
LaunchedEffect(filePath) {
try {
image = FilePathLoader.load(filePath)
} catch (cause: Throwable) {
cause.printStackTrace()
errorMessage = cause.message
}
dominantColorState.updateFrom(filePath)
}

DominantDemoContent(
dominantColorState = dominantColorState,
imageBitmap = image,
) {
if (errorMessage != null) {
Text(errorMessage!!)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.kmpalette.demo

actual fun sampleFile(): String {
return AndroidApp.sampleFile().absolutePath
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class HomeScreen : Screen {
Button(onClick = { navigator.push(NetworkDemoScreen()) }) {
Text("Dominant Color - Network")
}
Button(onClick = { navigator.push(FileDemoScreen()) }) {
Text("Dominant Color - Sample File")
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.kmpalette.demo

import cafe.adriel.voyager.core.screen.Screen

expect fun sampleFile(): String

expect class FileDemoScreen() : Screen
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.kmpalette.demo

import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import cafe.adriel.voyager.core.screen.Screen

actual fun sampleFile(): String {
error("Not supported by this platform")
}

actual class FileDemoScreen actual constructor(): Screen {

@Composable
override fun Content() {
Text("Not supported by this platform")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.kmpalette.demo

import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import cafe.adriel.voyager.core.screen.Screen

actual fun sampleFile(): String {
error("Not supported by this platform")
}

actual class FileDemoScreen actual constructor(): Screen {

@Composable
override fun Content() {
Text("Not supported by this platform")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.kmpalette.demo

import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import cafe.adriel.voyager.core.screen.Screen

actual fun sampleFile(): String {
error("Not supported by this platform")
}

actual class FileDemoScreen actual constructor(): Screen {

@Composable
override fun Content() {
Text("Not supported by this platform")
}
}
9 changes: 9 additions & 0 deletions demo/iosApp/iosApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
A93A953829CC810C00F8E227 /* Products */,
979913F6AE5D271756D4649D /* Pods */,
C4127409AE3703430489E7BC /* Frameworks */,
BB33396AAA5F8D0EE2AE568C /* xcschemes */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -80,6 +81,14 @@
path = "Preview Content";
sourceTree = "<group>";
};
BB33396AAA5F8D0EE2AE568C /* xcschemes */ = {
isa = PBXGroup;
children = (
);
name = xcschemes;
path = iosApp.xcworkspace/xcuserdata/jordon.xcuserdatad/xcschemes;
sourceTree = "<group>";
};
C4127409AE3703430489E7BC /* Frameworks */ = {
isa = PBXGroup;
children = (
Expand Down
4 changes: 1 addition & 3 deletions extensions-base64/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@file:Suppress("OPT_IN_USAGE")

plugins {
alias(libs.plugins.multiplatform)
alias(libs.plugins.compose)
Expand All @@ -12,7 +10,7 @@ plugins {
kotlin {
explicitApi()

targetHierarchy.default()
applyDefaultHierarchyTemplate()

androidTarget {
publishAllLibraryVariants()
Expand Down
4 changes: 1 addition & 3 deletions extensions-bytearray/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@file:Suppress("OPT_IN_USAGE")

plugins {
alias(libs.plugins.multiplatform)
alias(libs.plugins.compose)
Expand All @@ -12,7 +10,7 @@ plugins {
kotlin {
explicitApi()

targetHierarchy.default()
applyDefaultHierarchyTemplate()

androidTarget {
publishAllLibraryVariants()
Expand Down
57 changes: 57 additions & 0 deletions extensions-file/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# File Extensions

This extension provides `FilePathLoader` and `PathLoader` a `ImageBitmapLoader` for loading images
from an absolute path, or from an Okio `Path` object.

| Library | Loader | Input Class | Demo |
|----------------------------------|---------------------------------|-------------------|------|
| [okio](https://square.github.io) | `PathLoader` / `FilePathLoader` | `Path` / `String` | N/A |

## Setup

In order to use these extensions you will need to add the Okio dependency to
your `build.gradle.kts`, you will need the `core` library as well.

Then you need to add the following to your `build.gradle.kts` file:

```kotlin
kotlin {
sourceSets {
commonMain {
dependencies {
implementation(libs.kmpalette.core)
implementation(libs.kmpalette.extensions.file)
implementation("com.squareup.okio:okio:<latest okio version>")
}
}
}
}
```

## Usage

Now you can use the `FilePathLoader` to load images from an absolute path:

```kotlin
@Composable
fun MyComposable(absolutePath: String) {
val paletteState = rememberPaletteState(loader = FilePathLoader)
LaunchedEffect(absolutePath) {
paletteState.generate(absolutePath)
}
}
```

Or if you already have a `Path` object:

```kotlin
@Composable
fun MyComposable(path: Path) {
val paletteState = rememberPaletteState(loader = PathLoader)
LaunchedEffect(path) {
paletteState.generate(path)
}
}
```

The Image will be fetched, converted into a `ImageBitmap` then a palette will be generated.
Loading