Compare commits
No commits in common. "b5f52c7e135b5ce251e0e9c4ded778da4b380ae8" and "84b2c2c455fcfbc3cae23063647b7a67c988bc71" have entirely different histories.
b5f52c7e13
...
84b2c2c455
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
@ -1,7 +1,7 @@
|
|||||||
<!--suppress ALL -->
|
<!--suppress ALL -->
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>Keepass Fidelity</h1>
|
<h1>Keepass Fidelity</h1>
|
||||||
<img style="width: 120px;" src="./metadata/en-US/images/icon.png" alt="Logo">
|
<img src="./app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp" alt="Logo">
|
||||||
|
|
||||||
<p>A minimalist fidelity/loyalty card plugin</p>
|
<p>A minimalist fidelity/loyalty card plugin</p>
|
||||||
|
|
||||||
@ -18,9 +18,9 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width: 33%; height: 100px;"><img src="./metadata/en-US/images/phoneScreenshots/launcher.jpg" alt="Launcher" style="width: 100%; height: 100%;"></td>
|
<td style="width: 33%; height: 100px;"><img src=".github/images/launcher.jpg" alt="Launcher" style="width: 100%; height: 100%;"></td>
|
||||||
<td style="width: 33%; height: 100px;"><img src="./metadata/en-US/images/phoneScreenshots/view.jpg" alt="View" style="width: 100%; height: 100%;"></td>
|
<td style="width: 33%; height: 100px;"><img src=".github/images/view.jpg" alt="View" style="width: 100%; height: 100%;"></td>
|
||||||
<td style="width: 33%; height: 100px;"><img src="./metadata/en-US/images/phoneScreenshots/edit.jpg" alt="Edit" style="width: 100%; height: 100%;"></td>
|
<td style="width: 33%; height: 100px;"><img src=".github/images/edit.jpg" alt="Edit" style="width: 100%; height: 100%;"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:versionCode="6"
|
android:versionCode="5"
|
||||||
android:versionName="1.2a">
|
android:versionName="1.1c">
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" />
|
<uses-feature android:name="android.hardware.camera" />
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:icon="@mipmap/ic_launcher_round"
|
android:icon="@mipmap/ic_launcher_round"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
package net.helcel.fidelity.activity.fragment
|
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.graphics.BitmapFactory
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.activity.result.PickVisualMediaRequest
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import net.helcel.fidelity.R
|
|
||||||
import net.helcel.fidelity.tools.BarcodeScanner
|
|
||||||
import net.helcel.fidelity.tools.ErrorToaster
|
|
||||||
import net.helcel.fidelity.tools.KeepassWrapper
|
|
||||||
import java.io.FileNotFoundException
|
|
||||||
|
|
||||||
class FileScanner : Fragment() {
|
|
||||||
|
|
||||||
private var code: String = ""
|
|
||||||
private var fmt: String = ""
|
|
||||||
|
|
||||||
|
|
||||||
private val resultPermission =
|
|
||||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
|
||||||
resultLauncherOpenMediaPick.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
|
|
||||||
}
|
|
||||||
|
|
||||||
private val resultLauncherOpenMediaBase =
|
|
||||||
registerForActivityResult(ActivityResultContracts.GetContent()) {
|
|
||||||
loadUri(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val resultLauncherOpenMediaPick =
|
|
||||||
registerForActivityResult(ActivityResultContracts.PickVisualMedia()) {
|
|
||||||
loadUri(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater,
|
|
||||||
container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?
|
|
||||||
): View {
|
|
||||||
println(Build.VERSION.SDK_INT)
|
|
||||||
if (Build.VERSION.SDK_INT >= 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -49,11 +49,6 @@ class Launcher : Fragment() {
|
|||||||
startScanner()
|
startScanner()
|
||||||
hideMenuAdd()
|
hideMenuAdd()
|
||||||
}
|
}
|
||||||
binding.btnOpen.setOnClickListener {
|
|
||||||
startFileScanner()
|
|
||||||
hideMenuAdd()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
binding.btnManual.setOnClickListener {
|
binding.btnManual.setOnClickListener {
|
||||||
startCreateEntry()
|
startCreateEntry()
|
||||||
@ -101,10 +96,6 @@ class Launcher : Fragment() {
|
|||||||
startFragment(Scanner())
|
startFragment(Scanner())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startFileScanner() {
|
|
||||||
startFragment(FileScanner())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startCreateEntry() {
|
private fun startCreateEntry() {
|
||||||
startFragment(CreateEntry())
|
startFragment(CreateEntry())
|
||||||
}
|
}
|
||||||
|
@ -2,23 +2,25 @@ package net.helcel.fidelity.activity.fragment
|
|||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.camera.core.CameraSelector
|
import androidx.camera.core.CameraSelector
|
||||||
import androidx.camera.core.Preview
|
import androidx.camera.core.Preview
|
||||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import net.helcel.fidelity.R
|
import net.helcel.fidelity.R
|
||||||
import net.helcel.fidelity.databinding.FragScannerBinding
|
import net.helcel.fidelity.databinding.FragScannerBinding
|
||||||
import net.helcel.fidelity.tools.BarcodeScanner.analysisUseCase
|
import net.helcel.fidelity.tools.BarcodeScanner.getAnalysisUseCase
|
||||||
import net.helcel.fidelity.tools.ErrorToaster
|
|
||||||
import net.helcel.fidelity.tools.KeepassWrapper
|
import net.helcel.fidelity.tools.KeepassWrapper
|
||||||
|
|
||||||
|
private const val CAMERA_PERMISSION_REQUEST_CODE = 1
|
||||||
|
|
||||||
class Scanner : Fragment() {
|
class Scanner : Fragment() {
|
||||||
|
|
||||||
private lateinit var binding: FragScannerBinding
|
private lateinit var binding: FragScannerBinding
|
||||||
@ -26,17 +28,6 @@ class Scanner : Fragment() {
|
|||||||
private var code: String = ""
|
private var code: String = ""
|
||||||
private var fmt: String = ""
|
private var fmt: String = ""
|
||||||
|
|
||||||
|
|
||||||
private val resultPermissionRequest =
|
|
||||||
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
|
|
||||||
if (it) {
|
|
||||||
bindCameraUseCases()
|
|
||||||
} else {
|
|
||||||
parentFragmentManager.popBackStack()
|
|
||||||
ErrorToaster.noPermission(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
@ -46,8 +37,11 @@ class Scanner : Fragment() {
|
|||||||
binding.btnScanDone.setOnClickListener {
|
binding.btnScanDone.setOnClickListener {
|
||||||
startCreateEntry()
|
startCreateEntry()
|
||||||
}
|
}
|
||||||
|
when (hasCameraPermission()) {
|
||||||
|
true -> bindCameraUseCases()
|
||||||
|
else -> requestPermission()
|
||||||
|
}
|
||||||
binding.btnScanDone.isEnabled = false
|
binding.btnScanDone.isEnabled = false
|
||||||
resultPermissionRequest.launch(Manifest.permission.CAMERA)
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,16 +55,26 @@ class Scanner : Fragment() {
|
|||||||
.commit()
|
.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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,8 +89,16 @@ class Scanner : Fragment() {
|
|||||||
it.setSurfaceProvider(binding.cameraView.surfaceProvider)
|
it.setSurfaceProvider(binding.cameraView.surfaceProvider)
|
||||||
}
|
}
|
||||||
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
||||||
val analysisUseCase = analysisUseCase { code, format ->
|
val analysisUseCase = getAnalysisUseCase { code, format ->
|
||||||
scannerResult(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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
cameraProvider.bindToLifecycle(
|
cameraProvider.bindToLifecycle(
|
||||||
|
@ -4,6 +4,7 @@ import android.graphics.Bitmap
|
|||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.camera.core.ExperimentalGetImage
|
import androidx.camera.core.ExperimentalGetImage
|
||||||
import androidx.camera.core.ImageAnalysis
|
import androidx.camera.core.ImageAnalysis
|
||||||
|
import androidx.camera.core.ImageProxy
|
||||||
import com.google.zxing.BinaryBitmap
|
import com.google.zxing.BinaryBitmap
|
||||||
import com.google.zxing.MultiFormatReader
|
import com.google.zxing.MultiFormatReader
|
||||||
import com.google.zxing.NotFoundException
|
import com.google.zxing.NotFoundException
|
||||||
@ -14,13 +15,14 @@ import net.helcel.fidelity.tools.BarcodeFormatConverter.formatToString
|
|||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalGetImage::class)
|
|
||||||
object BarcodeScanner {
|
object BarcodeScanner {
|
||||||
|
|
||||||
private fun processImage(
|
@OptIn(ExperimentalGetImage::class)
|
||||||
bitmap: Bitmap,
|
private fun processImageProxy(
|
||||||
|
imageProxy: ImageProxy,
|
||||||
cb: (String?, String?) -> Unit
|
cb: (String?, String?) -> Unit
|
||||||
) {
|
) {
|
||||||
|
val bitmap = imageProxy.toBitmap() // Convert ImageProxy to Bitmap
|
||||||
val binaryBitmap = createBinaryBitmap(bitmap)
|
val binaryBitmap = createBinaryBitmap(bitmap)
|
||||||
val reader = MultiFormatReader()
|
val reader = MultiFormatReader()
|
||||||
try {
|
try {
|
||||||
@ -30,6 +32,8 @@ object BarcodeScanner {
|
|||||||
cb(null, null)
|
cb(null, null)
|
||||||
} catch (e: ReaderException) {
|
} catch (e: ReaderException) {
|
||||||
cb(null, null)
|
cb(null, null)
|
||||||
|
} finally {
|
||||||
|
imageProxy.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,21 +45,13 @@ object BarcodeScanner {
|
|||||||
return BinaryBitmap(HybridBinarizer(source))
|
return BinaryBitmap(HybridBinarizer(source))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun analysisUseCase(cb: (String?, String?) -> Unit): ImageAnalysis {
|
fun getAnalysisUseCase(cb: (String?, String?) -> Unit): ImageAnalysis {
|
||||||
val analysisUseCase = ImageAnalysis.Builder().build()
|
val analysisUseCase = ImageAnalysis.Builder().build()
|
||||||
analysisUseCase.setAnalyzer(
|
analysisUseCase.setAnalyzer(
|
||||||
Executors.newSingleThreadExecutor()
|
Executors.newSingleThreadExecutor()
|
||||||
) { imageProxy ->
|
) { imageProxy ->
|
||||||
val bitmap = imageProxy.toBitmap()
|
processImageProxy(imageProxy, cb)
|
||||||
imageProxy.close()
|
|
||||||
bitmapUseCase(bitmap, cb)
|
|
||||||
}
|
}
|
||||||
return analysisUseCase
|
return analysisUseCase
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bitmapUseCase(bitmap: Bitmap, cb: (String?, String?) -> Unit) {
|
|
||||||
processImage(bitmap, cb)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -20,12 +20,4 @@ object ErrorToaster {
|
|||||||
fun invalidFormat(activity: Context?) {
|
fun invalidFormat(activity: Context?) {
|
||||||
helper(activity, "Invalid Format", Toast.LENGTH_SHORT)
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="72dp"
|
|
||||||
android:height="72dp"
|
|
||||||
android:viewportWidth="58"
|
|
||||||
android:viewportHeight="58">
|
|
||||||
<group android:translateX="-10" android:translateY="-8">
|
|
||||||
<path
|
|
||||||
android:pathData="m57.008,20.304v-3.356l-27.338,-0.002c-0.198,0 -0.359,-0.165 -0.359,-0.368l-0.069,-1.517c-0.116,-1.788 -1.34,-3.003 -2.997,-3.003h-11.287c-1.657,0 -3,1.343 -3,3v40.943"
|
|
||||||
android:strokeLineJoin="round"
|
|
||||||
android:strokeWidth="2"
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:strokeColor="#000"
|
|
||||||
android:strokeLineCap="round"/>
|
|
||||||
<path
|
|
||||||
android:pathData="m17.027,55.568c-0.59,1.954 -2.972,4.139 -4.646,4.394l44.665,0.011c1.657,0 2.323,-0.439 3,-3s7,-31.657 7,-31.657c0,-0.552 -0.448,-1 -1,-1H24.965c-0.552,0 -1,0.448 -1,1 0,0 -6.348,28.299 -6.938,30.253Z"
|
|
||||||
android:strokeLineJoin="round"
|
|
||||||
android:strokeWidth="2"
|
|
||||||
android:fillColor="#00000000"
|
|
||||||
android:strokeColor="#000"
|
|
||||||
android:strokeLineCap="round"/>
|
|
||||||
</group>
|
|
||||||
</vector>
|
|
@ -56,16 +56,6 @@
|
|||||||
app:maxImageSize="32dp"
|
app:maxImageSize="32dp"
|
||||||
app:srcCompat="@drawable/camera" />
|
app:srcCompat="@drawable/camera" />
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/btnOpen"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
android:contentDescription="@string/open"
|
|
||||||
app:fabCustomSize="46dp"
|
|
||||||
app:maxImageSize="32dp"
|
|
||||||
app:srcCompat="@drawable/open" />
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/btnManual"
|
android:id="@+id/btnManual"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
<string name="code">Code</string>
|
<string name="code">Code</string>
|
||||||
<string name="format">Format</string>
|
<string name="format">Format</string>
|
||||||
<string name="save">Save</string>
|
<string name="save">Save</string>
|
||||||
<string name="open">Open</string>
|
|
||||||
<string-array name="format_array">
|
<string-array name="format_array">
|
||||||
<item>CODE_39</item>
|
<item>CODE_39</item>
|
||||||
<item>CODE_93</item>
|
<item>CODE_93</item>
|
||||||
|
@ -1 +0,0 @@
|
|||||||
<p><i>Keepass-Fidelity</i> adds an interface to view/save barcodes (QR included) to Keepass through the plugin interface of the Keepass2Android app.</p><p><br></p><ul><li><b>Launcher:</b> view and launch recent entries (a per entry flag can disable this behaviour)</li><li><b>View:</b> view entries from the history or queried from Keepass2Android</li><li><b>Create:</b> add entries from the camera, an image of by filling out a form. The entry is then created in the Keepass2Android app</li><li><b>Data:</b> 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).</li></ul>
|
|
Before Width: | Height: | Size: 22 KiB |
@ -1 +0,0 @@
|
|||||||
Fidelity (Membership/Loyalty) Card plugin for Keepass2Android
|
|