8 Commits
1.1a ... 1.2a

Author SHA1 Message Date
bcfcb85121 Update README.md 2024-03-29 13:29:40 +01:00
49c650c8a9 Update README.md 2024-03-29 13:29:19 +01:00
b5f52c7e13 Release 1.2a prep 2024-03-29 13:28:30 +01:00
d81922d2c9 File Scanner & Metadata 2024-03-29 13:27:27 +01:00
84b2c2c455 Update README.md 2024-03-25 07:13:58 +01:00
841c3dea24 Update README.md 2024-03-24 15:45:28 +01:00
6596f347a1 Updated icon 2024-03-24 15:41:26 +01:00
a2dd009533 Fixed versioning 2024-03-24 13:29:13 +01:00
28 changed files with 418 additions and 60 deletions

View File

@ -1,10 +1,10 @@
<!--suppress ALL --> <!--suppress ALL -->
<div align="center"> <div align="center">
<h1>Keepass Fidelity</h1> <h1>Keepass Fidelity</h1>
<img width="100px" src="./metadata/en-US/images/icon.png" alt="Logo">
<p>A minimalist fidelity/loyalty card plugin</p> <p>A minimalist fidelity/loyalty card plugin</p>
<img src="https://forthebadge.com/images/badges/built-for-android.svg" alt="Built for Android"> <img src="https://forthebadge.com/images/badges/built-for-android.svg" alt="Built for Android">
<img src="https://forthebadge.com/images/badges/built-with-love.svg" alt="Built with love"> <img src="https://forthebadge.com/images/badges/built-with-love.svg" alt="Built with love">
<br> <br>
@ -18,9 +18,9 @@
<div align="center"> <div align="center">
<table> <table>
<tr> <tr>
<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/launcher.jpg" alt="Launcher" 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/view.jpg" alt="View" 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> <td style="width: 33%; height: 100px;"><img src="./metadata/en-US/images/phoneScreenshots/edit.jpg" alt="Edit" style="width: 100%; height: 100%;"></td>
</tr> </tr>
</table> </table>
</div> </div>
@ -32,11 +32,14 @@
- Recently used history for fast access - Recently used history for fast access
- Protect entries from caching - Protect entries from caching
- Minimalist design and features - Minimalist design and features
- Supported Formats:CODE_39, CODE_93, CODE_128, EAN_8, EAN_13, UPC_A, UPC_E, CODE_QR, PDF_417, AZTEC, CODABAR, DATA_MATRIX, ITF - Supported Formats: CODE_39, CODE_93, CODE_128, EAN_8, EAN_13, UPC_A, UPC_E, CODE_QR, PDF_417, AZTEC, CODABAR, DATA_MATRIX, ITF
## 📳 Installation ## 📳 Installation
<div style="display: flex; justify-content: center; align-items: center; flex-direction: row;"> <div style="display: flex; justify-content: center; align-items: center; flex-direction: row;">
<a href="https://apt.izzysoft.de/fdroid/index/apk/net.helcel.fidelity">
<img width="200" height="80" alt="Izzy Download" src=".github/images/izzy.png">
</a>
<a href="https://github.com/choelzl/keepass-fidelity/releases/latest"> <a href="https://github.com/choelzl/keepass-fidelity/releases/latest">
<img width="200" height="84" alt="APK Download" src=".github/images/apk.png"> <img width="200" height="84" alt="APK Download" src=".github/images/apk.png">
</a> </a>

View File

@ -19,8 +19,6 @@ android {
resValue "string", "app_name", "Keepass Fidelity" resValue "string", "app_name", "Keepass Fidelity"
minSdk 28 minSdk 28
targetSdk 34 targetSdk 34
versionCode 1
versionName "1.0"
} }
@ -61,6 +59,14 @@ android {
buildFeatures { buildFeatures {
viewBinding true viewBinding true
} }
dependenciesInfo {
// Disables dependency metadata when building APKs.
includeInApk = false
// Disables dependency metadata when building Android App Bundles.
includeInBundle = false
}
} }

View File

@ -1,13 +1,16 @@
<?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="3" android:versionCode="6"
android:versionName="1.1a"> android:versionName="1.2a">
<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="@drawable/logo" android:icon="@mipmap/ic_launcher_round"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true"> android:supportsRtl="true">
<activity <activity

View File

@ -0,0 +1,107 @@
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()
}
}
}

View File

