diff --git a/.github/workflows/github_actions.yml b/.github/workflows/github_actions.yml
index eb6bd6b..4d494d1 100644
--- a/.github/workflows/github_actions.yml
+++ b/.github/workflows/github_actions.yml
@@ -14,13 +14,13 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'zulu'
- java-version: '11'
+ java-version: '17'
- name: Bundle Install
run: bundle install
@@ -32,7 +32,7 @@ jobs:
run: bundle exec fastlane coverage
- name: Setup sonarqube
- uses: warchant/setup-sonar-scanner@v3
+ uses: warchant/setup-sonar-scanner@v7
- name: Send to Sonarcloud
run: bundle exec fastlane sonarqube
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..6dca7d8
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,12 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+The changes documented here do not include those from the original repository.
+
+## [Unreleased]
+
+### 06-11-2023
+Android - First implementation of the scan barcode feature using zxing (https://outsystemsrd.atlassian.net/browse/RMET-2758)
\ No newline at end of file
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 0000000..dca55d8
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,60 @@
+# Android
+# Build your Android project with Gradle.
+# Add steps that test, sign, and distribute the APK, save build artifacts, and more:
+# https://docs.microsoft.com/azure/devops/pipelines/languages/android
+
+parameters:
+- name: publishBuild
+ displayName: Publish new build?
+ type: boolean
+ default: false
+
+trigger:
+- main
+
+pool:
+ vmImage: ubuntu-latest
+
+steps:
+- task: SonarCloudPrepare@1
+ inputs:
+ SonarCloud: 'SonarCloud'
+ organization: 'outsystemsrd'
+ scannerMode: 'CLI'
+ configMode: 'file'
+- task: Gradle@2
+ displayName: Build Project
+ inputs:
+ workingDirectory: ''
+ gradleWrapperFile: 'gradlew'
+ gradleOptions: '-Xmx4096M'
+ javaHomeOption: 'JDKVersion'
+ jdkVersionOption: '1.17'
+ jdkArchitectureOption: 'x64'
+ publishJUnitResults: true
+ testResultsFiles: '**/TEST-*.xml'
+ tasks: 'clean build'
+- task: CmdLine@2
+ displayName: Validate pom.xml
+ inputs:
+ script: |
+ /usr/bin/mvn -version
+ /usr/bin/mvn -f /home/vsts/work/1/s/pom.xml help:effective-pom
+- task: MavenAuthenticate@0
+ displayName: Authenticate in public repo
+ condition: or(${{parameters.publishBuild}}, eq(variables['Build.SourceBranch'], 'refs/heads/main'))
+ inputs:
+ artifactsFeeds: 'PublicArtifactRepository'
+- task: Bash@3
+ displayName: Deploy file
+ condition: or(${{parameters.publishBuild}}, eq(variables['Build.SourceBranch'], 'refs/heads/main'))
+ inputs:
+ targetType: 'inline'
+ script: |
+ /usr/bin/mvn deploy:deploy-file \
+ -DpomFile=/home/vsts/work/1/s/pom.xml \
+ -DgeneratePom=true \
+ -Dfile=build/outputs/aar/OSBarcodeLib-release.aar \
+ -Dpackaging=aar \
+ -DrepositoryId=PublicArtifactRepository \
+ -Durl=https://pkgs.dev.azure.com/OutSystemsRD/9e79bc5b-69b2-4476-9ca5-d67594972a52/_packaging/PublicArtifactRepository/maven/v1
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 0802323..8021aa8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,12 +1,12 @@
buildscript {
- ext.kotlin_version = "1.5.21"
+ ext.kotlin_version = "1.9.10"
ext.jacocoVersion = '0.8.7'
repositories {
google()
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.1.2'
+ classpath 'com.android.tools.build:gradle:8.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jacoco:org.jacoco.core:$jacocoVersion"
}
@@ -17,11 +17,13 @@ apply plugin: "kotlin-android"
apply plugin: "jacoco"
android {
- compileSdk 32
+
+ namespace "com.outsystems.plugins.barcode"
+ compileSdk 34
defaultConfig {
minSdk 26
- targetSdk 32
+ targetSdk 34
versionCode 1
versionName "1.0"
@@ -45,8 +47,8 @@ android {
task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
reports {
- xml.enabled = true
- html.enabled = true
+ //xml.enabled = true
+ //html.enabled = true
}
def fileFilter = ['**/BuildConfig.*', '**/Manifest*.*']
@@ -60,6 +62,17 @@ android {
"outputs/code-coverage/connected/*coverage.ec"
]))
}
+ buildFeatures {
+ compose true
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion '1.5.3'
+ }
+ packaging {
+ resources {
+ excludes += '/META-INF/{AL2.0,LGPL2.1}'
+ }
+ }
}
repositories {
@@ -73,7 +86,29 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
+ implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
+ implementation platform('androidx.compose:compose-bom:2023.03.00')
+ implementation 'androidx.compose.ui:ui'
+ implementation 'androidx.compose.ui:ui-graphics'
+ implementation 'androidx.compose.ui:ui-tooling-preview'
+ implementation 'androidx.compose.material3:material3'
+ implementation platform('androidx.compose:compose-bom:2023.03.00')
+ implementation platform('androidx.compose:compose-bom:2023.03.00')
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+
+ implementation "androidx.activity:activity-compose:1.8.0"
+ implementation "androidx.camera:camera-camera2:1.3.0"
+ implementation 'androidx.camera:camera-lifecycle:1.3.0'
+ implementation 'androidx.camera:camera-view:1.3.0'
+ implementation 'com.google.zxing:core:3.4.1'
+ androidTestImplementation platform('androidx.compose:compose-bom:2023.03.00')
+ androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
+ androidTestImplementation platform('androidx.compose:compose-bom:2023.03.00')
+ androidTestImplementation platform('androidx.compose:compose-bom:2023.03.00')
+ debugImplementation 'androidx.compose.ui:ui-tooling'
+ debugImplementation 'androidx.compose.ui:ui-test-manifest'
+
+
}
diff --git a/fastlane/Appfile b/fastlane/Appfile
index 920122f..b378394 100644
--- a/fastlane/Appfile
+++ b/fastlane/Appfile
@@ -1,2 +1,2 @@
json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one
-package_name("organizationidplaceholder.libtemplateplaceholder") # e.g. com.krausefx.app
+package_name("com.outsystems.plugins.barcode.osbarcodelib") # e.g. com.krausefx.app
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 8a7aeff..df49134 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Fri Apr 08 08:58:08 WEST 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..c0053cd
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,11 @@
+
+
+
+ 4.0.0
+ com.github.outsystems
+ osbarcode-android
+ 0.0.4
+
diff --git a/scripts/generator_script.sh b/scripts/generator_script.sh
deleted file mode 100644
index 6e2ff57..0000000
--- a/scripts/generator_script.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-### Welcome the user to this awesome tool
-
-printf "Welcome to the Android Library Generator!!\n\n"
-
-remoteURL=$(git config --get remote.origin.url)
-remoteURL=$(echo $remoteURL | perl -F/ -wane 'print $F[-1]')
-remoteURL=$(echo ${remoteURL%-*})
-
-### Inform the user of the library name to be used
-printf "Considering the repository name you gave while creating it, we will use '$remoteURL' as the Library's name."
-
-### Ask the user for the package identifier. Continue to ask while the input doesn't match the correct format.
-while true
-do
- printf "\nPlease write the desired package identifier. This should be composed only by lowercase letters, numbers and '.' (e.g. 'com.outsystems').\n"
- read organisationId
- result=$(echo $organisationId | awk '/^[a-z0-9]+([\.]?[a-z0-9]+)*?$/')
-
- if ! [ -z "$result" ]; then
- break
- else
- printf "Format not valid. Please try again."
- fi
-done
-
-### Delete current file
-currentFile=$(basename "$0")
-
-printf "\nThe '$currentFile' file will be removed as it should only be executed once.\n\n"
-
-rm -f $currentFile
-
-### Proceed to change all necessary placeholders
-cd ..
-
-LC_CTYPE=C && LANG=C && find . -type f -exec sed -e "s/LibTemplatePlaceholder/$remoteURL/g" -i '' '{}' ';'
-LC_CTYPE=C && LANG=C && find . -depth -name '*LibTemplatePlaceholder*' -print0|while IFS= read -rd '' f; do mv -i "$f" "$(echo "$f"|sed -E "s/(.*)LibTemplatePlaceholder/\1$remoteURL/")"; done
-
-LC_CTYPE=C && LANG=C && find . -type f -exec sed -e "s/organizationidplaceholder/$organisationId/g" -i '' '{}' ';'
-LC_CTYPE=C && LANG=C && find . -depth -name '*organizationidplaceholder*' -print0|while IFS= read -rd '' f; do mv -i "$f" "$(echo "$f"|sed -E "s/(.*)organizationidplaceholder/\1$organisationId/")"; done
-
-### Convert $remoteURL to lowercase
-lowercaseRemoteURL=$(echo "$remoteURL" | tr '[:upper:]' '[:lower:]')
-
-LC_CTYPE=C && LANG=C && find . -type f -exec sed -e "s/libtemplateplaceholder/$lowercaseRemoteURL/g" -i '' '{}' ';'
-LC_CTYPE=C && LANG=C && find . -depth -name '*libtemplateplaceholder*' -print0|while IFS= read -rd '' f; do mv -i "$f" "$(echo "$f"|sed -E "s/(.*)libtemplateplaceholder/\1$lowercaseRemoteURL/")"; done
-
-### Commit and push
-rm -f .git/index
-git reset
-git add .
-git commit -m "Finish initialisation by running the generator script."
-git push
-
-### Close
-printf "\n###Applause###\n"
-printf "Looks like everything's done. Enjoy!\n\n"
diff --git a/settings.gradle b/settings.gradle
index c50e98e..d2151be 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-rootProject.name = "LibTemplatePlaceholder"
\ No newline at end of file
+rootProject.name = "OSBarcodeLib"
\ No newline at end of file
diff --git a/sonar-project.properties b/sonar-project.properties
index f860a85..0752a6c 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -1,10 +1,16 @@
# Organization and project keys are displayed in the right sidebar of the project homepage
sonar.organization=outsystemsrd
-sonar.projectKey=OutSystems_LibTemplatePlaceholder-Android
+sonar.projectKey=OutSystems_OSBarcodeLib-Android
sonar.host.url=https://sonarcloud.io
sonar.language=kotlin
# Defining path to coverage file
sonar.coverage.jacoco.xmlReportPaths=**/jacocoTestReport/jacocoTestReport.xml
-sonar.junit.reportPaths=**/test-results/**/*.xml
\ No newline at end of file
+sonar.junit.reportPaths=**/test-results/**/*.xml
+
+# Removing tests, mocks and code not testable by unit tests from the coverage estimation
+sonar.coverage.exclusions=**/*Tests.kt,**/*Mock.kt,**/model/*.kt,**/controller/*.kt,**/view/*.kt,**/*Theme.kt,**/ui.theme/*.kt
+
+# Removing tests from code duplication checks
+sonar.cpd.exclusions=**/*Tests.kt
\ No newline at end of file
diff --git a/src/androidTest/java/organizationidplaceholder/libtemplateplaceholder/ExampleInstrumentedTest.kt b/src/androidTest/java/organizationidplaceholder/libtemplateplaceholder/ExampleInstrumentedTest.kt
deleted file mode 100644
index 72150fb..0000000
--- a/src/androidTest/java/organizationidplaceholder/libtemplateplaceholder/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package organizationidplaceholder.libtemplateplaceholder
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("organizationidplaceholder.libtemplateplaceholder", appContext.packageName)
- }
-}
\ No newline at end of file
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 4045313..46c545d 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -1,23 +1,10 @@
-
-
+ package="com.outsystems.plugins.barcode">
+
+
-
-
-
-
-
-
+ android:name=".view.OSBARCScannerActivity"
+ android:exported="false" />
-
\ No newline at end of file
diff --git a/src/main/java/organizationidplaceholder/libtemplateplaceholder/MainActivity.kt b/src/main/java/organizationidplaceholder/libtemplateplaceholder/MainActivity.kt
deleted file mode 100644
index a129d33..0000000
--- a/src/main/java/organizationidplaceholder/libtemplateplaceholder/MainActivity.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package organizationidplaceholder.libtemplateplaceholder
-
-import androidx.appcompat.app.AppCompatActivity
-import android.os.Bundle
-
-class MainActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- }
-}
\ No newline at end of file
diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCBarcodeAnalyzer.kt b/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCBarcodeAnalyzer.kt
new file mode 100644
index 0000000..0a4c154
--- /dev/null
+++ b/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCBarcodeAnalyzer.kt
@@ -0,0 +1,63 @@
+package com.outsystems.plugins.barcode.controller
+
+import android.graphics.Bitmap
+import android.graphics.Matrix
+import android.util.Log
+import androidx.camera.core.ImageAnalysis
+import androidx.camera.core.ImageProxy
+import com.google.zxing.BinaryBitmap
+import com.google.zxing.DecodeHintType
+import com.google.zxing.MultiFormatReader
+import com.google.zxing.RGBLuminanceSource
+import com.google.zxing.common.HybridBinarizer
+import java.lang.Exception
+
+class OSBARCBarcodeAnalyzer(
+ private val onBarcodeScanned: (String) -> Unit,
+ private val onScanningError: () -> Unit
+): ImageAnalysis.Analyzer {
+
+ companion object {
+ private const val LOG_TAG = "OSBARCBarcodeAnalyzer"
+ }
+
+ override fun analyze(image: ImageProxy) {
+ try {
+ var imageBitmap = image.toBitmap()
+
+ // rotate the image if it's in portrait mode (rotation = 90 or 270 degrees)
+ val rotationDegrees = image.imageInfo.rotationDegrees
+ if (rotationDegrees == 90 || rotationDegrees == 270) {
+ // create a matrix for rotation
+ val matrix = Matrix()
+ matrix.postRotate(rotationDegrees.toFloat())
+
+ // actually rotate the image
+ imageBitmap = Bitmap.createBitmap(imageBitmap, 0, 0, imageBitmap.width, imageBitmap.height, matrix, true)
+ }
+
+ // scan image using zxing
+ val width = imageBitmap.width
+ val height = imageBitmap.height
+ val pixels = IntArray(width * height)
+ imageBitmap.getPixels(pixels, 0, width, 0, 0, width, height)
+
+ val source = RGBLuminanceSource(width, height, pixels)
+ val binaryBitmap = BinaryBitmap(HybridBinarizer(source))
+ val result = MultiFormatReader().apply {
+ setHints(
+ mapOf(
+ DecodeHintType.TRY_HARDER to arrayListOf(true)
+ )
+ )
+ }.decode(binaryBitmap)
+ onBarcodeScanned(result.text)
+ } catch (e: Exception) {
+ e.message?.let { Log.e(LOG_TAG, it) }
+ onScanningError
+ } finally {
+ image.close()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCController.kt b/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCController.kt
new file mode 100644
index 0000000..53d5a4c
--- /dev/null
+++ b/src/main/kotlin/com/outsystems/plugins/barcode/controller/OSBARCController.kt
@@ -0,0 +1,67 @@
+package com.outsystems.plugins.barcode.controller
+
+import android.app.Activity
+import android.content.Intent
+import com.outsystems.plugins.barcode.model.OSBARCError
+import com.outsystems.plugins.barcode.model.OSBARCScanParameters
+import com.outsystems.plugins.barcode.view.OSBARCScannerActivity
+
+class OSBARCController {
+
+ companion object {
+ private const val SCAN_REQUEST_CODE = 112
+ private const val SCAN_INSTRUCTIONS = "SCAN_INSTRUCTIONS"
+ private const val CAMERA_DIRECTION = "CAMERA_DIRECTION"
+ private const val SCAN_ORIENTATION = "SCAN_ORIENTATION"
+ private const val SCAN_BUTTON = "SCAN_BUTTON"
+ private const val SCAN_BUTTON_TEXT = "SCAN_BUTTON_TEXT"
+ private const val SCAN_HINT = "SCAN_HINT"
+ private const val SCAN_LIBRARY = "SCAN_LIBRARY"
+ private const val SCAN_RESULT = "scanResult"
+ private const val CAMERA_PERMISSION_DENIED_RESULT_CODE = 1
+ private const val SCANNING_EXCEPTION_RESULT_CODE = 2
+ }
+
+ fun scanCode(activity: Activity, parameters: OSBARCScanParameters) {
+ val scanningIntent = Intent(activity, OSBARCScannerActivity::class.java).apply {
+ putExtra(SCAN_INSTRUCTIONS, parameters.scanInstructions)
+ putExtra(CAMERA_DIRECTION, parameters.cameraDirection)
+ putExtra(SCAN_ORIENTATION, parameters.scanOrientation)
+ putExtra(SCAN_BUTTON, parameters.scanButton)
+ putExtra(SCAN_BUTTON_TEXT, parameters.scanText)
+ putExtra(SCAN_HINT, parameters.hint)
+ putExtra(SCAN_LIBRARY, parameters.androidScanningLibrary)
+ }
+ activity.startActivityForResult(scanningIntent, SCAN_REQUEST_CODE)
+ }
+
+ fun handleActivityResult(
+ requestCode: Int,
+ resultCode: Int,
+ intent: Intent?,
+ onSuccess: (String) -> Unit,
+ onError: (OSBARCError) -> Unit
+ ) {
+ when (requestCode) {
+ SCAN_REQUEST_CODE -> {
+ when (resultCode) {
+ Activity.RESULT_OK -> {
+ val result = intent?.extras?.getString(SCAN_RESULT)
+ if (result.isNullOrEmpty()) {
+ onError(OSBARCError.SCANNING_GENERAL_ERROR)
+ return
+ }
+ onSuccess(result)
+ }
+ Activity.RESULT_CANCELED ->
+ onError(OSBARCError.SCAN_CANCELLED_ERROR)
+ CAMERA_PERMISSION_DENIED_RESULT_CODE ->
+ onError(OSBARCError.CAMERA_PERMISSION_DENIED_ERROR)
+ SCANNING_EXCEPTION_RESULT_CODE ->
+ onError(OSBARCError.SCANNING_GENERAL_ERROR)
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/model/OSBARCError.kt b/src/main/kotlin/com/outsystems/plugins/barcode/model/OSBARCError.kt
new file mode 100644
index 0000000..ef020c6
--- /dev/null
+++ b/src/main/kotlin/com/outsystems/plugins/barcode/model/OSBARCError.kt
@@ -0,0 +1,8 @@
+package com.outsystems.plugins.barcode.model
+
+enum class OSBARCError(val code: Int, val description: String) {
+ CAMERA_PERMISSION_DENIED_ERROR(1, "Couldn't access camera. Check your camera permissions and try again."),
+ INVALID_PARAMETERS_ERROR(2, "Barcode parameters are invalid."),
+ SCAN_CANCELLED_ERROR(3, "Barcode scanning was cancelled."),
+ SCANNING_GENERAL_ERROR(4, "There was an error scanning the barcode.")
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/model/OSBARCScanParameters.kt b/src/main/kotlin/com/outsystems/plugins/barcode/model/OSBARCScanParameters.kt
new file mode 100644
index 0000000..12b54d2
--- /dev/null
+++ b/src/main/kotlin/com/outsystems/plugins/barcode/model/OSBARCScanParameters.kt
@@ -0,0 +1,11 @@
+package com.outsystems.plugins.barcode.model
+
+data class OSBARCScanParameters(
+ val scanInstructions: String?,
+ val cameraDirection: Int?,
+ val scanOrientation: Int?,
+ val scanButton: Boolean?,
+ val scanText: String?,
+ val hint: Int?,
+ val androidScanningLibrary: String?
+)
\ No newline at end of file
diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/view/OSBARCScannerActivity.kt b/src/main/kotlin/com/outsystems/plugins/barcode/view/OSBARCScannerActivity.kt
new file mode 100644
index 0000000..c441037
--- /dev/null
+++ b/src/main/kotlin/com/outsystems/plugins/barcode/view/OSBARCScannerActivity.kt
@@ -0,0 +1,131 @@
+package com.outsystems.plugins.barcode.view
+
+import android.Manifest
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.ImageAnalysis
+import androidx.camera.core.Preview
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.view.PreviewView
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.camera.core.ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
+import androidx.compose.ui.Modifier
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.runtime.SideEffect
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.content.ContextCompat
+import com.outsystems.plugins.barcode.controller.OSBARCBarcodeAnalyzer
+import com.outsystems.plugins.barcode.view.ui.theme.BarcodeScannerTheme
+import java.lang.Exception
+
+class OSBARCScannerActivity : ComponentActivity() {
+
+ companion object {
+ private const val SCAN_SUCCESS_RESULT_CODE = -1
+ private const val CAMERA_PERMISSION_DENIED_RESULT_CODE = 1
+ private const val SCANNING_EXCEPTION_RESULT_CODE = 2
+ private const val SCAN_RESULT = "scanResult"
+ private const val LOG_TAG = "OSBARCScannerActivity"
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ BarcodeScannerTheme {
+ ScanScreen()
+ }
+ }
+ }
+
+ @Composable
+ fun ScanScreen() {
+ val lifecycleOwner = LocalLifecycleOwner.current
+ val context = LocalContext.current
+
+ // permissions
+ val requestPermissionLauncher = rememberLauncherForActivityResult(
+ ActivityResultContracts.RequestPermission()
+ ) { isGranted: Boolean ->
+ if (isGranted) {
+ // do nothing, continue
+ } else {
+ this.setResult(CAMERA_PERMISSION_DENIED_RESULT_CODE)
+ this.finish()
+ }
+ }
+ SideEffect {
+ requestPermissionLauncher.launch(Manifest.permission.CAMERA)
+ }
+
+ // rest of the UI
+ val cameraProviderFuture = remember {
+ ProcessCameraProvider.getInstance(context)
+ }
+ var barcode by remember {
+ mutableStateOf("")
+ }
+
+ Column (
+ modifier = Modifier.fillMaxSize()
+ ) {
+ AndroidView(
+ factory = { context ->
+ val previewView = PreviewView(context)
+ val preview = Preview.Builder().build()
+ val selector = CameraSelector.Builder()
+ .requireLensFacing(CameraSelector.LENS_FACING_BACK) // temporary
+ .build()
+ preview.setSurfaceProvider(previewView.surfaceProvider)
+ val imageAnalysis = ImageAnalysis.Builder()
+ .setBackpressureStrategy(STRATEGY_KEEP_ONLY_LATEST)
+ .build()
+ imageAnalysis.setAnalyzer(
+ ContextCompat.getMainExecutor(context),
+ OSBARCBarcodeAnalyzer(
+ { result ->
+ barcode = result
+ val resultIntent = Intent()
+ resultIntent.putExtra(SCAN_RESULT, result)
+ setResult(SCAN_SUCCESS_RESULT_CODE, resultIntent)
+ finish()
+ },
+ {
+ setResult(SCANNING_EXCEPTION_RESULT_CODE)
+ finish()
+ }
+ )
+ )
+ try {
+ cameraProviderFuture.get().bindToLifecycle(
+ lifecycleOwner,
+ selector,
+ preview,
+ imageAnalysis
+ )
+ } catch (e: Exception) {
+ e.message?.let { Log.e(LOG_TAG, it) }
+ setResult(SCANNING_EXCEPTION_RESULT_CODE)
+ finish()
+ }
+ previewView
+ },
+ modifier = Modifier.weight(1f)
+ )
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/view/ui.theme/Color.kt b/src/main/kotlin/com/outsystems/plugins/barcode/view/ui.theme/Color.kt
new file mode 100644
index 0000000..12c1a50
--- /dev/null
+++ b/src/main/kotlin/com/outsystems/plugins/barcode/view/ui.theme/Color.kt
@@ -0,0 +1,11 @@
+package com.outsystems.plugins.barcode.view.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple80 = Color(0xFFD0BCFF)
+val PurpleGrey80 = Color(0xFFCCC2DC)
+val Pink80 = Color(0xFFEFB8C8)
+
+val Purple40 = Color(0xFF6650a4)
+val PurpleGrey40 = Color(0xFF625b71)
+val Pink40 = Color(0xFF7D5260)
\ No newline at end of file
diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/view/ui.theme/Theme.kt b/src/main/kotlin/com/outsystems/plugins/barcode/view/ui.theme/Theme.kt
new file mode 100644
index 0000000..c96bd85
--- /dev/null
+++ b/src/main/kotlin/com/outsystems/plugins/barcode/view/ui.theme/Theme.kt
@@ -0,0 +1,70 @@
+package com.outsystems.plugins.barcode.view.ui.theme
+
+import android.app.Activity
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
+import androidx.core.view.WindowCompat
+
+private val DarkColorScheme = darkColorScheme(
+ primary = Purple80,
+ secondary = PurpleGrey80,
+ tertiary = Pink80
+)
+
+private val LightColorScheme = lightColorScheme(
+ primary = Purple40,
+ secondary = PurpleGrey40,
+ tertiary = Pink40
+
+ /* Other default colors to override
+ background = Color(0xFFFFFBFE),
+ surface = Color(0xFFFFFBFE),
+ onPrimary = Color.White,
+ onSecondary = Color.White,
+ onTertiary = Color.White,
+ onBackground = Color(0xFF1C1B1F),
+ onSurface = Color(0xFF1C1B1F),
+ */
+)
+
+@Composable
+fun BarcodeScannerTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+
+ dynamicColor: Boolean = true,
+ content: @Composable () -> Unit
+) {
+ val colorScheme = when {
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+
+ darkTheme -> DarkColorScheme
+ else -> LightColorScheme
+ }
+ val view = LocalView.current
+ if (!view.isInEditMode) {
+ SideEffect {
+ val window = (view.context as Activity).window
+ window.statusBarColor = colorScheme.primary.toArgb()
+ WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
+ }
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/view/ui.theme/Type.kt b/src/main/kotlin/com/outsystems/plugins/barcode/view/ui.theme/Type.kt
new file mode 100644
index 0000000..551c753
--- /dev/null
+++ b/src/main/kotlin/com/outsystems/plugins/barcode/view/ui.theme/Type.kt
@@ -0,0 +1,34 @@
+package com.outsystems.plugins.barcode.view.ui.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ bodyLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp
+ )
+ /* Other default text styles to override
+ titleLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ labelSmall = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Medium,
+ fontSize = 11.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.5.sp
+ )
+ */
+)
\ No newline at end of file
diff --git a/src/main/res/drawable-v24/ic_launcher_foreground.xml b/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index 2b068d1..0000000
--- a/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/res/drawable/ic_launcher_background.xml b/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 07d5da9..0000000
--- a/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/res/layout/activity_main.xml b/src/main/res/layout/activity_main.xml
deleted file mode 100644
index 4fc2444..0000000
--- a/src/main/res/layout/activity_main.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index eca70cf..0000000
--- a/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index eca70cf..0000000
--- a/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/res/mipmap-hdpi/ic_launcher.webp b/src/main/res/mipmap-hdpi/ic_launcher.webp
deleted file mode 100644
index c209e78..0000000
Binary files a/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ
diff --git a/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/src/main/res/mipmap-hdpi/ic_launcher_round.webp
deleted file mode 100644
index b2dfe3d..0000000
Binary files a/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/src/main/res/mipmap-mdpi/ic_launcher.webp b/src/main/res/mipmap-mdpi/ic_launcher.webp
deleted file mode 100644
index 4f0f1d6..0000000
Binary files a/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ
diff --git a/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/src/main/res/mipmap-mdpi/ic_launcher_round.webp
deleted file mode 100644
index 62b611d..0000000
Binary files a/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/src/main/res/mipmap-xhdpi/ic_launcher.webp b/src/main/res/mipmap-xhdpi/ic_launcher.webp
deleted file mode 100644
index 948a307..0000000
Binary files a/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ
diff --git a/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
deleted file mode 100644
index 1b9a695..0000000
Binary files a/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/src/main/res/mipmap-xxhdpi/ic_launcher.webp
deleted file mode 100644
index 28d4b77..0000000
Binary files a/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ
diff --git a/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
deleted file mode 100644
index 9287f50..0000000
Binary files a/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
deleted file mode 100644
index aa7d642..0000000
Binary files a/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ
diff --git a/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
deleted file mode 100644
index 9126ae3..0000000
Binary files a/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/src/main/res/values-night/themes.xml b/src/main/res/values-night/themes.xml
deleted file mode 100644
index 45f3135..0000000
--- a/src/main/res/values-night/themes.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml
deleted file mode 100644
index f8c6127..0000000
--- a/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- #FFBB86FC
- #FF6200EE
- #FF3700B3
- #FF03DAC5
- #FF018786
- #FF000000
- #FFFFFFFF
-
\ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
deleted file mode 100644
index 23a95d7..0000000
--- a/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- LibTemplatePlaceholder
-
\ No newline at end of file
diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml
deleted file mode 100644
index fb27870..0000000
--- a/src/main/res/values/themes.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/src/test/java/organizationidplaceholder/libtemplateplaceholder/ExampleUnitTest.kt b/src/test/java/organizationidplaceholder/libtemplateplaceholder/ExampleUnitTest.kt
deleted file mode 100644
index 9ac43a9..0000000
--- a/src/test/java/organizationidplaceholder/libtemplateplaceholder/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package organizationidplaceholder.libtemplateplaceholder
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
\ No newline at end of file
diff --git a/src/test/kotlin/com/outsystems/plugins/barcode/ScanCodeTests.kt b/src/test/kotlin/com/outsystems/plugins/barcode/ScanCodeTests.kt
new file mode 100644
index 0000000..b468341
--- /dev/null
+++ b/src/test/kotlin/com/outsystems/plugins/barcode/ScanCodeTests.kt
@@ -0,0 +1,11 @@
+package com.outsystems.plugins.barcode
+
+import org.junit.Test
+
+class ScanCodeTests {
+
+ @Test
+ fun scanCodeTest() {
+ // temporarily empty
+ }
+}
\ No newline at end of file