PromptPaymentFragment.kt (7226B)
1 /* 2 * This file is part of GNU Taler 3 * (C) 2025 Taler Systems S.A. 4 * 5 * GNU Taler is free software; you can redistribute it and/or modify it under the 6 * terms of the GNU General Public License as published by the Free Software 7 * Foundation; either version 3, or (at your option) any later version. 8 * 9 * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY 10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License along with 14 * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 package net.taler.wallet.payment 18 19 import android.graphics.Bitmap 20 import android.os.Bundle 21 import android.util.Log 22 import android.view.LayoutInflater 23 import android.view.View 24 import android.view.ViewGroup 25 import androidx.compose.runtime.getValue 26 import androidx.compose.runtime.livedata.observeAsState 27 import androidx.compose.ui.platform.ComposeView 28 import androidx.core.os.bundleOf 29 import androidx.fragment.app.Fragment 30 import androidx.fragment.app.activityViewModels 31 import androidx.lifecycle.lifecycleScope 32 import androidx.navigation.fragment.findNavController 33 import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_LONG 34 import com.google.android.material.snackbar.Snackbar 35 import kotlinx.coroutines.launch 36 import net.taler.common.showError 37 import net.taler.wallet.main.MainViewModel 38 import net.taler.wallet.R 39 import net.taler.wallet.main.TAG 40 import net.taler.wallet.compose.LoadingScreen 41 import net.taler.wallet.compose.TalerSurface 42 import net.taler.wallet.showError 43 44 class PromptPaymentFragment: Fragment(), ProductImageClickListener { 45 private val model: MainViewModel by activityViewModels() 46 private val paymentManager by lazy { model.paymentManager } 47 private val transactionManager by lazy { model.transactionManager } 48 49 override fun onCreateView( 50 inflater: LayoutInflater, 51 container: ViewGroup?, 52 savedInstanceState: Bundle? 53 ): View? = ComposeView(requireContext()).apply { 54 setContent { 55 TalerSurface { 56 val payStatus by paymentManager.payStatus.observeAsState(PayStatus.None) 57 when(val status = payStatus) { 58 is PayStatus.None, 59 is PayStatus.Loading, 60 is PayStatus.Prepared -> LoadingScreen() 61 is PayStatus.Checked -> {} // does not apply, only used for templates 62 is PayStatus.Choices -> { 63 PromptPaymentComposable(status, 64 onConfirm = { index, useDonau -> 65 paymentManager.confirmPay( 66 transactionId = status.transactionId, 67 choiceIndex = index, 68 useDonau = useDonau, 69 ) 70 }, 71 onCancel = { 72 transactionManager.abortTransaction( 73 status.transactionId, 74 onSuccess = { 75 Snackbar.make( 76 requireView(), 77 getString(R.string.payment_aborted), 78 LENGTH_LONG 79 ).show() 80 findNavController().popBackStack() 81 }, 82 onError = { error -> 83 Log.e(TAG, "Error abortTransaction $error") 84 if (model.devMode.value == false) { 85 showError(error.userFacingMsg) 86 } else { 87 showError(error) 88 } 89 } 90 ) 91 }, 92 onClickImage = { bitmap -> 93 onImageClick(bitmap) 94 }, 95 checkDonauStatus = { index -> 96 status.choices.find { it.choiceIndex == index }?.let { choice -> 97 paymentManager.checkDonauForChoice(choice) 98 } ?: DonauStatus.Unavailable 99 }, 100 onSetupDonau = { donauBaseUrl -> 101 findNavController().navigate( 102 R.id.action_main_to_setDonau, 103 bundleOf( 104 "donauBaseUrl" to donauBaseUrl, 105 "saveShouldExit" to true, 106 ), 107 ) 108 }, 109 ) 110 } 111 112 else -> {} 113 } 114 } 115 } 116 } 117 118 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 119 super.onViewCreated(view, savedInstanceState) 120 paymentManager.payStatus.observe(viewLifecycleOwner) { status -> 121 when (status) { 122 is PayStatus.Success -> { 123 paymentManager.resetPayStatus() 124 navigateToTransaction(status.transactionId) 125 if (status.automaticExecution) { 126 Snackbar.make(requireView(), R.string.payment_automatic_execution, LENGTH_LONG).show() 127 } 128 } 129 130 is PayStatus.AlreadyPaid -> { 131 paymentManager.resetPayStatus() 132 navigateToTransaction(status.transactionId) 133 Snackbar.make(requireView(), R.string.payment_already_paid, LENGTH_LONG).show() 134 } 135 136 is PayStatus.Pending -> { 137 paymentManager.resetPayStatus() 138 navigateToTransaction(status.transactionId) 139 if (status.error != null) { 140 if (model.devMode.value == true) { 141 showError(status.error) 142 } else { 143 showError(status.error.userFacingMsg) 144 } 145 } 146 } 147 148 else -> {} 149 } 150 } 151 } 152 153 override fun onImageClick(image: Bitmap) { 154 val f = ProductImageFragment.new(image) 155 f.show(parentFragmentManager, "image") 156 } 157 158 private fun navigateToTransaction(id: String?) { 159 lifecycleScope.launch { 160 if (id != null && transactionManager.selectTransaction(id)) { 161 findNavController().navigate(R.id.action_global_transactionPayment) 162 } else { 163 findNavController().navigate(R.id.action_global_main) 164 } 165 } 166 } 167 } 168