diff --git a/README.md b/README.md
index 837d0d2..5bd4de6 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
Keepass Fidelity
-
+
A minimalist fidelity/loyalty card plugin
@@ -18,9 +18,9 @@
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 82bd4a5..3c6e43d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,7 +5,10 @@
android:versionName="1.1c">
+
+
+
= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ resultPermission.launch(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED)
+ } else {
+ // resultLauncherOpenMediaBase.launch("image/*")
+ resultLauncherOpenMediaPick.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
+ }
+ return View(context)
+ }
+
+ private fun startCreateEntry() {
+ val createEntryFragment = CreateEntry()
+ createEntryFragment.arguments =
+ KeepassWrapper.bundleCreate(null, this.code, this.fmt)
+ requireActivity().supportFragmentManager.beginTransaction()
+ .replace(R.id.container, createEntryFragment)
+ .commit()
+ }
+
+ private fun scannerResult(code: String?, format: String?) {
+ if (!code.isNullOrEmpty() && !format.isNullOrEmpty()) {
+ this.code = code
+ this.fmt = format
+ }
+ val isDone = this.code.isNotEmpty() && this.fmt.isNotEmpty()
+ requireActivity().runOnUiThread {
+ if (isDone) {
+ startCreateEntry()
+ } else {
+ parentFragmentManager.popBackStack()
+ ErrorToaster.nothingFound(context)
+ }
+ }
+ }
+
+ private fun loadUri(it: Uri?) {
+ try {
+ run {
+ require(it != null)
+
+ val file = requireContext().contentResolver.openInputStream(it)
+ val image = BitmapFactory.decodeStream(file)
+ BarcodeScanner.bitmapUseCase(image) { code, format ->
+ scannerResult(code, format)
+ }
+ }
+ } catch (e: FileNotFoundException) {
+ e.printStackTrace()
+ println(e.message)
+ println(it)
+ ErrorToaster.noPermission(context)
+ parentFragmentManager.popBackStack()
+ } catch (e: IllegalArgumentException) {
+ ErrorToaster.nothingFound(context)
+ parentFragmentManager.popBackStack()
+ } catch (e: SecurityException) {
+ ErrorToaster.noPermission(context)
+ parentFragmentManager.popBackStack()
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/helcel/fidelity/activity/fragment/Launcher.kt b/app/src/main/java/net/helcel/fidelity/activity/fragment/Launcher.kt
index 21fd1a1..54ce24e 100644
--- a/app/src/main/java/net/helcel/fidelity/activity/fragment/Launcher.kt
+++ b/app/src/main/java/net/helcel/fidelity/activity/fragment/Launcher.kt
@@ -49,6 +49,11 @@ class Launcher : Fragment() {
startScanner()
hideMenuAdd()
}
+ binding.btnOpen.setOnClickListener {
+ startFileScanner()
+ hideMenuAdd()
+ }
+
binding.btnManual.setOnClickListener {
startCreateEntry()
@@ -96,6 +101,10 @@ class Launcher : Fragment() {
startFragment(Scanner())
}
+ private fun startFileScanner() {
+ startFragment(FileScanner())
+ }
+
private fun startCreateEntry() {
startFragment(CreateEntry())
}
diff --git a/app/src/main/java/net/helcel/fidelity/activity/fragment/Scanner.kt b/app/src/main/java/net/helcel/fidelity/activity/fragment/Scanner.kt
index 3ab5af4..589ec57 100644
--- a/app/src/main/java/net/helcel/fidelity/activity/fragment/Scanner.kt
+++ b/app/src/main/java/net/helcel/fidelity/activity/fragment/Scanner.kt
@@ -2,25 +2,23 @@ package net.helcel.fidelity.activity.fragment
import android.Manifest
import android.content.ContentValues
-import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.activity.result.contract.ActivityResultContracts
import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
-import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import net.helcel.fidelity.R
import net.helcel.fidelity.databinding.FragScannerBinding
-import net.helcel.fidelity.tools.BarcodeScanner.getAnalysisUseCase
+import net.helcel.fidelity.tools.BarcodeScanner.analysisUseCase
+import net.helcel.fidelity.tools.ErrorToaster
import net.helcel.fidelity.tools.KeepassWrapper
-private const val CAMERA_PERMISSION_REQUEST_CODE = 1
-
class Scanner : Fragment() {
private lateinit var binding: FragScannerBinding
@@ -28,6 +26,17 @@ class Scanner : Fragment() {
private var code: String = ""
private var fmt: String = ""
+
+ private val resultPermissionRequest =
+ registerForActivityResult(ActivityResultContracts.RequestPermission()) {
+ if (it) {
+ bindCameraUseCases()
+ } else {
+ parentFragmentManager.popBackStack()
+ ErrorToaster.noPermission(context)
+ }
+ }
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -37,11 +46,8 @@ class Scanner : Fragment() {
binding.btnScanDone.setOnClickListener {
startCreateEntry()
}
- when (hasCameraPermission()) {
- true -> bindCameraUseCases()
- else -> requestPermission()
- }
binding.btnScanDone.isEnabled = false
+ resultPermissionRequest.launch(Manifest.permission.CAMERA)
return binding.root
}
@@ -55,26 +61,16 @@ class Scanner : Fragment() {
.commit()
}
- private fun hasCameraPermission() =
- ActivityCompat.checkSelfPermission(
- requireContext(),
- Manifest.permission.CAMERA
- ) == PackageManager.PERMISSION_GRANTED
-
- private fun requestPermission() {
- ActivityCompat.requestPermissions(
- requireActivity(),
- arrayOf(Manifest.permission.CAMERA),
- CAMERA_PERMISSION_REQUEST_CODE
- )
- ActivityCompat.OnRequestPermissionsResultCallback { c, p, i ->
- require(c == CAMERA_PERMISSION_REQUEST_CODE)
- require(p.contains(Manifest.permission.CAMERA))
- val el = i[p.indexOf(Manifest.permission.CAMERA)]
- if (el != PackageManager.PERMISSION_GRANTED) {
- startCreateEntry()
- }
+ private fun scannerResult(code: String?, format: String?) {
+ if (!code.isNullOrEmpty() && !format.isNullOrEmpty()) {
+ this.code = code
+ this.fmt = format
+ }
+ val isDone = this.code.isNotEmpty() && this.fmt.isNotEmpty()
+ requireActivity().runOnUiThread {
+ binding.btnScanDone.isEnabled = isDone
+ binding.ScanActive.isEnabled = !isDone
}
}
@@ -89,16 +85,8 @@ class Scanner : Fragment() {
it.setSurfaceProvider(binding.cameraView.surfaceProvider)
}
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
- val analysisUseCase = getAnalysisUseCase { code, format ->
- if (!code.isNullOrEmpty() && !format.isNullOrEmpty()) {
- this.code = code
- this.fmt = format
- }
- val isDone = this.code.isNotEmpty() && this.fmt.isNotEmpty()
- requireActivity().runOnUiThread {
- binding.btnScanDone.isEnabled = isDone
- binding.ScanActive.isEnabled = !isDone
- }
+ val analysisUseCase = analysisUseCase { code, format ->
+ scannerResult(code, format)
}
try {
cameraProvider.bindToLifecycle(
diff --git a/app/src/main/java/net/helcel/fidelity/tools/BarcodeScanner.kt b/app/src/main/java/net/helcel/fidelity/tools/BarcodeScanner.kt
index f356320..a981899 100644
--- a/app/src/main/java/net/helcel/fidelity/tools/BarcodeScanner.kt
+++ b/app/src/main/java/net/helcel/fidelity/tools/BarcodeScanner.kt
@@ -4,7 +4,6 @@ import android.graphics.Bitmap
import androidx.annotation.OptIn
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
-import androidx.camera.core.ImageProxy
import com.google.zxing.BinaryBitmap
import com.google.zxing.MultiFormatReader
import com.google.zxing.NotFoundException
@@ -15,14 +14,13 @@ import net.helcel.fidelity.tools.BarcodeFormatConverter.formatToString
import java.util.concurrent.Executors
+@OptIn(ExperimentalGetImage::class)
object BarcodeScanner {
- @OptIn(ExperimentalGetImage::class)
- private fun processImageProxy(
- imageProxy: ImageProxy,
+ private fun processImage(
+ bitmap: Bitmap,
cb: (String?, String?) -> Unit
) {
- val bitmap = imageProxy.toBitmap() // Convert ImageProxy to Bitmap
val binaryBitmap = createBinaryBitmap(bitmap)
val reader = MultiFormatReader()
try {
@@ -32,8 +30,6 @@ object BarcodeScanner {
cb(null, null)
} catch (e: ReaderException) {
cb(null, null)
- } finally {
- imageProxy.close()
}
}
@@ -45,13 +41,21 @@ object BarcodeScanner {
return BinaryBitmap(HybridBinarizer(source))
}
- fun getAnalysisUseCase(cb: (String?, String?) -> Unit): ImageAnalysis {
+ fun analysisUseCase(cb: (String?, String?) -> Unit): ImageAnalysis {
val analysisUseCase = ImageAnalysis.Builder().build()
analysisUseCase.setAnalyzer(
Executors.newSingleThreadExecutor()
) { imageProxy ->
- processImageProxy(imageProxy, cb)
+ val bitmap = imageProxy.toBitmap()
+ imageProxy.close()
+ bitmapUseCase(bitmap, cb)
}
return analysisUseCase
}
+
+ fun bitmapUseCase(bitmap: Bitmap, cb: (String?, String?) -> Unit) {
+ processImage(bitmap, cb)
+ }
+
+
}
\ No newline at end of file
diff --git a/app/src/main/java/net/helcel/fidelity/tools/ErrorToaster.kt b/app/src/main/java/net/helcel/fidelity/tools/ErrorToaster.kt
index 70a754f..4973cc7 100644
--- a/app/src/main/java/net/helcel/fidelity/tools/ErrorToaster.kt
+++ b/app/src/main/java/net/helcel/fidelity/tools/ErrorToaster.kt
@@ -20,4 +20,12 @@ object ErrorToaster {
fun invalidFormat(activity: Context?) {
helper(activity, "Invalid Format", Toast.LENGTH_SHORT)
}
+
+ fun nothingFound(activity: Context?) {
+ helper(activity, "Nothing Found", Toast.LENGTH_SHORT)
+ }
+
+ fun noPermission(activity: Context?) {
+ helper(activity, "Missing Permission", Toast.LENGTH_LONG)
+ }
}
diff --git a/app/src/main/res/drawable/open.xml b/app/src/main/res/drawable/open.xml
new file mode 100644
index 0000000..9d82c42
--- /dev/null
+++ b/app/src/main/res/drawable/open.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/frag_launcher.xml b/app/src/main/res/layout/frag_launcher.xml
index c23c101..30a1cae 100644
--- a/app/src/main/res/layout/frag_launcher.xml
+++ b/app/src/main/res/layout/frag_launcher.xml
@@ -56,6 +56,16 @@
app:maxImageSize="32dp"
app:srcCompat="@drawable/camera" />
+
+
Code
Format
Save
+ Open
- CODE_39
- CODE_93
diff --git a/metadata/en-US/full_description.txt b/metadata/en-US/full_description.txt
new file mode 100644
index 0000000..eb5b650
--- /dev/null
+++ b/metadata/en-US/full_description.txt
@@ -0,0 +1 @@
+Keepass-Fidelity adds an interface to view/save barcodes (QR included) to Keepass through the plugin interface of the Keepass2Android app.
- Launcher: view and launch recent entries (a per entry flag can disable this behaviour)
- View: view entries from the history or queried from Keepass2Android
- Create: add entries from the camera, an image of by filling out a form. The entry is then created in the Keepass2Android app
- Data: the app uses the following data Title (entry name), barcode type (QR, UPC, ...), barcode content (number/text content) and a "secure" flag (enable/disable caching the entry).
\ No newline at end of file
diff --git a/metadata/en-US/images/icon.png b/metadata/en-US/images/icon.png
new file mode 100644
index 0000000..34087a0
Binary files /dev/null and b/metadata/en-US/images/icon.png differ
diff --git a/.github/images/edit.jpg b/metadata/en-US/images/phoneScreenshots/edit.jpg
similarity index 100%
rename from .github/images/edit.jpg
rename to metadata/en-US/images/phoneScreenshots/edit.jpg
diff --git a/.github/images/launcher.jpg b/metadata/en-US/images/phoneScreenshots/launcher.jpg
similarity index 100%
rename from .github/images/launcher.jpg
rename to metadata/en-US/images/phoneScreenshots/launcher.jpg
diff --git a/.github/images/view.jpg b/metadata/en-US/images/phoneScreenshots/view.jpg
similarity index 100%
rename from .github/images/view.jpg
rename to metadata/en-US/images/phoneScreenshots/view.jpg
diff --git a/metadata/en-US/short_description.txt b/metadata/en-US/short_description.txt
new file mode 100644
index 0000000..408c2f1
--- /dev/null
+++ b/metadata/en-US/short_description.txt
@@ -0,0 +1 @@
+Fidelity (Membership/Loyalty) Card plugin for Keepass2Android
\ No newline at end of file