diff --git a/.editorconfig b/.editorconfig index 5dc12414..aee9023b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,6 +14,7 @@ indent_style=space ij_kotlin_name_count_to_use_star_import=2147483647 ij_kotlin_name_count_to_use_star_import_for_members=2147483647 ij_kotlin_imports_layout=*,java.**,javax.**,kotlin.**,^ +ij_kotlin_line_break_after_multiline_when_entry=false [*.xml] ij_continuation_indent_size=4 diff --git a/.github/renovate.json b/.github/renovate.json index 97dc8aad..2a011b95 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -1,8 +1,9 @@ { "extends": [ - "config:base" + "config:base", + ":disableDependencyDashboard" ], "baseBranches": [ "develop" ] -} \ No newline at end of file +} diff --git a/README.md b/README.md index f0b724e1..67595fdf 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,10 @@ There are two different flavors available on `mavenCentral()`: ```kotlin // bundled: -implementation("io.github.g00fy2.quickie:quickie-bundled:1.7.0") +implementation("io.github.g00fy2.quickie:quickie-bundled:1.8.0") // unbundled: -implementation("io.github.g00fy2.quickie:quickie-unbundled:1.7.0") +implementation("io.github.g00fy2.quickie:quickie-unbundled:1.8.0") ``` ## Quick Start @@ -65,13 +65,13 @@ The activity result is a subclass of the sealed `QRResult` class: 1. `QRSuccess` when ML Kit successfully detected a QR code * 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 +2. `QRUserCanceled` when the Activity got canceled by the user +3. `QRMissingPermission` when the user didn't accept the camera permission +4. `QRError` when CameraX or ML Kit threw an exception * wraps the `exception` #### Content -The content type of the QR code detected by ML Kit is wrapped inside a subclass of the sealed `QRContent` class which always provides a `rawValue`. +The content type of the QR code detected by ML Kit is wrapped inside a subclass of the sealed `QRContent` class which always provides a `rawBytes` and `rawValue` (will only be `null` for non-UTF8 barcodes). Currently, supported subtypes are: `Plain`, `Wifi`, `Url`, `Sms`, `GeoPoint`, `Email`, `Phone`, `ContactInfo`, `CalendarEvent` diff --git a/build.gradle.kts b/build.gradle.kts index db089796..7822f923 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ import com.android.build.gradle.BaseExtension import com.android.build.gradle.BasePlugin import io.gitlab.arturbosch.detekt.Detekt import io.gitlab.arturbosch.detekt.extensions.DetektExtension +import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -11,36 +12,21 @@ plugins { alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.parcelize) apply false alias(libs.plugins.kotlin.dokka) apply false - alias(libs.plugins.misc.detekt) apply false - alias(libs.plugins.misc.gradleVersions) + alias(libs.plugins.detekt) apply false } subprojects { - apply(plugin = rootProject.libs.plugins.misc.detekt.get().pluginId) - extensions.configure { - toolVersion = rootProject.libs.versions.detekt.get() - config = files("$rootDir/detekt.yml") - buildUponDefaultConfig = true - ignoredBuildTypes = listOf("release") - } - dependencies { - add("detektPlugins", rootProject.libs.misc.detektFormatting) - } - tasks.withType().configureEach { - jvmTarget = "11" - } tasks.withType().configureEach { compilerOptions { allWarningsAsErrors.set(true) - freeCompilerArgs.addAll( - listOfNotNull( - "-progressive", - "-Xexplicit-api=strict".takeIf { (this@subprojects.name != "sample") }, - ) - ) + progressiveMode.set(true) jvmTarget.set(JvmTarget.JVM_11) } + if ((this@subprojects.name != "sample")) { + explicitApiMode.set(ExplicitApiMode.Strict) + } } + plugins.withType().configureEach { extensions.configure { compileSdkVersion(libs.versions.androidconfig.compileSdk.get().toInt()) @@ -55,20 +41,18 @@ subprojects { } } } -} -tasks.dependencyUpdates.configure { - gradleReleaseChannel = "current" - - fun releaseType(version: String): Int { - val qualifiers = listOf("alpha", "beta", "m", "rc") - val index = qualifiers.indexOfFirst { version.matches(".*[.\\-]$it[.\\-\\d]*".toRegex(RegexOption.IGNORE_CASE)) } - return if (index < 0) qualifiers.size else index + apply(plugin = rootProject.libs.plugins.detekt.get().pluginId) + extensions.configure { + toolVersion = rootProject.libs.versions.detekt.get() + config.setFrom(files("$rootDir/detekt.yml")) + buildUponDefaultConfig = true + ignoredBuildTypes = listOf("release") + } + dependencies { + add("detektPlugins", rootProject.libs.detektFormatting) + } + tasks.withType().configureEach { + jvmTarget = JvmTarget.JVM_11.target } - - rejectVersionIf { releaseType(candidate.version) < releaseType(currentVersion) } -} - -tasks.register("clean") { - delete(buildDir) } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4920bd27..63c8d6bc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,34 +1,35 @@ [versions] -quickie = "1.7.0" +quickie = "1.8.0" androidconfig-minSdk = "21" androidconfig-compileSdk = "33" androidconfig-targetSdk = "33" androidconfig-buildTools = "33.0.2" -androidGradle = "8.0.1" -kotlin = "1.8.21" +androidGradle = "8.0.2" +kotlin = "1.9.0" appcompat = "1.6.1" +core = "1.10.1" -cameraX = "1.2.2" +cameraX = "1.2.3" barcodeScanning = "17.1.0" barcodeScanningGms = "18.2.0" materialDesign = "1.9.0" -detekt = "1.22.0" -gradleVersions = "0.46.0" -dokka = "1.8.10" +detekt = "1.23.0" +dokka = "1.8.20" -junit = "5.9.3" +junit = "5.10.0" [libraries] androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } androidx-camera = { module = "androidx.camera:camera-camera2", version.ref = "cameraX" } androidx-cameraLifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "cameraX" } androidx-cameraPreview = { module = "androidx.camera:camera-view", version.ref = "cameraX" } +androidx-core = { module = "androidx.core:core-ktx", version.ref = "core" } mlkit-barcodeScanning = { module = "com.google.mlkit:barcode-scanning", version.ref = "barcodeScanning" } mlkit-barcodeScanningGms = { module = "com.google.android.gms:play-services-mlkit-barcode-scanning", version.ref = "barcodeScanningGms" } @@ -38,7 +39,7 @@ google-materialDesign = { module = "com.google.android.material:material", versi test-junitApi = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" } test-junitEngine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" } -misc-detektFormatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } +detektFormatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } [plugins] android-application = { id = "com.android.application", version.ref = "androidGradle" } @@ -48,5 +49,4 @@ kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } kotlin-dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } -misc-detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } -misc-gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" } \ No newline at end of file +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c1962a79..033e24c4 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37aef8d3..9f4197d5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index aeb74cbb..fcb6fca1 100755 --- a/gradlew +++ b/gradlew @@ -130,10 +130,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. diff --git a/gradlew.bat b/gradlew.bat index 6689b85b..93e3f59f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,92 +1,92 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/quickie/build.gradle.kts b/quickie/build.gradle.kts index 1f936559..9c6f9db0 100644 --- a/quickie/build.gradle.kts +++ b/quickie/build.gradle.kts @@ -30,6 +30,7 @@ android { dependencies { implementation(libs.androidx.appcompat) + implementation(libs.androidx.core) implementation(libs.androidx.camera) implementation(libs.androidx.cameraLifecycle) diff --git a/quickie/src/main/kotlin/io/github/g00fy2/quickie/QRScannerActivity.kt b/quickie/src/main/kotlin/io/github/g00fy2/quickie/QRScannerActivity.kt index 98ed1811..8393658b 100644 --- a/quickie/src/main/kotlin/io/github/g00fy2/quickie/QRScannerActivity.kt +++ b/quickie/src/main/kotlin/io/github/g00fy2/quickie/QRScannerActivity.kt @@ -19,6 +19,7 @@ import androidx.camera.core.Preview import androidx.camera.core.TorchState import androidx.camera.lifecycle.ProcessCameraProvider import androidx.core.content.ContextCompat +import androidx.core.content.IntentCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat @@ -143,14 +144,13 @@ internal class QRScannerActivity : AppCompatActivity() { binding.overlayView.isHighlighted = true if (hapticFeedback) { @Suppress("DEPRECATION") - binding.overlayView.performHapticFeedback( - HapticFeedbackConstants.KEYBOARD_TAP, - HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING or HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING - ) + val flags = HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING or HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING + binding.overlayView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, flags) } setResult( Activity.RESULT_OK, Intent().apply { + putExtra(EXTRA_RESULT_BYTES, result.rawBytes) putExtra(EXTRA_RESULT_VALUE, result.rawValue) putExtra(EXTRA_RESULT_TYPE, result.valueType) putExtra(EXTRA_RESULT_PARCELABLE, result.toParcelableContentType()) @@ -177,8 +177,7 @@ internal class QRScannerActivity : AppCompatActivity() { } private fun applyScannerConfig() { - @Suppress("DEPRECATION") - intent?.getParcelableExtra(EXTRA_CONFIG)?.let { + intent?.let { IntentCompat.getParcelableExtra(it, EXTRA_CONFIG, ParcelableScannerConfig::class.java) }?.let { barcodeFormats = it.formats binding.overlayView.setCustomText(it.stringRes) binding.overlayView.setCustomIcon(it.drawableRes) @@ -200,6 +199,7 @@ internal class QRScannerActivity : AppCompatActivity() { companion object { const val EXTRA_CONFIG = "quickie-config" + const val EXTRA_RESULT_BYTES = "quickie-bytes" const val EXTRA_RESULT_VALUE = "quickie-value" const val EXTRA_RESULT_TYPE = "quickie-type" const val EXTRA_RESULT_PARCELABLE = "quickie-parcelable" diff --git a/quickie/src/main/kotlin/io/github/g00fy2/quickie/content/QRContent.kt b/quickie/src/main/kotlin/io/github/g00fy2/quickie/content/QRContent.kt index 37d7e08e..c0e78945 100644 --- a/quickie/src/main/kotlin/io/github/g00fy2/quickie/content/QRContent.kt +++ b/quickie/src/main/kotlin/io/github/g00fy2/quickie/content/QRContent.kt @@ -1,55 +1,71 @@ package io.github.g00fy2.quickie.content -public sealed class QRContent(public open val rawValue: String) { - - override fun toString(): String = rawValue +@Suppress("ArrayInDataClass") +public sealed class QRContent( + public open val rawBytes: ByteArray?, + public open val rawValue: String?, +) { /** * Plain text or unknown content QR Code type. */ - public data class Plain internal constructor(override val rawValue: String) : QRContent(rawValue) + public data class Plain internal constructor( + override val rawBytes: ByteArray?, + override val rawValue: String? + ) : QRContent(rawBytes, rawValue) /** * Wi-Fi access point details from a 'WIFI:' or similar QR Code type. */ public data class Wifi internal constructor( - override val rawValue: String, + override val rawBytes: ByteArray?, + override val rawValue: String?, val encryptionType: Int, val password: String, val ssid: String - ) : QRContent(rawValue) + ) : QRContent(rawBytes, rawValue) /** * A URL or URL bookmark from a 'MEBKM:' or similar QR Code type. */ - public data class Url internal constructor(override val rawValue: String, val title: String, val url: String) : - QRContent(rawValue) + public data class Url internal constructor( + override val rawBytes: ByteArray?, + override val rawValue: String?, + val title: String, + val url: String + ) : QRContent(rawBytes, rawValue) /** * An SMS message from an 'SMS:' or similar QR Code type. */ public data class Sms internal constructor( - override val rawValue: String, + override val rawBytes: ByteArray?, + override val rawValue: String?, val message: String, val phoneNumber: String - ) : QRContent(rawValue) + ) : QRContent(rawBytes, rawValue) /** * GPS coordinates from a 'GEO:' or similar QR Code type. */ - public data class GeoPoint internal constructor(override val rawValue: String, val lat: Double, val lng: Double) : - QRContent(rawValue) + public data class GeoPoint internal constructor( + override val rawBytes: ByteArray?, + override val rawValue: String?, + val lat: Double, + val lng: Double + ) : QRContent(rawBytes, rawValue) /** * An email message from a 'MAILTO:' or similar QR Code type. */ public data class Email internal constructor( - override val rawValue: String, + override val rawBytes: ByteArray?, + override val rawValue: String?, val address: String, val body: String, val subject: String, val type: EmailType - ) : QRContent(rawValue) { + ) : QRContent(rawBytes, rawValue) { public enum class EmailType { UNKNOWN, WORK, HOME } @@ -58,8 +74,12 @@ public sealed class QRContent(public open val rawValue: String) { /** * A phone number from a 'TEL:' or similar QR Code type. */ - public data class Phone internal constructor(override val rawValue: String, val number: String, val type: PhoneType) : - QRContent(rawValue) { + public data class Phone internal constructor( + override val rawBytes: ByteArray?, + override val rawValue: String?, + val number: String, + val type: PhoneType + ) : QRContent(rawBytes, rawValue) { public enum class PhoneType { UNKNOWN, WORK, HOME, FAX, MOBILE } @@ -69,7 +89,8 @@ public sealed class QRContent(public open val rawValue: String) { * A person's or organization's business card. */ public data class ContactInfo internal constructor( - override val rawValue: String, + override val rawBytes: ByteArray?, + override val rawValue: String?, val addresses: List
, val emails: List, val name: PersonName, @@ -77,7 +98,7 @@ public sealed class QRContent(public open val rawValue: String) { val phones: List, val title: String, val urls: List - ) : QRContent(rawValue) { + ) : QRContent(rawBytes, rawValue) { public data class Address internal constructor(val addressLines: List, val type: AddressType) { public enum class AddressType { @@ -100,7 +121,8 @@ public sealed class QRContent(public open val rawValue: String) { * A calendar event extracted from a QR Code. */ public data class CalendarEvent internal constructor( - override val rawValue: String, + override val rawBytes: ByteArray?, + override val rawValue: String?, val description: String, val end: CalendarDateTime, val location: String, @@ -108,7 +130,7 @@ public sealed class QRContent(public open val rawValue: String) { val start: CalendarDateTime, val status: String, val summary: String - ) : QRContent(rawValue) { + ) : QRContent(rawBytes, rawValue) { public data class CalendarDateTime internal constructor( val day: Int, diff --git a/quickie/src/main/kotlin/io/github/g00fy2/quickie/extensions/IntentExtensions.kt b/quickie/src/main/kotlin/io/github/g00fy2/quickie/extensions/IntentExtensions.kt index 2f2f9edd..92709e7a 100644 --- a/quickie/src/main/kotlin/io/github/g00fy2/quickie/extensions/IntentExtensions.kt +++ b/quickie/src/main/kotlin/io/github/g00fy2/quickie/extensions/IntentExtensions.kt @@ -1,10 +1,13 @@ -@file:Suppress("DEPRECATION") - package io.github.g00fy2.quickie.extensions import android.content.Intent +import androidx.core.content.IntentCompat import com.google.mlkit.vision.barcode.common.Barcode import io.github.g00fy2.quickie.QRScannerActivity +import io.github.g00fy2.quickie.QRScannerActivity.Companion.EXTRA_RESULT_BYTES +import io.github.g00fy2.quickie.QRScannerActivity.Companion.EXTRA_RESULT_EXCEPTION +import io.github.g00fy2.quickie.QRScannerActivity.Companion.EXTRA_RESULT_PARCELABLE +import io.github.g00fy2.quickie.QRScannerActivity.Companion.EXTRA_RESULT_VALUE import io.github.g00fy2.quickie.content.AddressParcelable import io.github.g00fy2.quickie.content.CalendarDateTimeParcelable import io.github.g00fy2.quickie.content.CalendarEventParcelable @@ -34,74 +37,104 @@ import io.github.g00fy2.quickie.content.UrlBookmarkParcelable import io.github.g00fy2.quickie.content.WifiParcelable internal fun Intent?.toQuickieContentType(): QRContent { - val rawValue = this?.getStringExtra(QRScannerActivity.EXTRA_RESULT_VALUE).orEmpty() - return this?.toQuickieContentType(rawValue) ?: Plain(rawValue) + val rawBytes = this?.getByteArrayExtra(EXTRA_RESULT_BYTES) + val rawValue = this?.getStringExtra(EXTRA_RESULT_VALUE) + return this?.toQuickieContentType(rawBytes, rawValue) ?: Plain(rawBytes, rawValue) } @Suppress("LongMethod") -private fun Intent.toQuickieContentType(rawValue: String): QRContent? { - return when (getIntExtra(QRScannerActivity.EXTRA_RESULT_TYPE, Barcode.TYPE_UNKNOWN)) { +private fun Intent.toQuickieContentType(rawBytes: ByteArray?, rawValue: String?): QRContent? { + return when (extras?.getInt(QRScannerActivity.EXTRA_RESULT_TYPE, Barcode.TYPE_UNKNOWN)) { Barcode.TYPE_CONTACT_INFO -> { - getParcelableExtra(QRScannerActivity.EXTRA_RESULT_PARCELABLE)?.run { + IntentCompat.getParcelableExtra(this, EXTRA_RESULT_PARCELABLE, ContactInfoParcelable::class.java)?.let { ContactInfo( + rawBytes = rawBytes, rawValue = rawValue, - addresses = addressParcelables.map { it.toAddress() }, - emails = emailParcelables.map { it.toEmail(rawValue) }, - name = nameParcelable.toPersonName(), - organization = organization, - phones = phoneParcelables.map { it.toPhone(rawValue) }, - title = title, - urls = urls + addresses = it.addressParcelables.map { address -> address.toAddress() }, + emails = it.emailParcelables.map { mail -> mail.toEmail(rawBytes, rawValue) }, + name = it.nameParcelable.toPersonName(), + organization = it.organization, + phones = it.phoneParcelables.map { phone -> phone.toPhone(rawBytes, rawValue) }, + title = it.title, + urls = it.urls ) } } Barcode.TYPE_EMAIL -> { - getParcelableExtra(QRScannerActivity.EXTRA_RESULT_PARCELABLE)?.run { + IntentCompat.getParcelableExtra(this, EXTRA_RESULT_PARCELABLE, EmailParcelable::class.java)?.let { Email( + rawBytes = rawBytes, rawValue = rawValue, - address = address, - body = body, - subject = subject, - type = EmailType.values().getOrElse(type) { EmailType.UNKNOWN } + address = it.address, + body = it.body, + subject = it.subject, + type = EmailType.entries.getOrElse(it.type) { EmailType.UNKNOWN } ) } } Barcode.TYPE_PHONE -> { - getParcelableExtra(QRScannerActivity.EXTRA_RESULT_PARCELABLE)?.run { - Phone(rawValue = rawValue, number = number, type = PhoneType.values().getOrElse(type) { PhoneType.UNKNOWN }) + IntentCompat.getParcelableExtra(this, EXTRA_RESULT_PARCELABLE, PhoneParcelable::class.java)?.let { + Phone( + rawBytes = rawBytes, + rawValue = rawValue, + number = it.number, + type = PhoneType.entries.getOrElse(it.type) { PhoneType.UNKNOWN } + ) } } Barcode.TYPE_SMS -> { - getParcelableExtra(QRScannerActivity.EXTRA_RESULT_PARCELABLE)?.run { - Sms(rawValue = rawValue, message = message, phoneNumber = phoneNumber) + IntentCompat.getParcelableExtra(this, EXTRA_RESULT_PARCELABLE, SmsParcelable::class.java)?.let { + Sms( + rawBytes = rawBytes, + rawValue = rawValue, + message = it.message, + phoneNumber = it.phoneNumber + ) } } Barcode.TYPE_URL -> { - getParcelableExtra(QRScannerActivity.EXTRA_RESULT_PARCELABLE)?.run { - Url(rawValue = rawValue, title = title, url = url) + IntentCompat.getParcelableExtra(this, EXTRA_RESULT_PARCELABLE, UrlBookmarkParcelable::class.java)?.let { + Url( + rawBytes = rawBytes, + rawValue = rawValue, + title = it.title, + url = it.url + ) } } Barcode.TYPE_WIFI -> { - getParcelableExtra(QRScannerActivity.EXTRA_RESULT_PARCELABLE)?.run { - Wifi(rawValue = rawValue, encryptionType = encryptionType, password = password, ssid = ssid) + IntentCompat.getParcelableExtra(this, EXTRA_RESULT_PARCELABLE, WifiParcelable::class.java)?.let { + Wifi( + rawBytes = rawBytes, + rawValue = rawValue, + encryptionType = it.encryptionType, + password = it.password, + ssid = it.ssid + ) } } Barcode.TYPE_GEO -> { - getParcelableExtra(QRScannerActivity.EXTRA_RESULT_PARCELABLE)?.run { - GeoPoint(rawValue = rawValue, lat = lat, lng = lng) + IntentCompat.getParcelableExtra(this, EXTRA_RESULT_PARCELABLE, GeoPointParcelable::class.java)?.let { + GeoPoint( + rawBytes = rawBytes, + rawValue = rawValue, + lat = it.lat, + lng = it.lng + ) } } Barcode.TYPE_CALENDAR_EVENT -> { - getParcelableExtra(QRScannerActivity.EXTRA_RESULT_PARCELABLE)?.run { + IntentCompat.getParcelableExtra(this, EXTRA_RESULT_PARCELABLE, CalendarEventParcelable::class.java)?.let { CalendarEvent( + rawBytes = rawBytes, rawValue = rawValue, - description = description, - end = end.toCalendarEvent(), - location = location, - organizer = organizer, - start = start.toCalendarEvent(), - status = status, - summary = summary + description = it.description, + end = it.end.toCalendarEvent(), + location = it.location, + organizer = it.organizer, + start = it.start.toCalendarEvent(), + status = it.status, + summary = it.summary ) } } @@ -110,25 +143,33 @@ private fun Intent.toQuickieContentType(rawValue: String): QRContent? { } internal fun Intent?.getRootException(): Exception { - this?.getSerializableExtra(QRScannerActivity.EXTRA_RESULT_EXCEPTION).let { - return if (it is Exception) it else IllegalStateException("Could retrieve root exception") - } + return this?.let { IntentCompat.getParcelableExtra(it, EXTRA_RESULT_EXCEPTION, Exception::class.java) } + ?: IllegalStateException("Could retrieve root exception") } -private fun PhoneParcelable.toPhone(rawValue: String) = - Phone(rawValue = rawValue, number = number, type = PhoneType.values().getOrElse(type) { PhoneType.UNKNOWN }) +private fun PhoneParcelable.toPhone(rawBytes: ByteArray?, rawValue: String?) = + Phone( + rawBytes = rawBytes, + rawValue = rawValue, + number = number, + type = PhoneType.entries.getOrElse(type) { PhoneType.UNKNOWN } + ) -private fun EmailParcelable.toEmail(rawValue: String) = +private fun EmailParcelable.toEmail(rawBytes: ByteArray?, rawValue: String?) = Email( + rawBytes = rawBytes, rawValue = rawValue, address = address, body = body, subject = subject, - type = EmailType.values().getOrElse(type) { EmailType.UNKNOWN } + type = EmailType.entries.getOrElse(type) { EmailType.UNKNOWN } ) private fun AddressParcelable.toAddress() = - Address(addressLines = addressLines, type = AddressType.values().getOrElse(type) { AddressType.UNKNOWN }) + Address( + addressLines = addressLines, + type = AddressType.entries.getOrElse(type) { AddressType.UNKNOWN } + ) private fun PersonNameParcelable.toPersonName() = PersonName( diff --git a/quickie/src/test/kotlin/io/github/g00fy2/quickie/BarcodeFormatsTest.kt b/quickie/src/test/kotlin/io/github/g00fy2/quickie/BarcodeFormatsTest.kt index 15db7eb5..f9c45b55 100644 --- a/quickie/src/test/kotlin/io/github/g00fy2/quickie/BarcodeFormatsTest.kt +++ b/quickie/src/test/kotlin/io/github/g00fy2/quickie/BarcodeFormatsTest.kt @@ -15,7 +15,7 @@ internal class BarcodeFormatsTest { .filter { it.name != "FORMAT_UNKNOWN" } .associate { it.name to it.getInt(null) } - val quickieBarcodeFormats: Map = BarcodeFormat.values().associate { it.name to it.value } + val quickieBarcodeFormats: Map = BarcodeFormat.entries.associate { it.name to it.value } assertEquals(mlKitBarcodeFormats, quickieBarcodeFormats) } diff --git a/sample/src/main/kotlin/io/github/g00fy2/quickiesample/MainActivity.kt b/sample/src/main/kotlin/io/github/g00fy2/quickiesample/MainActivity.kt index 7da32667..1142eee8 100644 --- a/sample/src/main/kotlin/io/github/g00fy2/quickiesample/MainActivity.kt +++ b/sample/src/main/kotlin/io/github/g00fy2/quickiesample/MainActivity.kt @@ -58,7 +58,11 @@ class MainActivity : AppCompatActivity() { private fun showSnackbar(result: QRResult) { val text = when (result) { - is QRSuccess -> result.content.rawValue + is QRSuccess -> { + result.content.rawValue + // decoding with default UTF-8 charset when rawValue is null will not result in meaningful output, demo purpose + ?: result.content.rawBytes?.let { String(it) }.orEmpty() + } QRUserCanceled -> "User canceled" QRMissingPermission -> "Missing permission" is QRError -> "${result.exception.javaClass.simpleName}: ${result.exception.localizedMessage}" @@ -69,11 +73,14 @@ class MainActivity : AppCompatActivity() { maxLines = 5 setTextIsSelectable(true) } - if (result is QRSuccess && result.content is QRContent.Url) { - setAction(R.string.open_action) { openUrl(result.content.rawValue) } - } else { - setAction(R.string.ok_action) { } + if (result is QRSuccess) { + val content = result.content + if (content is QRContent.Url) { + setAction(R.string.open_action) { openUrl(content.url) } + return@apply + } } + setAction(R.string.ok_action) { } }.show() } @@ -86,12 +93,12 @@ class MainActivity : AppCompatActivity() { } private fun setBarcodeFormatDropdown() { - ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, BarcodeFormat.values().map { it.name }).let { + ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, BarcodeFormat.entries.map { it.name }).let { binding.barcodeFormatsAutoCompleteTextView.setAdapter(it) binding.barcodeFormatsAutoCompleteTextView.setText(it.getItem(it.getPosition(selectedBarcodeFormat.name)), false) } binding.barcodeFormatsAutoCompleteTextView.setOnItemClickListener { _, _, position, _ -> - selectedBarcodeFormat = BarcodeFormat.values()[position] + selectedBarcodeFormat = BarcodeFormat.entries[position] } }