Skip to content

Commit

Permalink
Merge branch 'release/0.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
G00fY2 committed Dec 6, 2020
2 parents da5fed2 + 818247f commit 05e6354
Show file tree
Hide file tree
Showing 17 changed files with 157 additions and 96 deletions.
72 changes: 70 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,80 @@
<img width="345" height="120" src="https://raw.githubusercontent.com/G00fY2/Quickie/gh-pages/media/logo.png">
</p>

**quickie** is an Quick Response (QR) Code scanning library for Android that is based on CameraX and ML Kit on-device barcode detection. It's written in Kotlin and allows easy integration by using the AndroidX Activity Result API. quickie features:
**quickie** is a Quick Response (QR) Code scanning library for Android that is based on CameraX and ML Kit on-device barcode detection. It's an alternative to ZXing and written in Kotlin. quickie features:
- Easy API for launching the QR scanner and receiving results by using the AndroidX Activity Result API
- Modern design, edge-to-edge scanning view with multilingual user hint
- Easy API for launching the QR scanner and receiving results
- Android Jetpack CameraX for communicating with the camera and showing the preview
- Firebase ML Kit on-device barcode recognition and decoding (no network connection required)

## Download [![Download](https://img.shields.io/maven-metadata/v?label=quickie-bundled&metadataUrl=https%3A%2F%2Fbintray.com%2Fg00fy2%2Fmaven%2Fdownload_file%3Ffile_path%3Dcom%252Fg00fy2%252Fquickie%252Fquickie-unbundled%252Fmaven-metadata.xml)](https://bintray.com/g00fy2/maven/quickie-bundled) [![Download](https://img.shields.io/maven-metadata/v?label=quickie-unbundled&metadataUrl=https%3A%2F%2Fbintray.com%2Fg00fy2%2Fmaven%2Fdownload_file%3Ffile_path%3Dcom%252Fg00fy2%252Fquickie%252Fquickie-bundled%252Fmaven-metadata.xml)](https://bintray.com/g00fy2/maven/quickie-unbundled)
There are two different flavors available on `jcenter()`:

| Bundled | Unbundled |
| ----------------------------------- | ------------------------------------------------- |
| ML Kit model is bundled inside app (independed of Google Services) | ML Kit model will be automatically downloaded via Play Services (after app install) |
| additional 1.1 MB size per ABI (you should use AAB or ABI splitting) | smaller app size |
| V2 barcode model is used (possibly faster, more accurate) | currently V1 will be downloaded
```kotlin
// bundled:
implementation("com.g00fy2.quickie:quickie-bundled:0.2.0")

// unbundled:
implementation("com.g00fy2.quickie:quickie-unbundled:0.2.0")
```

## Quick Start
To use the QR scanner simply register the `ScanQRCode()` ActivityResultContract together with a callback during `initialization` or in the `onCreate()` lifecycle of your Activity/Fragment and call `launch(null)` anywhere to start it:
```kotlin
private val scanQrCode = registerForActivityResult(ScanQRCode()) { handleResult(it) }

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...

binding.buttonQrScanner.setOnClickListener { scanQrCode.launch(null) }
}
```
⚠️ **You can't register the ActivityResultContract inside of the OnClickListener. This will fail since the code get's executed after the onCreate lifecycle!**

