gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-merchant-terminal-android] branch master updated: UX


From: gnunet
Subject: [GNUnet-SVN] [taler-merchant-terminal-android] branch master updated: UX improvements / prototype support for NFC tunneling
Date: Thu, 22 Aug 2019 23:38:14 +0200

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 2a457a1  UX improvements / prototype support for NFC tunneling
2a457a1 is described below

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

    UX improvements / prototype support for NFC tunneling
---
 .idea/codeStyles/Project.xml                       | 109 +++++++++
 app/build.gradle                                   |   2 +-
 app/src/main/AndroidManifest.xml                   |   7 +-
 .../java/net/taler/merchantpos/CreatePayment.kt    |  65 +-----
 .../java/net/taler/merchantpos/MainActivity.kt     | 251 ++++++++++++++++++++-
 .../java/net/taler/merchantpos/PaymentSuccess.kt   |  29 +++
 .../net/taler/merchantpos/PosTerminalViewModel.kt  |   9 +-
 .../java/net/taler/merchantpos/ProcessPayment.kt   | 126 +++++------
 .../main/res/layout/fragment_create_payment.xml    |   7 +-
 .../main/res/layout/fragment_payment_success.xml   |  38 ++++
 .../main/res/layout/fragment_process_payment.xml   |   3 +-
 app/src/main/res/navigation/nav_graph.xml          |  12 +-
 build.gradle                                       |   2 +-
 gradle/wrapper/gradle-wrapper.properties           |   4 +-
 14 files changed, 518 insertions(+), 146 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/build.gradle b/app/build.gradle
