taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

SetDonauFragment.kt (8051B)


      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.donau
     18 
     19 import android.os.Bundle
     20 import android.view.LayoutInflater
     21 import android.view.View
     22 import android.view.ViewGroup
     23 import android.widget.Toast
     24 import androidx.compose.foundation.layout.Column
     25 import androidx.compose.foundation.layout.fillMaxSize
     26 import androidx.compose.foundation.layout.fillMaxWidth
     27 import androidx.compose.foundation.layout.padding
     28 import androidx.compose.foundation.rememberScrollState
     29 import androidx.compose.foundation.text.KeyboardActions
     30 import androidx.compose.foundation.text.KeyboardOptions
     31 import androidx.compose.foundation.verticalScroll
     32 import androidx.compose.material3.Button
     33 import androidx.compose.material3.OutlinedTextField
     34 import androidx.compose.material3.Text
     35 import androidx.compose.runtime.Composable
     36 import androidx.compose.runtime.LaunchedEffect
     37 import androidx.compose.runtime.collectAsState
     38 import androidx.compose.runtime.getValue
     39 import androidx.compose.runtime.livedata.observeAsState
     40 import androidx.compose.runtime.mutableStateOf
     41 import androidx.compose.runtime.remember
     42 import androidx.compose.runtime.setValue
     43 import androidx.compose.ui.Alignment
     44 import androidx.compose.ui.Modifier
     45 import androidx.compose.ui.focus.FocusDirection
     46 import androidx.compose.ui.platform.ComposeView
     47 import androidx.compose.ui.platform.LocalFocusManager
     48 import androidx.compose.ui.platform.LocalSoftwareKeyboardController
     49 import androidx.compose.ui.res.stringResource
     50 import androidx.compose.ui.text.TextStyle
     51 import androidx.compose.ui.text.font.FontFamily.Companion.Monospace
     52 import androidx.compose.ui.text.input.ImeAction
     53 import androidx.compose.ui.tooling.preview.Preview
     54 import androidx.compose.ui.unit.dp
     55 import androidx.fragment.app.Fragment
     56 import androidx.fragment.app.activityViewModels
     57 import androidx.navigation.fragment.findNavController
     58 import net.taler.common.showError
     59 import net.taler.wallet.BottomInsetsSpacer
     60 import net.taler.wallet.main.MainViewModel
     61 import net.taler.wallet.R
     62 import net.taler.wallet.compose.ErrorComposable
     63 import net.taler.wallet.compose.LoadingScreen
     64 import net.taler.wallet.compose.TalerSurface
     65 import net.taler.wallet.donau.GetDonauStatus.Error
     66 import net.taler.wallet.donau.GetDonauStatus.Loading
     67 import net.taler.wallet.donau.GetDonauStatus.None
     68 import net.taler.wallet.donau.GetDonauStatus.Success
     69 import net.taler.wallet.showError
     70 
     71 class SetDonauFragment: Fragment() {
     72     val model: MainViewModel by activityViewModels()
     73     val donauManager by lazy { model.donauManager }
     74 
     75     override fun onCreateView(
     76         inflater: LayoutInflater,
     77         container: ViewGroup?,
     78         savedInstanceState: Bundle?
     79     ): View = ComposeView(requireContext()).apply {
     80         val saveShouldExit = arguments?.getBoolean("saveShouldExit") == true
     81         val donauBaseUrl = arguments?.getString("donauBaseUrl")
     82 
     83         setContent {
     84             TalerSurface {
     85                 val donauStatus by donauManager.donauStatus.collectAsState()
     86                 val devMode by model.devMode.observeAsState()
     87                 when(val status = donauStatus) {
     88                     Loading, None -> LoadingScreen()
     89                     is Success -> SetDonauComposable(
     90                         initialUrl = donauBaseUrl,
     91                         donauInfo = status.donauInfo,
     92                         onSetDonauInfo = { info ->
     93                             donauManager.setDonau(info,
     94                                 {
     95                                     Toast.makeText(
     96                                         requireContext(),
     97                                         getString(R.string.donau_ready),
     98                                         Toast.LENGTH_LONG).show()
     99                                     if (saveShouldExit) {
    100                                         findNavController().popBackStack()
    101                                     }
    102                                 },
    103                                 { error ->
    104                                     if(model.devMode.value == true) {
    105                                         showError(error)
    106                                     } else {
    107                                         showError(error.userFacingMsg)
    108                                     }
    109                                 }
    110                             )
    111                         },
    112                     )
    113                     is Error -> ErrorComposable(
    114                         error = status.error,
    115                         modifier = Modifier
    116                             .fillMaxSize()
    117                             .verticalScroll(rememberScrollState()),
    118                         devMode = devMode == true,
    119                     )
    120                 }
    121 
    122                 LaunchedEffect(Unit) {
    123                     donauManager.getDonau()
    124                 }
    125             }
    126         }
    127     }
    128 }
    129 
    130 @Composable
    131 fun SetDonauComposable(
    132     initialUrl: String? = null,
    133     donauInfo: DonauInfo?,
    134     onSetDonauInfo: (info: DonauInfo) -> Unit,
    135 ) {
    136     val focusManager = LocalFocusManager.current
    137     val keyboardController = LocalSoftwareKeyboardController.current
    138     var donauBaseUrl by remember { mutableStateOf(initialUrl ?: donauInfo?.donauBaseUrl ?: "") }
    139     var taxPayerId by remember { mutableStateOf(donauInfo?.taxPayerId ?: "") }
    140 
    141     Column(Modifier.fillMaxSize()) {
    142         OutlinedTextField(
    143             modifier = Modifier.padding(
    144                 bottom = 16.dp,
    145                 start = 16.dp,
    146                 end = 16.dp,
    147             ).fillMaxWidth(),
    148             value = donauBaseUrl,
    149             onValueChange = {
    150                 donauBaseUrl = it
    151             },
    152             singleLine = true,
    153             isError = donauBaseUrl.isBlank(),
    154             label = { Text(stringResource(R.string.donau_url)) },
    155             keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
    156             keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Next) }),
    157         )
    158 
    159         OutlinedTextField(
    160             modifier = Modifier.padding(
    161                 bottom = 16.dp,
    162                 start = 16.dp,
    163                 end = 16.dp,
    164             ).fillMaxWidth(),
    165             value = taxPayerId,
    166             onValueChange = {
    167                 taxPayerId = it
    168             },
    169             singleLine = true,
    170             textStyle = TextStyle(fontFamily = Monospace),
    171             isError = taxPayerId.isBlank(),
    172             label = { Text(stringResource(R.string.donau_id)) },
    173             keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
    174             keyboardActions = KeyboardActions(onNext = { focusManager.moveFocus(FocusDirection.Exit) }),
    175         )
    176 
    177         Button(
    178             modifier = Modifier
    179                 .padding(horizontal = 16.dp)
    180                 .align(Alignment.End),
    181             onClick = {
    182                 focusManager.clearFocus()
    183                 keyboardController?.hide()
    184                 onSetDonauInfo(DonauInfo(
    185                     donauBaseUrl = donauBaseUrl,
    186                     taxPayerId = taxPayerId,
    187                 ))
    188             },
    189             enabled = donauBaseUrl.isNotBlank() &&
    190                     taxPayerId.isNotBlank()
    191         ) {
    192             Text(stringResource(R.string.save))
    193         }
    194 
    195         BottomInsetsSpacer()
    196     }
    197 }
    198 
    199 @Preview
    200 @Composable
    201 fun SetDonauComposablePreview() {
    202     TalerSurface {
    203         SetDonauComposable("https://donau.test.taler.net/", null) {}
    204     }
    205 }