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

[WIP] feat: added v1 for kmm. needs cleanup #164

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
.externalNativeBuild
.cxx
local.properties
/.idea/
/.idea/
45 changes: 22 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,37 @@

![Capturable](art/header.png)

🚀A Jetpack Compose utility library for converting Composable content into Bitmap image 🖼️.
_Made with ❤️ for Android Developers and Composers_
🚀A Kotlin Multiplatform utility library for converting Composable content into Bitmap images 🖼️.
_Made with ❤️ for developers working across WebAssembly (WASM), Android, iOS, and desktop platforms._

[![Build](https://github.com/PatilShreyas/Capturable/actions/workflows/build.yml/badge.svg)](https://github.com/PatilShreyas/Capturable/actions/workflows/build.yml)
[![Maven Central](https://img.shields.io/maven-central/v/dev.shreyaspatil/capturable)](https://search.maven.org/artifact/dev.shreyaspatil/capturable)

## 💡Introduction
## 💡Introduction

In the previous View system, drawing Bitmap Image from `View` was very straightforward. But that's not the case with Jetpack Compose since it's different in many aspects from previous system. This library helps easy way to achieve the same results.
In the previous View system, drawing a Bitmap Image from a `View` was straightforward. However, with Jetpack Compose's unique characteristics, achieving the same is different. This library provides a simple way to capture Composable content into a Bitmap, making it suitable for Kotlin Multiplatform projects targeting WebAssembly (WASM), Android, iOS, and desktop.

## 🚀 Implementation

You can check [/app](/app) directory which includes example application for demonstration.
You can check the [/app](/app) directory, which includes an example application for demonstration.

### Gradle setup

In `build.gradle` of app module, include this dependency
In the `build.gradle.kts` of the relevant module, include this dependency:

```gradle
dependencies {
implementation "dev.shreyaspatil:capturable:2.1.0"
implementation("dev.shreyaspatil:capturable:2.1.0")
}
```

_You can find latest version and changelogs in the [releases](https://github.com/PatilShreyas/Capturable/releases)_.
_You can find the latest version and changelogs in the [releases](https://github.com/PatilShreyas/Capturable/releases)_.

### Usage

#### 1. Setup the controller

To be able to capture Composable content, you need instance of [`CaptureController`](https://patilshreyas.github.io/Capturable/capturable/dev.shreyaspatil.capturable.controller/-capture-controller/index.html) by which you can decide when to capture the content. You can get the instance as follow.
To capture Composable content, you need an instance of the [`CaptureController`](https://patilshreyas.github.io/Capturable/capturable/dev.shreyaspatil.capturable.controller/-capture-controller/index.html), which allows you to decide when to capture the content. You can obtain the instance as follows:

```kotlin
@Composable
Expand All @@ -45,15 +45,15 @@ _[`rememberCaptureController()`](https://patilshreyas.github.io/Capturable/captu

#### 2. Add the content

The component which needs to be captured, a `capturable()` Modifier should be applied on that @Composable component as follows.
To capture a specific component, apply a `capturable()` Modifier to that @Composable component as shown:

```kotlin
@Composable
fun TicketScreen() {
val captureController = rememberCaptureController()

// Composable content to be captured.
// Here, everything inside below Column will be get captured
// Here, everything inside the following Column will be captured
Column(modifier = Modifier.capturable(captureController)) {
MovieTicketContent(...)
}
Expand All @@ -62,10 +62,10 @@ fun TicketScreen() {

#### 3. Capture the content

To capture the content, use [`CaptureController#captureAsync()`](https://patilshreyas.github.io/Capturable/capturable/dev.shreyaspatil.capturable.controller/-capture-controller/captureAsync.html) as follows.
To capture the content, use [`CaptureController#captureAsync()`](https://patilshreyas.github.io/Capturable/capturable/dev.shreyaspatil.capturable.controller/-capture-controller/captureAsync.html) as follows:

```kotlin
// Example: Capture the content when button is clicked
// Example: Capture the content when a button is clicked
val scope = rememberCoroutineScope()
Button(onClick = {
// Capture content
Expand All @@ -75,38 +75,37 @@ Button(onClick = {
val bitmap = bitmapAsync.await()
// Do something with `bitmap`.
} catch (error: Throwable) {
// Error occurred, do something.
// Handle the error
}
}
}) { ... }
```

On calling this method, request for capturing the content will be sent and `ImageBitmap` will be
returned asynchronously. _This method is safe to be called from Main thread._
When this method is called, a request to capture the content is sent, and `ImageBitmap` is returned asynchronously. _This method is safe to call from the Main thread._

By default, it captures the Bitmap using [`Bitmap.Config`](https://developer.android.com/reference/android/graphics/Bitmap.Config) **ARGB_8888**. If you want to modify, you can provide config from [`Bitmap.Config` enum](https://developer.android.com/reference/android/graphics/Bitmap.Config).
By default, it captures the Bitmap using [`Bitmap.Config`](https://developer.android.com/reference/android/graphics/Bitmap.Config) **ARGB_8888**. You can modify this by providing a different config from the [`Bitmap.Config` enum](https://developer.android.com/reference/android/graphics/Bitmap.Config).

Example:

```kotlin
captureController.captureAsync(Bitmap.Config.ALPHA_8)
```

That's all needed!
That's all you need!

## 📄 API Documentation

[**Visit the API documentation of this library**](https://patilshreyas.github.io/Capturable) to get more information in detail.
[**Visit the API documentation of this library**](https://patilshreyas.github.io/Capturable) for more detailed information.

---

## 🙋‍♂️ Contribute
## 🙋‍♂️ Contribute

Read [contribution guidelines](CONTRIBUTING.md) for more information regarding contribution.
Read the [contribution guidelines](CONTRIBUTING.md) for more information on how to contribute.

## 💬 Discuss?
## 💬 Discuss?

Have any questions, doubts or want to present your opinions, views? You're always welcome. You can [start discussions](https://github.com/PatilShreyas/Capturable/discussions).
Have any questions, doubts, or want to share your opinions? You're always welcome. You can [start discussions](https://github.com/PatilShreyas/Capturable/discussions).

## 📝 License

Expand Down
96 changes: 79 additions & 17 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,61 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
id 'com.android.application'
id 'kotlin-android'
id "kotlin-multiplatform"
id "org.jetbrains.compose"
}

kotlin {
applyDefaultHierarchyTemplate()

androidTarget()
jvm("desktop")
wasmJs {
moduleName = "capturable-demo"
browser {
commonWebpackConfig {
outputFileName = "composeApp.js"
}
}
binaries.executable()
}

sourceSets {
commonMain {
dependencies {
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.material)
implementation(compose.ui)
implementation(compose.components.resources)

// Capturable library
// implementation "dev.shreyaspatil:capturable:2.1.0"
implementation(project(":capturable"))
}
}
androidMain {
dependencies {
// Android
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
implementation 'androidx.activity:activity-compose:1.8.2'
}
}
desktopMain {
dependencies {
implementation(compose.desktop.currentOs)
}
}
}

compilerOptions {
optIn.add("org.jetbrains.compose.resources.ExperimentalResourceApi")
}
}

android {
Expand All @@ -26,27 +81,22 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion composeCompilerVersion
kotlinCompilerExtensionVersion "1.5.8"
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
namespace 'dev.shreyaspatil.capturableExample'
}

dependencies {
dependencies {

// Capturable library
// implementation "dev.shreyaspatil:capturable:2.1.0"
Expand All @@ -59,11 +109,23 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.4'
implementation 'androidx.activity:activity-compose:1.9.1'

// Jetpack Compose
implementation platform("androidx.compose:compose-bom:$composeBomVersion")
implementation "androidx.compose.foundation:foundation"
implementation "androidx.compose.ui:ui"
implementation "androidx.compose.material:material"
implementation "androidx.compose.ui:ui-tooling-preview"
debugImplementation "androidx.compose.ui:ui-tooling"
// Jetpack Compose
implementation platform("androidx.compose:compose-bom:2024.04.00")
implementation "androidx.compose.ui:ui-tooling-preview"
debugImplementation "androidx.compose.ui:ui-tooling"
}
}

compose.desktop {
application {
mainClass = "MainKt"
}
}

compose.web {}

tasks.withType(KotlinJvmCompile.class).configureEach {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* MIT License
*
* Copyright (c) 2022 Shreyas Patil
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package dev.shreyaspatil.capturableExample

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.Button
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ExperimentalComposeApi
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import dev.shreyaspatil.capturable.capturable
import dev.shreyaspatil.capturable.controller.rememberCaptureController
import dev.shreyaspatil.capturableExample.ui.App
import dev.shreyaspatil.capturableExample.ui.TicketScreen
import dev.shreyaspatil.capturableExample.ui.theme.CapturableExampleTheme
import dev.shreyaspatil.capturableExample.ui.theme.LightGray
import dev.shreyaspatil.capturableExample.ui.theme.Teal200
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
App()
}
}
}


@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
CapturableExampleTheme {
TicketScreen()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<vector android:height="24dp" android:tint="#05CB4E"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
<path android:fillColor="#FFFFFF" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
</vector>
Loading