diff options
8 files changed, 114 insertions, 96 deletions
diff --git a/cashier/build.gradle b/cashier/build.gradle index f5c56a9..a749353 100644 --- a/cashier/build.gradle +++ b/cashier/build.gradle @@ -17,7 +17,6 @@ plugins { id "com.android.application" id "kotlin-android" - id "kotlin-android-extensions" id "kotlinx-serialization" id "androidx.navigation.safeargs.kotlin" } @@ -51,6 +50,10 @@ android { jvmTarget = "1.8" } + buildFeatures { + viewBinding = true + } + packagingOptions { exclude("META-INF/*.kotlin_module") } diff --git a/cashier/proguard-rules.pro b/cashier/proguard-rules.pro index f612b29..a1cc1f6 100644 --- a/cashier/proguard-rules.pro +++ b/cashier/proguard-rules.pro @@ -20,6 +20,8 @@ # hide the original source file name. #-renamesourcefileattribute SourceFile +-keep class net.taler.cashier.** {*;} + # androidx.security:security-crypto # https://github.com/google/tink/issues/361 -keep class * extends com.google.crypto.tink.shaded.protobuf.GeneratedMessageLite { *; } diff --git a/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt b/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt index cdfa142..1114080 100644 --- a/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt +++ b/cashier/src/main/java/net/taler/cashier/BalanceFragment.kt @@ -29,8 +29,8 @@ 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_balance.* import net.taler.cashier.BalanceFragmentDirections.Companion.actionBalanceFragmentToTransactionFragment +import net.taler.cashier.databinding.FragmentBalanceBinding import net.taler.cashier.withdraw.LastTransaction import net.taler.cashier.withdraw.WithdrawStatus import net.taler.common.exhaustive @@ -50,12 +50,15 @@ class BalanceFragment : Fragment() { private val configManager by lazy { viewModel.configManager} private val withdrawManager by lazy { viewModel.withdrawManager } + private lateinit var ui: FragmentBalanceBinding + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { setHasOptionsMenu(true) - return inflater.inflate(R.layout.fragment_balance, container, false) + ui = FragmentBalanceBinding.inflate(layoutInflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -65,24 +68,24 @@ class BalanceFragment : Fragment() { viewModel.balance.observe(viewLifecycleOwner, Observer { result -> onBalanceUpdated(result) }) - button5.setOnClickListener { onAmountButtonPressed(5) } - button10.setOnClickListener { onAmountButtonPressed(10) } - button20.setOnClickListener { onAmountButtonPressed(20) } - button50.setOnClickListener { onAmountButtonPressed(50) } + ui.button5.setOnClickListener { onAmountButtonPressed(5) } + ui.button10.setOnClickListener { onAmountButtonPressed(10) } + ui.button20.setOnClickListener { onAmountButtonPressed(20) } + ui.button50.setOnClickListener { onAmountButtonPressed(50) } if (savedInstanceState != null) { - amountView.editText!!.setText(savedInstanceState.getCharSequence("amountView")) + ui.amountView.editText!!.setText(savedInstanceState.getCharSequence("amountView")) } - amountView.editText!!.setOnEditorActionListener { _, actionId, _ -> + ui.amountView.editText!!.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_GO) { onAmountConfirmed(getAmountFromView()) true } else false } configManager.currency.observe(viewLifecycleOwner, Observer { currency -> - currencyView.text = currency + ui.currencyView.text = currency }) - confirmWithdrawalButton.setOnClickListener { onAmountConfirmed(getAmountFromView()) } + ui.confirmWithdrawalButton.setOnClickListener { onAmountConfirmed(getAmountFromView()) } } override fun onStart() { @@ -96,7 +99,7 @@ class BalanceFragment : Fragment() { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) // for some reason automatic restore isn't working at the moment!? - amountView?.editText?.text.let { + ui.amountView.editText?.text.let { outState.putCharSequence("amountView", it) } } @@ -121,34 +124,34 @@ class BalanceFragment : Fragment() { private fun onBalanceUpdated(result: BalanceResult) { val uiList = listOf( - introView, - button5, button10, button20, button50, - amountView, currencyView, confirmWithdrawalButton + ui.introView, + ui.button5, ui.button10, ui.button20, ui.button50, + ui.amountView, ui.currencyView, ui.confirmWithdrawalButton ) when (result) { is BalanceResult.Success -> { - balanceView.text = result.amount.toString() + ui.balanceView.text = result.amount.toString() uiList.forEach { it.fadeIn() } } is BalanceResult.Error -> { - balanceView.text = getString(R.string.balance_error, result.msg) + ui.balanceView.text = getString(R.string.balance_error, result.msg) uiList.forEach { it.fadeOut() } } BalanceResult.Offline -> { - balanceView.text = getString(R.string.balance_offline) + ui.balanceView.text = getString(R.string.balance_offline) uiList.forEach { it.fadeOut() } } }.exhaustive - progressBar.fadeOut() + ui.progressBar.fadeOut() } private fun onAmountButtonPressed(amount: Int) { - amountView.editText!!.setText(amount.toString()) - amountView.error = null + ui.amountView.editText!!.setText(amount.toString()) + ui.amountView.error = null } private fun getAmountFromView(): Amount { - val str = amountView.editText!!.text.toString() + val str = ui.amountView.editText!!.text.toString() val currency = configManager.currency.value!! if (str.isBlank()) return Amount.zero(currency) return Amount.fromString(currency, str) @@ -156,11 +159,11 @@ class BalanceFragment : Fragment() { private fun onAmountConfirmed(amount: Amount) { if (amount.isZero()) { - amountView.error = getString(R.string.withdraw_error_zero) + ui.amountView.error = getString(R.string.withdraw_error_zero) } else if (!withdrawManager.hasSufficientBalance(amount)) { - amountView.error = getString(R.string.withdraw_error_insufficient_balance) + ui.amountView.error = getString(R.string.withdraw_error_insufficient_balance) } else { - amountView.error = null + ui.amountView.error = null withdrawManager.withdraw(amount) actionBalanceFragmentToTransactionFragment().let { findNavController().navigate(it) @@ -177,13 +180,13 @@ class BalanceFragment : Fragment() { is WithdrawStatus.Aborted -> getString(R.string.transaction_last_aborted) else -> getString(R.string.transaction_last_error) } - lastTransactionView.text = text + ui.lastTransactionView.text = text val drawable = if (status == WithdrawStatus.Success) R.drawable.ic_check_circle else R.drawable.ic_error - lastTransactionView.setCompoundDrawablesRelativeWithIntrinsicBounds(drawable, 0, 0, 0) - lastTransactionView.visibility = VISIBLE + ui.lastTransactionView.setCompoundDrawablesRelativeWithIntrinsicBounds(drawable, 0, 0, 0) + ui.lastTransactionView.visibility = VISIBLE } } diff --git a/cashier/src/main/java/net/taler/cashier/MainActivity.kt b/cashier/src/main/java/net/taler/cashier/MainActivity.kt index ae31be5..2f4c4ec 100644 --- a/cashier/src/main/java/net/taler/cashier/MainActivity.kt +++ b/cashier/src/main/java/net/taler/cashier/MainActivity.kt @@ -25,18 +25,21 @@ import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment -import kotlinx.android.synthetic.main.activity_main.* +import net.taler.cashier.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { private val viewModel: MainViewModel by viewModels() private val configManager by lazy { viewModel.configManager} + + private lateinit var ui: ActivityMainBinding private lateinit var nav: NavController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - setSupportActionBar(toolbar) + ui = ActivityMainBinding.inflate(layoutInflater) + setContentView(ui.root) + setSupportActionBar(ui.toolbar) val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment nav = navHostFragment.navController diff --git a/cashier/src/main/java/net/taler/cashier/config/Config.kt b/cashier/src/main/java/net/taler/cashier/config/Config.kt index b50cf92..c898f95 100644 --- a/cashier/src/main/java/net/taler/cashier/config/Config.kt +++ b/cashier/src/main/java/net/taler/cashier/config/Config.kt @@ -17,7 +17,6 @@ package net.taler.cashier.config import kotlinx.serialization.Serializable -import net.taler.lib.common.Version import okhttp3.Credentials data class Config( diff --git a/cashier/src/main/java/net/taler/cashier/config/ConfigFragment.kt b/cashier/src/main/java/net/taler/cashier/config/ConfigFragment.kt index a7aaf2f..6498590 100644 --- a/cashier/src/main/java/net/taler/cashier/config/ConfigFragment.kt +++ b/cashier/src/main/java/net/taler/cashier/config/ConfigFragment.kt @@ -33,9 +33,9 @@ import androidx.lifecycle.Observer import androidx.navigation.fragment.findNavController import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_LONG import com.google.android.material.snackbar.Snackbar -import kotlinx.android.synthetic.main.fragment_config.* import net.taler.cashier.MainViewModel import net.taler.cashier.R +import net.taler.cashier.databinding.FragmentConfigBinding import net.taler.common.exhaustive private const val URL_BANK_TEST = "https://bank.test.taler.net" @@ -46,38 +46,41 @@ class ConfigFragment : Fragment() { private val viewModel: MainViewModel by activityViewModels() private val configManager by lazy { viewModel.configManager} + private lateinit var ui: FragmentConfigBinding + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_config, container, false) + ui = FragmentConfigBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { if (savedInstanceState == null) { if (configManager.config.bankUrl.isBlank()) { - urlView.editText!!.setText(URL_BANK_TEST) + ui.urlView.editText!!.setText(URL_BANK_TEST) } else { - urlView.editText!!.setText(configManager.config.bankUrl) + ui.urlView.editText!!.setText(configManager.config.bankUrl) } - usernameView.editText!!.setText(configManager.config.username) - passwordView.editText!!.setText(configManager.config.password) + ui.usernameView.editText!!.setText(configManager.config.username) + ui.passwordView.editText!!.setText(configManager.config.password) } else { - urlView.editText!!.setText(savedInstanceState.getCharSequence("urlView")) - usernameView.editText!!.setText(savedInstanceState.getCharSequence("usernameView")) - passwordView.editText!!.setText(savedInstanceState.getCharSequence("passwordView")) + ui.urlView.editText!!.setText(savedInstanceState.getCharSequence("urlView")) + ui.usernameView.editText!!.setText(savedInstanceState.getCharSequence("usernameView")) + ui.passwordView.editText!!.setText(savedInstanceState.getCharSequence("passwordView")) } - saveButton.setOnClickListener { + ui.saveButton.setOnClickListener { val config = Config( - bankUrl = urlView.editText!!.text.toString(), - username = usernameView.editText!!.text.toString(), - password = passwordView.editText!!.text.toString() + bankUrl = ui.urlView.editText!!.text.toString(), + username = ui.usernameView.editText!!.text.toString(), + password = ui.passwordView.editText!!.text.toString() ) if (checkConfig(config)) { // show progress - saveButton.visibility = INVISIBLE - progressBar.visibility = VISIBLE + ui.saveButton.visibility = INVISIBLE + ui.progressBar.visibility = VISIBLE // kick off check and observe result configManager.checkAndSaveConfig(config) configManager.configResult.observe(viewLifecycleOwner, onConfigResult) @@ -87,43 +90,43 @@ class ConfigFragment : Fragment() { inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0) } } - demoView.text = HtmlCompat.fromHtml( + ui.demoView.text = HtmlCompat.fromHtml( getString(R.string.config_demo_hint, URL_BANK_TEST_REGISTER), FROM_HTML_MODE_LEGACY ) - demoView.movementMethod = LinkMovementMethod.getInstance() + ui.demoView.movementMethod = LinkMovementMethod.getInstance() } override fun onStart() { super.onStart() // focus on password if it is the only missing value (like after locking) - if (urlView.editText!!.text.isNotBlank() - && usernameView.editText!!.text.isNotBlank() - && passwordView.editText!!.text.isBlank() + if (ui.urlView.editText!!.text.isNotBlank() + && ui.usernameView.editText!!.text.isNotBlank() + && ui.passwordView.editText!!.text.isBlank() ) { - passwordView.editText!!.requestFocus() + ui.passwordView.editText!!.requestFocus() } } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) // for some reason automatic restore isn't working at the moment!? - outState.putCharSequence("urlView", urlView.editText?.text) - outState.putCharSequence("usernameView", usernameView.editText?.text) - outState.putCharSequence("passwordView", passwordView.editText?.text) + outState.putCharSequence("urlView", ui.urlView.editText?.text) + outState.putCharSequence("usernameView", ui.usernameView.editText?.text) + outState.putCharSequence("passwordView", ui.passwordView.editText?.text) } private fun checkConfig(config: Config): Boolean { if (!config.bankUrl.startsWith("https://")) { - urlView.error = getString(R.string.config_bank_url_error) - urlView.requestFocus() + ui.urlView.error = getString(R.string.config_bank_url_error) + ui.urlView.requestFocus() return false } if (config.username.isBlank()) { - usernameView.error = getString(R.string.config_username_error) - usernameView.requestFocus() + ui.usernameView.error = getString(R.string.config_username_error) + ui.usernameView.requestFocus() return false } - urlView.isErrorEnabled = false + ui.urlView.isErrorEnabled = false return true } @@ -146,8 +149,8 @@ class ConfigFragment : Fragment() { } } }.exhaustive - saveButton.visibility = VISIBLE - progressBar.visibility = INVISIBLE + ui.saveButton.visibility = VISIBLE + ui.progressBar.visibility = INVISIBLE configManager.configResult.removeObservers(viewLifecycleOwner) } diff --git a/cashier/src/main/java/net/taler/cashier/withdraw/ErrorFragment.kt b/cashier/src/main/java/net/taler/cashier/withdraw/ErrorFragment.kt index ea33b0d..4f98847 100644 --- a/cashier/src/main/java/net/taler/cashier/withdraw/ErrorFragment.kt +++ b/cashier/src/main/java/net/taler/cashier/withdraw/ErrorFragment.kt @@ -24,33 +24,36 @@ 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_error.* import net.taler.cashier.MainViewModel import net.taler.cashier.R +import net.taler.cashier.databinding.FragmentErrorBinding class ErrorFragment : Fragment() { private val viewModel: MainViewModel by activityViewModels() private val withdrawManager by lazy { viewModel.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?) { withdrawManager.withdrawStatus.observe(viewLifecycleOwner, Observer { status -> if (status == null) return@Observer if (status is WithdrawStatus.Aborted) { - textView.setText(R.string.transaction_aborted) + ui.textView.setText(R.string.transaction_aborted) } else if (status is WithdrawStatus.Error) { - textView.text = status.msg + ui.textView.text = status.msg } withdrawManager.completeTransaction() }) - backButton.setOnClickListener { + ui.backButton.setOnClickListener { findNavController().popBackStack() } } diff --git a/cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt b/cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt index 0726a77..ffb1539 100644 --- a/cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt +++ b/cashier/src/main/java/net/taler/cashier/withdraw/TransactionFragment.kt @@ -25,11 +25,10 @@ import android.view.ViewGroup import androidx.core.content.ContextCompat.getColor 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_transaction.* import net.taler.cashier.MainViewModel import net.taler.cashier.R +import net.taler.cashier.databinding.FragmentTransactionBinding import net.taler.cashier.withdraw.TransactionFragmentDirections.Companion.actionTransactionFragmentToBalanceFragment import net.taler.cashier.withdraw.TransactionFragmentDirections.Companion.actionTransactionFragmentToErrorFragment import net.taler.cashier.withdraw.WithdrawResult.Error @@ -46,30 +45,33 @@ class TransactionFragment : Fragment() { private val withdrawManager by lazy { viewModel.withdrawManager } private val nfcManager = NfcManager() + private lateinit var ui: FragmentTransactionBinding + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - return inflater.inflate(R.layout.fragment_transaction, container, false) + ui = FragmentTransactionBinding.inflate(inflater, container, false) + return ui.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - withdrawManager.withdrawAmount.observe(viewLifecycleOwner, Observer { amount -> - amountView.text = amount?.toString() + withdrawManager.withdrawAmount.observe(viewLifecycleOwner, { amount -> + ui.amountView.text = amount?.toString() }) - withdrawManager.withdrawResult.observe(viewLifecycleOwner, Observer { result -> + withdrawManager.withdrawResult.observe(viewLifecycleOwner, { result -> onWithdrawResultReceived(result) }) - withdrawManager.withdrawStatus.observe(viewLifecycleOwner, Observer { status -> + withdrawManager.withdrawStatus.observe(viewLifecycleOwner, { status -> onWithdrawStatusChanged(status) }) // change intro text depending on whether NFC is available or not val hasNfc = NfcManager.hasNfc(requireContext()) val intro = if (hasNfc) R.string.transaction_intro_nfc else R.string.transaction_intro - introView.setText(intro) + ui.introView.setText(intro) - cancelButton.setOnClickListener { + ui.cancelButton.setOnClickListener { findNavController().popBackStack() } } @@ -95,9 +97,9 @@ class TransactionFragment : Fragment() { private fun onWithdrawResultReceived(result: WithdrawResult?) { if (result != null) { - progressBar.animate() + ui.progressBar.animate() .alpha(0f) - .withEndAction { progressBar?.visibility = INVISIBLE } + .withEndAction { ui.progressBar.visibility = INVISIBLE } .setDuration(750) .start() } @@ -113,12 +115,12 @@ class TransactionFragment : Fragment() { nfcManager ) // show QR code - qrCodeView.alpha = 0f - qrCodeView.animate() + ui.qrCodeView.alpha = 0f + ui.qrCodeView.animate() .alpha(1f) .withStartAction { - qrCodeView.visibility = VISIBLE - qrCodeView.setImageBitmap(result.qrCode) + ui.qrCodeView.visibility = VISIBLE + ui.qrCodeView.setImageBitmap(result.qrCode) } .setDuration(750) .start() @@ -129,30 +131,30 @@ class TransactionFragment : Fragment() { private fun setErrorMsg(str: String) { val c = getColor(requireContext(), R.color.design_default_color_error) - introView.setTextColor(c) - introView.text = str + ui.introView.setTextColor(c) + ui.introView.text = str } private fun onWithdrawStatusChanged(status: WithdrawStatus?): Any = when (status) { is WithdrawStatus.SelectionDone -> { - qrCodeView.fadeOut { - qrCodeView?.setImageResource(R.drawable.ic_arrow) - qrCodeView?.fadeIn() + ui.qrCodeView.fadeOut { + ui.qrCodeView.setImageResource(R.drawable.ic_arrow) + ui.qrCodeView.fadeIn() } - introView.fadeOut { - introView?.text = getString(R.string.transaction_intro_scanned) - introView?.fadeIn { - confirmButton?.isEnabled = true - confirmButton?.setOnClickListener { + ui.introView.fadeOut { + ui.introView.text = getString(R.string.transaction_intro_scanned) + ui.introView.fadeIn { + ui.confirmButton.isEnabled = true + ui.confirmButton.setOnClickListener { withdrawManager.confirm(status.withdrawalId) } } } } is WithdrawStatus.Confirming -> { - confirmButton.isEnabled = false - qrCodeView.fadeOut() - progressBar.fadeIn() + ui.confirmButton.isEnabled = false + ui.qrCodeView.fadeOut() + ui.progressBar.fadeIn() } is WithdrawStatus.Success -> { withdrawManager.completeTransaction() |