summaryrefslogtreecommitdiff
path: root/app/src/main/java/net
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/net')
-rw-r--r--app/src/main/java/net/taler/merchantpos/CreatePayment.kt81
-rw-r--r--app/src/main/java/net/taler/merchantpos/MainActivity.kt49
-rw-r--r--app/src/main/java/net/taler/merchantpos/MerchantConfig.kt16
-rw-r--r--app/src/main/java/net/taler/merchantpos/MerchantHistory.kt77
-rw-r--r--app/src/main/java/net/taler/merchantpos/MerchantInternalRequest.kt25
-rw-r--r--app/src/main/java/net/taler/merchantpos/MerchantSettings.kt31
-rw-r--r--app/src/main/java/net/taler/merchantpos/PosTerminalViewModel.kt15
-rw-r--r--app/src/main/java/net/taler/merchantpos/ProcessPayment.kt60
8 files changed, 321 insertions, 33 deletions
diff --git a/app/src/main/java/net/taler/merchantpos/CreatePayment.kt b/app/src/main/java/net/taler/merchantpos/CreatePayment.kt
index aac8db3..330b9e8 100644
--- a/app/src/main/java/net/taler/merchantpos/CreatePayment.kt
+++ b/app/src/main/java/net/taler/merchantpos/CreatePayment.kt
@@ -7,6 +7,16 @@ import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import android.widget.Button
+import androidx.lifecycle.ViewModelProviders
+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 org.json.JSONObject
+import com.google.android.material.snackbar.Snackbar
// TODO: Rename parameter arguments, choose names that match
@@ -28,6 +38,8 @@ class CreatePayment : Fragment() {
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)
@@ -35,6 +47,63 @@ class CreatePayment : Fragment() {
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 onRequestPayment() {
+ val amount = "TESTKUDOS:10.00"
+ model.activeAmount = amount
+ var order = JSONObject().also {
+ it.put("amount", amount)
+ it.put("summary", "hello world")
+ it.put("fulfillment_url", "https://example.com")
+ it.put("instance", "default")
+ }
+
+ var reqBody = JSONObject().also { it.put("order", order) }
+
+ var req = MerchantInternalRequest(
+ Request.Method.POST,
+ model.merchantConfig!!,
+ "order",
+ null,
+ reqBody,
+ Response.Listener { onOrderCreated(it) },
+ Response.ErrorListener { onNetworkError(it) })
+
+ queue.add(req)
+
+ }
+
+ private fun onNetworkError(volleyError: VolleyError?) {
+ val mySnackbar = Snackbar.make(view!!, "Network Error", Snackbar.LENGTH_SHORT)
+ mySnackbar.show()
+ }
+
+ private fun onOrderCreated(orderResponse: JSONObject) {
+ val merchantConfig = model.merchantConfig!!
+ val orderId = orderResponse.getString("order_id")
+ val params = mapOf("order_id" to orderId, "instance" to merchantConfig.instance)
+ model.activeOrderId = orderId
+
+ var req = MerchantInternalRequest(Request.Method.GET, model.merchantConfig!!, "check-payment", params, null,
+ Response.Listener { onCheckPayment(it) }, Response.ErrorListener { onNetworkError(it) })
+ queue.add(req)
+ }
+
+ private fun onCheckPayment(checkPaymentResponse: JSONObject) {
+ if (checkPaymentResponse.getBoolean("paid")) {
+ val mySnackbar = Snackbar.make(view!!, "Already paid?!", Snackbar.LENGTH_SHORT)
+ mySnackbar.show()
+ return
+ }
+ model.activeContractUri = checkPaymentResponse.getString("contract_url")
+ findNavController().navigate(R.id.action_createPayment_to_processPayment)
}
override fun onCreateView(
@@ -42,12 +111,12 @@ class CreatePayment : Fragment() {
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
- return inflater.inflate(R.layout.fragment_create_payment, container, false)
- }
-
- // TODO: Rename method, update argument and hook method into UI event
- fun onButtonPressed(uri: Uri) {
- listener?.onFragmentInteraction(uri)
+ val view = inflater.inflate(R.layout.fragment_create_payment, container, false)
+ val requestPaymentButton = view.findViewById<Button>(R.id.button_request_payment);
+ requestPaymentButton.setOnClickListener {
+ onRequestPayment()
+ }
+ return view
}
override fun onAttach(context: Context) {
diff --git a/app/src/main/java/net/taler/merchantpos/MainActivity.kt b/app/src/main/java/net/taler/merchantpos/MainActivity.kt
index ac34d2a..4f67f9c 100644
--- a/app/src/main/java/net/taler/merchantpos/MainActivity.kt
+++ b/app/src/main/java/net/taler/merchantpos/MainActivity.kt
@@ -2,18 +2,21 @@ package net.taler.merchantpos
import android.net.Uri
import android.os.Bundle
-import com.google.android.material.floatingactionbutton.FloatingActionButton
-import com.google.android.material.snackbar.Snackbar
import androidx.core.view.GravityCompat
-import androidx.appcompat.app.ActionBarDrawerToggle
import android.view.MenuItem
import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.navigation.NavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import android.view.Menu
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.NavController
+import androidx.navigation.findNavController
+import androidx.navigation.ui.AppBarConfiguration
+import androidx.navigation.ui.setupWithNavController
-class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, CreatePayment.OnFragmentInteractionListener, ProcessPayment.OnFragmentInteractionListener {
+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.
}
@@ -27,13 +30,19 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
- val toggle = ActionBarDrawerToggle(
- this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
- )
- drawerLayout.addDrawerListener(toggle)
- toggle.syncState()
navView.setNavigationItemSelectedListener(this)
+
+ val navController = findNavController(R.id.nav_host_fragment)
+ val appBarConfiguration =
+ 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")
+
}
override fun onBackPressed() {
@@ -63,28 +72,22 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
override fun onNavigationItemSelected(item: MenuItem): Boolean {
// Handle navigation view item clicks here.
+ val nav: NavController = findNavController(R.id.nav_host_fragment)
when (item.itemId) {
R.id.nav_home -> {
- // Handle the camera action
- }
- R.id.nav_gallery -> {
-
- }
- R.id.nav_slideshow -> {
-
+ nav.navigate(R.id.action_global_createPayment)
}
- R.id.nav_tools -> {
-
+ R.id.nav_history -> {
+ nav.navigate(R.id.action_global_merchantHistory)
}
- R.id.nav_share -> {
-
- }
- R.id.nav_send -> {
-
+ R.id.nav_settings -> {
+ nav.navigate(R.id.action_global_merchantSettings)
}
}
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
drawerLayout.closeDrawer(GravityCompat.START)
return true
}
+
+
}
diff --git a/app/src/main/java/net/taler/merchantpos/MerchantConfig.kt b/app/src/main/java/net/taler/merchantpos/MerchantConfig.kt
new file mode 100644
index 0000000..b0552c1
--- /dev/null
+++ b/app/src/main/java/net/taler/merchantpos/MerchantConfig.kt
@@ -0,0 +1,16 @@
+package net.taler.merchantpos
+
+import android.net.Uri
+
+data class MerchantConfig(val baseUrl: String, val instance: String, val apiKey: String) {
+ fun urlFor(endpoint: String, params: Map<String, String>?): String {
+ val uriBuilder = Uri.parse(baseUrl).buildUpon()
+ uriBuilder.appendPath(endpoint)
+ if (params != null) {
+ params.forEach {
+ uriBuilder.appendQueryParameter(it.key, it.value)
+ }
+ }
+ return uriBuilder.toString()
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/net/taler/merchantpos/MerchantHistory.kt b/app/src/main/java/net/taler/merchantpos/MerchantHistory.kt
new file mode 100644
index 0000000..c3bab06
--- /dev/null
+++ b/app/src/main/java/net/taler/merchantpos/MerchantHistory.kt
@@ -0,0 +1,77 @@
+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.TextView
+import androidx.lifecycle.ViewModelProviders
+import androidx.recyclerview.widget.DividerItemDecoration
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.android.volley.RequestQueue
+import com.android.volley.toolbox.Volley
+
+class MyAdapter(private val myDataset: Array<String>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
+ val textView = LayoutInflater.from(parent.context).inflate(R.layout.history_row, parent, false)
+ return MyViewHolder(textView as TextView)
+ }
+
+ override fun getItemCount(): Int {
+ return myDataset.size
+ }
+
+ override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
+ holder.textView.text = myDataset[position]
+ }
+
+ class MyViewHolder(val textView: TextView) : RecyclerView.ViewHolder(textView)
+}
+
+/**
+ * A simple [Fragment] subclass.
+ * Activities that contain this fragment must implement the
+ * [MerchantHistory.OnFragmentInteractionListener] interface
+ * to handle interaction events.
+ * Use the [MerchantHistory.newInstance] factory method to
+ * create an instance of this fragment.
+ *
+ */
+class MerchantHistory : Fragment() {
+ private lateinit var queue: RequestQueue
+ private lateinit var model: PosTerminalViewModel
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ model = activity?.run {
+ ViewModelProviders.of(this)[PosTerminalViewModel::class.java]
+ } ?: throw Exception("Invalid Activity")
+
+ queue = Volley.newRequestQueue(context)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val myLayoutManager = LinearLayoutManager(this@MerchantHistory.context)
+ val myItemDecoration = DividerItemDecoration(context, myLayoutManager.orientation)
+ // Inflate the layout for this fragment
+ val view = inflater.inflate(R.layout.fragment_merchant_history, container, false)
+ val myList = ArrayList<String>()
+ for (i in 0..100) {
+ myList.add("Element $i")
+ }
+ val myArray: Array<String> = myList.toTypedArray()
+ view.findViewById<RecyclerView>(R.id.list_history).apply {
+ layoutManager = myLayoutManager
+ adapter = MyAdapter(myArray)
+ addItemDecoration(myItemDecoration)
+ }
+ return view
+ }
+}
diff --git a/app/src/main/java/net/taler/merchantpos/MerchantInternalRequest.kt b/app/src/main/java/net/taler/merchantpos/MerchantInternalRequest.kt
new file mode 100644
index 0000000..b5ab98e
--- /dev/null
+++ b/app/src/main/java/net/taler/merchantpos/MerchantInternalRequest.kt
@@ -0,0 +1,25 @@
+package net.taler.merchantpos
+
+
+import android.util.ArrayMap
+import com.android.volley.Response
+import com.android.volley.toolbox.JsonObjectRequest
+import org.json.JSONObject
+
+class MerchantInternalRequest(
+ method: Int,
+ private val merchantConfig: MerchantConfig,
+ endpoint: String,
+ params: Map<String, String>?,
+ jsonRequest: JSONObject?,
+ listener: Response.Listener<JSONObject>,
+ errorListener: Response.ErrorListener
+) :
+ JsonObjectRequest(method, merchantConfig.urlFor(endpoint, params), jsonRequest, listener, errorListener) {
+
+ override fun getHeaders(): MutableMap<String, String> {
+ val headerMap = ArrayMap<String, String>()
+ headerMap["Authorization"] = "ApiKey " + merchantConfig.apiKey
+ return headerMap
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/net/taler/merchantpos/MerchantSettings.kt b/app/src/main/java/net/taler/merchantpos/MerchantSettings.kt
new file mode 100644
index 0000000..e3dffb4
--- /dev/null
+++ b/app/src/main/java/net/taler/merchantpos/MerchantSettings.kt
@@ -0,0 +1,31 @@
+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
+
+
+/**
+ * A simple [Fragment] subclass.
+ * Activities that contain this fragment must implement the
+ * [MerchantSettings.OnFragmentInteractionListener] interface
+ * to handle interaction events.
+ * Use the [MerchantSettings.newInstance] factory method to
+ * create an instance of this fragment.
+ *
+ */
+class MerchantSettings : Fragment() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ // Inflate the layout for this fragment
+ return inflater.inflate(R.layout.fragment_merchant_settings, container, false)
+ }
+}
diff --git a/app/src/main/java/net/taler/merchantpos/PosTerminalViewModel.kt b/app/src/main/java/net/taler/merchantpos/PosTerminalViewModel.kt
new file mode 100644
index 0000000..1e174ef
--- /dev/null
+++ b/app/src/main/java/net/taler/merchantpos/PosTerminalViewModel.kt
@@ -0,0 +1,15 @@
+package net.taler.merchantpos
+
+import androidx.lifecycle.ViewModel
+
+class PosTerminalViewModel : ViewModel() {
+ 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(":", " ")
+ }
+} \ 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 81c1901..c98a3e4 100644
--- a/app/src/main/java/net/taler/merchantpos/ProcessPayment.kt
+++ b/app/src/main/java/net/taler/merchantpos/ProcessPayment.kt
@@ -1,12 +1,28 @@
package net.taler.merchantpos
import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import com.google.zxing.BarcodeFormat
+import com.google.zxing.common.BitMatrix
+import com.google.zxing.qrcode.QRCodeWriter
+import android.opengl.ETC1.getWidth
+import android.opengl.ETC1.getHeight
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.activity.OnBackPressedCallback
+import androidx.activity.addCallback
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.findNavController
+import androidx.navigation.fragment.findNavController
+import com.google.android.material.snackbar.Snackbar
// TODO: Rename parameter arguments, choose names that match
@@ -29,12 +45,19 @@ class ProcessPayment : Fragment() {
private var param2: String? = null
private var listener: OnFragmentInteractionListener? = null
+ 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")
+
}
override fun onCreateView(
@@ -42,12 +65,41 @@ class ProcessPayment : Fragment() {
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
- return inflater.inflate(R.layout.fragment_process_payment, container, false)
+ val view = inflater.inflate(R.layout.fragment_process_payment, container, false)
+ val img = view.findViewById<ImageView>(R.id.qrcode)
+ val myBitmap = makeQrCode(model.activeContractUri!!)
+ img.setImageBitmap(myBitmap)
+ val cancelPaymentButton = view.findViewById<Button>(R.id.button_cancel_payment)
+ cancelPaymentButton.setOnClickListener {
+ onPaymentCancel()
+ }
+ val textViewAmount = view.findViewById<TextView>(R.id.text_view_amount)
+ textViewAmount.text = model.activeAmountPretty()
+ val textViewOrderId = view.findViewById<TextView>(R.id.text_view_order_reference)
+ textViewOrderId.text = "Order Reference: " + model.activeOrderId
+ return view
}
- // TODO: Rename method, update argument and hook method into UI event
- fun onButtonPressed(uri: Uri) {
- listener?.onFragmentInteraction(uri)
+ private fun onPaymentCancel() {
+ val navController = findNavController()
+ navController.popBackStack()
+
+ val mySnackbar = Snackbar.make(view!!, "Payment Canceled", Snackbar.LENGTH_SHORT)
+ mySnackbar.show()
+ }
+
+ fun makeQrCode(text: String): Bitmap {
+ val qrCodeWriter: QRCodeWriter = QRCodeWriter()
+ val bitMatrix: BitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, 256, 256)
+ val height = bitMatrix.height
+ val width = bitMatrix.width
+ val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
+ for (x in 0 until width) {
+ for (y in 0 until height) {
+ bmp.setPixel(x, y, if (bitMatrix.get(x, y)) Color.BLACK else Color.WHITE)
+ }
+ }
+ return bmp;
}
override fun onAttach(context: Context) {