taler-android

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

commit d10de2138f1359c1dd14af279fe367a3ce339893
parent 115d4e0da9e278e300be29634a7551072e356a82
Author: Iván Ávalos <avalos@disroot.org>
Date:   Thu, 22 Aug 2024 11:28:10 +0200

[pos] UI/UX improvements

Diffstat:
Mmerchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt | 5+++++
Mmerchant-terminal/src/main/java/net/taler/merchantpos/order/OrderFragment.kt | 29+++++++++++++++++++++++++++++
Mmerchant-terminal/src/main/java/net/taler/merchantpos/payment/ProcessPaymentFragment.kt | 12++++++++++--
Amerchant-terminal/src/main/res/drawable/ic_menu_reload.xml | 21+++++++++++++++++++++
Mmerchant-terminal/src/main/res/layout/fragment_process_payment.xml | 52+++++++++++++++++++++++++++++++++++++++++-----------
Amerchant-terminal/src/main/res/menu/order.xml | 25+++++++++++++++++++++++++
Mmerchant-terminal/src/main/res/values/strings.xml | 2++
Mtaler-kotlin-android/src/main/java/net/taler/common/AndroidUtils.kt | 10++++++++++
Mtaler-kotlin-android/src/main/res/values/strings.xml | 1+
Mwallet/src/main/java/net/taler/wallet/compose/ExpandableCard.kt | 2--
Mwallet/src/main/java/net/taler/wallet/compose/QrCodeUriComposable.kt | 8+-------
11 files changed, 145 insertions(+), 22 deletions(-)

diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/config/ConfigManager.kt @@ -119,6 +119,11 @@ class ConfigManager( } @UiThread + fun reloadConfig() { + fetchConfig(config, true, config.hasPassword()) + } + + @UiThread fun fetchConfig(config: Config, save: Boolean, savePassword: Boolean = false) { mConfigUpdateResult.value = null val configToSave = if (save) { diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/order/OrderFragment.kt @@ -18,10 +18,16 @@ package net.taler.merchantpos.order 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.ViewGroup +import android.widget.Toast +import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Lifecycle import androidx.transition.TransitionManager.beginDelayedTransition import net.taler.common.navigate import net.taler.merchantpos.MainViewModel @@ -52,6 +58,29 @@ class OrderFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + requireActivity().addMenuProvider(object: MenuProvider { + override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { + menuInflater.inflate(R.menu.order, menu) + } + + override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + return when(menuItem.itemId) { + R.id.reload -> { + viewModel.configManager.reloadConfig() + Toast.makeText( + requireContext(), + getString(R.string.toast_reloading), + Toast.LENGTH_LONG, + ).show() + true + } + + else -> false + } + } + }, viewLifecycleOwner, Lifecycle.State.RESUMED) + orderManager.currentOrderId.observe(viewLifecycleOwner) { orderId -> val liveOrder = orderManager.getOrder(orderId) onOrderSwitched(orderId, liveOrder) diff --git a/merchant-terminal/src/main/java/net/taler/merchantpos/payment/ProcessPaymentFragment.kt b/merchant-terminal/src/main/java/net/taler/merchantpos/payment/ProcessPaymentFragment.kt @@ -26,9 +26,11 @@ import androidx.navigation.fragment.findNavController import com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_LONG import com.google.android.material.snackbar.Snackbar import net.taler.common.QrCodeManager.makeQrCode +import net.taler.common.copyToClipBoard import net.taler.common.fadeIn import net.taler.common.fadeOut import net.taler.common.navigate +import net.taler.common.shareText import net.taler.common.showError import net.taler.lib.android.TalerNfcService.Companion.hasNfc import net.taler.merchantpos.MainViewModel @@ -80,12 +82,18 @@ class ProcessPaymentFragment : Fragment() { return } if (payment.claimed) { - ui.qrcodeView.fadeOut() + ui.qrcodeLayout.fadeOut() ui.payIntroView.setText(R.string.payment_claimed) } else { payment.talerPayUri?.let { ui.qrcodeView.setImageBitmap(makeQrCode(it)) - ui.qrcodeView.fadeIn() + ui.shareButton.setOnClickListener { _ -> + requireContext().shareText(it) + } + ui.copyButton.setOnClickListener { _ -> + copyToClipBoard(requireContext(), "Payment URI", it) + } + ui.qrcodeLayout.fadeIn() ui.progressBar.fadeOut() } } diff --git a/merchant-terminal/src/main/res/drawable/ic_menu_reload.xml b/merchant-terminal/src/main/res/drawable/ic_menu_reload.xml @@ -0,0 +1,21 @@ +<!-- + ~ This file is part of GNU Taler + ~ (C) 2024 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/> + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"> + + <path android:fillColor="@android:color/white" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/> + +</vector> diff --git a/merchant-terminal/src/main/res/layout/fragment_process_payment.xml b/merchant-terminal/src/main/res/layout/fragment_process_payment.xml @@ -21,29 +21,59 @@ android:layout_height="match_parent" tools:context=".payment.ProcessPaymentFragment"> - <ImageView - android:id="@+id/qrcodeView" + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/qrcodeLayout" android:layout_width="0dp" android:layout_height="0dp" - android:layout_margin="32dp" - android:visibility="invisible" + android:layout_margin="12dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/guideline" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - tools:ignore="ContentDescription" - tools:src="@tools:sample/avatars" - tools:visibility="visible" /> + android:visibility="invisible" + tools:visibility="visible"> + + <ImageView + android:id="@+id/qrcodeView" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_margin="12dp" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@id/shareButton" + tools:ignore="ContentDescription" + tools:src="@tools:sample/avatars" /> + + <Button + android:id="@+id/shareButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toStartOf="@id/copyButton" + android:text="@string/share"/> + + <Button + android:id="@+id/copyButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/shareButton" + android:text="@string/copy"/> + + </androidx.constraintlayout.widget.ConstraintLayout> <ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleLarge" android:layout_width="wrap_content" android:layout_height="wrap_content" - app:layout_constraintBottom_toBottomOf="@+id/qrcodeView" - app:layout_constraintEnd_toEndOf="@+id/qrcodeView" - app:layout_constraintStart_toStartOf="@+id/qrcodeView" - app:layout_constraintTop_toTopOf="@+id/qrcodeView" /> + app:layout_constraintBottom_toBottomOf="@+id/qrcodeLayout" + app:layout_constraintEnd_toEndOf="@+id/qrcodeLayout" + app:layout_constraintStart_toStartOf="@+id/qrcodeLayout" + app:layout_constraintTop_toTopOf="@+id/qrcodeLayout" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/guideline" diff --git a/merchant-terminal/src/main/res/menu/order.xml b/merchant-terminal/src/main/res/menu/order.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ This file is part of GNU Taler + ~ (C) 2024 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/> + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item + android:id="@+id/reload" + android:icon="@drawable/ic_menu_reload" + android:title="@string/menu_reload" + app:showAsAction="ifRoom" /> +</menu> +\ No newline at end of file diff --git a/merchant-terminal/src/main/res/values/strings.xml b/merchant-terminal/src/main/res/values/strings.xml @@ -6,6 +6,7 @@ <string name="menu_order">Orders</string> <string name="menu_history">History</string> <string name="menu_settings">Settings</string> + <string name="menu_reload">Reload</string> <string name="order_label_title">Order #%s</string> <!-- The placeholder is the total order amount with currency --> @@ -79,6 +80,7 @@ <string name="error_cancelled">Payment cancelled</string> <string name="error_history">Error fetching the order history</string> + <string name="toast_reloading">Reloading inventory</string> <string name="toast_back_to_exit">Click «back» again to exit</string> <string name="host_apdu_service_desc">Taler Merchant NFC payments</string> diff --git a/taler-kotlin-android/src/main/java/net/taler/common/AndroidUtils.kt b/taler-kotlin-android/src/main/java/net/taler/common/AndroidUtils.kt @@ -18,6 +18,8 @@ package net.taler.common import android.Manifest.permission.ACCESS_NETWORK_STATE import android.content.ActivityNotFoundException +import android.content.ClipData +import android.content.ClipboardManager import android.content.Context import android.content.Context.CONNECTIVITY_SERVICE import android.content.Intent @@ -46,6 +48,7 @@ import android.view.inputmethod.InputMethodManager import androidx.annotation.RequiresPermission import androidx.annotation.StringRes import androidx.core.content.ContextCompat.getSystemService +import androidx.core.content.getSystemService import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.navigation.NavDirections @@ -201,3 +204,9 @@ fun Version.getIncompatibleStringOrNull(context: Context, otherVersion: String): if (match.currentCmp > 0) return context.getString(R.string.version_too_new) throw AssertionError("$this == $other") } + +fun copyToClipBoard(context: Context, label: String, str: String) { + val clipboard = context.getSystemService<ClipboardManager>() + val clip = ClipData.newPlainText(label, str) + clipboard?.setPrimaryClip(clip) +} +\ No newline at end of file diff --git a/taler-kotlin-android/src/main/res/values/strings.xml b/taler-kotlin-android/src/main/res/values/strings.xml @@ -21,4 +21,5 @@ <string name="close">Close</string> <string name="share">Share</string> + <string name="copy">Copy</string> </resources> diff --git a/wallet/src/main/java/net/taler/wallet/compose/ExpandableCard.kt b/wallet/src/main/java/net/taler/wallet/compose/ExpandableCard.kt @@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.KeyboardArrowDown -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.OutlinedCard @@ -43,7 +42,6 @@ import androidx.compose.ui.draw.rotate import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -@OptIn(ExperimentalMaterial3Api::class) @Composable fun ExpandableCard( modifier: Modifier = Modifier, diff --git a/wallet/src/main/java/net/taler/wallet/compose/QrCodeUriComposable.kt b/wallet/src/main/java/net/taler/wallet/compose/QrCodeUriComposable.kt @@ -51,8 +51,8 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.min -import androidx.core.content.getSystemService import net.taler.common.QrCodeManager +import net.taler.common.copyToClipBoard import net.taler.wallet.R @Composable @@ -148,9 +148,3 @@ fun CopyToClipboardButton( Text(buttonText) } } - -fun copyToClipBoard(context: Context, label: String, str: String) { - val clipboard = context.getSystemService<ClipboardManager>() - val clip = ClipData.newPlainText(label, str) - clipboard?.setPrimaryClip(clip) -}