diff options
author | Florian Dold <florian.dold@gmail.com> | 2019-08-20 22:56:16 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2019-08-20 22:58:10 +0200 |
commit | eb9594079589a0126c3fb5f9a2f6ecd56950eebc (patch) | |
tree | 2ab7544f0230c78c1df15ac9e75ed5d47ac87bbf | |
parent | e63f231bc8583db065213a07019223f7c122c9fd (diff) | |
download | merchant-terminal-android-eb9594079589a0126c3fb5f9a2f6ecd56950eebc.tar.gz merchant-terminal-android-eb9594079589a0126c3fb5f9a2f6ecd56950eebc.tar.bz2 merchant-terminal-android-eb9594079589a0126c3fb5f9a2f6ecd56950eebc.zip |
prototype of payment request flow
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 Binary files differnew file mode 100644 index 0000000..e3b8075 --- /dev/null +++ b/app/src/main/ic_taler_logo-web.png 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 Binary files differnew file mode 100644 index 0000000..75273ec --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-hdpi/ic_taler_logo.png b/app/src/main/res/mipmap-hdpi/ic_taler_logo.png Binary files differnew file mode 100644 index 0000000..eaecede --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/ic_taler_logo.png 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 Binary files differnew file mode 100644 index 0000000..caa2a3e --- /dev/null +++ b/app/src/main/res/mipmap-hdpi/ic_taler_logo_round.png diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..a450287 --- /dev/null +++ b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-mdpi/ic_taler_logo.png b/app/src/main/res/mipmap-mdpi/ic_taler_logo.png Binary files differnew file mode 100644 index 0000000..e1f7374 --- /dev/null +++ b/app/src/main/res/mipmap-mdpi/ic_taler_logo.png 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 Binary files differnew file mode 100644 index 0000000..e92d2d3 --- /dev/null +++ b/app/src/main/res/mipmap-mdpi/ic_taler_logo_round.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..a5e875c --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xhdpi/ic_taler_logo.png b/app/src/main/res/mipmap-xhdpi/ic_taler_logo.png Binary files differnew file mode 100644 index 0000000..5ca4409 --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/ic_taler_logo.png 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 Binary files differnew file mode 100644 index 0000000..12b9056 --- /dev/null +++ b/app/src/main/res/mipmap-xhdpi/ic_taler_logo_round.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..e9d1fc9 --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xxhdpi/ic_taler_logo.png b/app/src/main/res/mipmap-xxhdpi/ic_taler_logo.png Binary files differnew file mode 100644 index 0000000..a786efa --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/ic_taler_logo.png 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 Binary files differnew file mode 100644 index 0000000..b22a84e --- /dev/null +++ b/app/src/main/res/mipmap-xxhdpi/ic_taler_logo_round.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png Binary files differnew file mode 100644 index 0000000..f8037d1 --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_taler_logo.png b/app/src/main/res/mipmap-xxxhdpi/ic_taler_logo.png Binary files differnew file mode 100644 index 0000000..0e9df6a --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/ic_taler_logo.png 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 Binary files differnew file mode 100644 index 0000000..6bef9bd --- /dev/null +++ b/app/src/main/res/mipmap-xxxhdpi/ic_taler_logo_round.png 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 |