gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-wallet-android] 02/02: UX improvements / prototype s


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-android] 02/02: UX improvements / prototype support for NFC tunneling
Date: Thu, 22 Aug 2019 23:37:31 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-android.

commit b9fd051a1bf453e923ddbbf86cf8602d154278e1
Author: Florian Dold <address@hidden>
AuthorDate: Thu Aug 22 23:37:22 2019 +0200

    UX improvements / prototype support for NFC tunneling
---
 .idea/codeStyles/Project.xml                       | 109 +++++++++++
 app/src/main/AndroidManifest.xml                   |  23 ++-
 app/src/main/java/net/taler/wallet/AlreadyPaid.kt  |  30 +++
 .../net/taler/wallet/HostCardEmulatorService.kt    | 204 +++++++++++++++++++++
 app/src/main/java/net/taler/wallet/MainActivity.kt |  50 ++++-
 .../main/java/net/taler/wallet/PromptPayment.kt    |  27 ++-
 .../main/java/net/taler/wallet/WalletViewModel.kt  |  39 ++++
 app/src/main/res/layout/fragment_already_paid.xml  |  38 ++++
 app/src/main/res/navigation/nav_graph.xml          |   9 +
 app/src/main/res/values/strings.xml                |   2 +
 app/src/main/res/xml/apduservice.xml               |   9 +
 11 files changed, 528 insertions(+), 12 deletions(-)

diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 1bec35e..ce889bd 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -3,6 +3,115 @@
     <JetCodeStyleSettings>
       <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
     </JetCodeStyleSettings>
+    <codeStyleSettings language="XML">
+      <arrangement>
+        <rules>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:android</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:id</NAME>
+                  <XML_ATTRIBUTE />
+                  
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:name</NAME>
+                  <XML_ATTRIBUTE />
+                  
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>style</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>ANDROID_ATTRIBUTE_ORDER</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>.*</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+        </rules>
+      </arrangement>
+    </codeStyleSettings>
     <codeStyleSettings language="kotlin">
       <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
     </codeStyleSettings>
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b675499..3a853c3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android";
-          package="net.taler.wallet">
+        xmlns:tools="http://schemas.android.com/tools";
+        package="net.taler.wallet">
+
+    <uses-permission android:name="android.permission.NFC" />
+    <uses-feature android:name="android.hardware.nfc.hce"
+            android:required="false" />
 
     <application
             android:allowBackup="true"
@@ -8,7 +13,8 @@
             android:label="@string/app_name"
             android:roundIcon="@mipmap/ic_launcher_round"
             android:supportsRtl="true"
-            android:theme="@style/AppTheme">
+            android:theme="@style/AppTheme"
+            tools:ignore="GoogleAppIndexingWarning">
         <activity
                 android:name=".MainActivity"
                 android:label="@string/app_name"