Check out the [samples](https://github.com/G00fY2/quickie/tree/master/sample) inside this repo or visit the [Activity Result API documentation](https://developer.android.com/training/basics/intents/result) for more information.

#### Responses
The callback you add to the `registerForActivityResult` will receive an instance of the sealed `QRResult` class:

1. `QRSuccess` when ML Kit successfully detected a QR code
1. wraps a `QRContent` object
1. `QRUserCanceled` when the activity got canceled by the user
1. `QRMissingPermission` when the user didn't accept the camera permission
1. `QRError` when CameraX or ML kit threw an exception
1. wraps the `exception`

#### Content
**quickie** wraps the content type of the QR code detected by ML Kit inside an sealed `QRContent` class. All of them provide the `rawValue`.

Currently supported types are:
`Plain`, `Wifi`, `Url`, `Sms`, `GeoPoint`, `Email`, `Phone`, `ContactInfo`, `CalendarEvent`

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
The library is designed to behave and look as generic as possible. Currently it's not possible to change the UI, but there are plans to add customizations in future releases.

### Screenshots
![Image](https://raw.githubusercontent.com/G00fY2/Quickie/gh-pages/media/quickie-device-demo.png)

## Release state
**quickie** relies on Google Jetpack libraries which are in pre-release state. CameraX has no stable release version yet and the Activity Result API is part of the latest AndroidX Activity and Fragment beta releases. Here is what Google says about this release state:
* Beta releases are functionally stable and have a feature-complete API surface.
* They are ready for production use but may contain bugs.

You should consider **quickie** to be in beta state too. I will raise the version to 1.0 once all dependent libraries hit stable.

## Requirements
* AndroidX
* Min SDK 21+
* (Google Play Services available on the end device if using `quickie-unbundled`)

## License
The MIT License (MIT)

Expand Down
22 changes: 18 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import com.android.build.gradle.BaseExtension
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
id(Plugins.Android.application) version Versions.androidGradle apply false
Expand All @@ -13,24 +15,36 @@ subprojects {
version.set(Versions.ktlint)
android.set(true)
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
allWarningsAsErrors = true
freeCompilerArgs = listOf("-progressive")
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
afterEvaluate {
extensions.configure<BaseExtension> {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
}
}

tasks.named("dependencyUpdates", DependencyUpdatesTask::class.java).configure {
gradleReleaseChannel = "current"
resolutionStrategy {
componentSelection {
all {
if (isNonStable(candidate.version) && !isNonStable(currentVersion)) {
if (Utils.isNonStable(candidate.version) && !Utils.isNonStable(currentVersion)) {
reject("Release candidate")
}
}
}
}
}

fun isNonStable(version: String) = listOf("alpha", "beta", "rc", "cr", "m", "preview")
.any { version.matches(".*[.\\-]$it[.\\-\\d]*".toRegex(RegexOption.IGNORE_CASE)) }

repositories {
mavenCentral()
}
Expand Down
5 changes: 5 additions & 0 deletions buildSrc/src/main/kotlin/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
object Utils {

fun isNonStable(version: String) = listOf("alpha", "beta", "rc", "cr", "m", "preview")
.any { version.matches(".*[.\\-]$it[.\\-\\d]*".toRegex(RegexOption.IGNORE_CASE)) }
}
10 changes: 5 additions & 5 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ object Versions {
const val kotlin = "1.4.20"

const val ktlintPlugin = "9.4.1"
const val ktlint = "0.39.0"
const val ktlint = "0.40.0"
const val gradleVersions = "0.36.0"
const val dokka = "1.4.10.2"
const val dokka = "1.4.20"

const val activity = "1.2.0-beta01"
const val fragment = "1.3.0-beta01"
const val activity = "1.2.0-beta02"
const val fragment = "1.3.0-beta02"
const val appcompat = "1.2.0"
const val core = "1.3.2"

const val navigation = "2.3.1"
const val navigation = "2.3.2"

const val camera = "1.0.0-beta12"
const val cameraLifecycle = "1.0.0-beta12"
Expand Down
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Allow usage of AndroidX instead of the old support libraries.
android.useAndroidX=true
# Use R8 in full mode instead of ProGuard compatibility mode.
android.enableR8.fullMode=true
# Set the build VMs heap size.
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# Disable publishing SHA256 and SHA512 checksums.
Expand Down
27 changes: 5 additions & 22 deletions quickie/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,18 @@ plugins {

android {
compileSdkVersion(Config.androidCompileSdkVersion)
defaultConfig {
minSdkVersion(Config.androidMinSdkVersion)
}
defaultConfig.minSdkVersion(Config.androidMinSdkVersion)
resourcePrefix = project.name
buildFeatures {
viewBinding = true
buildConfig = false
}
sourceSets {
getByName("main") {
java.srcDirs("src/main/kotlin")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
flavorDimensions("mlkit")
productFlavors {
create("bundled") {
dimension("mlkit")
}
create("unbundled") {
dimension("mlkit")
}
create("bundled").dimension("mlkit")
create("unbundled").dimension("mlkit")
}
sourceSets.getByName("main").java.srcDirs("src/main/kotlin")
}

repositories {
Expand Down Expand Up @@ -67,7 +50,7 @@ dependencies {
}

group = "com.g00fy2.quickie"
version = "0.1.0"
version = "0.2.0"

tasks.register<Jar>("androidJavadocJar") {
archiveClassifier.set("javadoc")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.util.Size
import android.view.View
import androidx.activity.ComponentActivity
Expand Down Expand Up @@ -102,7 +101,6 @@ internal class QRScannerActivity : ComponentActivity() {

private fun onFailure(exception: Exception) {
setResult(RESULT_ERROR, Intent().putExtra(EXTRA_RESULT_EXCEPTION, exception))
Log.e(localClassName, exception.message, exception)
finish()
}

Expand Down
4 changes: 3 additions & 1 deletion sample/activity-sample/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/build
/release
/release
/bundled
/unbundled
29 changes: 11 additions & 18 deletions sample/activity-sample/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ android {
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
splits {
abi {
isEnable = true
reset()
include("x86", "armeabi-v7a", "arm64-v8a", "x86_64")
isUniversalApk = true
}
}
flavorDimensions("mlkit")
productFlavors {
create("bundled") {
dimension("mlkit")
}
create("unbundled") {
dimension("mlkit")
}
create("bundled").dimension("mlkit")
create("unbundled").dimension("mlkit")
}
buildFeatures {
viewBinding = true
Expand All @@ -36,18 +40,7 @@ android {
resValues = false
shaders = false
}
sourceSets {
getByName("main") {
java.srcDirs("src/main/kotlin")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
sourceSets.getByName("main").java.srcDirs("src/main/kotlin")
}

repositories {
Expand Down
3 changes: 3 additions & 0 deletions sample/activity-sample/src/main/res/raw/keep.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:shrinkMode="strict"/>
4 changes: 3 additions & 1 deletion sample/advanced-sample/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/build
/release
/release
/bundled
/unbundled
29 changes: 11 additions & 18 deletions sample/advanced-sample/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ android {
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
splits {
abi {
isEnable = true
reset()
include("x86", "armeabi-v7a", "arm64-v8a", "x86_64")
isUniversalApk = true
}
}
flavorDimensions("mlkit")
productFlavors {
create("bundled") {
dimension("mlkit")
}
create("unbundled") {
dimension("mlkit")
}
create("bundled").dimension("mlkit")
create("unbundled").dimension("mlkit")
}
buildFeatures {
viewBinding = true
Expand All @@ -36,18 +40,7 @@ android {
resValues = false
shaders = false
}
sourceSets {
getByName("main") {
java.srcDirs("src/main/kotlin")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
sourceSets.getByName("main").java.srcDirs("src/main/kotlin")
}

repositories {
Expand Down
3 changes: 3 additions & 0 deletions sample/advanced-sample/src/main/res/raw/keep.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:shrinkMode="strict"/>
4 changes: 3 additions & 1 deletion sample/fragment-sample/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/build
/release
/release
/bundled
/unbundled
Loading

0 comments on commit 05e6354

Please sign in to comment.