Implemented reading QR coding and if it is a EPC QR code navigating to TransferMoneyDialog
This commit is contained in:
parent
4ef8e7330f
commit
64cb4ca9e2
|
@ -55,6 +55,8 @@ ext {
|
|||
|
||||
autocompleteVersion = "1.1.0"
|
||||
|
||||
zxingVersion = "3.3.0"
|
||||
|
||||
multiDexVersion = "2.0.1"
|
||||
|
||||
appCompatVersion = "1.1.0"
|
||||
|
@ -149,6 +151,7 @@ task jarAll {
|
|||
"fints4k:jvm6Jar",
|
||||
"fints4k-jvm:jar",
|
||||
"BankFinder:jvmJar",
|
||||
"EpcQrCodeParser:jvmJar",
|
||||
"BankingUiCommon:jvmJar",
|
||||
"fints4kBankingClient:jvmJar",
|
||||
"BankingUiCommon:jvmJar",
|
||||
|
@ -162,6 +165,7 @@ task packAllForXcode {
|
|||
"common:packForXcode",
|
||||
"fints4k:packForXcode",
|
||||
"BankFinder:packForXcode",
|
||||
"EpcQrCodeParser:packForXcode",
|
||||
"BankingUiCommon:packForXcode",
|
||||
"fints4kBankingClient:packForXcode",
|
||||
"BankingUiNativeIntegration:packForXcode"
|
||||
|
|
|
@ -106,6 +106,9 @@ dependencies {
|
|||
|
||||
implementation "com.otaliastudios:autocomplete:$autocompleteVersion"
|
||||
|
||||
implementation("com.journeyapps:zxing-android-embedded:4.1.0") { transitive = false } // transitive to use older Zxing version as ZXing 3.4.0 requires Android > 23
|
||||
implementation "com.google.zxing:core:$zxingVersion"
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesVersion"
|
||||
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="net.dankito.banking.ui.android">
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="net.dankito.banking.ui.android"
|
||||
>
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||
|
||||
<!-- Both are required for ZXing -->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android" />
|
||||
|
||||
|
||||
<application
|
||||
android:name="net.dankito.banking.ui.android.BankingApp"
|
||||
|
@ -15,7 +23,9 @@
|
|||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
android:hardwareAccelerated="true"
|
||||
android:theme="@style/AppTheme"
|
||||
>
|
||||
|
||||
<activity
|
||||
android:name=".activities.LandingActivity"
|
||||
|
|
|
@ -1,16 +1,27 @@
|
|||
package net.dankito.banking.ui.android
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.Menu
|
||||
import android.view.MotionEvent
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.os.postDelayed
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import com.github.clans.fab.FloatingActionMenu
|
||||
import com.google.zxing.integration.android.IntentIntegrator
|
||||
import com.google.zxing.integration.android.IntentResult
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import net.codinux.banking.tools.epcqrcode.ParseEpcQrCodeResult
|
||||
import net.dankito.banking.ui.android.activities.BaseActivity
|
||||
import net.dankito.banking.ui.android.di.BankingComponent
|
||||
import net.dankito.banking.ui.android.di.BankingModule
|
||||
import net.dankito.banking.ui.android.activities.BaseActivity
|
||||
import net.dankito.banking.ui.android.views.DrawerView
|
||||
import net.dankito.banking.ui.android.views.MainActivityFloatingActionMenuButton
|
||||
import net.dankito.banking.ui.presenter.BankingPresenter
|
||||
|
@ -87,6 +98,48 @@ class MainActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||
permissionsService.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
val scanQrCodeResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
|
||||
|
||||
if (scanQrCodeResult != null) {
|
||||
// at this point camera activity is still displayed and not returned yet to MainActivity -> app would crash if we don't wait
|
||||
Handler(Looper.getMainLooper()).postDelayed(250) {
|
||||
handleQrCodeScanResult(scanQrCodeResult)
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleQrCodeScanResult(scanQrCodeResult: IntentResult) {
|
||||
scanQrCodeResult.contents?.let { decodedQrCode ->
|
||||
val result = presenter.showTransferMoneyDialogWithDataFromQrCode(decodedQrCode)
|
||||
|
||||
if (result.successful == false) {
|
||||
showParseQrCodeError(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun showParseQrCodeError(result: ParseEpcQrCodeResult) {
|
||||
// TODO: show localized error message that matches ParseEpcQrCodeResultCode
|
||||
val errorMessage = getString(R.string.money_transfer_from_scanning_qr_code_error, result.error, result.decodedQrCode)
|
||||
|
||||
AlertDialog.Builder(this)
|
||||
.setMessage(errorMessage)
|
||||
.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() }
|
||||
.show()
|
||||
}
|
||||
|
||||
|
||||
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
||||
if(floatingActionMenuButton.handlesTouch(event)) { // close menu when menu is opened and touch is outside floatingActionMenuButton
|
||||
return true
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package net.dankito.banking.ui.android.views
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.github.clans.fab.FloatingActionButton
|
||||
import com.github.clans.fab.FloatingActionMenu
|
||||
import com.google.zxing.integration.android.IntentIntegrator
|
||||
import kotlinx.android.synthetic.main.view_floating_action_button_main.view.*
|
||||
import net.dankito.utils.multiplatform.toFile
|
||||
import net.dankito.banking.ui.android.R
|
||||
|
@ -27,6 +29,8 @@ open class MainActivityFloatingActionMenuButton(
|
|||
|
||||
protected lateinit var fabTransferMoney: FloatingActionButton
|
||||
|
||||
protected lateinit var fabMoneyTransferFromScanningQrCode: FloatingActionButton
|
||||
|
||||
protected lateinit var fabTransferMoneyFromPdf: FloatingActionButton
|
||||
|
||||
protected var lastSelectedFolder: File? = null
|
||||
|
@ -51,14 +55,19 @@ open class MainActivityFloatingActionMenuButton(
|
|||
}
|
||||
|
||||
fabTransferMoney = floatingActionMenu.fabTransferMoney
|
||||
fabMoneyTransferFromScanningQrCode = floatingActionMenu.fabMoneyTransferFromScanningQrCode
|
||||
fabTransferMoneyFromPdf = floatingActionMenu.fabTransferMoneyFromPdf
|
||||
|
||||
fabTransferMoney.setOnClickListener {
|
||||
executeAndCloseMenu { presenter.showTransferMoneyDialog() }
|
||||
}
|
||||
|
||||
fabMoneyTransferFromScanningQrCode.setOnClickListener {
|
||||
executeAndCloseMenu { scanQrCode() }
|
||||
}
|
||||
|
||||
fabTransferMoneyFromPdf.setOnClickListener {
|
||||
executeAndCloseMenu { transferMoneyWithDataFromPdf() }
|
||||
executeAndCloseMenu { showTransferMoneyDialogWithDataFromPdf() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,11 +76,27 @@ open class MainActivityFloatingActionMenuButton(
|
|||
protected open fun checkIfThereAreAccountsThatCanTransferMoney() {
|
||||
fabTransferMoney.isEnabled = presenter.hasAccountsSupportTransferringMoney
|
||||
|
||||
fabMoneyTransferFromScanningQrCode.isEnabled = presenter.hasAccountsSupportTransferringMoney
|
||||
|
||||
fabTransferMoneyFromPdf.isEnabled = presenter.hasAccountsSupportTransferringMoney
|
||||
}
|
||||
|
||||
|
||||
protected open fun transferMoneyWithDataFromPdf() {
|
||||
protected open fun scanQrCode() {
|
||||
permissionsService.checkPermission(Manifest.permission.CAMERA, R.string.rationale_camera_permission_to_scan_qr_code) { _, isGranted ->
|
||||
if (isGranted) {
|
||||
floatingActionMenu.context.asActivity()?.let { activity ->
|
||||
val intentIntegrator = IntentIntegrator(activity)
|
||||
intentIntegrator.setOrientationLocked(false)
|
||||
intentIntegrator.initiateScan(listOf(IntentIntegrator.QR_CODE))
|
||||
|
||||
// parsing decoded QR-code and showing TransferMoneyDialog is done in MainActivity.handleQrCodeScanResult()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun showTransferMoneyDialogWithDataFromPdf() {
|
||||
(floatingActionMenu.context.asActivity() as? FragmentActivity)?.let { activity ->
|
||||
val config = FileChooserDialogConfig(listOf("*.pdf"), lastSelectedFolder)
|
||||
|
||||
|
@ -79,17 +104,17 @@ open class MainActivityFloatingActionMenuButton(
|
|||
selectedFile?.let {
|
||||
lastSelectedFolder = selectedFile.parentFile
|
||||
|
||||
val result = presenter.transferMoneyWithDataFromPdf(selectedFile.toFile())
|
||||
val result = presenter.showTransferMoneyDialogWithDataFromPdf(selectedFile.toFile())
|
||||
|
||||
if (result.type != ExtractTransferMoneyDataFromPdfResultType.Success) {
|
||||
showTransferMoneyWithDataFromPdfError(activity, selectedFile, result)
|
||||
showTransferMoneyDialogWithDataFromPdfError(activity, selectedFile, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun showTransferMoneyWithDataFromPdfError(context: Context, pdfFile: File, result: ExtractTransferMoneyDataFromPdfResult) {
|
||||
protected open fun showTransferMoneyDialogWithDataFromPdfError(context: Context, pdfFile: File, result: ExtractTransferMoneyDataFromPdfResult) {
|
||||
val errorMessage = when (result.type) {
|
||||
ExtractTransferMoneyDataFromPdfResultType.NotASearchablePdf ->
|
||||
context.getString(R.string.transfer_money_from_pdf_error_message_not_a_searchable_pdf, pdfFile.absolutePath)
|
||||
|
|
|
@ -25,6 +25,14 @@
|
|||
fab:fab_label="@string/floating_action_menu_transfer_money_from_pdf"
|
||||
/>
|
||||
|
||||
<com.github.clans.fab.FloatingActionButton
|
||||
android:id="@+id/fabMoneyTransferFromScanningQrCode"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/MenuButtonsStyle"
|
||||
fab:fab_label="@string/floating_action_menu_money_transfer_from_scanning_qr_code"
|
||||
/>
|
||||
|
||||
<com.github.clans.fab.FloatingActionButton
|
||||
android:id="@+id/fabTransferMoney"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
|
@ -63,8 +63,11 @@
|
|||
|
||||
<string name="floating_action_menu_add_account">Konto</string>
|
||||
<string name="floating_action_menu_transfer_money">Überweisung</string>
|
||||
<string name="floating_action_menu_money_transfer_from_scanning_qr_code">Überweisung aus QR-Code</string>
|
||||
<string name="floating_action_menu_transfer_money_from_pdf">Überweisung aus PDF</string>
|
||||
|
||||
<string name="rationale_camera_permission_to_scan_qr_code">Um QR-Codes scannen zu können wird der Zugriff auf die Kamera benötigt.</string>
|
||||
|
||||
<string name="menu_home">Umsätze</string>
|
||||
|
||||
<string name="menu_main_update_transactions">Umsätze aktualisieren</string>
|
||||
|
@ -107,6 +110,8 @@
|
|||
<string name="dialog_transfer_money_message_transfer_successful">%1$s %2$s wurden erfolgreich an %3$s überwiesen.</string>
|
||||
<string name="dialog_transfer_money_message_transfer_failed">Konnte nicht %1$s %2$s an %3$s überweisen.\n\nFehlermeldung Ihrer Bank:\n\n%4$s</string>
|
||||
|
||||
<string name="money_transfer_from_scanning_qr_code_error">Überweisungsdaten konnten nicht aus QR-Code gelesen werden: %1$s\n\Gelesener QR-Code ist:\n%2$s</string>
|
||||
|
||||
<string name="transfer_money_from_pdf_error_message_not_a_searchable_pdf">Konnte Text nicht aus Datei "%1$s" extrahieren. Enthält die Datei auch Text oder nur Bilder?</string>
|
||||
<string name="transfer_money_from_pdf_error_message_could_not_extract_text">Text konnte nicht aus Datei "%1$s" extrahiert werden:\n\n%2$s</string>
|
||||
<string name="transfer_money_from_pdf_error_message_could_not_extract_invoice_data">Überweisungsdaten konnten aus der Datei "%1$s" nicht ausgelesen werden:\n\n%2$s</string>
|
||||
|
|
|
@ -63,8 +63,11 @@
|
|||
|
||||
<string name="floating_action_menu_add_account">Account</string>
|
||||
<string name="floating_action_menu_transfer_money">Transfer money</string>
|
||||
<string name="floating_action_menu_money_transfer_from_scanning_qr_code">Money transfer from scanning QR-Code</string>
|
||||
<string name="floating_action_menu_transfer_money_from_pdf">Transfer money from PDF</string>
|
||||
|
||||
<string name="rationale_camera_permission_to_scan_qr_code">To scan QR-Codes permission to access camera is required.</string>
|
||||
|
||||
<string name="menu_home">Home</string>
|
||||
|
||||
<string name="menu_main_update_transactions">Update transactions</string>
|
||||
|
@ -107,6 +110,8 @@
|
|||
<string name="dialog_transfer_money_message_transfer_successful">Successfully transferred %1$s %2$s to %3$s.</string>
|
||||
<string name="dialog_transfer_money_message_transfer_failed">Could not transfer %1$s %2$s to %3$s.\n\nError message from your bank:\n\n%4$s</string>
|
||||
|
||||
<string name="money_transfer_from_scanning_qr_code_error">Could not extract transfer data from QR-Code: %1$s\n\nExtracted QR-Code was:\n%2$s</string>
|
||||
|
||||
<string name="transfer_money_from_pdf_error_message_not_a_searchable_pdf">File "%1$s" is not a searchable PDF. Could therefore not extract text from it.</string>
|
||||
<string name="transfer_money_from_pdf_error_message_could_not_extract_text">Could not extract text from file "%1$s":\n\n%2$s</string>
|
||||
<string name="transfer_money_from_pdf_error_message_could_not_extract_invoice_data">Could not extract cash transfer data from file "%1$s":\n\n%2$s</string>
|
||||
|
|
|
@ -54,7 +54,7 @@ open class MainMenuBar(protected val presenter: BankingPresenter) : View() {
|
|||
item(messages["main.window.menu.file.new.cash.transfer.from.pdf"], KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN, KeyCodeCombination.SHIFT_DOWN)) {
|
||||
enableWhen(areAccountsThatCanTransferMoneyAdded)
|
||||
|
||||
action { transferMoneyWithDataFromPdf() }
|
||||
action { showTransferMoneyDialogWithDataFromPdf() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ open class MainMenuBar(protected val presenter: BankingPresenter) : View() {
|
|||
areAccountsThatCanTransferMoneyAdded.value = presenter.hasAccountsSupportTransferringMoney
|
||||
}
|
||||
|
||||
protected open fun transferMoneyWithDataFromPdf() {
|
||||
protected open fun showTransferMoneyDialogWithDataFromPdf() {
|
||||
val fileChooser = FileChooser()
|
||||
|
||||
fileChooser.initialDirectory = lastSelectedFolder
|
||||
|
@ -80,15 +80,15 @@ open class MainMenuBar(protected val presenter: BankingPresenter) : View() {
|
|||
fileChooser.showOpenDialog(currentStage)?.let { pdfFile ->
|
||||
lastSelectedFolder = pdfFile.parentFile
|
||||
|
||||
val result = presenter.transferMoneyWithDataFromPdf(pdfFile.toFile())
|
||||
val result = presenter.showTransferMoneyDialogWithDataFromPdf(pdfFile.toFile())
|
||||
|
||||
if (result.type != ExtractTransferMoneyDataFromPdfResultType.Success) {
|
||||
showTransferMoneyWithDataFromPdfError(pdfFile, result)
|
||||
showTransferMoneyDialogWithDataFromPdfError(pdfFile, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun showTransferMoneyWithDataFromPdfError(pdfFile: File, result: ExtractTransferMoneyDataFromPdfResult) {
|
||||
protected open fun showTransferMoneyDialogWithDataFromPdfError(pdfFile: File, result: ExtractTransferMoneyDataFromPdfResult) {
|
||||
val errorMessageKey = when (result.type) {
|
||||
ExtractTransferMoneyDataFromPdfResultType.NotASearchablePdf -> "transfer.money.from.pdf.error.message.not.a.searchable.pdf"
|
||||
ExtractTransferMoneyDataFromPdfResultType.CouldNotExtractText -> "transfer.money.from.pdf.error.message.could.not.extract.text"
|
||||
|
|
|
@ -40,6 +40,8 @@ kotlin {
|
|||
api project(":fints4k")
|
||||
|
||||
api project(":BankFinder")
|
||||
|
||||
api project(":EpcQrCodeParser")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ import net.dankito.banking.util.extraction.IInvoiceDataExtractor
|
|||
import net.dankito.banking.util.extraction.ITextExtractorRegistry
|
||||
import net.dankito.banking.util.extraction.NoOpInvoiceDataExtractor
|
||||
import net.dankito.banking.util.extraction.NoOpTextExtractorRegistry
|
||||
import net.codinux.banking.tools.epcqrcode.*
|
||||
import net.dankito.utils.multiplatform.*
|
||||
import net.dankito.utils.multiplatform.log.LoggerFactory
|
||||
import kotlin.collections.ArrayList
|
||||
|
@ -49,7 +50,8 @@ open class BankingPresenter(
|
|||
protected val textExtractorRegistry: ITextExtractorRegistry = NoOpTextExtractorRegistry(),
|
||||
protected val invoiceDataExtractor: IInvoiceDataExtractor = NoOpInvoiceDataExtractor(),
|
||||
protected val currencyInfoProvider: ICurrencyInfoProvider = CurrencyInfoProvider(),
|
||||
protected val asyncRunner: IAsyncRunner = CoroutinesAsyncRunner()
|
||||
protected val asyncRunner: IAsyncRunner = CoroutinesAsyncRunner(),
|
||||
protected val qrCodeParser: EpcQrCodeParser = EpcQrCodeParser() // TODO: create interface
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
@ -586,7 +588,28 @@ open class BankingPresenter(
|
|||
}
|
||||
}
|
||||
|
||||
open fun transferMoneyWithDataFromPdf(pdf: File): ExtractTransferMoneyDataFromPdfResult {
|
||||
open fun showTransferMoneyDialogWithDataFromQrCode(decodedQrCode: String): ParseEpcQrCodeResult {
|
||||
val result = qrCodeParser.parseEpcQrCode(decodedQrCode)
|
||||
|
||||
if (result.successful) {
|
||||
result.epcQrCode?.let { epcQrCode ->
|
||||
// TODO: show originatorInformation to user
|
||||
|
||||
val transferMoneyData = TransferMoneyData(
|
||||
allAccounts.first(),
|
||||
epcQrCode.receiverName,
|
||||
epcQrCode.iban,
|
||||
epcQrCode.bic ?: "",
|
||||
epcQrCode.amount?.let { BigDecimal(it) } ?: BigDecimal.Zero,
|
||||
epcQrCode.remittance)
|
||||
showTransferMoneyDialog(transferMoneyData)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
open fun showTransferMoneyDialogWithDataFromPdf(pdf: File): ExtractTransferMoneyDataFromPdfResult {
|
||||
val extractionResult = textExtractorRegistry.extractTextWithBestExtractorForFile(pdf)
|
||||
|
||||
if (extractionResult.couldExtractText == false || extractionResult.text == null) {
|
||||
|
|
|
@ -6,6 +6,8 @@ plugins {
|
|||
|
||||
ext.artifactName = "banking-ui-native-integration"
|
||||
|
||||
def frameworkName = "BankingUiSwift"
|
||||
|
||||
|
||||
kotlin {
|
||||
|
||||
|
@ -15,12 +17,13 @@ kotlin {
|
|||
fromPreset(iOSTarget, 'ios') {
|
||||
binaries {
|
||||
framework {
|
||||
baseName = "BankingUiSwift"
|
||||
baseName = frameworkName
|
||||
|
||||
// transitiveExport = true
|
||||
export(project(":BankingUiCommon"))
|
||||
export(project(":fints4kBankingClient"))
|
||||
export(project(":BankFinder"))
|
||||
export(project(":EpcQrCodeParser"))
|
||||
// do not add fints4k to exports, would lead to a lot of naming conflicts. In this way fints4k classes get prefixed with 'Fints4k' which is Ok
|
||||
// export(project(":fints4k"))
|
||||
// exporting common would lead to naming conflicts with Foundation classes like Date, UUID, Thread, ...
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
3642F01A2502931F005186FE /* RealTimeTransferInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F0192502931F005186FE /* RealTimeTransferInfoView.swift */; };
|
||||
3642F04B25031157005186FE /* SectionHeaderWithRightAlignedEditButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3642F04A25031157005186FE /* SectionHeaderWithRightAlignedEditButton.swift */; };
|
||||
36671255253A761500BD2301 /* BankCredentialsPasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36671254253A761500BD2301 /* BankCredentialsPasswordView.swift */; };
|
||||
36671290253F8D5200BD2301 /* ScanQrCodeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3667128F253F8D5200BD2301 /* ScanQrCodeViewController.swift */; };
|
||||
36671294254045E100BD2301 /* EpcQrCodeParser.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 36671293254045E100BD2301 /* EpcQrCodeParser.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
36693A4E25280BCB00BB7AE5 /* InfoButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36693A4D25280BCB00BB7AE5 /* InfoButton.swift */; };
|
||||
366FA4DA24C472A90094F009 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4D924C472A90094F009 /* Extensions.swift */; };
|
||||
366FA4DC24C479120094F009 /* BankInfoListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 366FA4DB24C479120094F009 /* BankInfoListItem.swift */; };
|
||||
|
@ -157,6 +159,7 @@
|
|||
dstPath = "";
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
36671294254045E100BD2301 /* EpcQrCodeParser.framework in Embed Frameworks */,
|
||||
3684EB8F250B7F3C0001139E /* BankingUiCommon.framework in Embed Frameworks */,
|
||||
36BCF86A24BA550D005BEC29 /* BankFinder.framework in Embed Frameworks */,
|
||||
36BCF85F24BA4DA8005BEC29 /* MultiplatformUtils.framework in Embed Frameworks */,
|
||||
|
@ -199,6 +202,8 @@
|
|||
3642F0192502931F005186FE /* RealTimeTransferInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealTimeTransferInfoView.swift; sourceTree = "<group>"; };
|
||||
3642F04A25031157005186FE /* SectionHeaderWithRightAlignedEditButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeaderWithRightAlignedEditButton.swift; sourceTree = "<group>"; };
|
||||
36671254253A761500BD2301 /* BankCredentialsPasswordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankCredentialsPasswordView.swift; sourceTree = "<group>"; };
|
||||
3667128F253F8D5200BD2301 /* ScanQrCodeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanQrCodeViewController.swift; sourceTree = "<group>"; };
|
||||
36671293254045E100BD2301 /* EpcQrCodeParser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EpcQrCodeParser.framework; path = "../../tools/EpcQrCodeParser/build/xcode-frameworks/EpcQrCodeParser.framework"; sourceTree = "<group>"; };
|
||||
36693A4D25280BCB00BB7AE5 /* InfoButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoButton.swift; sourceTree = "<group>"; };
|
||||
366FA4D924C472A90094F009 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
|
||||
366FA4DB24C479120094F009 /* BankInfoListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BankInfoListItem.swift; sourceTree = "<group>"; };
|
||||
|
@ -371,6 +376,7 @@
|
|||
36E21ED424DC549800649DC8 /* BankSettingsDialog.swift */,
|
||||
36E21ED624DC617200649DC8 /* BankAccountSettingsDialog.swift */,
|
||||
36B8A4472503D12100C15359 /* ProtectAppSettingsDialog.swift */,
|
||||
3667128F253F8D5200BD2301 /* ScanQrCodeViewController.swift */,
|
||||
);
|
||||
path = dialogs;
|
||||
sourceTree = "<group>";
|
||||
|
@ -481,6 +487,7 @@
|
|||
36FC928F24B39A05002B12E9 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
36671293254045E100BD2301 /* EpcQrCodeParser.framework */,
|
||||
3684EB8E250B7F3C0001139E /* BankingUiCommon.framework */,
|
||||
3684EB8C250B7F2B0001139E /* BankingUiCommon.framework.dSYM */,
|
||||
36FC929A24B39A05002B12E9 /* BankingiOSApp */,
|
||||
|
@ -992,6 +999,7 @@
|
|||
36BE068F24CEE1BD00CBBB68 /* AllBanksListItem.swift in Sources */,
|
||||
360782C324E49FF70098FEFE /* ValidationLabel.swift in Sources */,
|
||||
3684EB92250FD4AF0001139E /* LabelledValue.swift in Sources */,
|
||||
36671290253F8D5200BD2301 /* ScanQrCodeViewController.swift in Sources */,
|
||||
36BE069124CEF52800CBBB68 /* UpdateButton.swift in Sources */,
|
||||
36E21ED124DC540400649DC8 /* SettingsDialog.swift in Sources */,
|
||||
3684EB8B2508F6F00001139E /* SearchBarWithLabel.swift in Sources */,
|
||||
|
|
|
@ -130,7 +130,8 @@
|
|||
|
||||
/* New action sheet */
|
||||
|
||||
"Show transfer money dialog" = "Transfer money";
|
||||
"Show transfer money dialog" = "Money transfer";
|
||||
"Money transfer from scanning QR-Code" = "Money transfer from QR-Code";
|
||||
|
||||
|
||||
/* TransferMoneyDialog */
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Used to scan QR codes to initiate money transfer</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
|
|
|
@ -62,7 +62,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||
|
||||
DependencyInjector.register(dependency: presenter)
|
||||
|
||||
window.rootViewController = UINavigationController(rootViewController: TabBarController())
|
||||
window.rootViewController = UINavigationController(rootViewController: TabBarController(presenter))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
import SwiftUI
|
||||
import AVFoundation
|
||||
import BankingUiSwift
|
||||
|
||||
|
||||
class TabBarController : UITabBarController, UITabBarControllerDelegate {
|
||||
|
||||
@ObservedObject var data: AppData = AppData()
|
||||
|
||||
private let presenter: BankingPresenterSwift
|
||||
|
||||
|
||||
init(_ presenter: BankingPresenterSwift) {
|
||||
self.presenter = presenter
|
||||
|
||||
super.init(nibName: nil, bundle: Bundle.main)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
@ -88,15 +103,33 @@ class TabBarController : UITabBarController, UITabBarControllerDelegate {
|
|||
|
||||
|
||||
private func showNewOptionsActionSheet() {
|
||||
let transferMoneyAction = UIAlertAction.default("Show transfer money dialog".localize()) { SceneDelegate.navigateToView(TransferMoneyDialog()) }
|
||||
let moneyTransferFromQrCodeAction = UIAlertAction.default("Money transfer from scanning QR-Code".localize()) { self.scanQrCodeIfCameraAccessGranted() }
|
||||
moneyTransferFromQrCodeAction.isEnabled = data.hasAccountsThatSupportTransferringMoney
|
||||
|
||||
let transferMoneyAction = UIAlertAction.default("Show transfer money dialog".localize()) { self.presenter.showTransferMoneyDialog(preselectedValues: nil) }
|
||||
transferMoneyAction.isEnabled = data.hasAccountsThatSupportTransferringMoney
|
||||
|
||||
ActionSheet(
|
||||
nil,
|
||||
moneyTransferFromQrCodeAction,
|
||||
transferMoneyAction,
|
||||
UIAlertAction.default("Add account") { SceneDelegate.navigateToView(AddAccountDialog()) },
|
||||
UIAlertAction.cancel()
|
||||
).show(self.tabBar, self.tabBar.bounds.midX, 0)
|
||||
}
|
||||
|
||||
private func scanQrCodeIfCameraAccessGranted() {
|
||||
AVCaptureDevice.requestAccess(for: .video) { granted in
|
||||
if granted {
|
||||
DispatchQueue.main.async { // completionHandler is called on an arbitrary dispatch queue
|
||||
SceneDelegate.navigateToViewController(ScanQrCodeViewController() { decodedQrCode in
|
||||
if let decodedQrCode = decodedQrCode {
|
||||
self.presenter.showTransferMoneyDialogWithDataFromQrCode(decodedQrCode: decodedQrCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -132,6 +132,7 @@
|
|||
/* New action sheet */
|
||||
|
||||
"Show transfer money dialog" = "Überweisung";
|
||||
"Money transfer from scanning QR-Code" = "Überweisung aus QR-Code";
|
||||
|
||||
|
||||
/* TransferMoneyDialog */
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
import AVFoundation
|
||||
import UIKit
|
||||
|
||||
|
||||
/**
|
||||
Copied from Hacking with Swift: https://www.hackingwithswift.com/example-code/media/how-to-scan-a-qr-code
|
||||
*/
|
||||
class ScanQrCodeViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
|
||||
|
||||
private let scanResult: (String?) -> Void
|
||||
|
||||
private var captureSession: AVCaptureSession!
|
||||
|
||||
private var previewLayer: AVCaptureVideoPreviewLayer!
|
||||
|
||||
|
||||
init(_ scanResult: @escaping (String?) -> Void) {
|
||||
self.scanResult = scanResult
|
||||
|
||||
super.init(nibName: nil, bundle: Bundle.main)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.backgroundColor = UIColor.black
|
||||
captureSession = AVCaptureSession()
|
||||
|
||||
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
|
||||
let videoInput: AVCaptureDeviceInput
|
||||
|
||||
do {
|
||||
videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
if (captureSession.canAddInput(videoInput)) {
|
||||
captureSession.addInput(videoInput)
|
||||
} else {
|
||||
failed()
|
||||
return
|
||||
}
|
||||
|
||||
let metadataOutput = AVCaptureMetadataOutput()
|
||||
|
||||
if (captureSession.canAddOutput(metadataOutput)) {
|
||||
captureSession.addOutput(metadataOutput)
|
||||
|
||||
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
|
||||
metadataOutput.metadataObjectTypes = [.qr]
|
||||
} else {
|
||||
failed()
|
||||
return
|
||||
}
|
||||
|
||||
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
|
||||
previewLayer.frame = view.layer.bounds
|
||||
previewLayer.videoGravity = .resizeAspectFill
|
||||
view.layer.addSublayer(previewLayer)
|
||||
|
||||
captureSession.startRunning()
|
||||
}
|
||||
|
||||
func failed() {
|
||||
self.closeDialog()
|
||||
|
||||
let ac = UIAlertController(title: "Scanning not supported", message: "Your device does not support scanning a code from an item. Please use a device with a camera.", preferredStyle: .alert)
|
||||
ac.addAction(UIAlertAction(title: "OK", style: .default))
|
||||
present(ac, animated: true)
|
||||
captureSession = nil
|
||||
|
||||
scanResult(nil)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if (captureSession?.isRunning == false) {
|
||||
captureSession.startRunning()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
if (captureSession?.isRunning == true) {
|
||||
captureSession.stopRunning()
|
||||
}
|
||||
}
|
||||
|
||||
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
|
||||
captureSession.stopRunning()
|
||||
var didCloseDialog = false
|
||||
|
||||
if let metadataObject = metadataObjects.first {
|
||||
if let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject {
|
||||
if let decodedQrCode = readableObject.stringValue {
|
||||
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
|
||||
|
||||
didCloseDialog = true
|
||||
SceneDelegate.dismissCurrentView()
|
||||
|
||||
scanResult(decodedQrCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if didCloseDialog == false { // for all other cases where QR code could not successfully be read
|
||||
closeDialog()
|
||||
}
|
||||
}
|
||||
|
||||
private func closeDialog() {
|
||||
SceneDelegate.dismissCurrentView()
|
||||
}
|
||||
|
||||
override var prefersStatusBarHidden: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
||||
return .portrait
|
||||
}
|
||||
|
||||
}
|
|
@ -94,7 +94,7 @@ struct AccountTransactionListItem: View {
|
|||
}
|
||||
|
||||
private func navigateToTransferMoneyDialog(_ preselectedValues: TransferMoneyData) {
|
||||
SceneDelegate.navigateToView(TransferMoneyDialog(preselectedValues: preselectedValues))
|
||||
SceneDelegate.navigateToView(TransferMoneyDialog(preselectedValues))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue