aboutsummaryrefslogtreecommitdiff
path: root/cashier/src
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2020-03-18 15:54:36 -0300
committerTorsten Grote <t@grobox.de>2020-03-18 15:54:36 -0300
commit070fd9b7d3793082e447d7713d1dda932f237c99 (patch)
tree5d1c3ec9006eeb8adb23bac34edbced2642dba86 /cashier/src
parentd20a5a81af4fa88eb26ea9678ecc13a0dd3d64a5 (diff)
downloadtaler-android-070fd9b7d3793082e447d7713d1dda932f237c99.tar.gz
taler-android-070fd9b7d3793082e447d7713d1dda932f237c99.tar.bz2
taler-android-070fd9b7d3793082e447d7713d1dda932f237c99.zip
Factor out some cashier code into common library
Diffstat (limited to 'cashier/src')
-rw-r--r--cashier/src/main/AndroidManifest.xml4
-rw-r--r--cashier/src/main/java/net/taler/cashier/Amount.kt45
-rw-r--r--cashier/src/main/java/net/taler/cashier/BalanceFragment.kt3
-rw-r--r--cashier/src/main/java/net/taler/cashier/MainViewModel.kt3
-rw-r--r--cashier/src/main/java/net/taler/cashier/Utils.kt91
-rw-r--r--cashier/src/main/java/net/taler/cashier/withdraw/NfcManager.kt234
-rw-r--r--cashier/src/main/java/net/taler/cashier/withdraw/QrCodeManager.kt42
-rw-r--r--cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt5
-rw-r--r--cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt3
9 files changed, 10 insertions, 420 deletions
diff --git a/cashier/src/main/AndroidManifest.xml b/cashier/src/main/AndroidManifest.xml
index 345c9a1..bdc5ec0 100644
--- a/cashier/src/main/AndroidManifest.xml
+++ b/cashier/src/main/AndroidManifest.xml
@@ -3,10 +3,6 @@
xmlns:tools="http://schemas.android.com/tools"
package="net.taler.cashier">
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.NFC" />
-
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
diff --git a/cashier/src/main/java/net/taler/cashier/Amount.kt b/cashier/src/main/java/net/taler/cashier/Amount.kt
deleted file mode 100644
index 2c237c8..0000000
--- a/cashier/src/main/java/net/taler/cashier/Amount.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * This file is part of GNU Taler
- * (C) 2020 Taler Systems S.A.
- *
- * GNU Taler is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 3, or (at your option) any later version.
- *
- * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-package net.taler.cashier
-
-data class Amount(val currency: String, val amount: String) {
-
- companion object {
-
- private val SIGNED_REGEX = Regex("""([+\-])(\w+):([0-9.]+)""")
-
- @Suppress("unused")
- fun fromString(strAmount: String): Amount {
- val components = strAmount.split(":")
- return Amount(components[0], components[1])
- }
-
- fun fromStringSigned(strAmount: String): Amount? {
- val groups = SIGNED_REGEX.matchEntire(strAmount)?.groupValues ?: emptyList()
- if (groups.size < 4) return null
- var amount = groups[3].toDoubleOrNull() ?: return null
- if (groups[1] == "-") amount *= -1
- val currency = groups[2]
- val amountStr = amount.toString()
- // only display as many digits as required to precisely render the balance
- return Amount(currency, amountStr.removeSuffix(".0"))
- }
- }
-
- override fun toString() = "$amount $currency"
-
-}
diff --git a/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt b/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt
index b3a0221..2178a78 100644
--- a/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt
+++ b/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt
@@ -34,6 +34,9 @@ import kotlinx.android.synthetic.main.fragment_balance.*
import net.taler.cashier.BalanceFragmentDirections.Companion.actionBalanceFragmentToTransactionFragment
import net.taler.cashier.withdraw.LastTransaction
import net.taler.cashier.withdraw.WithdrawStatus
+import net.taler.common.Amount
+import net.taler.common.fadeIn
+import net.taler.common.fadeOut
sealed class BalanceResult {
object Error : BalanceResult()
diff --git a/cashier/src/main/java/net/taler/cashier/MainViewModel.kt b/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
index 3874038..6cd12ff 100644
--- a/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
+++ b/cashier/src/main/java/net/taler/cashier/MainViewModel.kt
@@ -32,9 +32,10 @@ import androidx.security.crypto.MasterKeys
import androidx.security.crypto.MasterKeys.AES256_GCM_SPEC
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-import net.taler.cashier.Amount.Companion.fromStringSigned
import net.taler.cashier.HttpHelper.makeJsonGetRequest
import net.taler.cashier.withdraw.WithdrawManager
+import net.taler.common.Amount.Companion.fromStringSigned
+import net.taler.common.isOnline
private val TAG = MainViewModel::class.java.simpleName
diff --git a/cashier/src/main/java/net/taler/cashier/Utils.kt b/cashier/src/main/java/net/taler/cashier/Utils.kt
deleted file mode 100644
index 62f7a77..0000000
--- a/cashier/src/main/java/net/taler/cashier/Utils.kt
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * This file is part of GNU Taler
- * (C) 2020 Taler Systems S.A.
- *
- * GNU Taler is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 3, or (at your option) any later version.
- *
- * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-package net.taler.cashier
-
-import android.content.Context
-import android.content.Context.CONNECTIVITY_SERVICE
-import android.net.ConnectivityManager
-import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
-import android.os.Build.VERSION.SDK_INT
-import android.view.View
-import android.view.View.INVISIBLE
-import android.view.View.VISIBLE
-
-object Utils {
-
- private const val HEX_CHARS = "0123456789ABCDEF"
-
- fun hexStringToByteArray(data: String): ByteArray {
- val result = ByteArray(data.length / 2)
-
- for (i in data.indices step 2) {
- val firstIndex = HEX_CHARS.indexOf(data[i])
- val secondIndex = HEX_CHARS.indexOf(data[i + 1])
-
- val octet = firstIndex.shl(4).or(secondIndex)
- result[i.shr(1)] = octet.toByte()
- }
- return result
- }
-
-
- private val HEX_CHARS_ARRAY = HEX_CHARS.toCharArray()
-
- @Suppress("unused")
- fun toHex(byteArray: ByteArray): String {
- val result = StringBuffer()
-
- byteArray.forEach {
- val octet = it.toInt()
- val firstIndex = (octet and 0xF0).ushr(4)
- val secondIndex = octet and 0x0F
- result.append(HEX_CHARS_ARRAY[firstIndex])
- result.append(HEX_CHARS_ARRAY[secondIndex])
- }
- return result.toString()
- }
-
-}
-
-fun View.fadeIn(endAction: () -> Unit = {}) {
- if (visibility == VISIBLE) return
- alpha = 0f
- visibility = VISIBLE
- animate().alpha(1f).withEndAction {
- endAction.invoke()
- }.start()
-}
-
-fun View.fadeOut(endAction: () -> Unit = {}) {
- if (visibility == INVISIBLE) return
- animate().alpha(0f).withEndAction {
- visibility = INVISIBLE
- alpha = 1f
- endAction.invoke()
- }.start()
-}
-
-fun Context.isOnline(): Boolean {
- val cm = getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
- return if (SDK_INT < 29) {
- @Suppress("DEPRECATION")
- cm.activeNetworkInfo?.isConnected == true
- } else {
- val capabilities = cm.getNetworkCapabilities(cm.activeNetwork) ?: return false
- capabilities.hasCapability(NET_CAPABILITY_INTERNET)
- }
-}
diff --git a/cashier/src/main/java/net/taler/cashier/withdraw/NfcManager.kt b/cashier/src/main/java/net/taler/cashier/withdraw/NfcManager.kt
deleted file mode 100644
index a487b5f..0000000
--- a/cashier/src/main/java/net/taler/cashier/withdraw/NfcManager.kt
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * This file is part of GNU Taler
- * (C) 2020 Taler Systems S.A.
- *
- * GNU Taler is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 3, or (at your option) any later version.
- *
- * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-package net.taler.cashier.withdraw
-
-import android.app.Activity
-import android.content.Context
-import android.nfc.NfcAdapter
-import android.nfc.NfcAdapter.FLAG_READER_NFC_A
-import android.nfc.NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK
-import android.nfc.NfcAdapter.getDefaultAdapter
-import android.nfc.Tag
-import android.nfc.tech.IsoDep
-import android.util.Log
-import net.taler.cashier.Utils.hexStringToByteArray
-import org.json.JSONObject
-import java.io.ByteArrayOutputStream
-import java.net.URL
-import javax.net.ssl.HttpsURLConnection
-
-@Suppress("unused")
-private const val TALER_AID = "A0000002471001"
-
-class NfcManager : NfcAdapter.ReaderCallback {
-
- companion object {
- const val TAG = "taler-merchant"
-
- /**
- * Returns true if NFC is supported and false otherwise.
- */
- fun hasNfc(context: Context): Boolean {
- return getNfcAdapter(context) != null
- }
-
- /**
- * Enables NFC reader mode. Don't forget to call [stop] afterwards.
- */
- fun start(activity: Activity, nfcManager: NfcManager) {
- getNfcAdapter(activity)?.enableReaderMode(activity, nfcManager, nfcManager.flags, null)
- }
-
- /**
- * Disables NFC reader mode. Call after [start].
- */
- fun stop(activity: Activity) {
- getNfcAdapter(activity)?.disableReaderMode(activity)
- }
-
- private fun getNfcAdapter(context: Context): NfcAdapter? {
- return getDefaultAdapter(context)
- }
- }
-
- private val flags = FLAG_READER_NFC_A or FLAG_READER_SKIP_NDEF_CHECK
-
- private var tagString: String? = null
- private var currentTag: IsoDep? = null
-
- fun setTagString(tagString: String) {
- this.tagString = tagString
- }
-
- override fun onTagDiscovered(tag: Tag?) {
-
- Log.v(TAG, "tag discovered")
-
- val isoDep = IsoDep.get(tag)
- isoDep.connect()
-
- currentTag = isoDep
-
- isoDep.transceive(apduSelectFile())
-
- val tagString: String? = tagString
- if (tagString != null) {
- isoDep.transceive(apduPutTalerData(1, tagString.toByteArray()))
- }
-
- // FIXME: use better pattern for sleeps in between requests
- // -> start with fast polling, poll more slowly if no requests are coming
-
- while (true) {
- try {
- val reqFrame = isoDep.transceive(apduGetData())
- if (reqFrame.size < 2) {
- Log.v(TAG, "request frame too small")
- break
- }
- val req = ByteArray(reqFrame.size - 2)
- if (req.isEmpty()) {
- continue
- }
- reqFrame.copyInto(req, 0, 0, reqFrame.size - 2)
- val jsonReq = JSONObject(req.toString(Charsets.UTF_8))
- val reqId = jsonReq.getInt("id")
- Log.v(TAG, "got request $jsonReq")
- val jsonInnerReq = jsonReq.getJSONObject("request")
- val method = jsonInnerReq.getString("method")
- val urlStr = jsonInnerReq.getString("url")
- Log.v(TAG, "url '$urlStr'")
- Log.v(TAG, "method '$method'")
- val url = URL(urlStr)
- val conn: HttpsURLConnection = url.openConnection() as HttpsURLConnection
- conn.setRequestProperty("Accept", "application/json")
- conn.connectTimeout = 5000
- conn.doInput = true
- when (method) {
- "get" -> {
- conn.requestMethod = "GET"
- }
- "postJson" -> {
- conn.requestMethod = "POST"
- conn.doOutput = true
- conn.setRequestProperty("Content-Type", "application/json; utf-8")
- val body = jsonInnerReq.getString("body")
- conn.outputStream.write(body.toByteArray(Charsets.UTF_8))
- }
- else -> {
- throw Exception("method not supported")
- }
- }
- Log.v(TAG, "connecting")
- conn.connect()
- Log.v(TAG, "connected")
-
- val statusCode = conn.responseCode
- val tunnelResp = JSONObject()
- tunnelResp.put("id", reqId)
- tunnelResp.put("status", conn.responseCode)
-
- if (statusCode == 200) {
- val stream = conn.inputStream
- val httpResp = stream.buffered().readBytes()
- tunnelResp.put("responseJson", JSONObject(httpResp.toString(Charsets.UTF_8)))
- }
-
- Log.v(TAG, "sending: $tunnelResp")
-
- isoDep.transceive(apduPutTalerData(2, tunnelResp.toString().toByteArray()))
- } catch (e: Exception) {
- Log.v(TAG, "exception during NFC loop: $e")
- break
- }
- }
-
- isoDep.close()
- }
-
- private fun writeApduLength(stream: ByteArrayOutputStream, size: Int) {
- when {
- size == 0 -> {
- // No size field needed!
- }
- size <= 255 -> // One byte size field
- stream.write(size)
- size <= 65535 -> {
- stream.write(0)
- // FIXME: is this supposed to be little or big endian?
- stream.write(size and 0xFF)
- stream.write((size ushr 8) and 0xFF)
- }
- else -> throw Error("payload too big")
- }
- }
-
- private fun apduSelectFile(): ByteArray {
- return hexStringToByteArray("00A4040007A0000002471001")
- }
-
- private fun apduPutData(payload: ByteArray): ByteArray {
- val stream = ByteArrayOutputStream()
-
- // Class
- stream.write(0x00)
-
- // Instruction 0xDA = put data
- stream.write(0xDA)
-
- // Instruction parameters
- // (proprietary encoding)
- stream.write(0x01)
- stream.write(0x00)
-
- writeApduLength(stream, payload.size)
-
- stream.write(payload)
-
- return stream.toByteArray()
- }
-
- private fun apduPutTalerData(talerInst: Int, payload: ByteArray): ByteArray {
- val realPayload = ByteArrayOutputStream()
- realPayload.write(talerInst)
- realPayload.write(payload)
- return apduPutData(realPayload.toByteArray())
- }
-
- private fun apduGetData(): ByteArray {
- val stream = ByteArrayOutputStream()
-
- // Class
- stream.write(0x00)
-
- // Instruction 0xCA = get data
- stream.write(0xCA)
-
- // Instruction parameters
- // (proprietary encoding)
- stream.write(0x01)
- stream.write(0x00)
-
- // Max expected response size, two
- // zero bytes denotes 65536
- stream.write(0x0)
- stream.write(0x0)
-
- return stream.toByteArray()
- }
-
-}
diff --git a/cashier/src/main/java/net/taler/cashier/withdraw/QrCodeManager.kt b/cashier/src/main/java/net/taler/cashier/withdraw/QrCodeManager.kt
deleted file mode 100644
index e3ffa92..0000000
--- a/cashier/src/main/java/net/taler/cashier/withdraw/QrCodeManager.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * This file is part of GNU Taler
- * (C) 2020 Taler Systems S.A.
- *
- * GNU Taler is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free Software
- * Foundation; either version 3, or (at your option) any later version.
- *
- * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-package net.taler.cashier.withdraw
-
-import android.graphics.Bitmap
-import android.graphics.Bitmap.Config.RGB_565
-import android.graphics.Color.BLACK
-import android.graphics.Color.WHITE
-import com.google.zxing.BarcodeFormat.QR_CODE
-import com.google.zxing.qrcode.QRCodeWriter
-
-object QrCodeManager {
-
- fun makeQrCode(text: String, size: Int = 256): Bitmap {
- val qrCodeWriter = QRCodeWriter()
- val bitMatrix = qrCodeWriter.encode(text, QR_CODE, size, size)
- val height = bitMatrix.height
- val width = bitMatrix.width
- val bmp = Bitmap.createBitmap(width, height, RGB_565)
- for (x in 0 until width) {
- for (y in 0 until height) {
- bmp.setPixel(x, y, if (bitMatrix.get(x, y)) BLACK else WHITE)
- }
- }
- return bmp
- }
-
-}
diff --git a/cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt b/cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt
index 8b782b0..8857bfa 100644
--- a/cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt
+++ b/cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt
@@ -30,13 +30,14 @@ import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.fragment_transaction.*
import net.taler.cashier.MainViewModel
import net.taler.cashier.R
-import net.taler.cashier.fadeIn
-import net.taler.cashier.fadeOut
import net.taler.cashier.withdraw.TransactionFragmentDirections.Companion.actionTransactionFragmentToBalanceFragment
import net.taler.cashier.withdraw.TransactionFragmentDirections.Companion.actionTransactionFragmentToErrorFragment
import net.taler.cashier.withdraw.WithdrawResult.Error
import net.taler.cashier.withdraw.WithdrawResult.InsufficientBalance
import net.taler.cashier.withdraw.WithdrawResult.Success
+import net.taler.common.NfcManager
+import net.taler.common.fadeIn
+import net.taler.common.fadeOut
class TransactionFragment : Fragment() {
diff --git a/cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt b/cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt
index 4c618ac..bfc82ce 100644
--- a/cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt
+++ b/cashier/src/main/java/net/taler/cashier/withdraw/WithdrawManager.kt
@@ -34,6 +34,7 @@ import net.taler.cashier.HttpJsonResult.Error
import net.taler.cashier.HttpJsonResult.Success
import net.taler.cashier.MainViewModel
import net.taler.cashier.R
+import net.taler.common.QrCodeManager.makeQrCode
import org.json.JSONObject
import java.util.concurrent.TimeUnit.MINUTES
import java.util.concurrent.TimeUnit.SECONDS
@@ -95,7 +96,7 @@ class WithdrawManager(
val withdrawResult = WithdrawResult.Success(
id = result.json.getString("withdrawal_id"),
talerUri = talerUri,
- qrCode = QrCodeManager.makeQrCode(talerUri)
+ qrCode = makeQrCode(talerUri)
)
mWithdrawResult.postValue(withdrawResult)
timer.start()