@ -49,6 +49,11 @@ class Launcher : Fragment() {
startScanner() startScanner()
hideMenuAdd() hideMenuAdd()
} }
binding.btnOpen.setOnClickListener {
startFileScanner()
hideMenuAdd()
}
binding.btnManual.setOnClickListener { binding.btnManual.setOnClickListener {
startCreateEntry() startCreateEntry()
@ -96,6 +101,10 @@ class Launcher : Fragment() {
startFragment(Scanner()) startFragment(Scanner())
} }
private fun startFileScanner() {
startFragment(FileScanner())
}
private fun startCreateEntry() { private fun startCreateEntry() {
startFragment(CreateEntry()) startFragment(CreateEntry())
} }

View File

@ -2,25 +2,23 @@ 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.getAnalysisUseCase import net.helcel.fidelity.tools.BarcodeScanner.analysisUseCase
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
@ -28,6 +26,17 @@ 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?,
@ -37,11 +46,8 @@ 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
} }
@ -55,26 +61,16 @@ class Scanner : Fragment() {
.commit() .commit()
} }
private fun hasCameraPermission() =
ActivityCompat.checkSelfPermission(
requireContext(),
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED
private fun requestPermission() { private fun scannerResult(code: String?, format: String?) {
ActivityCompat.requestPermissions( if (!code.isNullOrEmpty() && !format.isNullOrEmpty()) {
requireActivity(), this.code = code
arrayOf(Manifest.permission.CAMERA), this.fmt = format
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()
} }
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) it.setSurfaceProvider(binding.cameraView.surfaceProvider)
} }
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
val analysisUseCase = getAnalysisUseCase { code, format -> val analysisUseCase = analysisUseCase { code, format ->
if (!code.isNullOrEmpty() && !format.isNullOrEmpty()) { scannerResult(code, format)
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(

View File

@ -4,7 +4,6 @@ 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
@ -15,14 +14,13 @@ import net.helcel.fidelity.tools.BarcodeFormatConverter.formatToString
import java.util.concurrent.Executors import java.util.concurrent.Executors
@OptIn(ExperimentalGetImage::class)
object BarcodeScanner { object BarcodeScanner {
@OptIn(ExperimentalGetImage::class) private fun processImage(
private fun processImageProxy( bitmap: Bitmap,
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 {
@ -32,8 +30,6 @@ object BarcodeScanner {
cb(null, null) cb(null, null)
} catch (e: ReaderException) { } catch (e: ReaderException) {
cb(null, null) cb(null, null)
} finally {
imageProxy.close()
} }
} }
@ -45,13 +41,21 @@ object BarcodeScanner {
return BinaryBitmap(HybridBinarizer(source)) return BinaryBitmap(HybridBinarizer(source))
} }
fun getAnalysisUseCase(cb: (String?, String?) -> Unit): ImageAnalysis { fun analysisUseCase(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 ->
processImageProxy(imageProxy, cb) val bitmap = imageProxy.toBitmap()
imageProxy.close()
bitmapUseCase(bitmap, cb)
} }
return analysisUseCase return analysisUseCase
} }
fun bitmapUseCase(bitmap: Bitmap, cb: (String?, String?) -> Unit) {
processImage(bitmap, cb)
}
} }

View File

@ -20,4 +20,12 @@ 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)
}
} }

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/logo_g"/>
</adaptive-icon>

View File

@ -1,5 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:width="256dp"
android:height="256dp"
android:viewportWidth="256"
android:viewportHeight="256"
android:gravity="center"
>
<layer-list>
<item <item
android:width="128dp" android:width="128dp"
android:height="128dp" android:height="128dp"
@ -18,4 +26,5 @@
android:gravity="center" android:gravity="center"
android:left="72dp" android:left="72dp"
android:bottom="20dp" /> android:bottom="20dp" />
</layer-list></item>
</layer-list> </layer-list>

View File

@ -0,0 +1,167 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="128"
android:viewportHeight="128">
<group
android:translateX="28"
android:translateY="28">
<group>
<path
android:fillColor="#92D3F5"
android:pathData="M59.959,52.794H12.041c-0.552,0 -1,-0.448 -1,-1v-29.547c0,-0.552 0.448,-1 1,-1h47.918c0.552,0 1,0.448 1,1v29.547C60.959,52.347 60.511,52.794 59.959,52.794z"
android:strokeWidth="2"
android:strokeColor="#000000" />
</group>
<group
android:scaleX="0.5"
android:scaleY="0.5"
android:translateX="32"
android:translateY="16">
<path
android:fillColor="#EA5A47"
android:pathData="M46.5,56l-10,-11.151l-10,11.151l0,-45.042l20,0z"
android:strokeWidth="2"
android:strokeColor="#00000000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#D22F27"
android:pathData="M41.864,12.03l0,37.854l4.523,5.044l0,-42.898z"
android:strokeColor="#00000000" />
<path
android:fillColor="#00000000"
android:pathData="M46.5,56l-10,-11.151l-10,11.151l0,-45.042l20,0z"
android:strokeWidth="2"
android:strokeColor="#000000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M46.5,56l-10,-11.151l-10,11.151l0,-45.042l20,0z"
android:strokeWidth="2"
android:strokeColor="#000000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
</group>
<group
android:scaleX="0.5"
android:scaleY="0.5"
android:translateX="10"
android:translateY="18">
<path
android:fillColor="#00000000"
android:pathData="M9,21V52"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M12,21V52"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M20,21V50"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M28,21V50"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#000"
android:pathData="M15,50V21H17V50H15Z"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#000"
android:pathData="M23,50V21H25V50H23Z"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#000"
android:pathData="M31,50V21H32V50H31Z"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M46,21V50"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M49,21V50"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M57,21V50"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#000"
android:pathData="M41,50V21H43V50H41Z"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#000"
android:pathData="M52,50V21H54V50H52Z"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M60,21V52"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M63,21V52"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M35,21V52"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
<path
android:fillColor="#00000000"
android:pathData="M38,21V52"
android:strokeWidth="2"
android:strokeColor="#000"
android:strokeLineCap="round"
android:strokeLineJoin="round" />
</group>
</group>
</vector>

View File

@ -0,0 +1,22 @@
<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>

View File

@ -56,6 +56,16 @@
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"

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#393939</color>
</resources>

View File

@ -15,6 +15,7 @@
<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>

View File

@ -0,0 +1 @@
<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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1 @@
Fidelity (Membership/Loyalty) Card plugin for Keepass2Android