[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[GNUnet-SVN] [taler-wallet-android] branch master updated: withdraw via
From: |
gnunet |
Subject: |
[GNUnet-SVN] [taler-wallet-android] branch master updated: withdraw via QR code, various bug fixes |
Date: |
Sun, 01 Sep 2019 18:33:06 +0200 |
This is an automated email from the git hooks/post-receive script.
dold pushed a commit to branch master
in repository wallet-android.
The following commit(s) were added to refs/heads/master by this push:
new 6f963cd withdraw via QR code, various bug fixes
6f963cd is described below
commit 6f963cdfa0e918dbe35c99dafdb648dfa0586d7b
Author: Florian Dold <address@hidden>
AuthorDate: Sun Sep 1 18:33:03 2019 +0200
withdraw via QR code, various bug fixes
---
app/build.gradle | 2 +-
app/src/main/java/net/taler/wallet/MainActivity.kt | 41 +++--
.../main/java/net/taler/wallet/PromptPayment.kt | 2 +-
.../main/java/net/taler/wallet/PromptWithdraw.kt | 101 +++++++++++
app/src/main/java/net/taler/wallet/ShowBalance.kt | 38 +++--
.../main/java/net/taler/wallet/WalletViewModel.kt | 162 ++++++++++++++----
.../java/net/taler/wallet/WithdrawSuccessful.kt | 26 +++
app/src/main/res/drawable/ic_scan_qr.xml | 186 +++++++++++++++++++++
app/src/main/res/layout/app_bar_main.xml | 10 ++
app/src/main/res/layout/balance_row.xml | 40 +++++
.../main/res/layout/fragment_prompt_withdraw.xml | 79 +++++++++
app/src/main/res/layout/fragment_show_balance.xml | 7 +-
.../res/layout/fragment_withdraw_successful.xml | 53 ++++++
app/src/main/res/navigation/nav_graph.xml | 18 ++
14 files changed, 693 insertions(+), 72 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 0651d3d..4f8ea09 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,7 +8,7 @@ android {
buildToolsVersion "29.0.1"
defaultConfig {
applicationId "net.taler.wallet"
- minSdkVersion 28
+ minSdkVersion 18
targetSdkVersion 29
versionCode 1
versionName "1.0"
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
--
To stop receiving notification emails like this one, please contact
address@hidden.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [GNUnet-SVN] [taler-wallet-android] branch master updated: withdraw via QR code, various bug fixes,
gnunet <=