diff options
author | Florian Dold <florian.dold@gmail.com> | 2019-09-01 18:33:03 +0200 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2019-09-01 18:33:03 +0200 |
commit | 6f963cdfa0e918dbe35c99dafdb648dfa0586d7b (patch) | |
tree | 83ba25c8fa6d7f5eca46f38ba80e5040090436e4 /app/src | |
parent | b9fd051a1bf453e923ddbbf86cf8602d154278e1 (diff) | |
download | wallet-android-6f963cdfa0e918dbe35c99dafdb648dfa0586d7b.tar.gz wallet-android-6f963cdfa0e918dbe35c99dafdb648dfa0586d7b.tar.bz2 wallet-android-6f963cdfa0e918dbe35c99dafdb648dfa0586d7b.zip |
withdraw via QR code, various bug fixes
Diffstat (limited to 'app/src')
-rw-r--r-- | app/src/main/java/net/taler/wallet/MainActivity.kt | 41 | ||||
-rw-r--r-- | app/src/main/java/net/taler/wallet/PromptPayment.kt | 2 | ||||
-rw-r--r-- | app/src/main/java/net/taler/wallet/PromptWithdraw.kt | 101 | ||||
-rw-r--r-- | app/src/main/java/net/taler/wallet/ShowBalance.kt | 38 | ||||
-rw-r--r-- | app/src/main/java/net/taler/wallet/WalletViewModel.kt | 162 | ||||
-rw-r--r-- | app/src/main/java/net/taler/wallet/WithdrawSuccessful.kt | 26 | ||||
-rw-r--r-- | app/src/main/res/drawable/ic_scan_qr.xml | 186 | ||||
-rw-r--r-- | app/src/main/res/layout/app_bar_main.xml | 10 | ||||
-rw-r--r-- | app/src/main/res/layout/balance_row.xml | 40 | ||||
-rw-r--r-- | app/src/main/res/layout/fragment_prompt_withdraw.xml | 79 | ||||
-rw-r--r-- | app/src/main/res/layout/fragment_show_balance.xml | 7 | ||||
-rw-r--r-- | app/src/main/res/layout/fragment_withdraw_successful.xml | 53 | ||||
-rw-r--r-- | app/src/main/res/navigation/nav_graph.xml | 18 |
13 files changed, 692 insertions, 71 deletions
diff --git a/app/src/main/java/net/taler/wallet/MainActivity.kt b/app/src/main/java/net/taler/wallet/MainActivity.kt index 44cd8a6..3d07821 100644 --- a/app/src/main/java/net/taler/wallet/MainActivity.kt +++ b/app/src/main/java/net/taler/wallet/MainActivity.kt @@ -18,6 +18,7 @@ import androidx.lifecycle.ViewModelProviders import androidx.navigation.findNavController import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.setupWithNavController +import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.navigation.NavigationView import com.google.android.material.snackbar.Snackbar import com.google.zxing.integration.android.IntentIntegrator @@ -41,6 +42,14 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte navView.menu.getItem(0).isChecked = true + val fab: FloatingActionButton = findViewById(R.id.fab) + fab.setOnClickListener { + val integrator = IntentIntegrator(this) + integrator.setPrompt("Place merchant's QR Code inside the viewfinder rectangle to initiate payment.") + integrator.initiateScan(listOf("QR_CODE")) + } + fab.hide() + navView.setNavigationItemSelectedListener(this) @@ -166,20 +175,26 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte } val url = scanResult.contents!! - if (!url.startsWith("talerpay:")) { - val bar: Snackbar = Snackbar.make( - findViewById(R.id.nav_host_fragment), - "Scanned QR code doesn't contain Taler payment.", - Snackbar.LENGTH_SHORT - ) - bar.show() - return + when { + url.startsWith("taler://pay") -> { + Log.v(TAG, "navigating!") + findNavController(R.id.nav_host_fragment).navigate(R.id.action_showBalance_to_promptPayment) + model.preparePay(url) + } + url.startsWith("taler://withdraw") -> { + Log.v(TAG, "navigating!") + findNavController(R.id.nav_host_fragment).navigate(R.id.action_showBalance_to_promptWithdraw) + model.getWithdrawalInfo(url) + } + else -> { + val bar: Snackbar = Snackbar.make( + findViewById(R.id.nav_host_fragment), + "Scanned QR code doesn't contain Taler payment.", + Snackbar.LENGTH_SHORT + ) + bar.show() + } } - - Log.v(TAG, "navigating!") - - findNavController(R.id.nav_host_fragment).navigate(R.id.action_showBalance_to_promptPayment) - model.preparePay(url) } diff --git a/app/src/main/java/net/taler/wallet/PromptPayment.kt b/app/src/main/java/net/taler/wallet/PromptPayment.kt index 07d3dd2..1d828e3 100644 --- a/app/src/main/java/net/taler/wallet/PromptPayment.kt +++ b/app/src/main/java/net/taler/wallet/PromptPayment.kt @@ -32,7 +32,7 @@ class PromptPayment : Fragment() { var fragmentView: View? = null - fun triggerLoading() { + private fun triggerLoading() { val loading = model.payStatus.value == null || (model.payStatus.value is PayStatus.Loading) val myActivity = activity!! val progressBar = myActivity.findViewById<MaterialProgressBar>(R.id.progress_bar) diff --git a/app/src/main/java/net/taler/wallet/PromptWithdraw.kt b/app/src/main/java/net/taler/wallet/PromptWithdraw.kt new file mode 100644 index 0000000..785da42 --- /dev/null +++ b/app/src/main/java/net/taler/wallet/PromptWithdraw.kt @@ -0,0 +1,101 @@ +package net.taler.wallet + +import android.annotation.SuppressLint +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProviders +import androidx.navigation.findNavController +import com.google.android.material.snackbar.Snackbar +import me.zhanghai.android.materialprogressbar.MaterialProgressBar + + +class PromptWithdraw : Fragment() { + + private lateinit var model: WalletViewModel + + private fun triggerLoading() { + val loading = + model.withdrawStatus.value is WithdrawStatus.Loading || model.withdrawStatus.value is WithdrawStatus.Withdrawing + val myActivity = activity!! + val progressBar = myActivity.findViewById<MaterialProgressBar>(R.id.progress_bar) + if (loading) { + progressBar.visibility = View.VISIBLE + } else { + progressBar.visibility = View.INVISIBLE + } + } + + private fun showWithdrawStatus(view: View, status: WithdrawStatus) { + val confirmButton = view.findViewById<Button>(R.id.button_confirm_withdraw) + val promptWithdraw = view.findViewById<View>(R.id.prompt_withdraw) + when (status) { + is WithdrawStatus.ReceivedDetails -> { + promptWithdraw.visibility = View.VISIBLE + confirmButton.isEnabled = true + val promptWithdraw = view.findViewById<View>(R.id.prompt_withdraw) + promptWithdraw.visibility = View.VISIBLE + val amountView = view.findViewById<TextView>(R.id.withdraw_amount) + val exchangeView = view.findViewById<TextView>(R.id.withdraw_exchange) + exchangeView.text = status.suggestedExchange + @SuppressLint("SetTextI18n") + amountView.text = "${status.amount.amount} ${status.amount.currency}" + } + is WithdrawStatus.Success -> { + this.model.withdrawStatus.value = WithdrawStatus.None() + activity!!.findNavController(R.id.nav_host_fragment) + .navigate(R.id.action_promptWithdraw_to_withdrawSuccessful) + } + is WithdrawStatus.Loading -> { + promptWithdraw.visibility = View.INVISIBLE + // Wait + } + is WithdrawStatus.Withdrawing -> { + confirmButton.isEnabled = false + + } + is WithdrawStatus.None -> { + + } + else -> { + val bar = Snackbar.make(view, "Bug: Unexpected result", Snackbar.LENGTH_SHORT) + bar.show() + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + model = activity?.run { + ViewModelProviders.of(this)[WalletViewModel::class.java] + } ?: throw Exception("Invalid Activity") + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val view = inflater.inflate(R.layout.fragment_prompt_withdraw, container, false) + + this.model.withdrawStatus.observe(this, Observer { + triggerLoading() + showWithdrawStatus(view, it) + }) + + view.findViewById<Button>(R.id.button_confirm_withdraw).setOnClickListener { + val status = this.model.withdrawStatus.value + if (status !is WithdrawStatus.ReceivedDetails) { + return@setOnClickListener + } + model.acceptWithdrawal(status.talerWithdrawUri, status.suggestedExchange) + } + + return view + } +} diff --git a/app/src/main/java/net/taler/wallet/ShowBalance.kt b/app/src/main/java/net/taler/wallet/ShowBalance.kt index ba1bf14..a45d4fa 100644 --- a/app/src/main/java/net/taler/wallet/ShowBalance.kt +++ b/app/src/main/java/net/taler/wallet/ShowBalance.kt @@ -1,7 +1,6 @@ package net.taler.wallet -import android.app.Activity import android.os.Bundle import android.util.Log import androidx.fragment.app.Fragment @@ -15,7 +14,6 @@ import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.snackbar.Snackbar import com.google.zxing.integration.android.IntentIntegrator import me.zhanghai.android.materialprogressbar.MaterialProgressBar @@ -35,10 +33,22 @@ class MyAdapter(private var myDataset: WalletBalances) : RecyclerView.Adapter<My } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { + val amount = myDataset.byCurrency[position].available + val amountIncoming = myDataset.byCurrency[position].pendingIncoming val currencyView = holder.rowView.findViewById<TextView>(R.id.balance_currency) - currencyView.text = myDataset.byCurrency[position].currency + currencyView.text = amount.currency val amountView = holder.rowView.findViewById<TextView>(R.id.balance_amount) - amountView.text = myDataset.byCurrency[position].amount + amountView.text = amount.amount + + val amountIncomingRow = holder.rowView.findViewById<View>(R.id.balance_row_pending) + + val amountIncomingView = holder.rowView.findViewById<TextView>(R.id.balance_pending) + if (amountIncoming.isZero()) { + amountIncomingRow.visibility = View.GONE + } else { + amountIncomingRow.visibility = View.VISIBLE + amountIncomingView.text = "${amountIncoming.amount} ${amountIncoming.currency}" + } } fun update(updatedBalances: WalletBalances) { @@ -63,7 +73,7 @@ class ShowBalance : Fragment() { fun triggerLoading() { val loading: Boolean = - (model.isBalanceLoading.value == true) || (model.balances.value == null) || !model.balances.value!!.initialized + (model.testWithdrawalInProgress.value == true) || (model.balances.value == null) || !model.balances.value!!.initialized val myActivity = activity!! val progressBar = myActivity.findViewById<MaterialProgressBar>(R.id.progress_bar) @@ -87,11 +97,6 @@ class ShowBalance : Fragment() { ViewModelProviders.of(this)[WalletViewModel::class.java] } ?: throw Exception("Invalid Activity") - - model.isBalanceLoading.observe(this, Observer { loading -> - Log.v("taler-wallet", "observing balance loading ${loading} in show balance") - triggerLoading() - }) } @@ -129,13 +134,6 @@ class ShowBalance : Fragment() { model.withdrawTestkudos() } - val payNfcButton = view.findViewById<Button>(R.id.button_pay_nfc) - payNfcButton.setOnClickListener { - val bar: Snackbar = Snackbar.make(view, "Sorry, NFC is not implemented yet!", Snackbar.LENGTH_SHORT) - bar.show() - } - - this.balancesView = view.findViewById(R.id.list_balances) this.balancesPlaceholderView = view.findViewById(R.id.list_balances_placeholder) @@ -159,6 +157,12 @@ class ShowBalance : Fragment() { updateBalances(it) }) + model.testWithdrawalInProgress.observe(this, Observer { loading -> + Log.v("taler-wallet", "observing balance loading ${loading} in show balance") + withdrawTestkudosButton.isEnabled = !loading + triggerLoading() + }) + return view } } diff --git a/app/src/main/java/net/taler/wallet/WalletViewModel.kt b/app/src/main/java/net/taler/wallet/WalletViewModel.kt index f644c8d..356eb8a 100644 --- a/app/src/main/java/net/taler/wallet/WalletViewModel.kt +++ b/app/src/main/java/net/taler/wallet/WalletViewModel.kt @@ -9,13 +9,16 @@ import android.util.Log import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import org.json.JSONObject +import java.io.File import java.io.InputStream import java.lang.Exception -import java.nio.file.Paths val TAG = "taler-wallet" -class AssetModuleLoader(private val assetManager: AssetManager, private val rootPath: String = "node_modules") : +class AssetModuleLoader( + private val assetManager: AssetManager, + private val rootPath: String = "node_modules" +) : AkonoJni.LoadModuleHandler { private fun makeResult(localPath: String, stream: InputStream): ModuleResult { @@ -26,7 +29,8 @@ class AssetModuleLoader(private val assetManager: AssetManager, private val root } private fun tryPath(rawAssetPath: String): ModuleResult? { - val assetPath = Paths.get(rawAssetPath).normalize().toString() + //val assetPath = Paths.get(rawAssetPath).normalize().toString() + val assetPath = File(rawAssetPath).normalize().path try { val moduleStream = assetManager.open(assetPath) return makeResult(assetPath, moduleStream) @@ -54,13 +58,15 @@ class AssetModuleLoader(private val assetManager: AssetManager, private val root } Log.i(TAG, "main field is $mainFile") try { - val modPath = Paths.get("$assetPath/$mainFile").normalize().toString() + //val modPath = Paths.get("$assetPath/$mainFile").normalize().toString() + val modPath = File("$assetPath/$mainFile").normalize().path return makeResult(modPath, assetManager.open(modPath)) } catch (e: Exception) { // ignore } try { - val modPath = Paths.get("$assetPath/$mainFile.js").normalize().toString() + //val modPath = Paths.get("$assetPath/$mainFile.js").normalize().toString() + val modPath = File("$assetPath/$mainFile.js").normalize().path return makeResult(modPath, assetManager.open(modPath)) } catch (e: Exception) { } @@ -105,7 +111,8 @@ class AssetDataHandler(private val assetManager: AssetManager) : AkonoJni.GetDat override fun handleGetData(what: String): ByteArray? { if (what == "taler-emscripten-lib.wasm") { Log.i(TAG, "loading emscripten binary from taler-wallet") - val stream = assetManager.open("node_modules/taler-wallet/emscripten/taler-emscripten-lib.wasm") + val stream = + assetManager.open("node_modules/taler-wallet/emscripten/taler-emscripten-lib.wasm") val bytes: ByteArray = stream.readBytes() Log.i(TAG, "size of emscripten binary: ${bytes.size}") return bytes @@ -117,16 +124,24 @@ class AssetDataHandler(private val assetManager: AssetManager) : AkonoJni.GetDat } data class Amount(val currency: String, val amount: String) { + fun isZero(): Boolean { + return amount.toDouble() == 0.0 + } + companion object { - val FRACTIONAL_BASE = 1e8; + const val FRACTIONAL_BASE = 1e8; fun fromJson(jsonAmount: JSONObject): Amount { val amountCurrency = jsonAmount.getString("currency") val amountValue = jsonAmount.getString("value") val amountFraction = jsonAmount.getString("fraction") val amountIntValue = Integer.parseInt(amountValue) val amountIntFraction = Integer.parseInt(amountFraction) - return Amount(amountCurrency, (amountIntValue + amountIntFraction / FRACTIONAL_BASE).toString()) + return Amount( + amountCurrency, + (amountIntValue + amountIntFraction / FRACTIONAL_BASE).toString() + ) } + fun fromString(strAmount: String): Amount { val components = strAmount.split(":") return Amount(components[0], components[1]) @@ -134,40 +149,60 @@ data class Amount(val currency: String, val amount: String) { } } +data class BalanceEntry(val available: Amount, val pendingIncoming: Amount) -data class WalletBalances(val initialized: Boolean, val byCurrency: List<Amount>) + +data class WalletBalances(val initialized: Boolean, val byCurrency: List<BalanceEntry>) data class ContractTerms(val summary: String, val amount: Amount) open class PayStatus { class None : PayStatus() class Loading : PayStatus() - data class Prepared(val contractTerms: ContractTerms, val proposalId: Int, val totalFees: Amount) : PayStatus() + data class Prepared( + val contractTerms: ContractTerms, + val proposalId: Int, + val totalFees: Amount + ) : PayStatus() + data class InsufficientBalance(val contractTerms: ContractTerms) : PayStatus() data class AlreadyPaid(val contractTerms: ContractTerms) : PayStatus() data class Error(val error: String) : PayStatus() class Success : PayStatus() } +open class WithdrawStatus { + class None : WithdrawStatus() + data class Loading(val talerWithdrawUri: String) : WithdrawStatus() + class Success : WithdrawStatus() + data class ReceivedDetails( + val talerWithdrawUri: String, + val amount: Amount, + val suggestedExchange: String + ) : WithdrawStatus() + + data class Withdrawing(val talerWithdrawUri: String) : WithdrawStatus() +} + class WalletViewModel(val app: Application) : AndroidViewModel(app) { private lateinit var myAkono: AkonoJni private var initialized = false - private var withdrawInProgress: Int = 0 - - val balances: MutableLiveData<WalletBalances> = MutableLiveData() - - val isBalanceLoading: MutableLiveData<Boolean> = MutableLiveData() + val testWithdrawalInProgress: MutableLiveData<Boolean> = MutableLiveData<Boolean>().apply { + value = false + } - //val isProposalLoading: MutableLiveData<Boolean> = MutableLiveData() + val balances: MutableLiveData<WalletBalances> = MutableLiveData<WalletBalances>().apply { + value = WalletBalances(false, listOf()) + } - val payStatus: MutableLiveData<PayStatus> = MutableLiveData() + val payStatus: MutableLiveData<PayStatus> = MutableLiveData<PayStatus>().apply { + value = PayStatus.None() + } - init { - isBalanceLoading.value = false - balances.value = WalletBalances(false, listOf()) - payStatus.value = PayStatus.None() + val withdrawStatus: MutableLiveData<WithdrawStatus> = MutableLiveData<WithdrawStatus>().apply { + value = WithdrawStatus.None() } fun init() { @@ -201,23 +236,50 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) { Log.v(TAG, "got response for operation $operation") when (operation) { "withdrawTestkudos" -> { - withdrawInProgress-- - if (withdrawInProgress == 0) { - isBalanceLoading.postValue(false) - } + testWithdrawalInProgress.postValue(false) } "getBalances" -> { - val balanceList = mutableListOf<Amount>(); + val balanceList = mutableListOf<BalanceEntry>(); val result = message.getJSONObject("result") val byCurrency = result.getJSONObject("byCurrency") val currencyList = byCurrency.keys().asSequence().toList().sorted() for (currency in currencyList) { - val jsonAmount = byCurrency.getJSONObject(currency).getJSONObject("available") + val jsonAmount = byCurrency.getJSONObject(currency) + .getJSONObject("available") val amount = Amount.fromJson(jsonAmount) - balanceList.add(amount) + val jsonAmountIncoming = byCurrency.getJSONObject(currency) + .getJSONObject("pendingIncoming") + val amountIncoming = Amount.fromJson(jsonAmountIncoming) + balanceList.add(BalanceEntry(amount, amountIncoming)) } balances.postValue(WalletBalances(true, balanceList)) } + "getWithdrawalInfo" -> { + Log.v(TAG, "got getWithdrawalInfo result") + val status = withdrawStatus.value + if (status !is WithdrawStatus.Loading) { + Log.v(TAG, "ignoring withdrawal info result, not loading.") + return + } + val result = message.getJSONObject("result") + val suggestedExchange = result.getString("suggestedExchange") + val amount = Amount.fromJson(result.getJSONObject("amount")) + withdrawStatus.postValue( + WithdrawStatus.ReceivedDetails( + status.talerWithdrawUri, + amount, + suggestedExchange + ) + ) + } + "acceptWithdrawal" -> { + Log.v(TAG, "got acceptWithdrawal result") + val status = withdrawStatus.value + if (status !is WithdrawStatus.Withdrawing) { + Log.v(TAG, "ignoring acceptWithdrawal result, invalid state") + } + withdrawStatus.postValue(WithdrawStatus.Success()) + } "preparePay" -> { Log.v(TAG, "got preparePay result") val result = message.getJSONObject("result") @@ -238,9 +300,15 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) { totalFees = Amount.fromJson(result.getJSONObject("totalFees")) } val res = when (status) { - "payment-possible" -> PayStatus.Prepared(contractTerms!!, proposalId!!, totalFees!!) + "payment-possible" -> PayStatus.Prepared( + contractTerms!!, + proposalId!!, + totalFees!! + ) "paid" -> PayStatus.AlreadyPaid(contractTerms!!) - "insufficient-balance" -> PayStatus.InsufficientBalance(contractTerms!!) + "insufficient-balance" -> PayStatus.InsufficientBalance( + contractTerms!! + ) "error" -> PayStatus.Error("got some error") else -> PayStatus.Error("unkown status") } @@ -257,6 +325,7 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) { }) myAkono.evalNodeCode("console.log('hello world from taler wallet-android')") + myAkono.evalNodeCode("require('source-map-support').install();") myAkono.evalNodeCode("tw = require('taler-wallet');") myAkono.evalNodeCode("tw.installAndroidWalletListener();") @@ -296,8 +365,7 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) { return } - withdrawInProgress++ - this.isBalanceLoading.value = true + testWithdrawalInProgress.value = true val msg = JSONObject() msg.put("operation", "withdrawTestkudos") @@ -337,7 +405,7 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) { sendInitMessage() - isBalanceLoading.value = false + testWithdrawalInProgress.value = false balances.value = WalletBalances(false, listOf()) getBalances() @@ -365,6 +433,32 @@ class WalletViewModel(val app: Application) : AndroidViewModel(app) { msg.put("args", respJson) myAkono.sendMessage(msg.toString()) + } + + fun getWithdrawalInfo(talerWithdrawUri: String) { + val msg = JSONObject() + msg.put("operation", "getWithdrawalInfo") + + val args = JSONObject() + msg.put("args", args) + args.put("talerWithdrawUri", talerWithdrawUri) + + withdrawStatus.value = WithdrawStatus.Loading(talerWithdrawUri) + + myAkono.sendMessage(msg.toString()) + } + + fun acceptWithdrawal(talerWithdrawUri: String, selectedExchange: String) { + val msg = JSONObject() + msg.put("operation", "acceptWithdrawal") + val args = JSONObject() + msg.put("args", args) + args.put("talerWithdrawUri", talerWithdrawUri) + args.put("selectedExchange", selectedExchange) + + withdrawStatus.value = WithdrawStatus.Withdrawing(talerWithdrawUri) + + myAkono.sendMessage(msg.toString()) } -}
\ No newline at end of file +} diff --git a/app/src/main/java/net/taler/wallet/WithdrawSuccessful.kt b/app/src/main/java/net/taler/wallet/WithdrawSuccessful.kt new file mode 100644 index 0000000..c16dced --- /dev/null +++ b/app/src/main/java/net/taler/wallet/WithdrawSuccessful.kt @@ -0,0 +1,26 @@ +package net.taler.wallet + + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import androidx.navigation.findNavController + +/** + * A simple [Fragment] subclass. + */ +class WithdrawSuccessful : Fragment() { + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val view = inflater.inflate(R.layout.fragment_withdraw_successful, container, false) + view.findViewById<Button>(R.id.button_success_back).setOnClickListener { + activity!!.findNavController(R.id.nav_host_fragment).navigateUp() + } + return view + } +} diff --git a/app/src/main/res/drawable/ic_scan_qr.xml b/app/src/main/res/drawable/ic_scan_qr.xml new file mode 100644 index 0000000..c99daff --- /dev/null +++ b/app/src/main/res/drawable/ic_scan_qr.xml @@ -0,0 +1,186 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="278dp" + android:height="278dp" + android:viewportWidth="278" + android:viewportHeight="278"> + <path + android:pathData="M103,28l0,77 -76,0 0,-77 76,0zM93,38l-56,0 0,57 56,0 0,-57z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M251,28l0,77 -76,0 0,-77 76,0zM241,38l-56,0 0,57 56,0 0,-57z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M103,174l0,76 -76,0 0,-76 76,0zM93,184l-56,0 0,56 56,0 0,-56z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M157,240l0,-12l-10,0l0,12l-12,0l0,-21l20,0l0,-10l-33,0l0,-9l-10,0l1,19l12,0l0,11l-13,0l0,10l13,0l0,10l42,0l0,-10z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M38,123l-11,0l0,-10l11,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M123,69l-10,0l0,-10l10,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M124,39l-11,0l0,-10l11,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M222,215l-10,0l0,-10l10,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M50,144l10,0l0,-10l-10,0l0,-10l-10,0l0,9l-12,0l0,24l12,0l0,8l31,0l0,-19l-11,0l0,9l-10,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M242,113l0,10 9,0 0,10 -16,0 0,13 -29,0 0,9 16,0 0,23 -11,0 0,-13 -23,0 0,-10 8,0 0,-9 -6,0 0,-10 33,0 0,-18 -13,0 0,-5c10,0 21,0 32,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M121,123l-31,0l0,10l-21,0l0,-10l-11,0l0,-10l63,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M123,74l-10,0l0,17l0,17l10,0l11,0l0,-17l-11,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M134,85l11,0l0,18l13,0l0,-12l8,0l0,-16l-32,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M153,44l-28,0 0,11 18,0 0,11 13,0 0,-11 11,0c0,-9 0,-18 0,-27l-24,0 0,11 10,0 0,5z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M138,70l-10,0l0,-10l10,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M112,139l-10,0l0,-10l10,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M157,183l-11,0l0,-18l11,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M140,180l-10,0l0,-10l10,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M206,189l-10,0l0,-20l10,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M241,240l-18,0l0,10l28,0l0,-26l-10,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M183,237l-11,0l0,-18l11,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M123,187l-10,0l0,-23l10,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M172,196l-15,0l0,-10l-32,0l0,10l23,0l0,10l12,0l0,11l46,0l0,-11l-13,0l0,-10l-11,0l0,10l-10,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M185,179l-13,0l0,11l-10,0l0,-21l23,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M198,220l10,0 0,20 9,0 0,10c-16,0 -13,0 -31,0l0,-10 12,0 0,-20z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M118,155l-30,0l0,10l-10,0l0,-13l0,0l0,-7l40,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M136,123l0,9l21,0l0,-9l13,0l0,-10l-43,0l0,10z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M145,147l-22,0l0,-10l22,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M158,150l-11,0l0,10l22,0l0,-23l-11,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M251,184l-6,0l0,8l-10,0l0,-8l-5,0l0,-22l21,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M222,194l-11,0l0,-10l11,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M228,217c7,0 15,0 23,0l0,-19 -10,0 0,9 -13,0 0,10z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M237,236l-11,0l0,-10l11,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M82,82l-34,0l0,-32l34,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M230,82l-34,0l0,-32l34,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M82,228l-34,0l0,-32l34,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M183,250l-11,0l0,-10l11,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M140,162l-11,0l0,-10l11,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M218,132l-10,0l0,-10l10,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M193,123l-11,0l0,-10l11,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M251,159l-10,0l0,-10l10,0z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M0,53l0,-53l54,0l0,10l-44,0l0,43z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M278,53l0,-53l-54,0l0,10l44,0l0,43z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M0,225l0,53l54,0l0,-10l-44,0l0,-43z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> + <path + android:pathData="M278,225l0,53l-54,0l0,-10l44,0l0,-43z" + android:fillColor="#FFFFFF" + android:fillType="nonZero"/> +</vector> diff --git a/app/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/app_bar_main.xml index 0e95cb0..7bf5acd 100644 --- a/app/src/main/res/layout/app_bar_main.xml +++ b/app/src/main/res/layout/app_bar_main.xml @@ -38,4 +38,14 @@ <include layout="@layout/content_main" android:id="@+id/include"/> + <com.google.android.material.floatingactionbutton.FloatingActionButton + android:id="@+id/fab" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="bottom|end" + android:layout_margin="@dimen/fab_margin" + app:fabSize="normal" + android:scaleType="center" + app:srcCompat="@drawable/ic_scan_qr" /> + </androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/balance_row.xml b/app/src/main/res/layout/balance_row.xml index 75f143c..9a81489 100644 --- a/app/src/main/res/layout/balance_row.xml +++ b/app/src/main/res/layout/balance_row.xml @@ -28,4 +28,44 @@ </LinearLayout> + <LinearLayout android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:id="@+id/balance_row_pending"> + + <Space android:layout_width="5sp" + android:layout_height="match_parent"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="+" + android:textColor="#006600"> + </TextView> + + <Space android:layout_width="5sp" + android:layout_height="match_parent"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/balance_pending" + android:textColor="#006600" + android:textSize="20sp" tools:text="10 TESTKUDOS"> + </TextView> + + <Space android:layout_width="5sp" + android:layout_height="match_parent"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="inbound" + android:textColor="#006600"> + </TextView> + + </LinearLayout> + + + </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/fragment_prompt_withdraw.xml b/app/src/main/res/layout/fragment_prompt_withdraw.xml new file mode 100644 index 0000000..aa3c860 --- /dev/null +++ b/app/src/main/res/layout/fragment_prompt_withdraw.xml @@ -0,0 +1,79 @@ +<?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=".PromptWithdraw"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/prompt_withdraw"> + + <Space android:layout_width="match_parent" + android:layout_height="15dp" + android:layout_weight="1"/> + + <TextView + android:text="Do you want to withdraw" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:id="@+id/textView2"/> + + <TextView + android:text="(amount)" + tools:text="10.00 KUDOS" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="25sp" + android:layout_gravity="center" + android:id="@+id/withdraw_amount"/> + + <Space android:layout_width="match_parent" + android:layout_height="25dp"/> + + + <TextView + android:text="Using the exchange provider" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:id="@+id/textView3"/> + <TextView + android:text="(exchange base url)" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="25sp" + android:layout_gravity="center" + android:id="@+id/withdraw_exchange"/> + + <Space android:layout_width="match_parent" + android:layout_height="15dp" + android:layout_weight="1"/> + + + <Space android:layout_width="match_parent" + android:layout_height="15dp" + android:layout_weight="1"/> + + <LinearLayout android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <Button android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="Cancel" + android:id="@+id/button_cancel_withdraw"/> + + <Space android:layout_width="15dp" android:layout_height="match_parent" + android:layout_weight="1"/> + + <Button android:layout_width="wrap_content" android:layout_height="wrap_content" + android:id="@+id/button_confirm_withdraw" + android:text="Confirm Withdraw"/> + </LinearLayout> + + </LinearLayout> + +</FrameLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/fragment_show_balance.xml b/app/src/main/res/layout/fragment_show_balance.xml index e4d2e81..b93d2c2 100644 --- a/app/src/main/res/layout/fragment_show_balance.xml +++ b/app/src/main/res/layout/fragment_show_balance.xml @@ -38,14 +38,9 @@ android:layout_height="wrap_content" android:id="@+id/button_withdraw_testkudos"/> <Button - android:text="Pay via QR Code" + android:text="Scan Taler QR Code" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button_pay_qr"/> - <Button - android:text="Pay via NFC" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/button_pay_nfc"/> </LinearLayout> </androidx.core.widget.NestedScrollView> diff --git a/app/src/main/res/layout/fragment_withdraw_successful.xml b/app/src/main/res/layout/fragment_withdraw_successful.xml new file mode 100644 index 0000000..717ca20 --- /dev/null +++ b/app/src/main/res/layout/fragment_withdraw_successful.xml @@ -0,0 +1,53 @@ +<?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="10dp" + tools:context=".WithdrawSuccessful"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <Space + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="50dp" + android:layout_gravity="center" + android:autoSizeTextType="uniform" + android:text="Withdrawal confirmed." + android:textAlignment="center" + android:textColor="@android:color/holo_green_dark" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="50dp" + android:layout_gravity="center" + android:text="Your bank will now ask you to approve a transfer to the selected change. After you've confirmed the transfer with your bank, the digital cash will show in this wallet." + android:textAlignment="center" /> + + <Space + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> + + + <Space + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1" /> + + <Button + android:id="@+id/button_success_back" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Go Back" /> + + </LinearLayout> +</FrameLayout>
\ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 80a94ec..2f9787b 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -7,6 +7,9 @@ <fragment android:id="@+id/showBalance" android:name="net.taler.wallet.ShowBalance" android:label="Balances" tools:layout="@layout/fragment_show_balance"> <action android:id="@+id/action_showBalance_to_promptPayment" app:destination="@id/promptPayment"/> + <action + android:id="@+id/action_showBalance_to_promptWithdraw" + app:destination="@id/promptWithdraw" /> </fragment> <fragment android:id="@+id/promptPayment" android:name="net.taler.wallet.PromptPayment" android:label="Review Payment" tools:layout="@layout/fragment_prompt_payment"> @@ -28,4 +31,19 @@ android:name="net.taler.wallet.AlreadyPaid" android:label="Already Paid" tools:layout="@layout/fragment_already_paid" /> + <fragment + android:id="@+id/promptWithdraw" + android:name="net.taler.wallet.PromptWithdraw" + android:label="Withdraw Digital Cash" + tools:layout="@layout/fragment_prompt_withdraw" > + <action + android:id="@+id/action_promptWithdraw_to_withdrawSuccessful" + app:destination="@id/withdrawSuccessful" + app:popUpTo="@id/showBalance"/> + </fragment> + <fragment + android:id="@+id/withdrawSuccessful" + android:name="net.taler.wallet.WithdrawSuccessful" + android:label="Withdrawal Confirmed" + tools:layout="@layout/fragment_withdraw_successful" /> </navigation>
\ No newline at end of file |