summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2019-08-20 22:56:16 +0200
committerFlorian Dold <florian.dold@gmail.com>2019-08-20 22:58:10 +0200
commiteb9594079589a0126c3fb5f9a2f6ecd56950eebc (patch)
tree2ab7544f0230c78c1df15ac9e75ed5d47ac87bbf
parente63f231bc8583db065213a07019223f7c122c9fd (diff)
downloadmerchant-terminal-android-eb9594079589a0126c3fb5f9a2f6ecd56950eebc.tar.gz
merchant-terminal-android-eb9594079589a0126c3fb5f9a2f6ecd56950eebc.tar.bz2
merchant-terminal-android-eb9594079589a0126c3fb5f9a2f6ecd56950eebc.zip
prototype of payment request flow
-rw-r--r--.idea/dictionaries/dold.xml7
-rw-r--r--app/build.gradle28
-rw-r--r--app/src/main/AndroidManifest.xml6
-rw-r--r--app/src/main/ic_taler_logo-web.pngbin0 -> 25951 bytes
-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
-rw-r--r--app/src/main/res/drawable/ic_history_black_24dp.xml9
-rw-r--r--app/src/main/res/drawable/ic_launcher_background.xml10
-rw-r--r--app/src/main/res/drawable/ic_move_money_24dp.xml9
-rw-r--r--app/src/main/res/layout/fragment_merchant_history.xml32
-rw-r--r--app/src/main/res/layout/fragment_merchant_settings.xml15
-rw-r--r--app/src/main/res/layout/fragment_process_payment.xml60
-rw-r--r--app/src/main/res/layout/history_row.xml7
-rw-r--r--app/src/main/res/layout/nav_header_main.xml6
-rw-r--r--app/src/main/res/menu/activity_main_drawer.xml30
-rw-r--r--app/src/main/res/mipmap-anydpi-v26/ic_taler_logo.xml5
-rw-r--r--app/src/main/res/mipmap-anydpi-v26/ic_taler_logo_round.xml5
-rw-r--r--app/src/main/res/mipmap-hdpi/ic_launcher_foreground.pngbin0 -> 4307 bytes
-rw-r--r--app/src/main/res/mipmap-hdpi/ic_taler_logo.pngbin0 -> 2347 bytes
-rw-r--r--app/src/main/res/mipmap-hdpi/ic_taler_logo_round.pngbin0 -> 3638 bytes
-rw-r--r--app/src/main/res/mipmap-mdpi/ic_launcher_foreground.pngbin0 -> 2625 bytes
-rw-r--r--app/src/main/res/mipmap-mdpi/ic_taler_logo.pngbin0 -> 1532 bytes
-rw-r--r--app/src/main/res/mipmap-mdpi/ic_taler_logo_round.pngbin0 -> 2240 bytes
-rw-r--r--app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.pngbin0 -> 6077 bytes
-rw-r--r--app/src/main/res/mipmap-xhdpi/ic_taler_logo.pngbin0 -> 3336 bytes
-rw-r--r--app/src/main/res/mipmap-xhdpi/ic_taler_logo_round.pngbin0 -> 5273 bytes
-rw-r--r--app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.pngbin0 -> 10228 bytes
-rw-r--r--app/src/main/res/mipmap-xxhdpi/ic_taler_logo.pngbin0 -> 5422 bytes
-rw-r--r--app/src/main/res/mipmap-xxhdpi/ic_taler_logo_round.pngbin0 -> 8454 bytes
-rw-r--r--app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.pngbin0 -> 14083 bytes
-rw-r--r--app/src/main/res/mipmap-xxxhdpi/ic_taler_logo.pngbin0 -> 7786 bytes
-rw-r--r--app/src/main/res/mipmap-xxxhdpi/ic_taler_logo_round.pngbin0 -> 12377 bytes
-rw-r--r--app/src/main/res/navigation/nav_graph.xml14
39 files changed, 520 insertions, 77 deletions
diff --git a/.idea/dictionaries/dold.xml b/.idea/dictionaries/dold.xml
new file mode 100644
index 0000000..192913d
--- /dev/null
+++ b/.idea/dictionaries/dold.xml
@@ -0,0 +1,7 @@
+<component name="ProjectDictionaryState">
+ <dictionary name="dold">
+ <words>
+ <w>snackbar</w>
+ </words>
+ </dictionary>
+</component> \ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 0316543..22225ac 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,6 +1,7 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions'
@@ -21,11 +22,18 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
+
+ compileOptions {
+ sourceCompatibility = 1.8
+ targetCompatibility = 1.8
+ }
+
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
}
dependencies {
- def nav_version = "2.1.0-rc01"
-
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.0.2'
@@ -37,9 +45,25 @@ dependencies {
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ implementation "androidx.recyclerview:recyclerview:1.1.0-beta02"
+
+
+ def nav_version = "2.1.0-rc01"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
// HTTP Requests
implementation 'com.android.volley:volley:1.1.1'
+
+ // QR codes
+ implementation 'com.google.zxing:core:3.4.0'
+
+ // ViewModel and LiveData
+ def lifecycle_version = "2.0.0"
+ implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
+ implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
+ kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
+
+ // JSON literals and parsing
+ implementation 'com.beust:klaxon:5.0.11'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2a203e5..52f77ef 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="net.taler.merchantpos">
+ xmlns:tools="http://schemas.android.com/tools" package="net.taler.merchantpos">
<application
android:allowBackup="true"
@@ -8,7 +8,7 @@
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"
@@ -20,5 +20,7 @@
</intent-filter>
</activity>
</application>
+
+ <uses-permission android:name="android.permission.INTERNET"/>
</manifest> \ No newline at end of file
diff --git a/app/src/main/ic_taler_logo-web.png b/app/src/main/ic_taler_logo-web.png
new file mode 100644
index 0000000..e3b8075
--- /dev/null
+++ b/app/src/main/ic_taler_logo-web.png
Binary files differ
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) {
diff --git a/app/src/main/res/drawable/ic_history_black_24dp.xml b/app/src/main/res/drawable/ic_history_black_24dp.xml
new file mode 100644
index 0000000..a61de1b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_history_black_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
+</vector>
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
index a0ad202..2408e30 100644
--- a/app/src/main/res/drawable/ic_launcher_background.xml
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="108dp"
- android:width="108dp"
- android:viewportHeight="108"
- android:viewportWidth="108">
+ android:height="108dp"
+ android:width="108dp"
+ android:viewportHeight="108"
+ android:viewportWidth="108"
+ xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#008577"
android:pathData="M0,0h108v108h-108z"/>
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
diff --git a/app/src/main/res/drawable/ic_move_money_24dp.xml b/app/src/main/res/drawable/ic_move_money_24dp.xml
new file mode 100644
index 0000000..349f48f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_move_money_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3L4.99,3c-1.11,0 -1.98,0.9 -1.98,2L3,19c0,1.1 0.88,2 1.99,2L19,21c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,15h-4c0,1.66 -1.35,3 -3,3s-3,-1.34 -3,-3L4.99,15L4.99,5L19,5v10zM16,10h-2L14,7h-4v3L8,10l4,4 4,-4z"/>
+</vector>
diff --git a/app/src/main/res/layout/fragment_merchant_history.xml b/app/src/main/res/layout/fragment_merchant_history.xml
new file mode 100644
index 0000000..92abdf7
--- /dev/null
+++ b/app/src/main/res/layout/fragment_merchant_history.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.core.widget.NestedScrollView
+ 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">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <View
+ android:id="@+id/header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/list_history"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:layout_marginTop="47dp" android:layout_marginEnd="75dp"
+ android:layout_marginBottom="17dp"/>
+
+ <Button
+ android:text="Button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" android:id="@+id/button2"/>
+ </LinearLayout>
+</androidx.core.widget.NestedScrollView>
diff --git a/app/src/main/res/layout/fragment_merchant_settings.xml b/app/src/main/res/layout/fragment_merchant_settings.xml
new file mode 100644
index 0000000..8fcf233
--- /dev/null
+++ b/app/src/main/res/layout/fragment_merchant_settings.xml
@@ -0,0 +1,15 @@
+<?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=".MerchantSettings">
+
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ </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 b1d9813..0ff8257 100644
--- a/app/src/main/res/layout/fragment_process_payment.xml
+++ b/app/src/main/res/layout/fragment_process_payment.xml
@@ -1,14 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".ProcessPayment">
+ android:layout_margin="15dp"
+ tools:context=".ProcessPayment" android:id="@+id/frameLayout2">
- <!-- TODO: Update blank fragment layout -->
- <TextView
+ <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:text="@string/hello_blank_fragment"/>
+ android:id="@+id/constraintLayout">
+ <Button
+ android:id="@+id/button_cancel_payment"
+ android:text="Cancel Payment"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"/>
+ <ImageView
+ android:layout_width="269dp"
+ 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"/>
+ <TextView
+ android:text="Please scan QR Code or use NFC to pay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/textView2"
+ android:textSize="24sp"
+ android:layout_marginTop="15dp"
+ app:layout_constraintTop_toBottomOf="@+id/qrcode"
+ android:layout_marginBottom="15dp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/text_view_amount"
+ app:layout_constraintStart_toStartOf="parent"
+ android:textAlignment="center"/>
+ <TextView
+ android:text="AMOUNT"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/text_view_amount" android:textSize="30sp"
+ app:layout_constraintStart_toStartOf="parent"
+ android:layout_marginTop="39dp"
+ app:layout_constraintTop_toBottomOf="@+id/textView2"
+ app:layout_constraintBottom_toTopOf="@+id/text_view_order_reference"
+ app:layout_constraintEnd_toEndOf="parent"
+ tools:text="10.49 Eur"
+ />
+ <TextView
+ android:text="Order Reference: 1234"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/text_view_order_reference" android:layout_marginTop="8dp"
+ app:layout_constraintTop_toBottomOf="@+id/text_view_amount" android:layout_marginBottom="8dp"
+ app:layout_constraintBottom_toTopOf="@id/button_cancel_payment" app:layout_constraintStart_toStartOf="parent"
+ android:layout_marginStart="8dp" app:layout_constraintEnd_toEndOf="parent"
+ android:layout_marginEnd="8dp"
+ android:textAlignment="center"/>
+ </androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/history_row.xml b/app/src/main/res/layout/history_row.xml
new file mode 100644
index 0000000..a67cf37
--- /dev/null
+++ b/app/src/main/res/layout/history_row.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/historyText"
+ android:textSize="20sp">
+</TextView> \ No newline at end of file
diff --git a/app/src/main/res/layout/nav_header_main.xml b/app/src/main/res/layout/nav_header_main.xml
index 92ca611..84d4010 100644
--- a/app/src/main/res/layout/nav_header_main.xml
+++ b/app/src/main/res/layout/nav_header_main.xml
@@ -17,7 +17,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
- app:srcCompat="@mipmap/ic_launcher_round"
+ app:srcCompat="@mipmap/ic_taler_logo_round"
android:contentDescription="@string/nav_header_desc"
android:id="@+id/imageView"/>
@@ -25,13 +25,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
- android:text="@string/nav_header_title"
+ android:text="GNU Taler"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/nav_header_subtitle"
+ android:text="Merchant Terminal"
android:id="@+id/textView"/>
</LinearLayout>
diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml
index 9df2b3d..9ce4a06 100644
--- a/app/src/main/res/menu/activity_main_drawer.xml
+++ b/app/src/main/res/menu/activity_main_drawer.xml
@@ -6,33 +6,15 @@
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_home"
- android:icon="@drawable/ic_menu_camera"
+ android:icon="@drawable/ic_move_money_24dp"
android:title="@string/menu_home"/>
<item
- android:id="@+id/nav_gallery"
- android:icon="@drawable/ic_menu_gallery"
- android:title="@string/menu_gallery"/>
+ android:id="@+id/nav_history"
+ android:icon="@drawable/ic_history_black_24dp"
+ android:title="History"/>
<item
- android:id="@+id/nav_slideshow"
- android:icon="@drawable/ic_menu_slideshow"
- android:title="@string/menu_slideshow"/>
- <item
- android:id="@+id/nav_tools"
+ android:id="@+id/nav_settings"
android:icon="@drawable/ic_menu_manage"
- android:title="@string/menu_tools"/>
+ android:title="Settings"/>
</group>
-
- <item android:title="Communicate">
- <menu>
- <item
- android:id="@+id/nav_share"
- android:icon="@drawable/ic_menu_share"
- android:title="@string/menu_share"/>
- <item
- android:id="@+id/nav_send"
- android:icon="@drawable/ic_menu_send"
- android:title="@string/menu_send"/>
- </menu>
- </item>
-
</menu>
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_taler_logo.xml b/app/src/main/res/mipmap-anydpi-v26/ic_taler_logo.xml
new file mode 100644
index 0000000..c4a603d
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_taler_logo.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_taler_logo_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_taler_logo_round.xml
new file mode 100644
index 0000000..c4a603d
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_taler_logo_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..75273ec
--- /dev/null
+++ b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_taler_logo.png b/app/src/main/res/mipmap-hdpi/ic_taler_logo.png
new file mode 100644
index 0000000..eaecede
--- /dev/null
+++ b/app/src/main/res/mipmap-hdpi/ic_taler_logo.png
Binary files differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_taler_logo_round.png b/app/src/main/res/mipmap-hdpi/ic_taler_logo_round.png
new file mode 100644
index 0000000..caa2a3e
--- /dev/null
+++ b/app/src/main/res/mipmap-hdpi/ic_taler_logo_round.png
Binary files differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..a450287
--- /dev/null
+++ b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_taler_logo.png b/app/src/main/res/mipmap-mdpi/ic_taler_logo.png
new file mode 100644
index 0000000..e1f7374
--- /dev/null
+++ b/app/src/main/res/mipmap-mdpi/ic_taler_logo.png
Binary files differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_taler_logo_round.png b/app/src/main/res/mipmap-mdpi/ic_taler_logo_round.png
new file mode 100644
index 0000000..e92d2d3
--- /dev/null
+++ b/app/src/main/res/mipmap-mdpi/ic_taler_logo_round.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..a5e875c
--- /dev/null
+++ b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_taler_logo.png b/app/src/main/res/mipmap-xhdpi/ic_taler_logo.png
new file mode 100644
index 0000000..5ca4409
--- /dev/null
+++ b/app/src/main/res/mipmap-xhdpi/ic_taler_logo.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_taler_logo_round.png b/app/src/main/res/mipmap-xhdpi/ic_taler_logo_round.png
new file mode 100644
index 0000000..12b9056
--- /dev/null
+++ b/app/src/main/res/mipmap-xhdpi/ic_taler_logo_round.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..e9d1fc9
--- /dev/null
+++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_taler_logo.png b/app/src/main/res/mipmap-xxhdpi/ic_taler_logo.png
new file mode 100644
index 0000000..a786efa
--- /dev/null
+++ b/app/src/main/res/mipmap-xxhdpi/ic_taler_logo.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_taler_logo_round.png b/app/src/main/res/mipmap-xxhdpi/ic_taler_logo_round.png
new file mode 100644
index 0000000..b22a84e
--- /dev/null
+++ b/app/src/main/res/mipmap-xxhdpi/ic_taler_logo_round.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..f8037d1
--- /dev/null
+++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_taler_logo.png b/app/src/main/res/mipmap-xxxhdpi/ic_taler_logo.png
new file mode 100644
index 0000000..0e9df6a
--- /dev/null
+++ b/app/src/main/res/mipmap-xxxhdpi/ic_taler_logo.png
Binary files differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_taler_logo_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_taler_logo_round.png
new file mode 100644
index 0000000..6bef9bd
--- /dev/null
+++ b/app/src/main/res/mipmap-xxxhdpi/ic_taler_logo_round.png
Binary files differ
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
index eaa7bf9..b6345a1 100644
--- a/app/src/main/res/navigation/nav_graph.xml
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -5,8 +5,16 @@
app:startDestination="@id/createPayment">
<fragment android:id="@+id/createPayment" android:name="net.taler.merchantpos.CreatePayment"
- android:label="fragment_create_payment" tools:layout="@layout/fragment_create_payment"/>
+ android:label="Request Payment" tools:layout="@layout/fragment_create_payment">
+ <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="fragment_process_payment" tools:layout="@layout/fragment_process_payment"/>
- <action android:id="@+id/action_global_processPayment" app:destination="@id/processPayment"/>
+ android:label="Payment Prompt" tools:layout="@layout/fragment_process_payment"/>
+ <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"/>
+ <action android:id="@+id/action_global_createPayment" app:destination="@id/createPayment"/>
+ <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"/>
</navigation> \ No newline at end of file