index 22225ac..0f36545 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -10,7 +10,7 @@ android {
     buildToolsVersion "29.0.1"
     defaultConfig {
         applicationId "net.taler.merchantpos"
-        minSdkVersion 28
+        minSdkVersion 27
         targetSdkVersion 29
         versionCode 1
         versionName "1.0"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 52f77ef..0920771 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,6 +2,11 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android";
           xmlns:tools="http://schemas.android.com/tools"; 
package="net.taler.merchantpos">
 
+    <uses-permission android:name="android.permission.NFC" />
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-feature android:name="android.hardware.nfc"
+            android:required="true" />
+
     <application
             android:allowBackup="true"
             android:icon="@mipmap/ic_launcher"
@@ -20,7 +25,5 @@
             </intent-filter>
         </activity>
     </application>
-    
-    <uses-permission android:name="android.permission.INTERNET"/>
 
 </manifest>
\ No newline at end of file
diff --git a/app/src/main/java/net/taler/merchantpos/CreatePayment.kt 
b/app/src/main/java/net/taler/merchantpos/CreatePayment.kt
index 330b9e8..e07802f 100644
--- a/app/src/main/java/net/taler/merchantpos/CreatePayment.kt
+++ b/app/src/main/java/net/taler/merchantpos/CreatePayment.kt
@@ -8,6 +8,7 @@ import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.Button
+import android.widget.EditText
 import androidx.lifecycle.ViewModelProviders
 import androidx.navigation.fragment.findNavController
 import com.android.volley.Request
@@ -34,19 +35,11 @@ private const val ARG_PARAM2 = "param2"
  *
  */
 class CreatePayment : Fragment() {
-    // TODO: Rename and change types of parameters
-    private var param1: String? = null
-    private var param2: String? = null
-    private var listener: OnFragmentInteractionListener? = null
     private lateinit var queue: RequestQueue
     private lateinit var model: PosTerminalViewModel
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        arguments?.let {
-            param1 = it.getString(ARG_PARAM1)
-            param2 = it.getString(ARG_PARAM2)
-        }
 
         model = activity?.run {
             ViewModelProviders.of(this)[PosTerminalViewModel::class.java]
@@ -56,11 +49,14 @@ class CreatePayment : Fragment() {
     }
 
     private fun onRequestPayment() {
-        val amount = "TESTKUDOS:10.00"
+        val amountValStr = 
activity!!.findViewById<EditText>(R.id.edit_payment_amount).text
+        val amount = "TESTKUDOS:${amountValStr}"
         model.activeAmount = amount
+        model.activeSubject = 
activity!!.findViewById<EditText>(R.id.edit_payment_subject).text
+
         var order = JSONObject().also {
             it.put("amount", amount)
-            it.put("summary", "hello world")
+            it.put("summary", model.activeSubject!!)
             it.put("fulfillment_url", "https://example.com";)
             it.put("instance", "default")
         }
@@ -119,53 +115,4 @@ class CreatePayment : Fragment() {
         return view
     }
 
-    override fun onAttach(context: Context) {
-        super.onAttach(context)
-        if (context is OnFragmentInteractionListener) {
-            listener = context
-        } else {
-            throw RuntimeException(context.toString() + " must implement 
OnFragmentInteractionListener")
-        }
-    }
-
-    override fun onDetach() {
-        super.onDetach()
-        listener = null
-    }
-
-    /**
-     * This interface must be implemented by activities that contain this
-     * fragment to allow an interaction in this fragment to be communicated
-     * to the activity and potentially other fragments contained in that
-     * activity.
-     *
-     *
-     * See the Android Training lesson [Communicating with Other Fragments]
-     * 
(http://developer.android.com/training/basics/fragments/communicating.html)
-     * for more information.
-     */
-    interface OnFragmentInteractionListener {
-        // TODO: Update argument type and name
-        fun onFragmentInteraction(uri: Uri)
-    }
-
-    companion object {
-        /**
-         * Use this factory method to create a new instance of
-         * this fragment using the provided parameters.
-         *
-         * @param param1 Parameter 1.
-         * @param param2 Parameter 2.
-         * @return A new instance of fragment CreatePayment.
-         */
-        // TODO: Rename and change types and number of parameters
-        @JvmStatic
-        fun newInstance(param1: String, param2: String) =
-            CreatePayment().apply {
-                arguments = Bundle().apply {
-                    putString(ARG_PARAM1, param1)
-                    putString(ARG_PARAM2, param2)
-                }
-            }
-    }
 }
diff --git a/app/src/main/java/net/taler/merchantpos/MainActivity.kt 
b/app/src/main/java/net/taler/merchantpos/MainActivity.kt
index 4f67f9c..23d0417 100644
--- a/app/src/main/java/net/taler/merchantpos/MainActivity.kt
+++ b/app/src/main/java/net/taler/merchantpos/MainActivity.kt
@@ -1,7 +1,10 @@
 package net.taler.merchantpos
 
-import android.net.Uri
+import android.nfc.NfcAdapter
+import android.nfc.Tag
+import android.nfc.tech.IsoDep
 import android.os.Bundle
+import android.util.Log
 import androidx.core.view.GravityCompat
 import android.view.MenuItem
 import androidx.drawerlayout.widget.DrawerLayout
@@ -14,16 +17,243 @@ import androidx.navigation.NavController
 import androidx.navigation.findNavController
 import androidx.navigation.ui.AppBarConfiguration
 import androidx.navigation.ui.setupWithNavController
+import net.taler.merchantpos.Utils.Companion.hexStringToByteArray
+import org.json.JSONObject
+import java.io.ByteArrayOutputStream
+import java.net.URL
+import javax.net.ssl.HttpsURLConnection
+
+
+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()
+        }
+    }
+}
+
+val TALER_AID = "A0000002471001"
+
+
+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")
+    }
+}
+
+fun apduSelectFile(): ByteArray {
+    return hexStringToByteArray("00A4040007A0000002471001")
+}
+
+
+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()
+}
+
+fun apduPutTalerData(talerInst: Int, payload: ByteArray): ByteArray {
+    val realPayload = ByteArrayOutputStream()
+    realPayload.write(talerInst)
+    realPayload.write(payload)
+    return apduPutData(realPayload.toByteArray())
+}
+
+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()
+}
+
 
 class MainActivity : AppCompatActivity(), 
