commit 3c7308373cd4042bb9694233081971d4990b8d24
parent 3fa0612fae6cbd92701d04cd84ac60fccbac016e
Author: Iván Ávalos <avalos@disroot.org>
Date: Thu, 6 Jun 2024 11:59:16 -0600
[wallet] Show warning when scanning unrelated QR from send/receive screens
bug 0008902
Diffstat:
5 files changed, 70 insertions(+), 4 deletions(-)
diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
@@ -41,6 +41,7 @@ import androidx.navigation.ui.setupWithNavController
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceFragmentCompat.OnPreferenceStartFragmentCallback
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationView.OnNavigationItemSelectedListener
import com.google.zxing.client.android.Intents.Scan.MIXED_SCAN
import com.google.zxing.client.android.Intents.Scan.SCAN_TYPE
@@ -67,7 +68,11 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener,
private val barcodeLauncher = registerForActivityResult(ScanContract()) { result ->
if (result == null || result.contents == null) return@registerForActivityResult
- handleTalerUri(result.contents, "QR code")
+ if (model.checkScanQrContext(result.contents)) {
+ handleTalerUri(result.contents, "QR code")
+ } else {
+ confirmTalerUri(result.contents, "QR code")
+ }
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -175,6 +180,25 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener,
return super.onOptionsItemSelected(item)
}
+ private fun confirmTalerUri(uri: String, from: String) {
+ MaterialAlertDialogBuilder(this).apply {
+ setTitle(R.string.qr_scan_context_title)
+ setMessage(when (model.getScanQrContext()) {
+ ScanQrContext.Send -> R.string.qr_scan_context_send_message
+ ScanQrContext.Receive -> R.string.qr_scan_context_receive_message
+ else -> error("invalid value")
+ })
+
+ setNegativeButton(R.string.ok) { _, _ ->
+ handleTalerUri(uri, from)
+ }
+
+ setNeutralButton(R.string.cancel) { dialog, _ ->
+ dialog.dismiss()
+ }
+ }.show()
+ }
+
private fun handleTalerUri(uri: String, from: String) {
val args = bundleOf("uri" to uri, "from" to from)
nav.navigate(R.id.action_global_handle_uri, args)
diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
@@ -17,6 +17,7 @@
package net.taler.wallet
import android.app.Application
+import android.net.Uri
import android.util.Log
import androidx.annotation.UiThread
import androidx.lifecycle.AndroidViewModel
@@ -68,6 +69,19 @@ private val observabilityNotifications = listOf(
"request-observability-event",
)
+private val sendUriActions = listOf(
+ "pay",
+ "tip",
+ "pay-pull",
+ "pay-template",
+)
+
+private val receiveUriActions = listOf(
+ "withdraw",
+ "refund",
+ "pay-push",
+)
+
class MainViewModel(
app: Application,
) : AndroidViewModel(app), VersionReceiver, NotificationReceiver {
@@ -116,6 +130,9 @@ class MainViewModel(
private val mScanCodeEvent = MutableLiveData<Event<Boolean>>()
val scanCodeEvent: LiveData<Event<Boolean>> = mScanCodeEvent
+ @set:Synchronized
+ private var scanQrContext = ScanQrContext.Unknown
+
override fun onVersionReceived(versionInfo: WalletCoreVersion) {
walletVersion = versionInfo.implementationSemver
walletVersionHash = versionInfo.implementationGitHash
@@ -209,10 +226,23 @@ class MainViewModel(
}
@UiThread
- fun scanCode() {
+ fun scanCode(context: ScanQrContext = ScanQrContext.Unknown) {
+ scanQrContext = context
mScanCodeEvent.value = true.toEvent()
}
+ fun getScanQrContext() = scanQrContext
+
+ fun checkScanQrContext(uri: String): Boolean {
+ val parsed = Uri.parse(uri)
+ val action = parsed.host
+ return when (scanQrContext) {
+ ScanQrContext.Send -> action in sendUriActions
+ ScanQrContext.Receive -> action in receiveUriActions
+ else -> true
+ }
+ }
+
fun setDevMode(enabled: Boolean, onError: (error: TalerErrorInfo) -> Unit) {
mDevMode.postValue(enabled)
viewModelScope.launch {
@@ -262,6 +292,12 @@ class MainViewModel(
}
+enum class ScanQrContext {
+ Send,
+ Receive,
+ Unknown,
+}
+
sealed class AmountResult {
class Success(val amount: Amount) : AmountResult()
object InsufficientBalance : AmountResult()
diff --git a/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt b/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
@@ -125,7 +125,7 @@ class ReceiveFundsFragment : Fragment() {
}
private fun onScanQr() {
- model.scanCode()
+ model.scanCode(ScanQrContext.Receive)
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
@@ -103,7 +103,7 @@ class SendFundsFragment : Fragment() {
}
private fun onScanQr() {
- model.scanCode()
+ model.scanCode(ScanQrContext.Send)
}
}
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
@@ -72,6 +72,12 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card
<string name="share_payment">Share payment link</string>
<string name="uri_invalid">Not a valid Taler URI</string>
+ <!-- QR code handling -->
+
+ <string name="qr_scan_context_title">Possibly unintended action</string>
+ <string name="qr_scan_context_receive_message">Seems like you were intending to receive money, but the QR code that you scanned corresponds to a different action. Do you wish to continue?</string>
+ <string name="qr_scan_context_send_message">Seems like you were intending to send money, but the QR code that you scanned corresponds to a different action. Do you wish to continue?</string>
+
<!-- Errors -->
<string name="error_unsupported_uri">Error: This Taler URI is not supported.</string>