summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2020-01-27 17:23:30 -0300
committerTorsten Grote <t@grobox.de>2020-01-30 15:03:38 -0300
commit7d299bf8358c854987aab61d139ca74c83079d17 (patch)
treeb9dcae61779a808a3b0871b76dd9be6c42ba5323 /app
parent8080e4b4e96655c79fda8cf1bc9b4d8f084dec00 (diff)
downloadmerchant-terminal-android-7d299bf8358c854987aab61d139ca74c83079d17.tar.gz
merchant-terminal-android-7d299bf8358c854987aab61d139ca74c83079d17.tar.bz2
merchant-terminal-android-7d299bf8358c854987aab61d139ca74c83079d17.zip
Add screen to process an order
Diffstat (limited to 'app')
-rw-r--r--app/build.gradle3
-rw-r--r--app/src/main/AndroidManifest.xml21
-rw-r--r--app/src/main/java/net/taler/merchantpos/CreatePayment.kt19
-rw-r--r--app/src/main/java/net/taler/merchantpos/MainActivity.kt23
-rw-r--r--app/src/main/java/net/taler/merchantpos/MerchantHistory.kt14
-rw-r--r--app/src/main/java/net/taler/merchantpos/ProcessPayment.kt28
-rw-r--r--app/src/main/java/net/taler/merchantpos/order/CategoriesFragment.kt88
-rw-r--r--app/src/main/java/net/taler/merchantpos/order/Definitions.kt25
-rw-r--r--app/src/main/java/net/taler/merchantpos/order/OrderFragment.kt37
-rw-r--r--app/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt84
-rw-r--r--app/src/main/java/net/taler/merchantpos/order/OrderViewModel.kt130
-rw-r--r--app/src/main/java/net/taler/merchantpos/order/ProductsFragment.kt93
-rw-r--r--app/src/main/res/layout/fragment_categories.xml28
-rw-r--r--app/src/main/res/layout/fragment_order.xml97
-rw-r--r--app/src/main/res/layout/fragment_order_state.xml31
-rw-r--r--app/src/main/res/layout/fragment_products.xml28
-rw-r--r--app/src/main/res/layout/list_item_category.xml17
-rw-r--r--app/src/main/res/layout/list_item_order.xml40
-rw-r--r--app/src/main/res/layout/list_item_product.xml40
-rw-r--r--app/src/main/res/menu/activity_main_drawer.xml14
-rw-r--r--app/src/main/res/navigation/nav_graph.xml18
-rw-r--r--app/src/main/res/values/colors.xml2
-rw-r--r--app/src/main/res/values/strings.xml6
-rw-r--r--app/src/main/res/values/styles.xml2
24 files changed, 821 insertions, 67 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 1408c8a..3a6c23b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -60,6 +60,9 @@ dependencies {
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
+ // JSON parsing and serialization
+ implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.7"
+
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0920771..49c9892 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,27 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools" package="net.taler.merchantpos">
+ 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"
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <uses-feature
+ android:name="android.hardware.nfc"
android:required="true" />
+ <uses-feature
+ android:name="android.hardware.telephony"
+ android:required="false" />
+
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
- android:theme="@style/AppTheme" tools:ignore="GoogleAppIndexingWarning">
+ android:theme="@style/AppTheme"
+ tools:ignore="GoogleAppIndexingWarning">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
+ android:screenOrientation="landscape"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
+ <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
diff --git a/app/src/main/java/net/taler/merchantpos/CreatePayment.kt b/app/src/main/java/net/taler/merchantpos/CreatePayment.kt
index 83dbed8..f92bac7 100644
--- a/app/src/main/java/net/taler/merchantpos/CreatePayment.kt
+++ b/app/src/main/java/net/taler/merchantpos/CreatePayment.kt
@@ -9,7 +9,7 @@ import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.fragment.app.Fragment
-import androidx.lifecycle.ViewModelProviders
+import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.android.volley.Request
import com.android.volley.RequestQueue
@@ -25,7 +25,7 @@ import org.json.JSONObject
*/
class CreatePayment : Fragment() {
private lateinit var queue: RequestQueue
- private lateinit var model: PosTerminalViewModel
+ private val model: PosTerminalViewModel by activityViewModels()
private var paused: Boolean = false
@@ -47,10 +47,6 @@ class CreatePayment : Fragment() {
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)
}
@@ -92,8 +88,13 @@ class CreatePayment : Fragment() {
val params = mapOf("order_id" to orderId, "instance" to merchantConfig.instance)
model.activeOrderId = orderId
- val req = MerchantInternalRequest(Request.Method.GET, model.merchantConfig!!, "check-payment", params, null,
- Response.Listener { onCheckPayment(it) }, Response.ErrorListener { onNetworkError(it) })
+ val req = MerchantInternalRequest(Request.Method.GET,
+ model.merchantConfig!!,
+ "check-payment",
+ params,
+ null,
+ Response.Listener { onCheckPayment(it) },
+ Response.ErrorListener { onNetworkError(it) })
queue.add(req)
}
@@ -119,7 +120,7 @@ class CreatePayment : Fragment() {
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_create_payment, container, false)
- val requestPaymentButton = view.findViewById<Button>(R.id.button_request_payment);
+ val requestPaymentButton = view.findViewById<Button>(R.id.button_request_payment)
requestPaymentButton.setOnClickListener {
onRequestPayment()
}
diff --git a/app/src/main/java/net/taler/merchantpos/MainActivity.kt b/app/src/main/java/net/taler/merchantpos/MainActivity.kt
index 7c8330a..8cc2788 100644
--- a/app/src/main/java/net/taler/merchantpos/MainActivity.kt
+++ b/app/src/main/java/net/taler/merchantpos/MainActivity.kt
@@ -6,18 +6,18 @@ 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.Menu
import android.view.MenuItem
-import androidx.drawerlayout.widget.DrawerLayout
-import com.google.android.material.navigation.NavigationView
+import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
-import android.view.Menu
-import androidx.lifecycle.ViewModelProviders
+import androidx.core.view.GravityCompat
+import androidx.drawerlayout.widget.DrawerLayout
import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupWithNavController
+import com.google.android.material.navigation.NavigationView
import net.taler.merchantpos.Utils.Companion.hexStringToByteArray
import org.json.JSONObject
import java.io.ByteArrayOutputStream
@@ -143,7 +143,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
const val TAG = "taler-merchant"
}
- private lateinit var model: PosTerminalViewModel
+ private val model: PosTerminalViewModel by viewModels()
private var nfcAdapter: NfcAdapter? = null
private var currentTag: IsoDep? = null
@@ -265,9 +265,9 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
navView.setNavigationItemSelectedListener(this)
val navController = findNavController(R.id.nav_host_fragment)
- val appBarConfiguration =
- AppBarConfiguration(
+ val appBarConfiguration = AppBarConfiguration(
setOf(
+ R.id.order,
R.id.createPayment,
R.id.merchantSettings,
R.id.merchantHistory
@@ -284,10 +284,8 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
val apiKey = prefs.getString("merchantBackendApiKey", "sandbox")
val currency = prefs.getString("merchantBackendCurrency", "TESTKUDOS")
- model = ViewModelProviders.of(this)[PosTerminalViewModel::class.java]
model.merchantConfig =
MerchantConfig(baseUrl!!, instance!!, apiKey!!, currency!!)
-
}
override fun onBackPressed() {
@@ -319,9 +317,8 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
// Handle navigation view item clicks here.
val nav: NavController = findNavController(R.id.nav_host_fragment)
when (item.itemId) {
- R.id.nav_home -> {
- nav.navigate(R.id.action_global_createPayment)
- }
+ R.id.nav_home -> nav.navigate(R.id.action_global_createPayment)
+ R.id.nav_order -> nav.navigate(R.id.action_global_order)
R.id.nav_history -> {
nav.navigate(R.id.action_global_merchantHistory)
}
diff --git a/app/src/main/java/net/taler/merchantpos/MerchantHistory.kt b/app/src/main/java/net/taler/merchantpos/MerchantHistory.kt
index b79f836..577615b 100644
--- a/app/src/main/java/net/taler/merchantpos/MerchantHistory.kt
+++ b/app/src/main/java/net/taler/merchantpos/MerchantHistory.kt
@@ -4,13 +4,13 @@ package net.taler.merchantpos
import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
-import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModelProviders
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@@ -87,7 +87,7 @@ fun parseTalerTimestamp(s: String): Instant {
*/
class MerchantHistory : Fragment() {
private lateinit var queue: RequestQueue
- private lateinit var model: PosTerminalViewModel
+ private val model: PosTerminalViewModel by activityViewModels()
private val historyListAdapter = MyAdapter(listOf())
private val isLoading = MutableLiveData<Boolean>().apply { value = false }
@@ -95,10 +95,6 @@ class MerchantHistory : Fragment() {
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)
}
@@ -166,7 +162,7 @@ class MerchantHistory : Fragment() {
}
this.isLoading.observe(this, androidx.lifecycle.Observer { loading ->
- Log.v(TAG, "setting refreshing to ${loading}")
+ Log.v(TAG, "setting refreshing to $loading")
refreshLayout.isRefreshing = loading
})
@@ -174,6 +170,6 @@ class MerchantHistory : Fragment() {
}
companion object {
- val TAG = "taler-merchant"
+ const val TAG = "taler-merchant"
}
}
diff --git a/app/src/main/java/net/taler/merchantpos/ProcessPayment.kt b/app/src/main/java/net/taler/merchantpos/ProcessPayment.kt
index fbd60c6..d78d873 100644
--- a/app/src/main/java/net/taler/merchantpos/ProcessPayment.kt
+++ b/app/src/main/java/net/taler/merchantpos/ProcessPayment.kt
@@ -1,28 +1,17 @@
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.os.Handler
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.os.Handler
-import android.util.Log
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.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.android.volley.Request
import com.android.volley.RequestQueue
@@ -30,8 +19,10 @@ import com.android.volley.Response
import com.android.volley.VolleyError
import com.android.volley.toolbox.Volley
import com.google.android.material.snackbar.Snackbar
+import com.google.zxing.BarcodeFormat
+import com.google.zxing.common.BitMatrix
+import com.google.zxing.qrcode.QRCodeWriter
import org.json.JSONObject
-import java.net.URLEncoder
// TODO: Rename parameter arguments, choose names that match
@@ -47,17 +38,12 @@ class ProcessPayment : Fragment() {
private var paused: Boolean = true
private lateinit var queue: RequestQueue
- private lateinit var model: PosTerminalViewModel
+ private val model: PosTerminalViewModel by activityViewModels()
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)
-
}
private fun onCheckPayment(checkPaymentResponse: JSONObject) {
diff --git a/app/src/main/java/net/taler/merchantpos/order/CategoriesFragment.kt b/app/src/main/java/net/taler/merchantpos/order/CategoriesFragment.kt
new file mode 100644
index 0000000..9d1ac5e
--- /dev/null
+++ b/app/src/main/java/net/taler/merchantpos/order/CategoriesFragment.kt
@@ -0,0 +1,88 @@
+package net.taler.merchantpos.order
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.View.INVISIBLE
+import android.view.ViewGroup
+import android.widget.Button
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.Adapter
+import kotlinx.android.synthetic.main.fragment_categories.*
+import net.taler.merchantpos.R
+import net.taler.merchantpos.order.CategoryAdapter.CategoryViewHolder
+
+interface CategorySelectionListener {
+ fun onCategorySelected(category: Category)
+}
+
+class CategoriesFragment : Fragment(), CategorySelectionListener {
+
+ private val viewModel: OrderViewModel by activityViewModels()
+ private val adapter = CategoryAdapter(this)
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.fragment_categories, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ categoriesList.apply {
+ adapter = this@CategoriesFragment.adapter
+ layoutManager = LinearLayoutManager(requireContext())
+ }
+
+ viewModel.categories.observe(viewLifecycleOwner, Observer { categories ->
+ adapter.setItems(categories)
+ progressBar.visibility = INVISIBLE
+ })
+ }
+
+ override fun onCategorySelected(category: Category) {
+ viewModel.setCurrentCategory(category)
+ }
+
+}
+
+private class CategoryAdapter(
+ private val listener: CategorySelectionListener
+) : Adapter<CategoryViewHolder>() {
+
+ private val categories = ArrayList<Category>()
+
+ override fun getItemCount() = categories.size
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CategoryViewHolder {
+ val view =
+ LayoutInflater.from(parent.context).inflate(R.layout.list_item_category, parent, false)
+ return CategoryViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: CategoryViewHolder, position: Int) {
+ holder.bind(categories[position])
+ }
+
+ fun setItems(items: List<Category>) {
+ categories.clear()
+ categories.addAll(items)
+ notifyDataSetChanged()
+ }
+
+ private inner class CategoryViewHolder(v: View) : RecyclerView.ViewHolder(v) {
+ private val button: Button = v.findViewById(R.id.button)
+
+ fun bind(category: Category) {
+ button.text = category.name
+ button.isPressed = category.selected
+ button.setOnClickListener { listener.onCategorySelected(category) }
+ }
+ }
+
+}
diff --git a/app/src/main/java/net/taler/merchantpos/order/Definitions.kt b/app/src/main/java/net/taler/merchantpos/order/Definitions.kt
new file mode 100644
index 0000000..ec7f77e
--- /dev/null
+++ b/app/src/main/java/net/taler/merchantpos/order/Definitions.kt
@@ -0,0 +1,25 @@
+package net.taler.merchantpos.order
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import net.taler.merchantpos.Amount
+
+data class Category(
+ val id: Int,
+ val name: String
+) {
+ var selected: Boolean = false
+}
+
+data class Product(
+ @JsonProperty("product_id")
+ val id: String,
+ val description: String,
+ val price: String,
+ val categories: List<Int>,
+ @JsonProperty("delivery_location")
+ val location: String
+) {
+ val priceAsDouble by lazy { Amount.fromString(price).amount.toDouble() }
+}
+
+typealias OrderLine = Pair<Product, Int>
diff --git a/app/src/main/java/net/taler/merchantpos/order/OrderFragment.kt b/app/src/main/java/net/taler/merchantpos/order/OrderFragment.kt
new file mode 100644
index 0000000..3743281
--- /dev/null
+++ b/app/src/main/java/net/taler/merchantpos/order/OrderFragment.kt
@@ -0,0 +1,37 @@
+package net.taler.merchantpos.order
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.navigation.NavController
+import androidx.navigation.Navigation.findNavController
+import kotlinx.android.synthetic.main.fragment_order.*
+import net.taler.merchantpos.R
+
+class OrderFragment : Fragment() {
+
+ private val viewModel: OrderViewModel by activityViewModels()
+
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.fragment_order, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ restartButton.setOnClickListener { viewModel.restart() }
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+ val nav: NavController = findNavController(requireActivity(), R.id.nav_host_fragment)
+ historyButton.setOnClickListener { nav.navigate(R.id.action_global_merchantHistory) }
+ }
+
+}
diff --git a/app/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt b/app/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt
new file mode 100644
index 0000000..928b688
--- /dev/null
+++ b/app/src/main/java/net/taler/merchantpos/order/OrderStateFragment.kt
@@ -0,0 +1,84 @@
+package net.taler.merchantpos.order
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import kotlinx.android.synthetic.main.fragment_order_state.*
+import net.taler.merchantpos.R
+import net.taler.merchantpos.order.OrderAdapter.OrderViewHolder
+
+class OrderStateFragment : Fragment() {
+
+ private val viewModel: OrderViewModel by activityViewModels()
+ private val adapter = OrderAdapter()
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.fragment_order_state, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ orderList.apply {
+ adapter = this@OrderStateFragment.adapter
+ layoutManager = LinearLayoutManager(requireContext())
+ }
+
+ viewModel.order.observe(viewLifecycleOwner, Observer { order ->
+ adapter.setItems(order)
+ })
+ viewModel.orderTotal.observe(viewLifecycleOwner, Observer { orderTotal ->
+ if (orderTotal == 0.0) {
+ totalView.text = null
+ } else {
+ totalView.text = getString(R.string.order_total, orderTotal)
+ }
+ })
+ }
+
+}
+
+private class OrderAdapter : RecyclerView.Adapter<OrderViewHolder>() {
+
+ private val orderLines = ArrayList<OrderLine>()
+
+ override fun getItemCount() = orderLines.size
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OrderViewHolder {
+ val view =
+ LayoutInflater.from(parent.context).inflate(R.layout.list_item_order, parent, false)
+ return OrderViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: OrderViewHolder, position: Int) {
+ holder.bind(orderLines[position])
+ }
+
+ fun setItems(items: HashMap<Product, Int>) {
+ orderLines.clear()
+ items.forEach { t -> orderLines.add(t.toPair()) }
+ notifyDataSetChanged()
+ }
+
+ private inner class OrderViewHolder(v: View) : RecyclerView.ViewHolder(v) {
+ private val quantity: TextView = v.findViewById(R.id.quantity)
+ private val name: TextView = v.findViewById(R.id.name)
+ private val price: TextView = v.findViewById(R.id.price)
+
+ fun bind(orderLine: OrderLine) {
+ quantity.text = orderLine.second.toString()
+ name.text = orderLine.first.description
+ price.text = String.format("%.2f", orderLine.first.priceAsDouble * orderLine.second)
+ }
+ }
+
+}
diff --git a/app/src/main/java/net/taler/merchantpos/order/OrderViewModel.kt b/app/src/main/java/net/taler/merchantpos/order/OrderViewModel.kt
new file mode 100644
index 0000000..02ee33f
--- /dev/null
+++ b/app/src/main/java/net/taler/merchantpos/order/OrderViewModel.kt
@@ -0,0 +1,130 @@
+package net.taler.merchantpos.order
+
+import android.app.Application
+import android.util.Log
+import androidx.annotation.UiThread
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Transformations.map
+import androidx.lifecycle.viewModelScope
+import com.android.volley.Request.Method.GET
+import com.android.volley.Response.ErrorListener
+import com.android.volley.Response.Listener
+import com.android.volley.toolbox.JsonObjectRequest
+import com.android.volley.toolbox.Volley
+import com.fasterxml.jackson.core.type.TypeReference
+import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.kotlin.KotlinModule
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import org.json.JSONObject
+
+class OrderViewModel(app: Application) : AndroidViewModel(app) {
+
+ companion object {
+ val TAG = OrderViewModel::class.java.simpleName
+ }
+
+ private val url = "https://grobox.de/taler/products.json"
+ private val queue = Volley.newRequestQueue(app)
+ private val mapper = ObjectMapper()
+ .registerModule(KotlinModule())
+ .configure(FAIL_ON_UNKNOWN_PROPERTIES, false)
+
+ private val productsByCategory = HashMap<Category, ArrayList<Product>>()
+
+ private val mOrder = MutableLiveData<HashMap<Product, Int>>()
+ internal val order: LiveData<HashMap<Product, Int>> = mOrder
+ internal val orderTotal: LiveData<Double> = map(mOrder) { map -> getTotal(map) }
+
+ private val mProducts = MutableLiveData<List<Product>>()
+ internal val products: LiveData<List<Product>> = mProducts
+
+ private val mCategories = MutableLiveData<List<Category>>()
+ internal val categories: LiveData<List<Category>> = mCategories
+
+ init {
+ val stringRequest = JsonObjectRequest(GET, url, null,
+ Listener { response -> onConfigurationReceived(response) },
+ ErrorListener { onConfigurationError() }
+ )
+ queue.add(stringRequest)
+ }
+
+ override fun onCleared() {
+ queue.cancelAll { !it.isCanceled }
+ }
+
+ private fun onConfigurationReceived(json: JSONObject) = viewModelScope.launch(Dispatchers.IO) {
+ // parse categories
+ val categoriesStr = json.getJSONArray("categories").toString()
+ val categoriesType = object : TypeReference<List<Category>>() {}
+ val categories: List<Category> = mapper.readValue(categoriesStr, categoriesType)
+ mCategories.postValue(categories)
+
+ // parse products (live data gets updated in setCurrentCategory())
+ val productsStr = json.getJSONArray("products").toString()
+ val productsType = object : TypeReference<List<Product>>() {}
+ val products: List<Product> = mapper.readValue(productsStr, productsType)
+
+ // group products by categories
+ productsByCategory.clear()
+ products.forEach { product ->
+ product.categories.forEach { categoryId ->
+ val category = categories.find { it.id == categoryId }
+ if (category == null) {
+ Log.e(TAG, "Product $product has unknown category $categoryId")
+ onConfigurationError()
+ return@launch
+ }
+ if (productsByCategory.containsKey(category)) {
+ productsByCategory[category]?.add(product)
+ } else {
+ productsByCategory[category] = ArrayList<Product>().apply { add(product) }
+ }
+ }
+ }
+ // pre-select the first category
+ if (productsByCategory.size > 0) setCurrentCategory(categories[0])
+ else onConfigurationError()
+ }
+
+ private fun onConfigurationError() {
+ Log.e("TEST", "ERROR")
+ // TODO
+ }
+
+ internal fun setCurrentCategory(category: Category) {
+ val newCategories = categories.value?.apply {
+ forEach { if (it.selected) it.selected = false }
+ category.selected = true
+ }
+ mCategories.postValue(newCategories)
+ mProducts.postValue(productsByCategory[category])
+ }
+
+ @UiThread
+ internal fun addProduct(product: Product) {
+ val map = mOrder.value ?: HashMap()
+ val quantity = map[product] ?: 0
+ map[product] = quantity + 1
+ mOrder.value = map
+ }
+
+ @UiThread
+ internal fun restart() {
+ mOrder.value = HashMap()
+ }
+
+ private fun getTotal(map: HashMap<Product, Int>): Double {
+ var total = 0.0
+ map.forEach {
+ val price = it.key.priceAsDouble
+ total += price * it.value
+ }
+ return total
+ }
+
+}
diff --git a/app/src/main/java/net/taler/merchantpos/order/ProductsFragment.kt b/app/src/main/java/net/taler/merchantpos/order/ProductsFragment.kt
new file mode 100644
index 0000000..2a028c0
--- /dev/null
+++ b/app/src/main/java/net/taler/merchantpos/order/ProductsFragment.kt
@@ -0,0 +1,93 @@
+package net.taler.merchantpos.order
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.View.INVISIBLE
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView.Adapter
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import kotlinx.android.synthetic.main.fragment_products.*
+import net.taler.merchantpos.R
+import net.taler.merchantpos.order.ProductAdapter.ProductViewHolder
+
+interface ProductSelectionListener {
+ fun onProductSelected(product: Product)
+}
+
+class ProductsFragment : Fragment(), ProductSelectionListener {
+
+ private val viewModel: OrderViewModel by activityViewModels()
+ private val adapter = ProductAdapter(this)
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.fragment_products, container, false)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ productsList.apply {
+ adapter = this@ProductsFragment.adapter
+ layoutManager = GridLayoutManager(requireContext(), 3)
+ }
+
+ viewModel.products.observe(viewLifecycleOwner, Observer { products ->
+ if (products == null) {
+ adapter.setItems(emptyList())
+ } else {
+ adapter.setItems(products)
+ }
+ progressBar.visibility = INVISIBLE
+ })
+ }
+
+ override fun onProductSelected(product: Product) {
+ viewModel.addProduct(product)
+ }
+
+}
+
+private class ProductAdapter(
+ private val listener: ProductSelectionListener
+) : Adapter<ProductViewHolder>() {
+
+ private val products = ArrayList<Product>()
+
+ override fun getItemCount() = products.size
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
+ val view =
+ LayoutInflater.from(parent.context).inflate(R.layout.list_item_product, parent, false)
+ return ProductViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
+ holder.bind(products[position])
+ }
+
+ fun setItems(items: List<Product>) {
+ products.clear()
+ products.addAll(items)
+ notifyDataSetChanged()
+ }
+
+ private inner class ProductViewHolder(private val v: View) : ViewHolder(v) {
+ private val name: TextView = v.findViewById(R.id.name)
+ private val price: TextView = v.findViewById(R.id.price)
+
+ fun bind(product: Product) {
+ name.text = product.description
+ price.text = product.priceAsDouble.toString()
+ v.setOnClickListener { listener.onProductSelected(product) }
+ }
+ }
+
+}
diff --git a/app/src/main/res/layout/fragment_categories.xml b/app/src/main/res/layout/fragment_categories.xml
new file mode 100644
index 0000000..dcd6bd5
--- /dev/null
+++ b/app/src/main/res/layout/fragment_categories.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_height="match_parent">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/categoriesList"
+ android:layout_width="0dp"
+ tools:listitem="@layout/list_item_category"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <ProgressBar
+ android:id="@+id/progressBar"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout/fragment_order.xml b/app/src/main/res/layout/fragment_order.xml
new file mode 100644
index 0000000..462264d
--- /dev/null
+++ b/app/src/main/res/layout/fragment_order.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ 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">
+
+ <androidx.fragment.app.FragmentContainerView
+ android:id="@+id/fragment1"
+ android:name="net.taler.merchantpos.order.OrderStateFragment"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toTopOf="@+id/restartButton"
+ app:layout_constraintEnd_toStartOf="@+id/guideline1"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:layout="@layout/fragment_order_state" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_percent="0.25" />
+
+ <androidx.fragment.app.FragmentContainerView
+ android:id="@+id/fragment2"
+ android:name="net.taler.merchantpos.order.ProductsFragment"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toTopOf="@+id/restartButton"
+ app:layout_constraintEnd_toStartOf="@+id/guideline2"
+ app:layout_constraintStart_toStartOf="@+id/guideline1"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:layout="@layout/fragment_products" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_percent="0.75" />
+
+ <androidx.fragment.app.FragmentContainerView
+ android:id="@+id/fragment3"
+ android:name="net.taler.merchantpos.order.CategoriesFragment"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toTopOf="@+id/restartButton"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="@+id/guideline2"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:layout="@layout/fragment_categories" />
+
+ <Button
+ android:id="@+id/restartButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:backgroundTint="@color/bottomButtons"
+ android:text="@string/order_restart"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <Button
+ android:id="@+id/reconfigureButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:backgroundTint="@color/bottomButtons"
+ android:text="@string/button_reconfigure"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/restartButton" />
+
+ <Button
+ android:id="@+id/historyButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="16dp"
+ android:backgroundTint="@color/bottomButtons"
+ android:text="@string/button_history"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/reconfigureButton" />
+
+ <Button
+ android:id="@+id/completeButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:backgroundTint="@color/bottomButtons"
+ android:text="@string/button_complete"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1.0"
+ app:layout_constraintStart_toEndOf="@+id/historyButton" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout/fragment_order_state.xml b/app/src/main/res/layout/fragment_order_state.xml
new file mode 100644
index 0000000..0cd9c75
--- /dev/null
+++ b/app/src/main/res/layout/fragment_order_state.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ 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">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/orderList"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toTopOf="@+id/totalView"
+ tools:listitem="@layout/list_item_order"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <TextView
+ android:id="@+id/totalView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/orderList"
+ app:layout_constraintVertical_bias="1.0"
+ tools:text="Total: 23.75" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout/fragment_products.xml b/app/src/main/res/layout/fragment_products.xml
new file mode 100644
index 0000000..909fece
--- /dev/null
+++ b/app/src/main/res/layout/fragment_products.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_height="match_parent">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/productsList"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ tools:listitem="@layout/list_item_product" />
+
+ <ProgressBar
+ android:id="@+id/progressBar"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout/list_item_category.xml b/app/src/main/res/layout/list_item_category.xml
new file mode 100644
index 0000000..496b96b
--- /dev/null
+++ b/app/src/main/res/layout/list_item_category.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:id="@+id/button"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="Snacks" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout/list_item_order.xml b/app/src/main/res/layout/list_item_order.xml
new file mode 100644
index 0000000..5dc2f86
--- /dev/null
+++ b/app/src/main/res/layout/list_item_order.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="8dp"
+ android:paddingTop="8dp"
+ android:paddingEnd="8dp">
+
+ <TextView
+ android:id="@+id/quantity"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:gravity="end"
+ android:minWidth="24dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="31" />
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ app:layout_constraintEnd_toStartOf="@+id/price"
+ app:layout_constraintStart_toEndOf="@+id/quantity"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="An order product item that in some cases could have a very long name" />
+
+ <TextView
+ android:id="@+id/price"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="23.42" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/src/main/res/layout/list_item_product.xml b/app/src/main/res/layout/list_item_product.xml
new file mode 100644
index 0000000..51178a5
--- /dev/null
+++ b/app/src/main/res/layout/list_item_product.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dp"
+ android:clickable="true"
+ android:focusable="true"
+ app:cardUseCompatPadding="true">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp">
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:textColor="?android:textColorPrimary"
+ android:textStyle="bold"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="Steak and two Eggs" />
+
+ <TextView
+ android:id="@+id/price"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:textColor="?android:textColorSecondary"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/name"
+ tools:text="7.95" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+</com.google.android.material.card.MaterialCardView> \ No newline at end of file
diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml
index 9ce4a06..7a70824 100644
--- a/app/src/main/res/menu/activity_main_drawer.xml
+++ b/app/src/main/res/menu/activity_main_drawer.xml
@@ -1,20 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- tools:showIn="navigation_view">
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:showIn="navigation_view">
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_home"
android:icon="@drawable/ic_move_money_24dp"
- android:title="@string/menu_home"/>
+ android:title="@string/menu_home" />
+ <item
+ android:id="@+id/nav_order"
+ android:icon="@drawable/ic_move_money_24dp"
+ android:title="Order" />
<item
android:id="@+id/nav_history"
android:icon="@drawable/ic_history_black_24dp"
- android:title="History"/>
+ android:title="History" />
<item
android:id="@+id/nav_settings"
android:icon="@drawable/ic_menu_manage"
- android:title="Settings"/>
+ android:title="Settings" />
</group>
</menu>
diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml
index 3a5b470..5951e77 100644
--- a/app/src/main/res/navigation/nav_graph.xml
+++ b/app/src/main/res/navigation/nav_graph.xml
@@ -1,8 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools" android:id="@+id/nav_graph"
- app:startDestination="@id/createPayment">
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/nav_graph"
+ app:startDestination="@id/order">
+
+ <fragment
+ android:id="@+id/order"
+ android:name="net.taler.merchantpos.order.OrderFragment"
+ android:label="Order"
+ tools:layout="@layout/fragment_order">
+ <action
+ android:id="@+id/action_createPayment_to_processPayment"
+ app:destination="@id/processPayment" />
+ </fragment>
<fragment android:id="@+id/createPayment" android:name="net.taler.merchantpos.CreatePayment"
android:label="Request Payment" tools:layout="@layout/fragment_create_payment">
@@ -19,6 +30,7 @@
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"/>
+ <action android:id="@+id/action_global_order" app:destination="@id/order"/>
<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"/>
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index cbe99a2..950c107 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -3,4 +3,6 @@
<color name="colorPrimary">#795548</color>
<color name="colorPrimaryDark">#5D4037</color>
<color name="colorAccent">#FFEB3B</color>
+
+ <color name="bottomButtons">#9E9D24</color>
</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f3103af..09c7342 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -16,4 +16,10 @@
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
+
+ <string name="order_total">Total: %1$.2f</string>
+ <string name="order_restart">Restart</string>
+ <string name="button_reconfigure">Reconfigure</string>
+ <string name="button_history">History</string>
+ <string name="button_complete">Complete</string>
</resources>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 1eb4629..9eac8b0 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -1,6 +1,6 @@
<resources>
<!-- Base application theme. -->
- <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>