diff options
Diffstat (limited to 'wallet/src/main/java/net/taler')
23 files changed, 498 insertions, 310 deletions
diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt b/wallet/src/main/java/net/taler/wallet/MainActivity.kt index 838ed2d..0605976 100644 --- a/wallet/src/main/java/net/taler/wallet/MainActivity.kt +++ b/wallet/src/main/java/net/taler/wallet/MainActivity.kt @@ -47,8 +47,6 @@ import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_SHORT import com.google.android.material.snackbar.Snackbar import com.google.zxing.integration.android.IntentIntegrator import com.google.zxing.integration.android.IntentIntegrator.parseActivityResult -import kotlinx.android.synthetic.main.activity_main.* -import kotlinx.android.synthetic.main.app_bar_main.* import net.taler.common.isOnline import net.taler.wallet.BuildConfig.VERSION_CODE import net.taler.wallet.BuildConfig.VERSION_NAME @@ -56,6 +54,7 @@ import net.taler.wallet.HostCardEmulatorService.Companion.HTTP_TUNNEL_RESPONSE import net.taler.wallet.HostCardEmulatorService.Companion.MERCHANT_NFC_CONNECTED import net.taler.wallet.HostCardEmulatorService.Companion.MERCHANT_NFC_DISCONNECTED import net.taler.wallet.HostCardEmulatorService.Companion.TRIGGER_PAYMENT_ACTION +import net.taler.wallet.databinding.ActivityMainBinding import net.taler.wallet.refund.RefundStatus import java.util.Locale.ROOT @@ -64,35 +63,37 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, private val model: MainViewModel by viewModels() + private lateinit var ui: ActivityMainBinding private lateinit var nav: NavController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) + ui = ActivityMainBinding.inflate(layoutInflater) + setContentView(ui.root) val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment nav = navHostFragment.navController - nav_view.setupWithNavController(nav) - nav_view.setNavigationItemSelectedListener(this) + ui.navView.setupWithNavController(nav) + ui.navView.setNavigationItemSelectedListener(this) if (savedInstanceState == null) { - nav_view.menu.getItem(0).isChecked = true + ui.navView.menu.getItem(0).isChecked = true } - setSupportActionBar(toolbar) + setSupportActionBar(ui.content.toolbar) val appBarConfiguration = AppBarConfiguration( setOf(R.id.nav_main, R.id.nav_settings, R.id.nav_pending_operations), - drawer_layout + ui.drawerLayout ) - toolbar.setupWithNavController(nav, appBarConfiguration) + ui.content.toolbar.setupWithNavController(nav, appBarConfiguration) - model.showProgressBar.observe(this, Observer { show -> - progress_bar.visibility = if (show) VISIBLE else INVISIBLE + model.showProgressBar.observe(this, { show -> + ui.content.progressBar.visibility = if (show) VISIBLE else INVISIBLE }) - val versionView: TextView = nav_view.getHeaderView(0).findViewById(R.id.versionView) - model.devMode.observe(this, Observer { enabled -> - nav_view.menu.findItem(R.id.nav_dev).isVisible = enabled + val versionView: TextView = ui.navView.getHeaderView(0).findViewById(R.id.versionView) + model.devMode.observe(this, { enabled -> + ui.navView.menu.findItem(R.id.nav_dev).isVisible = enabled if (enabled) { @SuppressLint("SetTextI18n") versionView.text = "$VERSION_NAME ($VERSION_CODE)" @@ -113,7 +114,7 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, } override fun onBackPressed() { - if (drawer_layout.isDrawerOpen(START)) drawer_layout.closeDrawer(START) + if (ui.drawerLayout.isDrawerOpen(START)) ui.drawerLayout.closeDrawer(START) else super.onBackPressed() } @@ -123,7 +124,7 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, R.id.nav_settings -> nav.navigate(R.id.nav_settings) R.id.nav_pending_operations -> nav.navigate(R.id.nav_pending_operations) } - drawer_layout.closeDrawer(START) + ui.drawerLayout.closeDrawer(START) return true } @@ -167,7 +168,7 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, } else -> { Snackbar.make( - nav_view, + ui.navView, "URL from $from doesn't contain a supported Taler Uri.", LENGTH_SHORT ).show() @@ -179,13 +180,13 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener, model.showProgressBar.value = false when (status) { is RefundStatus.Error -> { - Snackbar.make(nav_view, R.string.refund_error, LENGTH_LONG).show() + Snackbar.make(ui.navView, R.string.refund_error, LENGTH_LONG).show() } is RefundStatus.Success -> { val amount = status.response.amountRefundGranted model.showTransactions(amount.currency) val str = getString(R.string.refund_success, amount.amountStr) - Snackbar.make(nav_view, str, LENGTH_LONG).show() + Snackbar.make(ui.navView, str, LENGTH_LONG).show() } } } diff --git a/wallet/src/main/java/net/taler/wallet/MainFragment.kt b/wallet/src/main/java/net/taler/wallet/MainFragment.kt index d5bd3fc..1479bc0 100644 --- a/wallet/src/main/java/net/taler/wallet/MainFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/MainFragment.kt @@ -22,14 +22,13 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer import androidx.navigation.fragment.findNavController -import kotlinx.android.synthetic.main.fragment_main.* import net.taler.common.EventObserver import net.taler.wallet.CurrencyMode.MULTI import net.taler.wallet.CurrencyMode.SINGLE import net.taler.wallet.balances.BalanceItem import net.taler.wallet.balances.BalancesFragment +import net.taler.wallet.databinding.FragmentMainBinding import net.taler.wallet.transactions.TransactionsFragment enum class CurrencyMode { SINGLE, MULTI } @@ -39,16 +38,19 @@ class MainFragment : Fragment() { private val model: MainViewModel by activityViewModels() private var currencyMode: CurrencyMode? = null + private lateinit var ui: FragmentMainBinding + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_main, container, false) + ui = FragmentMainBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - model.balances.observe(viewLifecycleOwner, Observer { + model.balances.observe(viewLifecycleOwner, { onBalancesChanged(it) }) model.transactionsEvent.observe(viewLifecycleOwner, EventObserver { currency -> @@ -59,10 +61,10 @@ class MainFragment : Fragment() { } }) - mainFab.setOnClickListener { + ui.mainFab.setOnClickListener { scanQrCode(requireActivity()) } - mainFab.setOnLongClickListener { + ui.mainFab.setOnLongClickListener { findNavController().navigate(R.id.action_nav_main_to_nav_uri_input) true } diff --git a/wallet/src/main/java/net/taler/wallet/UriInputFragment.kt b/wallet/src/main/java/net/taler/wallet/UriInputFragment.kt index eaa6d16..d17977b 100644 --- a/wallet/src/main/java/net/taler/wallet/UriInputFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/UriInputFragment.kt @@ -27,40 +27,43 @@ import android.view.ViewGroup import android.widget.Toast import android.widget.Toast.LENGTH_LONG import androidx.fragment.app.Fragment -import kotlinx.android.synthetic.main.fragment_uri_input.* +import net.taler.wallet.databinding.FragmentUriInputBinding class UriInputFragment : Fragment() { + private lateinit var ui: FragmentUriInputBinding + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_uri_input, container, false) + ui = FragmentUriInputBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val clipboard = requireContext().getSystemService(ClipboardManager::class.java)!! - pasteButton.setOnClickListener { + ui.pasteButton.setOnClickListener { val item = clipboard.primaryClip?.getItemAt(0) if (item?.text != null) { - uriView.setText(item.text) + ui.uriView.setText(item.text) } else { if (item?.uri != null) { - uriView.setText(item.uri.toString()) + ui.uriView.setText(item.uri.toString()) } else { Toast.makeText(requireContext(), R.string.paste_invalid, LENGTH_LONG).show() } } } - okButton.setOnClickListener { - if (uriView.text?.startsWith("taler://") == true) { - uriLayout.error = null - val i = Intent(ACTION_VIEW, Uri.parse(uriView.text.toString())) + ui.okButton.setOnClickListener { + if (ui.uriView.text?.startsWith("taler://") == true) { + ui.uriLayout.error = null + val i = Intent(ACTION_VIEW, Uri.parse(ui.uriView.text.toString())) startActivity(i) } else { - uriLayout.error = getString(R.string.uri_invalid) + ui.uriLayout.error = getString(R.string.uri_invalid) } } } diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt b/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt index 2b4d032..afd9a23 100644 --- a/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt @@ -26,13 +26,11 @@ import android.view.View.VISIBLE import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL -import kotlinx.android.synthetic.main.fragment_balances.* import net.taler.common.fadeIn import net.taler.wallet.MainViewModel -import net.taler.wallet.R +import net.taler.wallet.databinding.FragmentBalancesBinding interface BalanceClickListener { fun onBalanceClick(currency: String) @@ -43,6 +41,7 @@ class BalancesFragment : Fragment(), private val model: MainViewModel by activityViewModels() + private lateinit var ui: FragmentBalancesBinding private val balancesAdapter = BalanceAdapter(this) override fun onCreateView( @@ -50,16 +49,17 @@ class BalancesFragment : Fragment(), container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_balances, container, false) + ui = FragmentBalancesBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - mainList.apply { + ui.mainList.apply { adapter = balancesAdapter addItemDecoration(DividerItemDecoration(context, VERTICAL)) } - model.balances.observe(viewLifecycleOwner, Observer { + model.balances.observe(viewLifecycleOwner, { onBalancesChanged(it) }) } @@ -67,12 +67,12 @@ class BalancesFragment : Fragment(), private fun onBalancesChanged(balances: List<BalanceItem>) { beginDelayedTransition(view as ViewGroup) if (balances.isEmpty()) { - mainEmptyState.visibility = VISIBLE - mainList.visibility = GONE + ui.mainEmptyState.visibility = VISIBLE + ui.mainList.visibility = GONE } else { balancesAdapter.setItems(balances) - mainEmptyState.visibility = INVISIBLE - mainList.fadeIn() + ui.mainEmptyState.visibility = INVISIBLE + ui.mainList.fadeIn() } } diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt index c7da205..86b2519 100644 --- a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt @@ -24,43 +24,45 @@ import android.widget.Toast import android.widget.Toast.LENGTH_LONG import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL -import kotlinx.android.synthetic.main.fragment_exchange_list.* import net.taler.common.EventObserver import net.taler.common.fadeIn import net.taler.common.fadeOut import net.taler.wallet.MainViewModel import net.taler.wallet.R +import net.taler.wallet.databinding.FragmentExchangeListBinding class ExchangeListFragment : Fragment(), ExchangeClickListener { private val model: MainViewModel by activityViewModels() private val exchangeManager by lazy { model.exchangeManager } + + private lateinit var ui: FragmentExchangeListBinding private val exchangeAdapter by lazy { ExchangeAdapter(this) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_exchange_list, container, false) + ui = FragmentExchangeListBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - list.apply { + ui.list.apply { adapter = exchangeAdapter addItemDecoration(DividerItemDecoration(context, VERTICAL)) } - addExchangeFab.setOnClickListener { + ui.addExchangeFab.setOnClickListener { AddExchangeDialogFragment().show(parentFragmentManager, "ADD_EXCHANGE") } - exchangeManager.progress.observe(viewLifecycleOwner, Observer { show -> - if (show) progressBar.fadeIn() else progressBar.fadeOut() + exchangeManager.progress.observe(viewLifecycleOwner, { show -> + if (show) ui.progressBar.fadeIn() else ui.progressBar.fadeOut() }) - exchangeManager.exchanges.observe(viewLifecycleOwner, Observer { exchanges -> + exchangeManager.exchanges.observe(viewLifecycleOwner, { exchanges -> onExchangeUpdate(exchanges) }) exchangeManager.addError.observe(viewLifecycleOwner, EventObserver { error -> @@ -71,11 +73,11 @@ class ExchangeListFragment : Fragment(), ExchangeClickListener { private fun onExchangeUpdate(exchanges: List<ExchangeItem>) { exchangeAdapter.update(exchanges) if (exchanges.isEmpty()) { - emptyState.fadeIn() - list.fadeOut() + ui.emptyState.fadeIn() + ui.list.fadeOut() } else { - emptyState.fadeOut() - list.fadeIn() + ui.emptyState.fadeOut() + ui.list.fadeIn() } } diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt b/wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt index 9f5a916..a95a51c 100644 --- a/wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/exchanges/SelectExchangeFragment.kt @@ -27,12 +27,12 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.RecyclerView.Adapter import androidx.recyclerview.widget.RecyclerView.ViewHolder -import kotlinx.android.synthetic.main.fragment_select_exchange.* -import net.taler.lib.common.Amount import net.taler.common.toRelativeTime import net.taler.common.toShortDate +import net.taler.lib.common.Amount import net.taler.wallet.MainViewModel import net.taler.wallet.R +import net.taler.wallet.databinding.FragmentSelectExchangeBinding import net.taler.wallet.exchanges.CoinFeeAdapter.CoinFeeViewHolder import net.taler.wallet.exchanges.WireFeeAdapter.WireFeeViewHolder @@ -41,28 +41,29 @@ class SelectExchangeFragment : Fragment() { private val model: MainViewModel by activityViewModels() private val withdrawManager by lazy { model.withdrawManager } + private lateinit var ui: FragmentSelectExchangeBinding + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_select_exchange, container, false) + ui = FragmentSelectExchangeBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val fees = withdrawManager.exchangeFees ?: throw IllegalStateException() if (fees.withdrawFee.isZero()) { - withdrawFeeLabel.visibility = GONE - withdrawFeeView.visibility = GONE - } else withdrawFeeView.setAmount(fees.withdrawFee) + ui.withdrawFeeLabel.visibility = GONE + ui.withdrawFeeView.visibility = GONE + } else ui.withdrawFeeView.setAmount(fees.withdrawFee) if (fees.overhead.isZero()) { - overheadLabel.visibility = GONE - overheadView.visibility = GONE - } else overheadView.setAmount(fees.overhead) - expirationView.text = fees.earliestDepositExpiration.ms.toRelativeTime(requireContext()) - coinFeesList.adapter = - CoinFeeAdapter(fees.coinFees) - wireFeesList.adapter = - WireFeeAdapter(fees.wireFees) + ui.overheadLabel.visibility = GONE + ui.overheadView.visibility = GONE + } else ui.overheadView.setAmount(fees.overhead) + ui.expirationView.text = fees.earliestDepositExpiration.ms.toRelativeTime(requireContext()) + ui.coinFeesList.adapter = CoinFeeAdapter(fees.coinFees) + ui.wireFeesList.adapter = WireFeeAdapter(fees.wireFees) } private fun TextView.setAmount(amount: Amount) { diff --git a/wallet/src/main/java/net/taler/wallet/payment/AlreadyPaidFragment.kt b/wallet/src/main/java/net/taler/wallet/payment/AlreadyPaidFragment.kt index 33e3a1d..df2b2b8 100644 --- a/wallet/src/main/java/net/taler/wallet/payment/AlreadyPaidFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/payment/AlreadyPaidFragment.kt @@ -22,8 +22,7 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController -import kotlinx.android.synthetic.main.fragment_already_paid.* -import net.taler.wallet.R +import net.taler.wallet.databinding.FragmentAlreadyPaidBinding /** * Display the message that the user already paid for the order @@ -31,15 +30,18 @@ import net.taler.wallet.R */ class AlreadyPaidFragment : Fragment() { + private lateinit var ui: FragmentAlreadyPaidBinding + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_already_paid, container, false) + ui = FragmentAlreadyPaidBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - backButton.setOnClickListener { + ui.backButton.setOnClickListener { findNavController().navigateUp() } } diff --git a/wallet/src/main/java/net/taler/wallet/payment/ProductImageFragment.kt b/wallet/src/main/java/net/taler/wallet/payment/ProductImageFragment.kt index ff70f75..75de93f 100644 --- a/wallet/src/main/java/net/taler/wallet/payment/ProductImageFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/payment/ProductImageFragment.kt @@ -22,11 +22,12 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.DialogFragment -import kotlinx.android.synthetic.main.fragment_product_image.* -import net.taler.wallet.R +import net.taler.wallet.databinding.FragmentProductImageBinding class ProductImageFragment private constructor() : DialogFragment() { + private lateinit var ui: FragmentProductImageBinding + companion object { private const val IMAGE = "image" @@ -41,12 +42,13 @@ class ProductImageFragment private constructor() : DialogFragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_product_image, container, false) + ui = FragmentProductImageBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val bitmap = requireArguments().getParcelable<Bitmap>(IMAGE) - productImageView.setImageBitmap(bitmap) + ui.productImageView.setImageBitmap(bitmap) } } diff --git a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt index 99a6ec8..8815408 100644 --- a/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/payment/PromptPaymentFragment.kt @@ -32,14 +32,13 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.transition.TransitionManager.beginDelayedTransition import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar.LENGTH_LONG -import kotlinx.android.synthetic.main.payment_bottom_bar.* -import kotlinx.android.synthetic.main.payment_details.* import net.taler.common.ContractTerms import net.taler.common.fadeIn import net.taler.common.fadeOut import net.taler.lib.common.Amount import net.taler.wallet.MainViewModel import net.taler.wallet.R +import net.taler.wallet.databinding.FragmentPromptPaymentBinding /** * Show a payment and ask the user to accept/decline. @@ -48,13 +47,16 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener { private val model: MainViewModel by activityViewModels() private val paymentManager by lazy { model.paymentManager } + + private lateinit var ui: FragmentPromptPaymentBinding private val adapter = ProductAdapter(this) override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_prompt_payment, container, false) + ui = FragmentPromptPaymentBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -62,14 +64,14 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener { paymentManager.detailsShown.observe(viewLifecycleOwner, Observer { shown -> beginDelayedTransition(view as ViewGroup) val res = if (shown) R.string.payment_hide_details else R.string.payment_show_details - detailsButton.setText(res) - productsList.visibility = if (shown) VISIBLE else GONE + ui.details.detailsButton.setText(res) + ui.details.productsList.visibility = if (shown) VISIBLE else GONE }) - detailsButton.setOnClickListener { + ui.details.detailsButton.setOnClickListener { paymentManager.toggleDetailsShown() } - productsList.apply { + ui.details.productsList.apply { adapter = this@PromptPaymentFragment.adapter layoutManager = LinearLayoutManager(requireContext()) } @@ -85,9 +87,9 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener { private fun showLoading(show: Boolean) { model.showProgressBar.value = show if (show) { - progressBar.fadeIn() + ui.details.progressBar.fadeIn() } else { - progressBar.fadeOut() + ui.details.progressBar.fadeOut() } } @@ -97,22 +99,22 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener { showLoading(false) val fees = payStatus.amountEffective - payStatus.amountRaw showOrder(payStatus.contractTerms, payStatus.amountRaw, fees) - confirmButton.isEnabled = true - confirmButton.setOnClickListener { + ui.bottom.confirmButton.isEnabled = true + ui.bottom.confirmButton.setOnClickListener { model.showProgressBar.value = true paymentManager.confirmPay( payStatus.proposalId, payStatus.contractTerms.amount.currency ) - confirmButton.fadeOut() - confirmProgressBar.fadeIn() + ui.bottom.confirmButton.fadeOut() + ui.bottom.confirmProgressBar.fadeIn() } } is PayStatus.InsufficientBalance -> { showLoading(false) showOrder(payStatus.contractTerms, payStatus.amountRaw) - errorView.setText(R.string.payment_balance_insufficient) - errorView.fadeIn() + ui.details.errorView.setText(R.string.payment_balance_insufficient) + ui.details.errorView.fadeIn() } is PayStatus.Success -> { showLoading(false) @@ -128,8 +130,8 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener { } is PayStatus.Error -> { showLoading(false) - errorView.text = getString(R.string.payment_error, payStatus.error) - errorView.fadeIn() + ui.details.errorView.text = getString(R.string.payment_error, payStatus.error) + ui.details.errorView.fadeIn() } is PayStatus.None -> { // No payment active. @@ -143,21 +145,21 @@ class PromptPaymentFragment : Fragment(), ProductImageClickListener { } private fun showOrder(contractTerms: ContractTerms, amount:Amount, totalFees: Amount? = null) { - orderView.text = contractTerms.summary + ui.details.orderView.text = contractTerms.summary adapter.setItems(contractTerms.products) if (contractTerms.products.size == 1) paymentManager.toggleDetailsShown() - totalView.text = amount.toString() + ui.bottom.totalView.text = amount.toString() if (totalFees != null && !totalFees.isZero()) { - feeView.text = getString(R.string.payment_fee, totalFees) - feeView.fadeIn() + ui.bottom.feeView.text = getString(R.string.payment_fee, totalFees) + ui.bottom.feeView.fadeIn() } else { - feeView.visibility = GONE + ui.bottom.feeView.visibility = GONE } - orderLabelView.fadeIn() - orderView.fadeIn() - if (contractTerms.products.size > 1) detailsButton.fadeIn() - totalLabelView.fadeIn() - totalView.fadeIn() + ui.details.orderLabelView.fadeIn() + ui.details.orderView.fadeIn() + if (contractTerms.products.size > 1) ui.details.detailsButton.fadeIn() + ui.bottom.totalLabelView.fadeIn() + ui.bottom.totalView.fadeIn() } override fun onImageClick(image: Bitmap) { diff --git a/wallet/src/main/java/net/taler/wallet/pending/PendingOperationsFragment.kt b/wallet/src/main/java/net/taler/wallet/pending/PendingOperationsFragment.kt index 293dbdb..e2f3ca1 100644 --- a/wallet/src/main/java/net/taler/wallet/pending/PendingOperationsFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/pending/PendingOperationsFragment.kt @@ -30,16 +30,15 @@ import android.widget.LinearLayout import android.widget.TextView import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer 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.android.material.snackbar.Snackbar.LENGTH_SHORT -import kotlinx.android.synthetic.main.fragment_pending_operations.* import net.taler.wallet.MainViewModel import net.taler.wallet.R import net.taler.wallet.TAG +import net.taler.wallet.databinding.FragmentPendingOperationsBinding import org.json.JSONObject interface PendingOperationClickListener { @@ -52,6 +51,7 @@ class PendingOperationsFragment : Fragment(), PendingOperationClickListener { private val model: MainViewModel by activityViewModels() private val pendingOperationsManager by lazy { model.pendingOperationsManager } + private lateinit var ui: FragmentPendingOperationsBinding private val pendingAdapter = PendingOperationsAdapter(emptyList(), this) override fun onCreate(savedInstanceState: Bundle?) { @@ -64,13 +64,14 @@ class PendingOperationsFragment : Fragment(), PendingOperationClickListener { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_pending_operations, container, false) + ui = FragmentPendingOperationsBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - list_pending.apply { + ui.listPending.apply { val myLayoutManager = LinearLayoutManager(requireContext()) val myItemDecoration = DividerItemDecoration(requireContext(), myLayoutManager.orientation) @@ -79,7 +80,7 @@ class PendingOperationsFragment : Fragment(), PendingOperationClickListener { addItemDecoration(myItemDecoration) } - pendingOperationsManager.pendingOperations.observe(viewLifecycleOwner, Observer { + pendingOperationsManager.pendingOperations.observe(viewLifecycleOwner, { updatePending(it) }) } diff --git a/wallet/src/main/java/net/taler/wallet/settings/SettingsFragment.kt b/wallet/src/main/java/net/taler/wallet/settings/SettingsFragment.kt index a52b9d8..63492f4 100644 --- a/wallet/src/main/java/net/taler/wallet/settings/SettingsFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/settings/SettingsFragment.kt @@ -20,7 +20,6 @@ import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreferenceCompat @@ -75,12 +74,12 @@ class SettingsFragment : PreferenceFragmentCompat() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - model.lastBackup.observe(viewLifecycleOwner, Observer { + model.lastBackup.observe(viewLifecycleOwner, { val time = it.toRelativeTime(requireContext()) prefBackup.summary = getString(R.string.backup_last, time) }) - model.devMode.observe(viewLifecycleOwner, Observer { enabled -> + model.devMode.observe(viewLifecycleOwner, { enabled -> prefDevMode.isChecked = enabled if (enabled) { prefVersionApp.summary = "$VERSION_NAME ($FLAVOR $VERSION_CODE)" @@ -95,7 +94,7 @@ class SettingsFragment : PreferenceFragmentCompat() { true } - withdrawManager.testWithdrawalInProgress.observe(viewLifecycleOwner, Observer { loading -> + withdrawManager.testWithdrawalInProgress.observe(viewLifecycleOwner, { loading -> prefWithdrawTest.isEnabled = !loading model.showProgressBar.value = loading }) diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt index f15e34f..302e684 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt @@ -19,68 +19,34 @@ package net.taler.wallet.transactions import android.content.Intent import android.net.Uri import android.os.Bundle -import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater import android.view.MenuItem -import android.view.View -import android.view.View.GONE -import android.view.ViewGroup -import android.widget.Toast -import android.widget.Toast.LENGTH_LONG -import androidx.core.content.ContextCompat.getColor +import android.widget.TextView import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import kotlinx.android.synthetic.main.fragment_transaction_payment.* -import kotlinx.android.synthetic.main.fragment_transaction_withdrawal.* -import kotlinx.android.synthetic.main.fragment_transaction_withdrawal.feeView -import kotlinx.android.synthetic.main.fragment_transaction_withdrawal.timeView import net.taler.common.isSafe -import net.taler.common.toAbsoluteTime import net.taler.lib.common.Amount import net.taler.wallet.MainViewModel import net.taler.wallet.R -import net.taler.wallet.cleanExchange -import net.taler.wallet.transactions.WithdrawalDetails.TalerBankIntegrationApi -class TransactionDetailFragment : Fragment() { +abstract class TransactionDetailFragment : Fragment() { private val model: MainViewModel by activityViewModels() private val transactionManager by lazy { model.transactionManager } - private val transaction by lazy { requireNotNull(transactionManager.selectedTransaction) } + protected val transaction: Transaction? get() = transactionManager.selectedTransaction override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(model.devMode.value == true) } - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(transaction.detailPageLayout, container, false) - } - override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) requireActivity().apply { - title = getString(transaction.generalTitleRes) - } - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - timeView.text = transaction.timestamp.ms.toAbsoluteTime(requireContext()) - when (val e = transaction) { - is TransactionWithdrawal -> bind(e) - is TransactionPayment -> bind(e) - is TransactionRefund -> bind(e) - is TransactionRefresh -> bind(e) - else -> Toast.makeText( - requireContext(), - "Transaction ${e.javaClass.simpleName} not implemented.", - LENGTH_LONG - ).show() + transaction?.generalTitleRes?.let { + title = getString(it) + } } } @@ -94,54 +60,15 @@ class TransactionDetailFragment : Fragment() { } } - private fun bind(t: TransactionWithdrawal) { - effectiveAmountLabel.text = getString(R.string.withdraw_total) - effectiveAmountView.text = t.amountEffective.toString() - if (t.pending && t.withdrawalDetails is TalerBankIntegrationApi && - !t.confirmed && t.withdrawalDetails.bankConfirmationUrl != null - ) { - val i = Intent().apply { - data = Uri.parse(t.withdrawalDetails.bankConfirmationUrl) - } - if (i.isSafe(requireContext())) { - confirmWithdrawalButton.setOnClickListener { startActivity(i) } - } - } else confirmWithdrawalButton.visibility = GONE - chosenAmountLabel.text = getString(R.string.amount_chosen) - chosenAmountView.text = - getString(R.string.amount_positive, t.amountRaw.toString()) - val fee = t.amountRaw - t.amountEffective - feeView.text = getString(R.string.amount_negative, fee.toString()) - exchangeView.text = cleanExchange(t.exchangeBaseUrl) - } - - private fun bind(t: TransactionPayment) { - amountPaidWithFeesView.text = t.amountEffective.toString() - val fee = t.amountEffective - t.amountRaw - bindOrderAndFee(t.info, t.amountRaw, fee) - } - - private fun bind(t: TransactionRefund) { - amountPaidWithFeesLabel.text = getString(R.string.transaction_refund) - amountPaidWithFeesView.setTextColor(getColor(requireContext(), R.color.green)) - amountPaidWithFeesView.text = - getString(R.string.amount_positive, t.amountEffective.toString()) - val fee = t.amountRaw - t.amountEffective - bindOrderAndFee(t.info, t.amountRaw, fee) - } - - private fun bind(t: TransactionRefresh) { - effectiveAmountLabel.visibility = GONE - effectiveAmountView.visibility = GONE - confirmWithdrawalButton.visibility = GONE - chosenAmountLabel.visibility = GONE - chosenAmountView.visibility = GONE - val fee = t.amountEffective - feeView.text = getString(R.string.amount_negative, fee.toString()) - exchangeView.text = cleanExchange(t.exchangeBaseUrl) - } - - private fun bindOrderAndFee(info: TransactionInfo, raw: Amount, fee: Amount) { + protected fun bindOrderAndFee( + orderSummaryView: TextView, + orderAmountView: TextView, + orderIdView: TextView, + feeView: TextView, + info: TransactionInfo, + raw: Amount, + fee: Amount + ) { orderAmountView.text = raw.toString() feeView.text = getString(R.string.amount_negative, fee.toString()) orderSummaryView.text = if (info.fulfillmentMessage == null) { diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionPaymentFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionPaymentFragment.kt new file mode 100644 index 0000000..84c5c77 --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionPaymentFragment.kt @@ -0,0 +1,56 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.wallet.transactions + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import net.taler.common.toAbsoluteTime +import net.taler.wallet.databinding.FragmentTransactionPaymentBinding + +class TransactionPaymentFragment : TransactionDetailFragment() { + + private lateinit var ui: FragmentTransactionPaymentBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + ui = FragmentTransactionPaymentBinding.inflate(inflater, container, false) + return ui.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val t = transaction as TransactionPayment + ui.timeView.text = t.timestamp.ms.toAbsoluteTime(requireContext()) + + ui.amountPaidWithFeesView.text = t.amountEffective.toString() + val fee = t.amountEffective - t.amountRaw + bindOrderAndFee( + ui.orderSummaryView, + ui.orderAmountView, + ui.orderIdView, + ui.feeView, + t.info, + t.amountRaw, + fee + ) + } + +} diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefreshFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefreshFragment.kt new file mode 100644 index 0000000..717dd33 --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefreshFragment.kt @@ -0,0 +1,56 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.wallet.transactions + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.View.GONE +import android.view.ViewGroup +import net.taler.common.toAbsoluteTime +import net.taler.wallet.R +import net.taler.wallet.cleanExchange +import net.taler.wallet.databinding.FragmentTransactionWithdrawalBinding + +class TransactionRefreshFragment : TransactionDetailFragment() { + + private lateinit var ui: FragmentTransactionWithdrawalBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + ui = FragmentTransactionWithdrawalBinding.inflate(inflater, container, false) + return ui.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val t = transaction as TransactionRefresh + ui.timeView.text = t.timestamp.ms.toAbsoluteTime(requireContext()) + + ui.effectiveAmountLabel.visibility = GONE + ui.effectiveAmountView.visibility = GONE + ui.confirmWithdrawalButton.visibility = GONE + ui.chosenAmountLabel.visibility = GONE + ui.chosenAmountView.visibility = GONE + val fee = t.amountEffective + ui.feeView.text = getString(R.string.amount_negative, fee.toString()) + ui. exchangeView.text = cleanExchange(t.exchangeBaseUrl) + } + +} diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefundFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefundFragment.kt new file mode 100644 index 0000000..6628d6c --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefundFragment.kt @@ -0,0 +1,61 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.wallet.transactions + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.content.ContextCompat.getColor +import net.taler.common.toAbsoluteTime +import net.taler.wallet.R +import net.taler.wallet.databinding.FragmentTransactionPaymentBinding + +class TransactionRefundFragment : TransactionDetailFragment() { + + private lateinit var ui: FragmentTransactionPaymentBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + ui = FragmentTransactionPaymentBinding.inflate(inflater, container, false) + return ui.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val t = transaction as TransactionRefund + ui.timeView.text = t.timestamp.ms.toAbsoluteTime(requireContext()) + + ui.amountPaidWithFeesLabel.text = getString(R.string.transaction_refund) + ui.amountPaidWithFeesView.setTextColor(getColor(requireContext(), R.color.green)) + ui.amountPaidWithFeesView.text = + getString(R.string.amount_positive, t.amountEffective.toString()) + val fee = t.amountRaw - t.amountEffective + bindOrderAndFee( + ui.orderSummaryView, + ui.orderAmountView, + ui.orderIdView, + ui.feeView, + t.info, + t.amountRaw, + fee + ) + } + +} diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt new file mode 100644 index 0000000..26965ef --- /dev/null +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt @@ -0,0 +1,68 @@ +/* + * This file is part of GNU Taler + * (C) 2020 Taler Systems S.A. + * + * GNU Taler is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3, or (at your option) any later version. + * + * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +package net.taler.wallet.transactions + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import net.taler.common.isSafe +import net.taler.common.toAbsoluteTime +import net.taler.wallet.R +import net.taler.wallet.cleanExchange +import net.taler.wallet.databinding.FragmentTransactionWithdrawalBinding + +class TransactionWithdrawalFragment : TransactionDetailFragment() { + + private lateinit var ui: FragmentTransactionWithdrawalBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + ui = FragmentTransactionWithdrawalBinding.inflate(inflater, container, false) + return ui.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val t = transaction as TransactionWithdrawal + ui.timeView.text = t.timestamp.ms.toAbsoluteTime(requireContext()) + + ui.effectiveAmountLabel.text = getString(R.string.withdraw_total) + ui.effectiveAmountView.text = t.amountEffective.toString() + if (t.pending && t.withdrawalDetails is WithdrawalDetails.TalerBankIntegrationApi && + !t.confirmed && t.withdrawalDetails.bankConfirmationUrl != null + ) { + val i = Intent().apply { + data = Uri.parse(t.withdrawalDetails.bankConfirmationUrl) + } + if (i.isSafe(requireContext())) { + ui.confirmWithdrawalButton.setOnClickListener { startActivity(i) } + } + } else ui.confirmWithdrawalButton.visibility = View.GONE + ui.chosenAmountLabel.text = getString(R.string.amount_chosen) + ui.chosenAmountView.text = + getString(R.string.amount_positive, t.amountRaw.toString()) + val fee = t.amountRaw - t.amountEffective + ui.feeView.text = getString(R.string.amount_negative, fee.toString()) + ui.exchangeView.text = cleanExchange(t.exchangeBaseUrl) + } + +} diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt index 0817c36..50181c5 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt @@ -18,7 +18,7 @@ package net.taler.wallet.transactions import android.content.Context import androidx.annotation.DrawableRes -import androidx.annotation.LayoutRes +import androidx.annotation.IdRes import androidx.annotation.StringRes import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -48,8 +48,8 @@ sealed class Transaction { @get:DrawableRes abstract val icon: Int - @get:LayoutRes - abstract val detailPageLayout: Int + @get:IdRes + abstract val detailPageNav: Int abstract val amountType: AmountType @@ -78,7 +78,8 @@ class TransactionWithdrawal( override val amountEffective: Amount ) : Transaction() { override val icon = R.drawable.transaction_withdrawal - override val detailPageLayout = R.layout.fragment_transaction_withdrawal + + override val detailPageNav = R.id.action_nav_transactions_detail_withdrawal @Transient override val amountType = AmountType.Positive @@ -135,7 +136,7 @@ class TransactionPayment( override val amountEffective: Amount ) : Transaction() { override val icon = R.drawable.ic_cash_usd_outline - override val detailPageLayout = R.layout.fragment_transaction_payment + override val detailPageNav = R.id.action_nav_transactions_detail_payment @Transient override val amountType = AmountType.Negative @@ -194,7 +195,7 @@ class TransactionRefund( override val amountEffective: Amount ) : Transaction() { override val icon = R.drawable.transaction_refund - override val detailPageLayout = R.layout.fragment_transaction_payment + override val detailPageNav = R.id.action_nav_transactions_detail_refund @Transient override val amountType = AmountType.Positive @@ -219,7 +220,7 @@ class TransactionTip( override val amountEffective: Amount ) : Transaction() { override val icon = R.drawable.transaction_tip_accepted // TODO different when declined - override val detailPageLayout = R.layout.fragment_transaction_payment + override val detailPageNav = 0 @Transient override val amountType = AmountType.Positive @@ -242,7 +243,7 @@ class TransactionRefresh( override val amountEffective: Amount ) : Transaction() { override val icon = R.drawable.transaction_refresh - override val detailPageLayout = R.layout.fragment_transaction_withdrawal + override val detailPageNav = R.id.action_nav_transactions_detail_refresh @Transient override val amountType = AmountType.Negative diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt index 8d47a3f..90510e6 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt @@ -30,18 +30,17 @@ import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView.OnQueryTextListener import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer import androidx.navigation.fragment.findNavController import androidx.recyclerview.selection.SelectionPredicates import androidx.recyclerview.selection.SelectionTracker import androidx.recyclerview.selection.StorageStrategy import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL -import kotlinx.android.synthetic.main.fragment_transactions.* import net.taler.common.fadeIn import net.taler.common.fadeOut import net.taler.wallet.MainViewModel import net.taler.wallet.R +import net.taler.wallet.databinding.FragmentTransactionsBinding interface OnTransactionClickListener { fun onTransactionClicked(transaction: Transaction) @@ -52,6 +51,7 @@ class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode. private val model: MainViewModel by activityViewModels() private val transactionManager by lazy { model.transactionManager } + private lateinit var ui: FragmentTransactionsBinding private val transactionAdapter by lazy { TransactionAdapter(this) } private val currency by lazy { transactionManager.selectedCurrency!! } private var tracker: SelectionTracker<String>? = null @@ -66,19 +66,20 @@ class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode. inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_transactions, container, false) + ui = FragmentTransactionsBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - list.apply { + ui.list.apply { adapter = transactionAdapter addItemDecoration(DividerItemDecoration(context, VERTICAL)) } val tracker = SelectionTracker.Builder( "transaction-selection-id", - list, + ui.list, transactionAdapter.keyProvider, - TransactionLookup(list, transactionAdapter), + TransactionLookup(ui.list, transactionAdapter), StorageStrategy.createStringStorage() ).withSelectionPredicate( SelectionPredicates.createSelectAnything() @@ -101,17 +102,17 @@ class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode. } }) - transactionManager.progress.observe(viewLifecycleOwner, Observer { show -> - if (show) progressBar.fadeIn() else progressBar.fadeOut() + transactionManager.progress.observe(viewLifecycleOwner, { show -> + if (show) ui.progressBar.fadeIn() else ui.progressBar.fadeOut() }) - transactionManager.transactions.observe(viewLifecycleOwner, Observer { result -> + transactionManager.transactions.observe(viewLifecycleOwner, { result -> onTransactionsResult(result) }) } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - model.balances.observe(viewLifecycleOwner, Observer { balances -> + model.balances.observe(viewLifecycleOwner, { balances -> balances.find { it.currency == currency }?.available?.let { amount -> requireActivity().title = getString(R.string.transactions_detail_title_balance, amount) @@ -153,35 +154,35 @@ class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode. override fun onTransactionClicked(transaction: Transaction) { if (actionMode != null) return // don't react on clicks while in action mode - if (transaction.detailPageLayout != 0) { + if (transaction.detailPageNav != 0) { transactionManager.selectedTransaction = transaction - findNavController().navigate(R.id.action_nav_transaction_detail) + findNavController().navigate(transaction.detailPageNav) } } private fun onTransactionsResult(result: TransactionsResult) = when (result) { is TransactionsResult.Error -> { - list.fadeOut() - emptyState.text = getString(R.string.transactions_error, result.msg) - emptyState.fadeIn() + ui.list.fadeOut() + ui.emptyState.text = getString(R.string.transactions_error, result.msg) + ui.emptyState.fadeIn() } is TransactionsResult.Success -> { if (result.transactions.isEmpty()) { val isSearch = transactionManager.searchQuery.value != null - emptyState.setText(if (isSearch) R.string.transactions_empty_search else R.string.transactions_empty) - emptyState.fadeIn() - list.fadeOut() + ui.emptyState.setText(if (isSearch) R.string.transactions_empty_search else R.string.transactions_empty) + ui.emptyState.fadeIn() + ui.list.fadeOut() } else { - emptyState.fadeOut() + ui.emptyState.fadeOut() transactionAdapter.update(result.transactions) - list.fadeIn() + ui.list.fadeIn() } } } private fun onSearch(query: String) { - list.fadeOut() - progressBar.fadeIn() + ui.list.fadeOut() + ui.progressBar.fadeIn() transactionManager.searchQuery.value = query } diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/ErrorFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/ErrorFragment.kt index fa5ab2f..8b4ca9d 100644 --- a/wallet/src/main/java/net/taler/wallet/withdraw/ErrorFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/withdraw/ErrorFragment.kt @@ -25,38 +25,41 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController -import kotlinx.android.synthetic.main.fragment_error.* -import net.taler.wallet.R import net.taler.wallet.MainViewModel +import net.taler.wallet.R +import net.taler.wallet.databinding.FragmentErrorBinding class ErrorFragment : Fragment() { private val model: MainViewModel by activityViewModels() private val withdrawManager by lazy { model.withdrawManager } + private lateinit var ui: FragmentErrorBinding + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_error, container, false) + ui = FragmentErrorBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - errorTitle.setText(R.string.withdraw_error_title) - errorMessage.setText(R.string.withdraw_error_message) + ui.errorTitle.setText(R.string.withdraw_error_title) + ui.errorMessage.setText(R.string.withdraw_error_message) // show dev error message if dev mode is on val status = withdrawManager.withdrawStatus.value if (model.devMode.value == true && status is WithdrawStatus.Error) { - errorDevMessage.visibility = VISIBLE - errorDevMessage.text = status.message + ui.errorDevMessage.visibility = VISIBLE + ui.errorDevMessage.text = status.message } else { - errorDevMessage.visibility = GONE + ui.errorDevMessage.visibility = GONE } - backButton.setOnClickListener { + ui.backButton.setOnClickListener { findNavController().navigateUp() } } diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawFragment.kt index af76e9b..4b56dd0 100644 --- a/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/withdraw/ManualWithdrawFragment.kt @@ -25,11 +25,11 @@ import android.widget.Toast import android.widget.Toast.LENGTH_SHORT import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import kotlinx.android.synthetic.main.fragment_manual_withdraw.* import net.taler.common.hideKeyboard import net.taler.lib.common.Amount import net.taler.wallet.MainViewModel import net.taler.wallet.R +import net.taler.wallet.databinding.FragmentManualWithdrawBinding import net.taler.wallet.scanQrCode import java.util.Locale @@ -40,33 +40,36 @@ class ManualWithdrawFragment : Fragment() { private val exchangeItem by lazy { requireNotNull(exchangeManager.withdrawalExchange) } private val withdrawManager by lazy { model.withdrawManager } + private lateinit var ui: FragmentManualWithdrawBinding + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_manual_withdraw, container, false) + ui = FragmentManualWithdrawBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - qrCodeButton.setOnClickListener { scanQrCode(requireActivity()) } - currencyView.text = exchangeItem.currency + ui.qrCodeButton.setOnClickListener { scanQrCode(requireActivity()) } + ui.currencyView.text = exchangeItem.currency val paymentOptions = exchangeItem.paytoUris.mapNotNull {paytoUri -> Uri.parse(paytoUri).authority?.toUpperCase(Locale.getDefault()) }.joinToString(separator = "\n", prefix = "• ") - paymentOptionsLabel.text = + ui.paymentOptionsLabel.text = getString(R.string.withdraw_manual_payment_options, exchangeItem.name, paymentOptions) - checkFeesButton.setOnClickListener { onCheckFees() } + ui.checkFeesButton.setOnClickListener { onCheckFees() } } private fun onCheckFees() { - if (amountView.text?.isEmpty() ?: true) { - amountLayout.setError(getString(R.string.withdraw_amount_error)) + if (ui.amountView.text?.isEmpty() != false) { + ui.amountLayout.error = getString(R.string.withdraw_amount_error) return } - amountLayout.setError(null) - val value = amountView.text.toString().toLong() + ui.amountLayout.error = null + val value = ui.amountView.text.toString().toLong() val amount = Amount(exchangeItem.currency, value, 0) - amountView.hideKeyboard() + ui.amountView.hideKeyboard() Toast.makeText(requireContext(), "Not implemented: $amount", LENGTH_SHORT).show() withdrawManager.getWithdrawalDetails(exchangeItem.exchangeBaseUrl, amount) } diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt index ffc64d4..0c7687c 100644 --- a/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/withdraw/PromptWithdrawFragment.kt @@ -24,17 +24,16 @@ import android.widget.Toast import android.widget.Toast.LENGTH_SHORT import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer import androidx.navigation.fragment.findNavController import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar.LENGTH_LONG -import kotlinx.android.synthetic.main.fragment_prompt_withdraw.* import net.taler.common.fadeIn import net.taler.common.fadeOut import net.taler.lib.common.Amount import net.taler.wallet.MainViewModel import net.taler.wallet.R import net.taler.wallet.cleanExchange +import net.taler.wallet.databinding.FragmentPromptWithdrawBinding import net.taler.wallet.withdraw.WithdrawStatus.Loading import net.taler.wallet.withdraw.WithdrawStatus.TosReviewRequired import net.taler.wallet.withdraw.WithdrawStatus.Withdrawing @@ -44,17 +43,20 @@ class PromptWithdrawFragment : Fragment() { private val model: MainViewModel by activityViewModels() private val withdrawManager by lazy { model.withdrawManager } + private lateinit var ui: FragmentPromptWithdrawBinding + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_prompt_withdraw, container, false) + ui = FragmentPromptWithdrawBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - withdrawManager.withdrawStatus.observe(viewLifecycleOwner, Observer { + withdrawManager.withdrawStatus.observe(viewLifecycleOwner, { showWithdrawStatus(it) }) } @@ -62,11 +64,11 @@ class PromptWithdrawFragment : Fragment() { private fun showWithdrawStatus(status: WithdrawStatus?): Any = when (status) { is WithdrawStatus.ReceivedDetails -> { showContent(status.amountRaw, status.amountEffective, status.exchangeBaseUrl) - confirmWithdrawButton.apply { + ui.confirmWithdrawButton.apply { text = getString(R.string.withdraw_button_confirm) setOnClickListener { it.fadeOut() - confirmProgressBar.fadeIn() + ui.confirmProgressBar.fadeIn() withdrawManager.acceptWithdrawal() } isEnabled = true @@ -87,7 +89,7 @@ class PromptWithdrawFragment : Fragment() { } is TosReviewRequired -> { showContent(status.amountRaw, status.amountEffective, status.exchangeBaseUrl) - confirmWithdrawButton.apply { + ui.confirmWithdrawButton.apply { text = getString(R.string.withdraw_button_tos) setOnClickListener { findNavController().navigate(R.id.action_promptWithdraw_to_reviewExchangeTOS) @@ -104,29 +106,29 @@ class PromptWithdrawFragment : Fragment() { private fun showContent(amountRaw: Amount, amountEffective: Amount, exchange: String) { model.showProgressBar.value = false - progressBar.fadeOut() + ui.progressBar.fadeOut() - introView.fadeIn() - effectiveAmountView.text = amountEffective.toString() - effectiveAmountView.fadeIn() + ui.introView.fadeIn() + ui.effectiveAmountView.text = amountEffective.toString() + ui.effectiveAmountView.fadeIn() - chosenAmountLabel.fadeIn() - chosenAmountView.text = amountRaw.toString() - chosenAmountView.fadeIn() + ui.chosenAmountLabel.fadeIn() + ui.chosenAmountView.text = amountRaw.toString() + ui.chosenAmountView.fadeIn() - feeLabel.fadeIn() - feeView.text = getString(R.string.amount_negative, (amountRaw - amountEffective).toString()) - feeView.fadeIn() + ui.feeLabel.fadeIn() + ui.feeView.text = getString(R.string.amount_negative, (amountRaw - amountEffective).toString()) + ui.feeView.fadeIn() - exchangeIntroView.fadeIn() - withdrawExchangeUrl.text = cleanExchange(exchange) - withdrawExchangeUrl.fadeIn() - selectExchangeButton.fadeIn() - selectExchangeButton.setOnClickListener { + ui.exchangeIntroView.fadeIn() + ui.withdrawExchangeUrl.text = cleanExchange(exchange) + ui.withdrawExchangeUrl.fadeIn() + ui.selectExchangeButton.fadeIn() + ui.selectExchangeButton.setOnClickListener { Toast.makeText(context, "Not yet implemented", LENGTH_SHORT).show() } - withdrawCard.fadeIn() + ui.withdrawCard.fadeIn() } } diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt b/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt index db1f326..73fe760 100644 --- a/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/withdraw/ReviewExchangeTosFragment.kt @@ -25,17 +25,19 @@ import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer import androidx.navigation.fragment.findNavController import io.noties.markwon.Markwon -import kotlinx.android.synthetic.main.fragment_review_exchange_tos.* import net.taler.common.fadeIn import net.taler.common.fadeOut import net.taler.wallet.MainViewModel import net.taler.wallet.R +import net.taler.wallet.databinding.FragmentReviewExchangeTosBinding import java.text.ParseException class ReviewExchangeTosFragment : Fragment() { private val model: MainViewModel by activityViewModels() private val withdrawManager by lazy { model.withdrawManager } + + private lateinit var ui: FragmentReviewExchangeTosBinding private val markwon by lazy { Markwon.builder(requireContext()).build() } private val adapter by lazy { TosAdapter(markwon) } @@ -44,13 +46,14 @@ class ReviewExchangeTosFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_review_exchange_tos, container, false) + ui = FragmentReviewExchangeTosBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - acceptTosCheckBox.isChecked = false - acceptTosCheckBox.setOnCheckedChangeListener { _, _ -> + ui.acceptTosCheckBox.isChecked = false + ui.acceptTosCheckBox.setOnCheckedChangeListener { _, _ -> withdrawManager.acceptCurrentTermsOfService() } withdrawManager.withdrawStatus.observe(viewLifecycleOwner, Observer { @@ -65,11 +68,11 @@ class ReviewExchangeTosFragment : Fragment() { return@Observer } adapter.setSections(sections) - tosList.adapter = adapter - tosList.fadeIn() + ui.tosList.adapter = adapter + ui.tosList.fadeIn() - acceptTosCheckBox.fadeIn() - progressBar.fadeOut() + ui.acceptTosCheckBox.fadeIn() + ui.progressBar.fadeOut() } is WithdrawStatus.Loading -> { findNavController().navigate(R.id.action_reviewExchangeTOS_to_promptWithdraw) @@ -82,11 +85,11 @@ class ReviewExchangeTosFragment : Fragment() { } private fun onTosError(msg: String) { - tosList.fadeIn() - progressBar.fadeOut() - buttonCard.fadeOut() - errorView.text = getString(R.string.exchange_tos_error, "\n\n$msg") - errorView.fadeIn() + ui.tosList.fadeIn() + ui.progressBar.fadeOut() + ui.buttonCard.fadeOut() + ui.errorView.text = getString(R.string.exchange_tos_error, "\n\n$msg") + ui.errorView.fadeIn() } } diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt index b6b4285..25c5b72 100644 --- a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt +++ b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt @@ -24,8 +24,8 @@ import kotlinx.coroutines.launch import kotlinx.serialization.Serializable import net.taler.lib.common.Amount import net.taler.wallet.TAG -import net.taler.wallet.backend.WalletBackendApi import net.taler.wallet.backend.TalerErrorInfo +import net.taler.wallet.backend.WalletBackendApi import net.taler.wallet.exchanges.ExchangeFees import net.taler.wallet.exchanges.ExchangeItem import net.taler.wallet.withdraw.WithdrawStatus.ReceivedDetails @@ -89,14 +89,11 @@ class WithdrawManager( fun getWithdrawalDetails(uri: String) = scope.launch { withdrawStatus.value = WithdrawStatus.Loading(uri) - val response = - api.request("getWithdrawalDetailsForUri", WithdrawalDetailsForUri.serializer()) { - put("talerWithdrawUri", uri) - } - response.onError { error -> + api.request("getWithdrawalDetailsForUri", WithdrawalDetailsForUri.serializer()) { + put("talerWithdrawUri", uri) + }.onError { error -> handleError("getWithdrawalDetailsForUri", error) - } - response.onSuccess { details -> + }.onSuccess { details -> if (details.defaultExchangeBaseUrl == null) { // TODO go to exchange selection screen instead val chosenExchange = details.possibleExchanges[0].exchangeBaseUrl @@ -113,15 +110,12 @@ class WithdrawManager( uri: String? = null ) = scope.launch { withdrawStatus.value = WithdrawStatus.Loading(uri) - val response = - api.request("getWithdrawalDetailsForAmount", WithdrawalDetails.serializer()) { - put("exchangeBaseUrl", exchangeBaseUrl) - put("amount", amount.toJSONString()) - } - response.onError { error -> + api.request("getWithdrawalDetailsForAmount", WithdrawalDetails.serializer()) { + put("exchangeBaseUrl", exchangeBaseUrl) + put("amount", amount.toJSONString()) + }.onError { error -> handleError("getWithdrawalDetailsForAmount", error) - } - response.onSuccess { details -> + }.onSuccess { details -> if (details.tosAccepted) { withdrawStatus.value = ReceivedDetails( talerWithdrawUri = uri, @@ -138,13 +132,11 @@ class WithdrawManager( details: WithdrawalDetails, uri: String? ) = scope.launch { - val response = api.request("getExchangeTos", TosResponse.serializer()) { + api.request("getExchangeTos", TosResponse.serializer()) { put("exchangeBaseUrl", exchangeBaseUrl) - } - response.onError { + }.onError { handleError("getExchangeTos", it) - } - response.onSuccess { + }.onSuccess { withdrawStatus.value = WithdrawStatus.TosReviewRequired( talerWithdrawUri = uri, exchangeBaseUrl = exchangeBaseUrl, |