Skip to content

Commit

Permalink
qr code trying
Browse files Browse the repository at this point in the history
  • Loading branch information
T8RIN committed May 23, 2024
1 parent 6019b7e commit 4f9f14b
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 7 deletions.
15 changes: 8 additions & 7 deletions core/ui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,21 @@ plugins {
android.namespace = "ru.tech.imageresizershrinker.core.ui"

dependencies {
api(projects.core.resources)
api(projects.core.domain)
implementation(projects.core.settings)

// Navigation
api(libs.reimagined)
api(libs.reimagined.hilt)

api(libs.androidx.documentfile)

//AndroidX
api(libs.activityCompose)
api(libs.splashScreen)
api(libs.androidx.exifinterface)
api(libs.appCompat)
api(libs.androidx.lifecycle.viewmodel.compose)
api(libs.androidx.documentfile)

//Konfetti
api(libs.konfetti.compose)
Expand All @@ -47,7 +51,6 @@ dependencies {

//Modules
api(libs.imageToolboxLibs)
api(projects.core.domain)

api(libs.reorderable)

Expand All @@ -68,10 +71,8 @@ dependencies {
"marketImplementation"(libs.app.update)
"marketImplementation"(libs.app.update.ktx)

api(projects.core.resources)

implementation(projects.core.settings)

"marketImplementation"(libs.mlkit.document.scanner)
"fossImplementation"(libs.documentscanner)

api(libs.zxing.android.embedded)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* ImageToolbox is an image editor for android
* Copyright (c) 2024 T8RIN (Malik Mukhametzyanov)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* You should have received a copy of the Apache License
* along with this program. If not, see <http://www.apache.org/licenses/LICENSE-2.0>.
*/

package ru.tech.imageresizershrinker.feature.filters.presentation.components

import android.graphics.Bitmap
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.WriterException
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

/**
* Creates a [BitmapPainter] that draws a QR code for the given [content].
* The [size] parameter defines the size of the QR code in dp.
* The [padding] parameter defines the padding of the QR code in dp.
*/
@Composable
fun rememberQrBitmapPainter(
content: String,
size: Dp = 150.dp,
padding: Dp = 0.5.dp,
foregroundColor: Color = MaterialTheme.colorScheme.onTertiaryContainer,
backgroundColor: Color = MaterialTheme.colorScheme.tertiaryContainer
): BitmapPainter {

check(content.isNotEmpty()) { "Content must not be empty" }
check(size >= 0.dp) { "Size must be positive" }
check(padding >= 0.dp) { "Padding must be positive" }

val density = LocalDensity.current
val sizePx = with(density) { size.roundToPx() }
val paddingPx = with(density) { padding.roundToPx() }

val bitmapState = remember {
mutableStateOf<Bitmap?>(null)
}

// Use dependency on 'content' to re-trigger the effect when content changes
LaunchedEffect(content) {
bitmapState.value = generateQrBitmap(
content = content,
sizePx = sizePx,
paddingPx = paddingPx,
foregroundColor = foregroundColor,
backgroundColor = backgroundColor
)
}

val bitmap = bitmapState.value ?: createDefaultBitmap(sizePx)

return remember(bitmap) {
BitmapPainter(bitmap.asImageBitmap())
}
}


/**
* Generates a QR code bitmap for the given [content].
* The [sizePx] parameter defines the size of the QR code in pixels.
* The [paddingPx] parameter defines the padding of the QR code in pixels.
* Returns null if the QR code could not be generated.
* This function is suspendable and should be called from a coroutine is thread-safe.
*/
private suspend fun generateQrBitmap(
content: String,
sizePx: Int,
paddingPx: Int,
foregroundColor: Color,
backgroundColor: Color
): Bitmap? = withContext(Dispatchers.IO) {
val qrCodeWriter = QRCodeWriter()

// Set the QR code margin to the given padding
val encodeHints = mutableMapOf<EncodeHintType, Any?>()
.apply {
this[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.H
this[EncodeHintType.MARGIN] = paddingPx
}

try {
val bitmapMatrix = qrCodeWriter.encode(
content, BarcodeFormat.QR_CODE,
sizePx, sizePx, encodeHints
)

val matrixWidth = bitmapMatrix.width
val matrixHeight = bitmapMatrix.height

val colors = IntArray(matrixWidth * matrixHeight) { index ->
val x = index % matrixWidth
val y = index / matrixWidth
val shouldColorPixel = bitmapMatrix.get(x, y)
if (shouldColorPixel) foregroundColor.toArgb() else backgroundColor.toArgb()
}

Bitmap.createBitmap(colors, matrixWidth, matrixHeight, Bitmap.Config.ARGB_8888)
} catch (ex: WriterException) {
null
}
}

/**
* Creates a default bitmap with the given [sizePx].
* The bitmap is transparent.
* This is used as a fallback if the QR code could not be generated.
* The bitmap is created on the UI thread.
*/
private fun createDefaultBitmap(sizePx: Int): Bitmap {
return Bitmap.createBitmap(sizePx, sizePx, Bitmap.Config.ARGB_8888).apply {
eraseColor(Color.Transparent.toArgb())
}
}
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ material = "1.13.0-alpha02"

documentScanner = "1.3.7"
mlkitDocumentScanner = "16.0.0-beta1"
zxingAndroidEmbedded = "4.3.0"

[libraries]
documentscanner = { module = "com.github.T8RIN:DocumentScanner", version.ref = "documentScanner" }
Expand Down Expand Up @@ -131,6 +132,7 @@ agp-gradle = { module = "com.android.tools.build:gradle", version.ref = "agp" }
detekt-gradle = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
detekt-compose = { module = "io.nlopez.compose.rules:detekt", version.ref = "detektCompose" }
zxing-android-embedded = { module = "com.journeyapps:zxing-android-embedded", version.ref = "zxingAndroidEmbedded" }

[plugins]
image-toolbox-library = { id = "image.toolbox.library", version = "unspecified" }
Expand Down

0 comments on commit 4f9f14b

Please sign in to comment.