Skip to content

Commit d7d9115

Browse files
committed
Merge branch 'release/1.1.0' into main
2 parents 5928197 + 0cee983 commit d7d9115

File tree

24 files changed

+412
-99
lines changed

24 files changed

+412
-99
lines changed

.github/workflows/android.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ jobs:
2020
- name: Run detekt with ktlint
2121
run: ./gradlew detektBundledDebug detektUnbundledDebug
2222

23+
unit_tests:
24+
name: Run unit tests
25+
runs-on: ubuntu-20.04
26+
27+
steps:
28+
- uses: actions/checkout@v2
29+
- name: Run bundled and unbundled unit tests
30+
run: ./gradlew test
31+
2332
build_bundled:
2433
name: Build bundled debug
2534
runs-on: ubuntu-20.04

README.md

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@ There are two different flavors available on `mavenCentral()`:
1313

1414
| Bundled | Unbundled |
1515
| ----------------------------------- | ------------------------------------------------- |
16-
| ML Kit model is bundled inside app (independent of Google Services) | ML Kit model will be automatically downloaded via Play Services (after app install) |
16+
| ML Kit model is bundled inside app (independent of Google Services) | ML Kit model will be automatically downloaded via Play Services (once after app install) |
1717
| additional 1.1 MB per ABI (you should use App Bundle or ABI splitting) | smaller app size |
1818
| V2 model is used (possibly faster, more accurate) | currently V1 model will be downloaded
1919
```kotlin
2020
// bundled:
21-
implementation("io.github.g00fy2.quickie:quickie-bundled:1.0.0")
21+
implementation("io.github.g00fy2.quickie:quickie-bundled:1.1.0")
2222

2323
// unbundled:
24-
implementation("io.github.g00fy2.quickie:quickie-unbundled:1.0.0")
24+
implementation("io.github.g00fy2.quickie:quickie-unbundled:1.1.0")
2525
```
2626

