Skip to content

Commit

Permalink
Feature: File and absolute path support (#50)
Browse files Browse the repository at this point in the history
* add support for file paths

* Update README.md

* clean up gradle files

* fix extensions

* add demo

* run apiDump
  • Loading branch information
jordond authored Nov 29, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent e58cfa6 commit 252ee4e
Showing 39 changed files with 615 additions and 40 deletions.
30 changes: 19 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -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

@@ -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:
@@ -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)
}
}
}
@@ -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
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)
@@ -13,7 +11,7 @@ plugins {
kotlin {
explicitApi()

targetHierarchy.default()
applyDefaultHierarchyTemplate()

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

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

androidTarget()

@@ -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)
11 changes: 11 additions & 0 deletions demo/composeApp/composeApp.podspec
Original file line number Diff line number Diff line change
@@ -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',
1 change: 1 addition & 0 deletions demo/composeApp/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -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"
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
@@ -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)
}
}
}
}
}

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
@@ -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")
}
}
}
}
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
@@ -51,6 +51,7 @@
A93A953829CC810C00F8E227 /* Products */,
979913F6AE5D271756D4649D /* Pods */,
C4127409AE3703430489E7BC /* Frameworks */,
BB33396AAA5F8D0EE2AE568C /* xcschemes */,
);
sourceTree = "<group>";
};
@@ -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 = (
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)
@@ -12,7 +10,7 @@ plugins {
kotlin {
explicitApi()

targetHierarchy.default()
applyDefaultHierarchyTemplate()

androidTarget {
publishAllLibraryVariants()
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)
@@ -12,7 +10,7 @@ plugins {
kotlin {
explicitApi()

targetHierarchy.default()
applyDefaultHierarchyTemplate()

androidTarget {
publishAllLibraryVariants()
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

0 comments on commit 252ee4e

Please sign in to comment.