@@ -19,6 +25,19 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+
+        <service
+                android:name=".HostCardEmulatorService"
+                android:exported="true"
+                android:permission="android.permission.BIND_NFC_SERVICE">
+            <intent-filter>
+                <action 
android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />
+            </intent-filter>
+
+            <meta-data
+                    android:name="android.nfc.cardemulation.host_apdu_service"
+                    android:resource="@xml/apduservice" />
+        </service>
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/app/src/main/java/net/taler/wallet/AlreadyPaid.kt 
b/app/src/main/java/net/taler/wallet/AlreadyPaid.kt
new file mode 100644
index 0000000..40903f9
--- /dev/null
+++ b/app/src/main/java/net/taler/wallet/AlreadyPaid.kt
@@ -0,0 +1,30 @@
+package net.taler.wallet
+
+
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import androidx.navigation.findNavController
+
+/**
+ * A simple [Fragment] subclass.
+ */
+class AlreadyPaid : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        // Inflate the layout for this fragment
+        val view = inflater.inflate(R.layout.fragment_already_paid, container, 
false)
+        view.findViewById<Button>(R.id.button_success_back).setOnClickListener 
{
+            activity!!.findNavController(R.id.nav_host_fragment).navigateUp()
+        }
+        return view
+    }
+
+
+}
diff --git a/app/src/main/java/net/taler/wallet/HostCardEmulatorService.kt 
b/app/src/main/java/net/taler/wallet/HostCardEmulatorService.kt
new file mode 100644
index 0000000..cdcf492
--- /dev/null
+++ b/app/src/main/java/net/taler/wallet/HostCardEmulatorService.kt
@@ -0,0 +1,204 @@
+package net.taler.wallet
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.nfc.cardemulation.HostApduService
+import android.os.Bundle
+import android.util.Log
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.util.*
+import java.util.concurrent.ConcurrentLinkedDeque
+import java.util.concurrent.ConcurrentLinkedQueue
+
+class Utils {
+    companion object {
+        private val HEX_CHARS = "0123456789ABCDEF"
+        fun hexStringToByteArray(data: String) : ByteArray {
+
+            val result = ByteArray(data.length / 2)
+
+            for (i in 0 until data.length 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.set(i.shr(1), octet.toByte())
+            }
+
+            return result
+        }
+
+        private val HEX_CHARS_ARRAY = "0123456789ABCDEF".toCharArray()
+        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 makeApduSuccessResponse(payload: ByteArray): ByteArray {
+    val stream = ByteArrayOutputStream()
+    stream.write(payload)
+    stream.write(0x90)
+    stream.write(0x00)
+    return stream.toByteArray()
+}
+
+
+fun makeApduFailureResponse(): ByteArray {
+    val stream = ByteArrayOutputStream()
+    stream.write(0x6F)
+    stream.write(0x00)
+    return stream.toByteArray()
+}
+
+
+fun readApduBodySize(stream: ByteArrayInputStream): Int {
+    val b0 = stream.read()
+    if (b0 == -1) {
+        return 0;
+    }
+    if (b0 != 0) {
+        return b0
+    }
+    val b1 = stream.read()
+    val b2 = stream.read()
+
+    return (b1 shl 8) and b2
+}
+
+
+class HostCardEmulatorService: HostApduService() {
+
+    val queuedRequests: ConcurrentLinkedDeque<String> = ConcurrentLinkedDeque()
+
+    override fun onCreate() {
+        IntentFilter(HTTP_TUNNEL_REQUEST).also { filter ->
+            registerReceiver(object : BroadcastReceiver() {
+                override fun onReceive(p0: Context?, p1: Intent?) {
+                    
queuedRequests.addLast(p1!!.getStringExtra("tunnelMessage"))
+                }
+            }, filter)
+        }
+    }
+
+    override fun onDeactivated(reason: Int) {
+        Log.d(TAG, "Deactivated: " + reason)
+        Intent().also { intent ->
+            intent.action = MERCHANT_NFC_DISCONNECTED
+            sendBroadcast(intent)
+        }
+    }
+
+    override fun processCommandApdu(commandApdu: ByteArray?,
+                                    extras: Bundle?): ByteArray {
+
+        //Log.d(TAG, "Processing command APDU")
+
+        if (commandApdu == null) {
+            Log.d(TAG, "APDU is null")
+            return makeApduFailureResponse()
+        }
+
+        val stream = ByteArrayInputStream(commandApdu)
+
+        val command = stream.read()
+
+        if (command != 0) {
+            Log.d(TAG, "APDU has invalid command")
+            return makeApduFailureResponse()
+        }
+
+        val instruction = stream.read()
+
+        val instructionStr = "%02x".format(instruction)
+
+        //Log.v(TAG, "Processing instruction $instructionStr")
+
+        val p1 = stream.read()
+        val p2 = stream.read()
+
+        //Log.v(TAG, "instruction paramaters $p1 $p2")
+
+        if (instruction == SELECT_INS) {
+            // FIXME: validate body!
+            return makeApduSuccessResponse(ByteArray(0))
+        }
+
+        if (instruction == GET_INS) {
+            val req = queuedRequests.poll()
+            return if (req != null) {
+                Log.v(TAG,"sending tunnel request")
+                makeApduSuccessResponse(req.toByteArray(Charsets.UTF_8))
+            } else {
+                makeApduSuccessResponse(ByteArray(0))
+            }
+        }
+
+        if (instruction == PUT_INS) {
+            val bodySize = readApduBodySize(stream)
+
+
+            val talerInstr = stream.read()
+            val bodyBytes = stream.readBytes()
+
+
+            when (talerInstr.toInt()) {
+                1 -> {
+                    val url = String(bodyBytes, Charsets.UTF_8)
+
+                    Intent().also { intent ->
+                        intent.action = TRIGGER_PAYMENT_ACTION
+                        intent.putExtra("contractUrl", url)
+                        sendBroadcast(intent)
+                    }
+                }
+                2 -> {
+                    Log.v(TAG, "got http response: 
${bodyBytes.toString(Charsets.UTF_8)}")
+
+                    Intent().also { intent ->
+                        intent.action = HTTP_TUNNEL_RESPONSE
+                        intent.putExtra("response", 
bodyBytes.toString(Charsets.UTF_8))
+                        sendBroadcast(intent)
+                    }
+                }
+                else -> {
+                    Log.v(TAG, "taler instruction $talerInstr unknown")
+                }
+            }
+
+            return makeApduSuccessResponse(ByteArray(0))
+        }
+
+        return makeApduFailureResponse()
+    }
+
+    companion object {
+        val TAG = "taler-wallet-hce"
+        val AID = "A0000002471001"
+        val SELECT_INS = 0xA4
+        val PUT_INS = 0xDA
+        val GET_INS = 0xCA
+
+        val TRIGGER_PAYMENT_ACTION = "net.taler.TRIGGER_PAYMENT_ACTION"
+
+        val MERCHANT_NFC_CONNECTED = "net.taler.MERCHANT_NFC_CONNECTED"
+        val MERCHANT_NFC_DISCONNECTED = "net.taler.MERCHANT_NFC_DISCONNECTED"
+
+        val HTTP_TUNNEL_RESPONSE = "net.taler.HTTP_TUNNEL_RESPONSE"
+        val HTTP_TUNNEL_REQUEST = "net.taler.HTTP_TUNNEL_REQUEST"
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/taler/wallet/MainActivity.kt 
b/app/src/main/java/net/taler/wallet/MainActivity.kt
index e539cfd..44cd8a6 100644
--- a/app/src/main/java/net/taler/wallet/MainActivity.kt
+++ b/app/src/main/java/net/taler/wallet/MainActivity.kt
@@ -1,7 +1,10 @@
 package net.taler.wallet
 
 
+import android.content.BroadcastReceiver
+import android.content.Context
 import android.content.Intent
+import android.content.IntentFilter
 import android.os.Bundle
 import android.util.Log
 import android.view.Menu
@@ -11,7 +14,6 @@ import androidx.appcompat.app.AppCompatActivity
 import androidx.appcompat.widget.Toolbar
 import androidx.core.view.GravityCompat
 import androidx.drawerlayout.widget.DrawerLayout
-import androidx.lifecycle.Observer
 import androidx.lifecycle.ViewModelProviders
 import androidx.navigation.findNavController
 import androidx.navigation.ui.AppBarConfiguration
@@ -22,6 +24,8 @@ import com.google.zxing.integration.android.IntentIntegrator
 import com.google.zxing.integration.android.IntentResult
 import me.zhanghai.android.materialprogressbar.MaterialProgressBar
 
+
+
 class MainActivity : AppCompatActivity(), 
NavigationView.OnNavigationItemSelectedListener {
 
     lateinit var model: WalletViewModel
@@ -54,6 +58,50 @@ class MainActivity : AppCompatActivity(), 
NavigationView.OnNavigationItemSelecte
 
         model.init()
         model.getBalances()
+
+        val triggerPaymentFilter = 
IntentFilter(HostCardEmulatorService.TRIGGER_PAYMENT_ACTION)
+        registerReceiver(object : BroadcastReceiver() {
+            override fun onReceive(p0: Context?, p1: Intent?) {
+
+                if (model.payStatus.value !is PayStatus.None) {
+                    return
+                }
+
+                val url = p1!!.extras!!.get("contractUrl") as String
+
+                
findNavController(R.id.nav_host_fragment).navigate(R.id.action_showBalance_to_promptPayment)
+                model.preparePay(url)
+
+            }
+        }, triggerPaymentFilter)
+
+        val nfcConnectedFilter = 
IntentFilter(HostCardEmulatorService.MERCHANT_NFC_CONNECTED)
+        registerReceiver(object : BroadcastReceiver() {
+            override fun onReceive(p0: Context?, p1: Intent?) {
+                Log.v(TAG, "got MERCHANT_NFC_CONNECTED")
+                //model.startTunnel()
+            }
+        }, nfcConnectedFilter)
+
+        val nfcDisconnectedFilter = 
IntentFilter(HostCardEmulatorService.MERCHANT_NFC_DISCONNECTED)
+        registerReceiver(object : BroadcastReceiver() {
+            override fun onReceive(p0: Context?, p1: Intent?) {
+                Log.v(TAG, "got MERCHANT_NFC_DISCONNECTED")
+                //model.stopTunnel()
+            }
+        }, nfcDisconnectedFilter)
+
+
+        IntentFilter(HostCardEmulatorService.HTTP_TUNNEL_RESPONSE).also { 
filter ->
+            registerReceiver(object : BroadcastReceiver() {
+                override fun onReceive(p0: Context?, p1: Intent?) {
+                    Log.v("taler-tunnel", "got HTTP_TUNNEL_RESPONSE")
+                    model.tunnelResponse(p1!!.getStringExtra("response"))
+                }
+            }, filter)
+        }
+
+        //model.startTunnel()
     }
 
     override fun onBackPressed() {
diff --git a/app/src/main/java/net/taler/wallet/PromptPayment.kt 
b/app/src/main/java/net/taler/wallet/PromptPayment.kt
index 9486814..07d3dd2 100644
--- a/app/src/main/java/net/taler/wallet/PromptPayment.kt
+++ b/app/src/main/java/net/taler/wallet/PromptPayment.kt
@@ -32,7 +32,8 @@ class PromptPayment : Fragment() {
 
     var fragmentView: View? = null
 
-    fun triggerLoading(loading: Boolean) {
+    fun triggerLoading() {
+        val loading = model.payStatus.value == null || (model.payStatus.value 
is PayStatus.Loading)
         val myActivity = activity!!
         val progressBar = 
myActivity.findViewById<MaterialProgressBar>(R.id.progress_bar)
         if (loading) {
@@ -49,13 +50,13 @@ class PromptPayment : Fragment() {
             ViewModelProviders.of(this)[WalletViewModel::class.java]
         } ?: throw Exception("Invalid Activity")
 
-        triggerLoading(true)
+        triggerLoading()
     }
 
     override fun onResume() {
         super.onResume()
         Log.v("taler-wallet", "called onResume on PromptPayment")
-        triggerLoading(model.payStatus.value == null || model.payStatus.value 
is PayStatus.Loading)
+        triggerLoading()
     }
 
     fun fillOrderInfo(view: View, contractTerms: ContractTerms, totalFees: 
Amount?) {
@@ -90,24 +91,31 @@ class PromptPayment : Fragment() {
 
                 confirmPaymentButton.setOnClickListener {
                     model.confirmPay(payStatus.proposalId)
-                    triggerLoading(true)
                     confirmPaymentButton.isEnabled = false
                 }
-                triggerLoading(false)
             }
             is PayStatus.InsufficientBalance -> {
                 fillOrderInfo(view, payStatus.contractTerms, null)
                 promptPaymentDetails.visibility = View.VISIBLE
                 balanceInsufficientWarning.visibility = View.VISIBLE
                 confirmPaymentButton.isEnabled = false
-                triggerLoading(false)
             }
             is PayStatus.Success -> {
-                triggerLoading(false)
+                model.payStatus.value = PayStatus.None()
                 
activity!!.findNavController(R.id.nav_host_fragment).navigate(R.id.action_promptPayment_to_paymentSuccessful)
             }
+            is PayStatus.AlreadyPaid -> {
+                
activity!!.findNavController(R.id.nav_host_fragment).navigate(R.id.action_promptPayment_to_alreadyPaid)
+                model.payStatus.value = PayStatus.None()
+            }
+            is PayStatus.None -> {
+                // No payment active.
+            }
+            is PayStatus.Loading -> {
+                // Wait until loaded ...
+            }
             else -> {
-                val bar = Snackbar.make(view , "Unexpected result", 
Snackbar.LENGTH_SHORT)
+                val bar = Snackbar.make(view , "Bug: Unexpected result", 
Snackbar.LENGTH_SHORT)
                 bar.show()
             }
         }
@@ -133,9 +141,10 @@ class PromptPayment : Fragment() {
             activity!!.findNavController(R.id.nav_host_fragment).navigateUp()
         }
 
-        triggerLoading(true)
+        triggerLoading()
 
         model.payStatus.observe(this, Observer {
+            triggerLoading()
             showPayStatus(view, it)
         })
         return view
diff --git a/app/src/main/java/net/taler/wallet/WalletViewModel.kt 
b/app/src/main/java/net/taler/wallet/WalletViewModel.kt
index 7c82f81..f644c8d 100644
--- a/app/src/main/java/net/taler/wallet/WalletViewModel.kt
+++ b/app/src/main/java/net/taler/wallet/WalletViewModel.kt
@@ -3,6 +3,7 @@ package net.taler.wallet
 import akono.AkonoJni
 import akono.ModuleResult
 import android.app.Application
+import android.content.Intent
 import android.content.res.AssetManager
 import android.util.Log
 import androidx.lifecycle.AndroidViewModel
@@ -139,6 +140,7 @@ data class WalletBalances(val initialized: Boolean, val 
byCurrency: List<Amount>
 data class ContractTerms(val summary: String, val amount: Amount)
 
 open class PayStatus {
+    class None : PayStatus()
     class Loading : PayStatus()
     data class Prepared(val contractTerms: ContractTerms, val proposalId: Int, 
val totalFees: Amount) : PayStatus()
     data class InsufficientBalance(val contractTerms: ContractTerms) : 
PayStatus()
@@ -165,6 +167,7 @@ class WalletViewModel(val app: Application) : 
AndroidViewModel(app) {
     init {
         isBalanceLoading.value = false
         balances.value = WalletBalances(false, listOf())
+        payStatus.value = PayStatus.None()
     }
 
     fun init() {
@@ -185,6 +188,14 @@ class WalletViewModel(val app: Application) : 
AndroidViewModel(app) {
                     "notification" -> {
                         getBalances()
                     }
+                    "tunnelHttp" -> {
+                        Log.v(TAG, "got http tunnel request!")
+                        Intent().also { intent ->
+                            intent.action = 
HostCardEmulatorService.HTTP_TUNNEL_REQUEST
+                            intent.putExtra("tunnelMessage", messageStr)
+                            app.sendBroadcast(intent)
+                        }
+                    }
                     "response" -> {
                         val operation = message.getString("operation")
                         Log.v(TAG, "got response for operation $operation")
@@ -251,6 +262,7 @@ class WalletViewModel(val app: Application) : 
AndroidViewModel(app) {
 
         sendInitMessage()
 
+
         this.initialized = true
     }
 
@@ -301,6 +313,8 @@ class WalletViewModel(val app: Application) : 
AndroidViewModel(app) {
         msg.put("args", args)
         args.put("url", url)
 
+        this.payStatus.value = PayStatus.Loading()
+
         myAkono.sendMessage(msg.toString())
     }
 
@@ -328,4 +342,29 @@ class WalletViewModel(val app: Application) : 
AndroidViewModel(app) {
 
         getBalances()
     }
+
+    fun startTunnel() {
+        val msg = JSONObject()
+        msg.put("operation", "startTunnel")
+
+        myAkono.sendMessage(msg.toString())
+    }
+
+    fun stopTunnel() {
+        val msg = JSONObject()
+        msg.put("operation", "stopTunnel")
+
+        myAkono.sendMessage(msg.toString())
+    }
+
+    fun tunnelResponse(resp: String) {
+        val respJson = JSONObject(resp)
+
+        val msg = JSONObject()
+        msg.put("operation", "tunnelResponse")
+        msg.put("args", respJson)
+
+        myAkono.sendMessage(msg.toString())
+
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_already_paid.xml 
b/app/src/main/res/layout/fragment_already_paid.xml
new file mode 100644
index 0000000..69c949e
--- /dev/null
+++ b/app/src/main/res/layout/fragment_already_paid.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android";
+        xmlns:tools="http://schemas.android.com/tools";
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_margin="15dp"
+        tools:context=".PaymentSuccessful">
+
+
+    <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+        <Space android:layout_width="match_parent" android:layout_height="0dp"
+                android:layout_weight="1"/>
+
+        <TextView
+                android:layout_gravity="center"
+                android:layout_width="match_parent"
+                android:textAlignment="center"
+                android:layout_height="50dp"
+                android:text="You've already paid for this order."
+                android:autoSizeTextType="uniform"
+                android:textColor="@android:color/holo_green_dark"/>
+
+
+        <Space android:layout_width="match_parent" android:layout_height="0dp"
+                android:layout_weight="1"/>
+        <Button
+                android:text="Go Back"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:id="@+id/button_success_back"/>
+
+    </LinearLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/app/src/main/res/navigation/nav_graph.xml 
b/app/src/main/res/navigation/nav_graph.xml
index 174c907..80a94ec 100644
--- a/app/src/main/res/navigation/nav_graph.xml
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -12,6 +12,10 @@
               android:label="Review Payment" 
tools:layout="@layout/fragment_prompt_payment">
         <action android:id="@+id/action_promptPayment_to_paymentSuccessful" 
app:destination="@id/paymentSuccessful"
                 app:popUpTo="@id/showBalance"/>
+        <action
+                android:id="@+id/action_promptPayment_to_alreadyPaid"
+                app:destination="@id/alreadyPaid"
+                app:popUpTo="@id/showBalance"/>
     </fragment>
     <fragment android:id="@+id/paymentSuccessful" 
android:name="net.taler.wallet.PaymentSuccessful"
               android:label="Payment Successful" 
tools:layout="@layout/fragment_payment_successful"/>
@@ -19,4 +23,9 @@
               tools:layout="@layout/fragment_settings"/>
     <fragment android:id="@+id/walletHistory" 
android:name="net.taler.wallet.WalletHistory"
               android:label="History" 
tools:layout="@layout/fragment_show_history"/>
+    <fragment
+            android:id="@+id/alreadyPaid"
+            android:name="net.taler.wallet.AlreadyPaid"
+            android:label="Already Paid"
+            tools:layout="@layout/fragment_already_paid" />
 </navigation>
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml 
b/app/src/main/res/values/strings.xml
index f059311..658dc62 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -16,4 +16,6 @@
 
     <!-- TODO: Remove or change this placeholder text -->
     <string name="hello_blank_fragment">Hello blank fragment</string>
+    <string name="servicedesc">my service</string>
+    <string name="aiddescription">my aid</string>
 </resources>
diff --git a/app/src/main/res/xml/apduservice.xml 
b/app/src/main/res/xml/apduservice.xml
new file mode 100644
index 0000000..b862ccb
--- /dev/null
+++ b/app/src/main/res/xml/apduservice.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android";
+        android:description="@string/servicedesc"
+        android:requireDeviceUnlock="false">
+    <aid-group android:description="@string/aiddescription"
+            android:category="other">
+        <aid-filter android:name="A0000002471001"/>
+    </aid-group>
+</host-apdu-service>
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]