NavigationView.OnNavigationItemSelectedListener,
-    CreatePayment.OnFragmentInteractionListener, 
ProcessPayment.OnFragmentInteractionListener {
-    override fun onFragmentInteraction(uri: Uri) {
-        TODO("not implemented") //To change body of created functions use File 
| Settings | File Templates.
+    NfcAdapter.ReaderCallback {
+
+    companion object {
+        const val TAG = "taler-merchant"
+    }
+
+    private lateinit var model: PosTerminalViewModel
+    private var nfcAdapter: NfcAdapter? = null
+
+    private var currentTag: IsoDep? = null
+
+    override fun onTagDiscovered(tag: Tag?) {
+
+        Log.v(TAG, "tag discovered")
+
+        val isoDep = IsoDep.get(tag)
+        isoDep.connect()
+
+        currentTag = isoDep
+
+        isoDep.transceive(apduSelectFile())
+
+        val contractUri: String? = model.activeContractUri
+
+        if (contractUri != null) {
+            isoDep.transceive(apduPutTalerData(1, contractUri.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()
+    }
+
+    public override fun onResume() {
+        super.onResume()
+        nfcAdapter?.enableReaderMode(
+            this, this,
+            NfcAdapter.FLAG_READER_NFC_A or
+                    NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK,
+            null
+        )
     }
 
+    public override fun onPause() {
+        super.onPause()
+        nfcAdapter?.disableReaderMode(this)
+    }
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
+
+        nfcAdapter = NfcAdapter.getDefaultAdapter(this)
+
         setContentView(R.layout.activity_main)
         val toolbar: Toolbar = findViewById(R.id.toolbar)
         setSupportActionBar(toolbar)
@@ -35,13 +265,20 @@ class MainActivity : AppCompatActivity(), 
NavigationView.OnNavigationItemSelecte
 
         val navController = findNavController(R.id.nav_host_fragment)
         val appBarConfiguration =
-            AppBarConfiguration(setOf(R.id.createPayment, 
R.id.merchantSettings, R.id.merchantHistory), drawerLayout)
+            AppBarConfiguration(
+                setOf(
+                    R.id.createPayment,
+                    R.id.merchantSettings,
+                    R.id.merchantHistory
+                ), drawerLayout
+            )
 
         findViewById<Toolbar>(R.id.toolbar)
             .setupWithNavController(navController, appBarConfiguration)
 
-        val model = 
ViewModelProviders.of(this)[PosTerminalViewModel::class.java]
-        model.merchantConfig = 
MerchantConfig("https://backend.test.taler.net";, "default", "sandbox")
+        model = ViewModelProviders.of(this)[PosTerminalViewModel::class.java]
+        model.merchantConfig =
+            MerchantConfig("https://backend.test.taler.net";, "default", 
"sandbox")
 
     }
 
diff --git a/app/src/main/java/net/taler/merchantpos/PaymentSuccess.kt 
b/app/src/main/java/net/taler/merchantpos/PaymentSuccess.kt
new file mode 100644
index 0000000..20b6ed1
--- /dev/null
+++ b/app/src/main/java/net/taler/merchantpos/PaymentSuccess.kt
@@ -0,0 +1,29 @@
+package net.taler.merchantpos
+
+
+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 PaymentSuccess : Fragment() {
+
+    override fun onCreateView(
+        inflater: LayoutInflater, container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        val view = inflater.inflate(R.layout.fragment_payment_success, 
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/merchantpos/PosTerminalViewModel.kt 
b/app/src/main/java/net/taler/merchantpos/PosTerminalViewModel.kt
index 1e174ef..bcbc0c8 100644
--- a/app/src/main/java/net/taler/merchantpos/PosTerminalViewModel.kt
+++ b/app/src/main/java/net/taler/merchantpos/PosTerminalViewModel.kt
@@ -1,15 +1,18 @@
 package net.taler.merchantpos
 
+import android.text.Editable
 import androidx.lifecycle.ViewModel
 
 class PosTerminalViewModel : ViewModel() {
+    var activeSubject: Editable? = null
     var merchantConfig: MerchantConfig? = null
     var activeOrderId: String? = null
     var activeAmount: String? = null
     var activeContractUri: String? = null
 
     fun activeAmountPretty(): String? {
-        val a = activeAmount ?: return null
-        return a.replace(":", " ")
+        val amount = activeAmount ?: return null
+        val components = amount.split(":")
+        return "${components[1]} ${components[0]}"
     }
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/net/taler/merchantpos/ProcessPayment.kt 
b/app/src/main/java/net/taler/merchantpos/ProcessPayment.kt
index c98a3e4..26ef1c7 100644
--- a/app/src/main/java/net/taler/merchantpos/ProcessPayment.kt
+++ b/app/src/main/java/net/taler/merchantpos/ProcessPayment.kt
@@ -14,6 +14,8 @@ import com.google.zxing.common.BitMatrix
 import com.google.zxing.qrcode.QRCodeWriter
 import android.opengl.ETC1.getWidth
 import android.opengl.ETC1.getHeight
+import android.os.Handler
+import android.util.Log
 import android.widget.Button
 import android.widget.ImageView
 import android.widget.TextView
@@ -22,7 +24,14 @@ import androidx.activity.addCallback
 import androidx.lifecycle.ViewModelProviders
 import androidx.navigation.findNavController
 import androidx.navigation.fragment.findNavController
+import com.android.volley.Request
+import com.android.volley.RequestQueue
+import com.android.volley.Response
+import com.android.volley.VolleyError
+import com.android.volley.toolbox.Volley
 import com.google.android.material.snackbar.Snackbar
+import org.json.JSONObject
+import java.net.URLEncoder
 
 
 // TODO: Rename parameter arguments, choose names that match
@@ -32,32 +41,68 @@ private const val ARG_PARAM2 = "param2"
 
 /**
  * A simple [Fragment] subclass.
- * Activities that contain this fragment must implement the
- * [ProcessPayment.OnFragmentInteractionListener] interface
- * to handle interaction events.
- * Use the [ProcessPayment.newInstance] factory method to
- * create an instance of this fragment.
  *
  */
 class ProcessPayment : Fragment() {
-    // TODO: Rename and change types of parameters
-    private var param1: String? = null
-    private var param2: String? = null
-    private var listener: OnFragmentInteractionListener? = null
 
+    private var paused: Boolean = true
+    private lateinit var queue: RequestQueue
     private lateinit var model: PosTerminalViewModel
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        arguments?.let {
-            param1 = it.getString(ARG_PARAM1)
-            param2 = it.getString(ARG_PARAM2)
-        }
 
         model = activity?.run {
             ViewModelProviders.of(this)[PosTerminalViewModel::class.java]
         } ?: throw Exception("Invalid Activity")
 
+        queue = Volley.newRequestQueue(context)
+
+    }
+
+    private fun onCheckPayment(checkPaymentResponse: JSONObject) {
+        if (paused) {
+            return
+        }
+        //Log.v("taler-merchant", "got check payment result 
${checkPaymentResponse}")
+        if (checkPaymentResponse.getBoolean("paid")) {
+            queue.cancelAll { true }
+            
findNavController().navigate(R.id.action_processPayment_to_paymentSuccess)
+            return
+        }
+    }
+
+    private fun onNetworkError(volleyError: VolleyError?) {
+        val mySnackbar = Snackbar.make(view!!, "Network Error", 
Snackbar.LENGTH_SHORT)
+        mySnackbar.show()
+    }
+
+    private fun checkPaid() {
+        if (paused) {
+            return
+        }
+        //Log.v("taler-merchant", "checkig if payment happened")
+        val params = mapOf("order_id" to model.activeOrderId!!, "instance" to 
model.merchantConfig!!.instance)
+        var req = MerchantInternalRequest(Request.Method.GET, 
model.merchantConfig!!, "check-payment", params, null,
+            Response.Listener { onCheckPayment(it) }, Response.ErrorListener { 
onNetworkError(it) })
+        queue.add(req)
+        val handler = Handler()
+        handler.postDelayed({
+            checkPaid()
+        }, 500)
+
+    }
+
+    override fun onResume() {
+        this.paused = false
+        checkPaid()
+        super.onResume()
+    }
+
+    override fun onPause() {
+        this.paused = true
+        super.onPause()
+        queue.cancelAll { true }
     }
 
     override fun onCreateView(
@@ -67,7 +112,8 @@ class ProcessPayment : Fragment() {
         // Inflate the layout for this fragment
         val view = inflater.inflate(R.layout.fragment_process_payment, 
container, false)
         val img = view.findViewById<ImageView>(R.id.qrcode)
-        val myBitmap = makeQrCode(model.activeContractUri!!)
+        val talerPayUrl = "talerpay:" + 
URLEncoder.encode(model.activeContractUri!!, "utf-8")
+        val myBitmap = makeQrCode(talerPayUrl)
         img.setImageBitmap(myBitmap)
         val cancelPaymentButton = 
view.findViewById<Button>(R.id.button_cancel_payment)
         cancelPaymentButton.setOnClickListener {
@@ -99,56 +145,6 @@ class ProcessPayment : Fragment() {
                 bmp.setPixel(x, y, if (bitMatrix.get(x, y)) Color.BLACK else 
Color.WHITE)
             }
         }
-        return bmp;
-    }
-
-    override fun onAttach(context: Context) {
-        super.onAttach(context)
-        if (context is OnFragmentInteractionListener) {
-            listener = context
-        } else {
-            throw RuntimeException(context.toString() + " must implement 
OnFragmentInteractionListener")
-        }
-    }
-
-    override fun onDetach() {
-        super.onDetach()
-        listener = null
-    }
-
-    /**
-     * This interface must be implemented by activities that contain this
-     * fragment to allow an interaction in this fragment to be communicated
-     * to the activity and potentially other fragments contained in that
-     * activity.
-     *
-     *
-     * See the Android Training lesson [Communicating with Other Fragments]
-     * 
(http://developer.android.com/training/basics/fragments/communicating.html)
-     * for more information.
-     */
-    interface OnFragmentInteractionListener {
-        // TODO: Update argument type and name
-        fun onFragmentInteraction(uri: Uri)
-    }
-
-    companion object {
-        /**
-         * Use this factory method to create a new instance of
-         * this fragment using the provided parameters.
-         *
-         * @param param1 Parameter 1.
-         * @param param2 Parameter 2.
-         * @return A new instance of fragment ProcessPayment.
-         */
-        // TODO: Rename and change types and number of parameters
-        @JvmStatic
-        fun newInstance(param1: String, param2: String) =
-            ProcessPayment().apply {
-                arguments = Bundle().apply {
-                    putString(ARG_PARAM1, param1)
-                    putString(ARG_PARAM2, param2)
-                }
-            }
+        return bmp
     }
 }
diff --git a/app/src/main/res/layout/fragment_create_payment.xml 
b/app/src/main/res/layout/fragment_create_payment.xml
index 2549e91..f72f263 100644
--- a/app/src/main/res/layout/fragment_create_payment.xml
+++ b/app/src/main/res/layout/fragment_create_payment.xml
@@ -28,10 +28,10 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:inputType="textPersonName"
-                android:text="Payment Subject"
+                android:text="Demo Payment"
                 android:ems="10"
                 android:layout_gravity="top"
-                android:id="@+id/editText5"/>
+                android:id="@+id/edit_payment_subject"/>
         <TextView
                 android:text="Amount (TESTKUDOS)"
                 android:layout_width="match_parent"
@@ -42,7 +42,8 @@
                 android:layout_height="wrap_content"
                 android:inputType="numberDecimal"
                 android:ems="10"
-                android:id="@+id/editText6" android:layout_weight="0" 
android:text="10"/>
+                android:id="@+id/edit_payment_amount" android:layout_weight="0"
+                android:text="1"/>
         <Space
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/fragment_payment_success.xml 
b/app/src/main/res/layout/fragment_payment_success.xml
new file mode 100644
index 0000000..8db64df
--- /dev/null
+++ b/app/src/main/res/layout/fragment_payment_success.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="Payment Received"
+                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/layout/fragment_process_payment.xml 
b/app/src/main/res/layout/fragment_process_payment.xml
index 0ff8257..3f1764e 100644
--- a/app/src/main/res/layout/fragment_process_payment.xml
+++ b/app/src/main/res/layout/fragment_process_payment.xml
@@ -23,8 +23,7 @@
                 android:layout_height="275dp" 
tools:src="@tools:sample/backgrounds/scenic[16]"
                 android:id="@+id/qrcode" 
app:layout_constraintStart_toStartOf="parent"
                 app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintTop_toTopOf="parent" 
android:layout_marginTop="32dp"
-                android:background="#CEC02424"/>
+                app:layout_constraintTop_toTopOf="parent" 
android:layout_marginTop="32dp" />
         <TextView
                 android:text="Please scan QR Code or use NFC to pay"
                 android:layout_width="wrap_content"
diff --git a/app/src/main/res/navigation/nav_graph.xml 
b/app/src/main/res/navigation/nav_graph.xml
index b6345a1..3a5b470 100644
--- a/app/src/main/res/navigation/nav_graph.xml
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -9,7 +9,12 @@
         <action android:id="@+id/action_createPayment_to_processPayment" 
app:destination="@id/processPayment"/>
     </fragment>
     <fragment android:id="@+id/processPayment" 
android:name="net.taler.merchantpos.ProcessPayment"
-              android:label="Payment Prompt" 
tools:layout="@layout/fragment_process_payment"/>
+              android:label="Payment Prompt" 
tools:layout="@layout/fragment_process_payment">
+        <action
+                android:id="@+id/action_processPayment_to_paymentSuccess"
+                app:destination="@id/paymentSuccess"
+                app:popUpTo="@id/createPayment"/>
+    </fragment>
     <fragment android:id="@+id/merchantHistory" 
android:name="net.taler.merchantpos.MerchantHistory"
               android:label="Payment History" 
tools:layout="@layout/fragment_merchant_history"/>
     <action android:id="@+id/action_global_merchantHistory" 
app:destination="@id/merchantHistory"/>
@@ -17,4 +22,9 @@
     <fragment android:id="@+id/merchantSettings" 
android:name="net.taler.merchantpos.MerchantSettings"
               android:label="Merchant Settings" 
tools:layout="@layout/fragment_merchant_settings"/>
     <action android:id="@+id/action_global_merchantSettings" 
app:destination="@id/merchantSettings"/>
+    <fragment
+            android:id="@+id/paymentSuccess"
+            android:name="net.taler.merchantpos.PaymentSuccess"
+            android:label="Payment Received"
+            tools:layout="@layout/fragment_payment_success" />
 </navigation>
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 438d280..4420c59 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,7 +8,7 @@ buildscript {
         
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.4.2'
+        classpath 'com.android.tools.build:gradle:3.5.0'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
diff --git a/gradle/wrapper/gradle-wrapper.properties 
b/gradle/wrapper/gradle-wrapper.properties
index a20e04a..344e98a 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sat Aug 10 13:25:15 CEST 2019
+#Tue Aug 20 23:27:29 CEST 2019
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

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



reply via email to

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