diff options
Diffstat (limited to 'app/src/main/java/net')
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) { |