2727
## Quick Start
28-
To use the QR scanner simply register the `ScanQRCode()` ActivityResultContract together with a callback during `init` or `onCreate()` lifecycle of your Activity/Fragment and use the returned ActivityResultLauncher to launch the QR scanner activity.
28+
To use the QR scanner simply register the `ScanQRCode()` ActivityResultContract together with a callback during `init` or `onCreate()` lifecycle of your Activity/Fragment and use the returned ActivityResultLauncher to launch the QR scanner Activity.
2929
```kotlin
3030
val scanQrCode = registerForActivityResult(ScanQRCode(), ::handleResult)
3131

@@ -47,9 +47,9 @@ The callback you add to the `registerForActivityResult` will receive a subclass
4747
4848
1. `QRSuccess` when ML Kit successfully detected a QR code
4949
* wraps a `QRContent` object
50-
1. `QRUserCanceled` when the activity got canceled by the user
50+
1. `QRUserCanceled` when the Activity got canceled by the user
5151
1. `QRMissingPermission` when the user didn't accept the camera permission
52-
1. `QRError` when CameraX or ML kit threw an exception
52+
1. `QRError` when CameraX or ML Kit threw an exception
5353
* wraps the `exception`
5454

5555
### Content
@@ -61,7 +61,49 @@ Currently, supported subtypes are:
6161
See the ML Kit [Barcode documentation](https://developers.google.com/android/reference/com/google/mlkit/vision/barcode/Barcode#nested-class-summary) for further details.
6262

6363
### Customization
64-
The library is designed to behave and look as generic as possible while matching Material Design guidelines. Currently, it's not possible to change the UI, but there are plans to add customizations in future releases.
64+
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.
65+
66+
<details>
67+
<summary>BarcodeFormat options</summary>
68+
69+
```kotlin
70+
BarcodeFormat.FORMAT_ALL_FORMATS
71+
BarcodeFormat.FORMAT_CODE_128
72+
BarcodeFormat.FORMAT_CODE_39
73+
BarcodeFormat.FORMAT_CODE_93
74+
BarcodeFormat.FORMAT_CODABAR
75+
BarcodeFormat.FORMAT_DATA_MATRIX
76+
BarcodeFormat.FORMAT_EAN_13
77+
BarcodeFormat.FORMAT_EAN_8
78+
BarcodeFormat.FORMAT_ITF
79+
BarcodeFormat.FORMAT_QR_CODE
80+
BarcodeFormat.FORMAT_UPC_A
81+
BarcodeFormat.FORMAT_UPC_E
82+
BarcodeFormat.FORMAT_PDF417
83+
BarcodeFormat.FORMAT_AZTEC
84+
```
85+
</details>
86+
87+
```kotlin
88+
val scanCustomCode = registerForActivityResult(ScanCustomCode(), ::handleResult)
89+
90+
override fun onCreate(savedInstanceState: Bundle?) {
91+
super.onCreate(savedInstanceState)
92+
93+
binding.button.setOnClickListener {
94+
scanCustomCode.launch(
95+
ScannerConfig.build {
96+
setBarcodeFormats(listOf(BarcodeFormat.FORMAT_CODE_128))
97+
setOverlayStringRes(R.string.scan_barcode)
98+
setOverlayDrawableRes(R.drawable.ic_scan_barcode)
99+
}
100+
)
101+
}
102+
}
103+
104+
fun handleResult(result: QRResult) {
105+
106+
```
65107

66108
## Screenshots / Sample App
67109
You can find the sample app APKs inside the [release](https://github.com/G00fY2/quickie/releases) assets.

build.gradle.kts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ subprojects {
2828
tasks.withType<KotlinCompile>().configureEach {
2929
kotlinOptions {
3030
allWarningsAsErrors = true
31-
val arguments = mutableListOf("-progressive")
32-
if (this@subprojects.name != "sample") arguments += "-Xexplicit-api=strict"
33-
freeCompilerArgs = freeCompilerArgs + arguments
31+
freeCompilerArgs = freeCompilerArgs + listOfNotNull(
32+
"-progressive",
33+
if (this@subprojects.name != "sample") "-Xexplicit-api=strict" else null,
34+
)
3435
jvmTarget = "1.8"
3536
}
3637
}

buildSrc/src/main/kotlin/Deps.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
object Deps {
22

33
object AndroidX {
4-
const val activity = "androidx.activity:activity:${Versions.activity}"
5-
const val fragment = "androidx.fragment:fragment:${Versions.fragment}"
64
const val appcompat = "androidx.appcompat:appcompat:${Versions.appcompat}"
7-
const val core = "androidx.core:core:${Versions.core}"
85

96
const val camera = "androidx.camera:camera-camera2:${Versions.cameraX}"
107
const val cameraLifecycle = "androidx.camera:camera-lifecycle:${Versions.cameraX}"
@@ -20,4 +17,9 @@ object Deps {
2017
const val barcodeScanningGms =
2118
"com.google.android.gms:play-services-mlkit-barcode-scanning:${Versions.barcodeScanningGms}"
2219
}
20+
21+
object Test {
22+
const val junitApi = "org.junit.jupiter:junit-jupiter-api:${Versions.junit}"
23+
const val junitEngine = "org.junit.jupiter:junit-jupiter-engine:${Versions.junit}"
24+
}
2325
}

buildSrc/src/main/kotlin/Versions.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,10 @@ object Versions {
55
const val androidTargetSdk = 30
66
const val androidBuildTools = "30.0.3"
77

8-
const val androidGradle = "4.2.0"
8+
const val androidGradle = "4.2.1"
99
const val kotlin = "1.5.0"
1010

11-
const val activity = "1.2.3"
12-
const val fragment = "1.3.3"
13-
const val appcompat = "1.2.0"
14-
const val core = "1.3.2"
11+
const val appcompat = "1.3.0"
1512

1613
const val cameraX = "1.0.0"
1714
const val cameraView = "1.0.0-alpha24"
@@ -21,10 +18,12 @@ object Versions {
2118
const val barcodeScanning = "16.1.1"
2219
const val barcodeScanningGms = "16.1.4"
2320

24-
const val detekt = "1.17.0-RC2"
21+
const val detekt = "1.17.1"
2522
const val gradleVersions = "0.38.0"
2623
const val dokka = "1.4.32"
2724

25+
const val junit = "5.7.2"
26+
2827
fun maturityLevel(version: String): Int {
2928
val levels = listOf("alpha", "beta", "m", "rc")
3029
levels.forEachIndexed { index, s ->

config/detekt/detekt.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ formatting:
1717
ParameterListWrapping:
1818
indentSize: 2
1919

20+
performance:
21+
SpreadOperator:
22+
active: false
23+
2024
style:
2125
MagicNumber:
2226
active: false
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0-all.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists

quickie/build.gradle.kts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,26 @@ android {
2424
getByName("main").java.srcDirs("src/main/kotlin")
2525
getByName("bundled").java.srcDirs("src/bundled/kotlin")
2626
getByName("unbundled").java.srcDirs("src/unbundled/kotlin")
27+
getByName("test").java.srcDirs("src/test/kotlin")
2728
}
2829
}
2930

30-
val bundledImplementation by configurations
31-
val unbundledImplementation by configurations
3231
dependencies {
33-
implementation(Deps.AndroidX.activity)
34-
implementation(Deps.AndroidX.fragment)
3532
implementation(Deps.AndroidX.appcompat)
36-
implementation(Deps.AndroidX.core)
3733

3834
implementation(Deps.AndroidX.camera)
3935
implementation(Deps.AndroidX.cameraLifecycle)
4036
implementation(Deps.AndroidX.cameraPreview)
4137

42-
bundledImplementation(Deps.MLKit.barcodeScanning)
43-
unbundledImplementation(Deps.MLKit.barcodeScanningGms)
38+
"bundledImplementation"(Deps.MLKit.barcodeScanning)
39+
"unbundledImplementation"(Deps.MLKit.barcodeScanningGms)
40+
41+
testImplementation(Deps.Test.junitApi)
42+
testRuntimeOnly(Deps.Test.junitEngine)
4443
}
4544

4645
group = "io.github.g00fy2.quickie"
47-
version = "1.0.0"
46+
version = "1.1.0"
4847

4948
tasks.register<Jar>("androidJavadocJar") {
5049
archiveClassifier.set("javadoc")
@@ -58,6 +57,12 @@ tasks.register<Jar>("androidSourcesJar") {
5857
}
5958

6059
afterEvaluate {
60+
tasks.withType<Test>().configureEach {
61+
useJUnitPlatform()
62+
testLogging.events("failed", "passed", "skipped")
63+
enabled = name.endsWith("DebugUnitTest")
64+
}
65+
6166
publishing {
6267
publications {
6368
create<MavenPublication>("bundledRelease") {

quickie/src/main/kotlin/io/github/g00fy2/quickie/QRCodeAnalyzer.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,20 @@ import com.google.mlkit.vision.barcode.BarcodeScanning
1010
import com.google.mlkit.vision.common.InputImage
1111

1212
@ExperimentalGetImage
13-
internal class QRCodeAnalyzer(val onSuccess: ((Barcode) -> Unit), val onFailure: ((Exception) -> Unit)) :
14-
ImageAnalysis.Analyzer {
13+
internal class QRCodeAnalyzer(
14+
private val barcodeFormats: IntArray,
15+
private val onSuccess: ((Barcode) -> Unit),
16+
private val onFailure: ((Exception) -> Unit)
17+
) : ImageAnalysis.Analyzer {
1518

1619
private var pendingTask: Task<List<Barcode>>? = null
1720
private val barcodeScanner by lazy {
18-
BarcodeScanning.getClient(BarcodeScannerOptions.Builder().setBarcodeFormats(Barcode.FORMAT_QR_CODE).build())
21+
val optionsBuilder = if (barcodeFormats.size > 1) {
22+
BarcodeScannerOptions.Builder().setBarcodeFormats(barcodeFormats.first(), *barcodeFormats.drop(1).toIntArray())
23+
} else {
24+
BarcodeScannerOptions.Builder().setBarcodeFormats(barcodeFormats.firstOrNull() ?: Barcode.FORMAT_UNKNOWN)
25+
}
26+
BarcodeScanning.getClient(optionsBuilder.build())
1927
}
2028

2129
override fun analyze(imageProxy: ImageProxy) {

quickie/src/main/kotlin/io/github/g00fy2/quickie/QROverlayView.kt

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.github.g00fy2.quickie
22

33
import android.content.Context
4+
import android.content.res.Resources.NotFoundException
45
import android.graphics.Bitmap
56
import android.graphics.Bitmap.Config.ARGB_8888
67
import android.graphics.Canvas
@@ -9,13 +10,15 @@ import android.graphics.Paint
910
import android.graphics.PorterDuff.Mode.CLEAR
1011
import android.graphics.PorterDuffXfermode
1112
import android.graphics.RectF
13+
import android.graphics.drawable.Drawable
1214
import android.util.AttributeSet
1315
import android.util.TypedValue
1416
import android.view.LayoutInflater
1517
import android.view.View
1618
import android.widget.FrameLayout
1719
import androidx.appcompat.widget.AppCompatTextView
1820
import androidx.core.content.ContextCompat
21+
import androidx.core.content.res.ResourcesCompat
1922
import androidx.core.graphics.ColorUtils
2023
import io.github.g00fy2.quickie.databinding.QuickieTextviewBinding
2124
import kotlin.math.min
@@ -29,12 +32,11 @@ internal class QROverlayView @JvmOverloads constructor(
2932

3033
private val strokeColor = ContextCompat.getColor(context, R.color.quickie_stroke)
3134
private val highlightedStrokeColor = getAccentColor()
32-
private val backgroundColor =
33-
ColorUtils.setAlphaComponent(ContextCompat.getColor(context, R.color.quickie_black), BACKGROUND_ALPHA.roundToInt())
35+
private val backgroundColor = ColorUtils.setAlphaComponent(Color.BLACK, BACKGROUND_ALPHA.roundToInt())
3436
private val alphaPaint = Paint().apply { alpha = BACKGROUND_ALPHA.roundToInt() }
3537
private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG)
3638
private val transparentPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
37-
color = ContextCompat.getColor(context, R.color.quickie_transparent)
39+
color = Color.TRANSPARENT
3840
xfermode = PorterDuffXfermode(CLEAR)
3941
}
4042
private val radius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, OUT_RADIUS, resources.displayMetrics)
@@ -76,6 +78,25 @@ internal class QROverlayView @JvmOverloads constructor(
7678
super.onDraw(canvas)
7779
}
7880

81+
fun setCustomTextAndIcon(stringRes: Int, drawableRes: Int) {
82+
if (stringRes != 0) {
83+
try {
84+
titleTextView.setText(stringRes)
85+
} catch (ignore: NotFoundException) {
86+
// string resource not found
87+
}
88+
}
89+
if (drawableRes != 0) {
90+
try {
91+
ResourcesCompat.getDrawable(resources, drawableRes, null)?.limitDrawableSize(ICON_DP_MAX_HEIGHT)?.let {
92+
titleTextView.setCompoundDrawables(null, it, null, null)
93+
}
94+
} catch (ignore: NotFoundException) {
95+
// drawable resource not found
96+
}
97+
}
98+
}
99+
79100
private fun calculateFrameAndTitlePos() {
80101
val centralX = width / 2
81102
val centralY = height / 2
@@ -114,10 +135,18 @@ internal class QROverlayView @JvmOverloads constructor(
114135
layoutParams = params
115136
}
116137

138+
private fun Drawable.limitDrawableSize(maxDpHeight: Int): Drawable {
139+
val scale = (maxDpHeight * resources.displayMetrics.density) / minimumHeight
140+
if (scale < 1) setBounds(0, 0, (minimumWidth * scale).roundToInt(), (minimumHeight * scale).roundToInt())
141+
else setBounds(0, 0, minimumWidth, minimumHeight)
142+
return this
143+
}
144+
117145
companion object {
118146
private const val BACKGROUND_ALPHA = 0.77 * 255
119147
private const val STROKE_WIDTH = 4f
120148
private const val OUT_RADIUS = 16f
121149
private const val FRAME_MARGIN_RATIO = 1f / 4
150+
private const val ICON_DP_MAX_HEIGHT = 56
122151
}
123152
}

0 commit comments

Comments
 (0)