Skip to content

Commit

Permalink
Merge branch 'release/1.2.0-rc01' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
G00fY2 committed Jun 24, 2021
2 parents 3613d96 + 9fddc17 commit 82e6b5c
Show file tree
Hide file tree
Showing 101 changed files with 563 additions and 228 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ There are two different flavors available on `mavenCentral()`:
| V2 model is used (possibly faster, more accurate) | currently V1 model will be downloaded
```kotlin
// bundled:
implementation("io.github.g00fy2.quickie:quickie-bundled:1.1.2")
implementation("io.github.g00fy2.quickie:quickie-bundled:1.2.0-rc01")

// unbundled:
implementation("io.github.g00fy2.quickie:quickie-unbundled:1.1.2")
implementation("io.github.g00fy2.quickie:quickie-unbundled:1.2.0-rc01")
```

## Quick Start
Expand Down Expand Up @@ -61,7 +61,7 @@ Currently, supported subtypes are:
See the ML Kit [Barcode documentation](https://developers.google.com/android/reference/com/google/mlkit/vision/barcode/Barcode#nested-class-summary) for further details.

### Customization
Use the `ScanCustomCode()` ActivityResultContract to create a configurable barcode scan. When launching the ActivityResultLauncher pass in a `ScannerConfig` object. You can set the supported `BarcodeFormat` list, `overlayStringRes` and `overlayDrawableRes` resource ID and control the `hapticSuccessFeedback`.
Use the `ScanCustomCode()` ActivityResultContract to create a configurable barcode scan. When launching the ActivityResultLauncher pass in a `ScannerConfig` object:

<details>
<summary>BarcodeFormat options</summary>
Expand Down Expand Up @@ -93,10 +93,11 @@ override fun onCreate(savedInstanceState: Bundle?) {
binding.button.setOnClickListener {
scanCustomCode.launch(
ScannerConfig.build {
setBarcodeFormats(listOf(BarcodeFormat.FORMAT_CODE_128))
setOverlayStringRes(R.string.scan_barcode)
setOverlayDrawableRes(R.drawable.ic_scan_barcode)
setHapticSuccessFeedback(false)
setBarcodeFormats(listOf(BarcodeFormat.FORMAT_CODE_128)) // set interested barcode formats
setOverlayStringRes(R.string.scan_barcode) // string resource used for the scanner overlay
setOverlayDrawableRes(R.drawable.ic_scan_barcode) // drawable resource used for the scanner overlay
setHapticSuccessFeedback(false) // enable (default) or disable haptic feedback when a barcode was detected
setShowTorchToggle(true) // show or hide (default) torch/flashlight toggle button
}
)
}
Expand Down
8 changes: 4 additions & 4 deletions buildSrc/src/main/kotlin/Deps.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ object Deps {
const val cameraPreview = "androidx.camera:camera-view:${Versions.cameraView}"
}

object UI {
const val materialDesign = "com.google.android.material:material:${Versions.materialDesign}"
}

object MLKit {
const val barcodeScanning = "com.google.mlkit:barcode-scanning:${Versions.barcodeScanning}"
const val barcodeScanningGms =
"com.google.android.gms:play-services-mlkit-barcode-scanning:${Versions.barcodeScanningGms}"
}

object UI {
const val materialDesign = "com.google.android.material:material:${Versions.materialDesign}"
}

object Test {
const val junitApi = "org.junit.jupiter:junit-jupiter-api:${Versions.junit}"
const val junitEngine = "org.junit.jupiter:junit-jupiter-engine:${Versions.junit}"
Expand Down
6 changes: 3 additions & 3 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ object Versions {
const val androidBuildTools = "30.0.3"

const val androidGradle = "4.2.1"
const val kotlin = "1.5.10"
const val kotlin = "1.5.20"

const val appcompat = "1.3.0"

const val cameraX = "1.0.0"
const val cameraView = "1.0.0-alpha25"

const val materialDesign = "1.3.0"

const val barcodeScanning = "16.1.2"
const val barcodeScanningGms = "16.1.5"

const val materialDesign = "1.3.0"

const val detekt = "1.17.1"
const val gradleVersions = "0.39.0"
const val dokka = "1.4.32"
Expand Down
2 changes: 1 addition & 1 deletion quickie/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ dependencies {
}

group = "io.github.g00fy2.quickie"
version = "1.1.2"
version = "1.2.0-rc01"

tasks.register<Jar>("androidJavadocJar") {
archiveClassifier.set("javadoc")
Expand Down
2 changes: 1 addition & 1 deletion quickie/config/detekt/baseline-bundledDebug.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<SmellBaseline>
<ManuallySuppressedIssues></ManuallySuppressedIssues>
<CurrentIssues>
<ID>FunctionOnlyReturningConstant:PlayServicesValidator.kt$PlayServicesValidator$@Suppress("UNUSED_PARAMETER") fun handleGooglePlayServicesError(activity: Activity, exception: Exception)</ID>
<ID>FunctionOnlyReturningConstant:MlKitErrorHandler.kt$MlKitErrorHandler$@Suppress("UNUSED_PARAMETER") fun isResolvableError(activity: QRScannerActivity, exception: Exception)</ID>
<ID>LongMethod:IntentExtensions.kt$private fun Intent.toQuickieContentType(rawValue: String): QRContent?</ID>
<ID>UnsafeCallOnNullableType:QRCodeAnalyzer.kt$QRCodeAnalyzer$image!!</ID>
<ID>UnsafeCallOnNullableType:QROverlayView.kt$QROverlayView$maskBitmap!!</ID>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.g00fy2.quickie.utils

import io.github.g00fy2.quickie.QRScannerActivity

internal object MlKitErrorHandler {

@Suppress("UNUSED_PARAMETER")
fun isResolvableError(activity: QRScannerActivity, exception: Exception) = false // always false when bundled
}

This file was deleted.

25 changes: 22 additions & 3 deletions quickie/src/main/kotlin/io/github/g00fy2/quickie/QRCodeAnalyzer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import com.google.mlkit.vision.common.InputImage
internal class QRCodeAnalyzer(
private val barcodeFormats: IntArray,
private val onSuccess: ((Barcode) -> Unit),
private val onFailure: ((Exception) -> Unit)
private val onFailure: ((Exception) -> Unit),
private val onPassCompleted: ((Boolean) -> Unit)
) : ImageAnalysis.Analyzer {

private val barcodeScanner by lazy {
Expand All @@ -23,14 +24,32 @@ internal class QRCodeAnalyzer(
BarcodeScanning.getClient(optionsBuilder.build())
}

@Volatile
private var failureOccurred = false
private var failureTimestamp = 0L

@ExperimentalGetImage
override fun analyze(imageProxy: ImageProxy) {
if (imageProxy.image == null) return

// throttle analysis if error occurred in previous pass
if (failureOccurred && System.currentTimeMillis() - failureTimestamp < 1000L) {
imageProxy.close()
return
}

failureOccurred = false
barcodeScanner.process(imageProxy.toInputImage())
.addOnSuccessListener { codes -> codes.mapNotNull { it }.firstOrNull()?.let { onSuccess(it) } }
.addOnFailureListener { onFailure(it) }
.addOnCompleteListener { imageProxy.close() }
.addOnFailureListener {
failureOccurred = true
failureTimestamp = System.currentTimeMillis()
onFailure(it)
}
.addOnCompleteListener {
onPassCompleted(failureOccurred)
imageProxy.close()
}
}

@ExperimentalGetImage
Expand Down
31 changes: 27 additions & 4 deletions quickie/src/main/kotlin/io/github/g00fy2/quickie/QROverlayView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import android.widget.FrameLayout
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.ColorUtils
import io.github.g00fy2.quickie.databinding.QuickieProgressViewBinding
import io.github.g00fy2.quickie.databinding.QuickieTextviewBinding
import io.github.g00fy2.quickie.databinding.QuickieTorchImageviewBinding
import kotlin.math.min
import kotlin.math.roundToInt

Expand All @@ -33,22 +35,33 @@ internal class QROverlayView @JvmOverloads constructor(
private val backgroundColor = ColorUtils.setAlphaComponent(Color.BLACK, BACKGROUND_ALPHA.roundToInt())
private val alphaPaint = Paint().apply { alpha = BACKGROUND_ALPHA.roundToInt() }
private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG)
private val loadingBackgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = backgroundColor }
private val transparentPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.TRANSPARENT
xfermode = PorterDuffXfermode(CLEAR)
}
private val outerRadius = OUT_RADIUS.toPx()
private val innerRadius = (OUT_RADIUS - STROKE_WIDTH).toPx()
private val titleTextView = QuickieTextviewBinding.inflate(LayoutInflater.from(context), this, true).root
private val outerFrame = RectF()
private val innerFrame = RectF()
private val titleTextView = QuickieTextviewBinding.inflate(LayoutInflater.from(context), this, true).root
private val progressLinearLayout = QuickieProgressViewBinding.inflate(LayoutInflater.from(context), this, true).root
private val torchImageView = QuickieTorchImageviewBinding.inflate(LayoutInflater.from(context), this, true).root
private var maskBitmap: Bitmap? = null
private var maskCanvas: Canvas? = null

var isHighlighted = false
set(value) {
field = value
invalidate()
if (field != value) {
field = value
invalidate()
}
}
var isLoading = false
set(value) {
if (field != value) {
field = value
progressLinearLayout.visibility = if (value) View.VISIBLE else View.GONE
}
}

init {
Expand All @@ -69,6 +82,7 @@ internal class QROverlayView @JvmOverloads constructor(
maskCanvas!!.drawColor(backgroundColor)
maskCanvas!!.drawRoundRect(outerFrame, outerRadius, outerRadius, strokePaint)
maskCanvas!!.drawRoundRect(innerFrame, innerRadius, innerRadius, transparentPaint)
if (isLoading) maskCanvas!!.drawRoundRect(innerFrame, innerRadius, innerRadius, loadingBackgroundPaint)
canvas.drawBitmap(maskBitmap!!, 0f, 0f, alphaPaint)
super.onDraw(canvas)
}
Expand All @@ -92,6 +106,15 @@ internal class QROverlayView @JvmOverloads constructor(
}
}

fun setTorchVisibilityAndOnClick(visible: Boolean, action: (Boolean) -> Unit = {}) {
torchImageView.visibility = if (visible) View.VISIBLE else View.INVISIBLE
torchImageView.setOnClickListener { action(!it.isSelected) }
}

fun setTorchState(on: Boolean) {
torchImageView.isSelected = on
}

private fun calculateFrameAndTitlePos() {
val centralX = width / 2
val centralY = height / 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ package io.github.g00fy2.quickie

import android.Manifest.permission.CAMERA
import android.app.Activity
import android.app.Dialog
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Size
import android.view.HapticFeedbackConstants
import android.view.KeyEvent
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ContextThemeWrapper
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.Preview
import androidx.camera.core.TorchState
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
Expand All @@ -23,7 +26,7 @@ import com.google.mlkit.vision.barcode.Barcode
import io.github.g00fy2.quickie.config.ParcelableScannerConfig
import io.github.g00fy2.quickie.databinding.QuickieScannerActivityBinding
import io.github.g00fy2.quickie.extensions.toParcelableContentType
import io.github.g00fy2.quickie.utils.PlayServicesValidator
import io.github.g00fy2.quickie.utils.MlKitErrorHandler
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

Expand All @@ -33,6 +36,21 @@ internal class QRScannerActivity : AppCompatActivity() {
private lateinit var analysisExecutor: ExecutorService
private var barcodeFormats = intArrayOf(Barcode.FORMAT_QR_CODE)
private var hapticFeedback = true
private var showTorchToggle = false
internal var errorDialog: Dialog? = null
set(value) {
field = value
value?.show()
value?.setOnKeyListener { dialog, keyCode, _ ->
if (keyCode == KeyEvent.KEYCODE_BACK) {
finish()
dialog.dismiss()
true
} else {
false
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand Down Expand Up @@ -75,24 +93,29 @@ internal class QRScannerActivity : AppCompatActivity() {
.also {
it.setAnalyzer(analysisExecutor,
QRCodeAnalyzer(
barcodeFormats,
{ barcode ->
barcodeFormats = barcodeFormats,
onSuccess = { barcode ->
it.clearAnalyzer()
onSuccess(barcode)
},
{ exception ->
it.clearAnalyzer()
onFailure(exception)
}
onFailure = { exception -> onFailure(exception) },
onPassCompleted = { failureOccurred -> onPassCompleted(failureOccurred) }
)
)
}

cameraProvider.unbindAll()
try {
cameraProvider.bindToLifecycle(this, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageAnalysis)
val camera = cameraProvider.bindToLifecycle(this, CameraSelector.DEFAULT_BACK_CAMERA, preview, imageAnalysis)
binding.overlayView.visibility = View.VISIBLE
if (showTorchToggle && camera.cameraInfo.hasFlashUnit()) {
binding.overlayView.setTorchVisibilityAndOnClick(true) { camera.cameraControl.enableTorch(it) }
camera.cameraInfo.torchState.observe(this) { binding.overlayView.setTorchState(it == TorchState.ON) }
} else {
binding.overlayView.setTorchVisibilityAndOnClick(false)
}
} catch (e: Exception) {
binding.overlayView.visibility = View.INVISIBLE
onFailure(e)
}
}, ContextCompat.getMainExecutor(this))
Expand All @@ -119,7 +142,11 @@ internal class QRScannerActivity : AppCompatActivity() {

private fun onFailure(exception: Exception) {
setResult(RESULT_ERROR, Intent().putExtra(EXTRA_RESULT_EXCEPTION, exception))
if (!PlayServicesValidator.handleGooglePlayServicesError(this, exception)) finish()
if (!MlKitErrorHandler.isResolvableError(this, exception)) finish()
}

private fun onPassCompleted(failureOccurred: Boolean) {
if (!isFinishing) binding.overlayView.isLoading = failureOccurred
}

private fun setupEdgeToEdgeUI() {
Expand All @@ -135,6 +162,7 @@ internal class QRScannerActivity : AppCompatActivity() {
barcodeFormats = it.formats
binding.overlayView.setCustomTextAndIcon(it.stringRes, it.drawableRes)
hapticFeedback = it.hapticFeedback
showTorchToggle = it.showTorchToggle
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ internal class ParcelableScannerConfig(
val formats: IntArray,
val stringRes: Int,
val drawableRes: Int,
val hapticFeedback: Boolean
val hapticFeedback: Boolean,
val showTorchToggle: Boolean,
) : Parcelable
Loading

0 comments on commit 82e6b5c

Please sign in to comment.