improve qr encoding ui, save previous selection and add raw encoding for tx hex
What changed, and why it matters
This commit is a user-interface improvement for Sparrow Wallet's QR code sharing feature. It replaces a simple on/off toggle between two QR formats with a dropdown menu that also adds a new 'Raw' option for transaction hex, and it remembers the user's last chosen format. There is no indication this fixes a security vulnerability.
No security action required; review as normal UI/feature code.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change refactors QR encoding selection from a boolean useBbqrEncoding flag to a QREncoding enum (UR, BBQR, RAW). It persists the user’s choice via Config.setQrEncoding(), updates all call sites to pass the enum, and adds raw hex encoding for transaction QR displays. New SVG icons are added for the dropdown UI. No cryptographic, input-validation, or privilege changes are present.
Changed components
QRDisplayDialogQREncoding enum (new)Config persistenceAppControllerHeadersControllerSettingsControllerFileWalletExportPaneFileKeystoreExportPaneDescriptorQRDisplayDialogInspect captured patch +204 / −43
diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java
index 42f4c91..e816e59 100644
--- a/src/main/java/com/sparrowwallet/sparrow/AppController.java
+++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java
@@ -771,7 +771,8 @@ public class AppController implements Initializable {
byte[] txBytes = transaction.bitcoinSerialize();
UR ur = UR.fromBytes(txBytes);
BBQR bbqr = new BBQR(BBQRType.TXN, txBytes);
- QRDisplayDialog qrDisplayDialog = new QRDisplayDialog(ur, bbqr, false, false, false);
+ String raw = Utils.bytesToHex(txBytes);
+ QRDisplayDialog qrDisplayDialog = new QRDisplayDialog(ur, bbqr, raw, false, false, QREncoding.UR);
qrDisplayDialog.initOwner(rootStack.getScene().getWindow());
qrDisplayDialog.showAndWait();
} catch(Exception e) {
@@ -873,7 +874,7 @@ public class AppController implements Initializable {
byte[] psbtBytes = transactionTabData.getPsbt().getForExport().serialize();
CryptoPSBT cryptoPSBT = new CryptoPSBT(psbtBytes);
BBQR bbqr = new BBQR(BBQRType.PSBT, psbtBytes);
- QRDisplayDialog qrDisplayDialog = new QRDisplayDialog(cryptoPSBT.toUR(), bbqr, false, true, false);
+ QRDisplayDialog qrDisplayDialog = new QRDisplayDialog(cryptoPSBT.toUR(), bbqr, false, true, QREncoding.UR);
qrDisplayDialog.initOwner(rootStack.getScene().getWindow());
qrDisplayDialog.show();
}
diff --git a/src/main/java/com/sparrowwallet/sparrow/control/DescriptorQRDisplayDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/DescriptorQRDisplayDialog.java
index ab1d2c2..db4bf1c 100644
--- a/src/main/java/com/sparrowwallet/sparrow/control/DescriptorQRDisplayDialog.java
+++ b/src/main/java/com/sparrowwallet/sparrow/control/DescriptorQRDisplayDialog.java
@@ -9,8 +9,8 @@ import javafx.scene.control.*;
import javafx.scene.control.Button;
public class DescriptorQRDisplayDialog extends QRDisplayDialog {
- public DescriptorQRDisplayDialog(String walletName, String outputDescriptor, UR ur, BBQR bbqr, boolean selectBbqrButton) {
- super(ur, bbqr, false, false, selectBbqrButton);
+ public DescriptorQRDisplayDialog(String walletName, String outputDescriptor, UR ur, BBQR bbqr, QREncoding encoding) {
+ super(ur, bbqr, false, false, encoding);
DialogPane dialogPane = getDialogPane();
final ButtonType pdfButtonType = new javafx.scene.control.ButtonType("Save PDF...", ButtonBar.ButtonData.HELP_2);
@@ -20,7 +20,7 @@ public class DescriptorQRDisplayDialog extends QRDisplayDialog {
pdfButton.setGraphicTextGap(5);
pdfButton.setGraphic(getGlyph(FontAwesome5.Glyph.FILE_PDF));
pdfButton.addEventFilter(ActionEvent.ACTION, event -> {
- PdfUtils.saveOutputDescriptor(walletName, outputDescriptor, ur, isUseBbqrEncoding() ? bbqr : null);
+ PdfUtils.saveOutputDescriptor(walletName, outputDescriptor, ur, getEncoding() == QREncoding.BBQR ? bbqr : null);
event.consume();
});
}
diff --git a/src/main/java/com/sparrowwallet/sparrow/control/FileKeystoreExportPane.java b/src/main/java/com/sparrowwallet/sparrow/control/FileKeystoreExportPane.java
index 6eecf0f..7340bea 100644
--- a/src/main/java/com/sparrowwallet/sparrow/control/FileKeystoreExportPane.java
+++ b/src/main/java/com/sparrowwallet/sparrow/control/FileKeystoreExportPane.java
@@ -157,7 +157,7 @@ public class FileKeystoreExportPane extends TitledDescriptionPane {
if(exporter instanceof Bip129) {
UR ur = UR.fromBytes(baos.toByteArray());
BBQR bbqr = new BBQR(BBQRType.UNICODE, baos.toByteArray());
- qrDisplayDialog = new QRDisplayDialog(ur, bbqr, false, true, false);
+ qrDisplayDialog = new QRDisplayDialog(ur, bbqr, false, true, QREncoding.UR);
} else {
qrDisplayDialog = new QRDisplayDialog(baos.toString(StandardCharsets.UTF_8));
}
diff --git a/src/main/java/com/sparrowwallet/sparrow/control/FileWalletExportPane.java b/src/main/java/com/sparrowwallet/sparrow/control/FileWalletExportPane.java
index 4980de8..81ffd20 100644
--- a/src/main/java/com/sparrowwallet/sparrow/control/FileWalletExportPane.java
+++ b/src/main/java/com/sparrowwallet/sparrow/control/FileWalletExportPane.java
@@ -171,18 +171,18 @@ public class FileWalletExportPane extends TitledDescriptionPane {
} else if(exporter instanceof Bip129 || exporter instanceof WalletLabels) {
UR ur = UR.fromBytes(outputStream.toByteArray());
BBQR bbqr = new BBQR(BBQRType.UNICODE, outputStream.toByteArray());
- qrDisplayDialog = new QRDisplayDialog(ur, bbqr, false, false, false);
+ qrDisplayDialog = new QRDisplayDialog(ur, bbqr, false, false, QREncoding.UR);
} else if(exporter instanceof Descriptor) {
boolean addBbqrOption = exportWallet.getKeystores().stream().anyMatch(keystore -> keystore.getWalletModel().showBbqr());
- boolean selectBbqrOption = exportWallet.getKeystores().stream().allMatch(keystore -> keystore.getWalletModel().selectBbqr());
+ QREncoding encoding = exportWallet.getKeystores().stream().allMatch(keystore -> keystore.getWalletModel().selectBbqr()) ? QREncoding.BBQR : QREncoding.UR;
OutputDescriptor outputDescriptor = OutputDescriptor.getOutputDescriptor(exportWallet, KeyPurpose.DEFAULT_PURPOSES, null);
CryptoOutput cryptoOutput = getCryptoOutput(exportWallet);
BBQR bbqr = addBbqrOption ? new BBQR(BBQRType.UNICODE, outputDescriptor.toString(true).getBytes(StandardCharsets.UTF_8)) : null;
- qrDisplayDialog = new DescriptorQRDisplayDialog(exportWallet.getFullDisplayName(), outputDescriptor.toString(true), cryptoOutput.toUR(), bbqr, selectBbqrOption);
+ qrDisplayDialog = new DescriptorQRDisplayDialog(exportWallet.getFullDisplayName(), outputDescriptor.toString(true), cryptoOutput.toUR(), bbqr, encoding);
} else if(exporter.getClass().equals(ColdcardMultisig.class)) {
UR ur = UR.fromBytes(outputStream.toByteArray());
BBQR bbqr = new BBQR(BBQRType.UNICODE, outputStream.toByteArray());
- qrDisplayDialog = new QRDisplayDialog(ur, bbqr, false, false, true);
+ qrDisplayDialog = new QRDisplayDialog(ur, bbqr, false, false, QREncoding.BBQR);
} else {
qrDisplayDialog = new QRDisplayDialog(outputStream.toString(StandardCharsets.UTF_8));
}
diff --git a/src/main/java/com/sparrowwallet/sparrow/control/QRDisplayDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/QRDisplayDialog.java
index c6137cc..29636a8 100644
--- a/src/main/java/com/sparrowwallet/sparrow/control/QRDisplayDialog.java
+++ b/src/main/java/com/sparrowwallet/sparrow/control/QRDisplayDialog.java
@@ -57,7 +57,11 @@ public class QRDisplayDialog extends Dialog<ButtonType> {
private final BBQR bbqr;
private BBQREncoder bbqrEncoder;
- private boolean useBbqrEncoding;
+
+ private final String raw;
+ private final boolean rawEncodable;
+
+ private QREncoding encoding = QREncoding.UR;
private final ImageView qrImageView;
@@ -72,14 +76,18 @@ public class QRDisplayDialog extends Dialog<ButtonType> {
private static boolean initialDensityChange;
public QRDisplayDialog(String type, byte[] data, boolean addLegacyEncodingOption) throws UR.URException {
- this(UR.fromBytes(type, data), null, addLegacyEncodingOption, false, false);
+ this(UR.fromBytes(type, data), null, addLegacyEncodingOption, false, QREncoding.UR);
}
public QRDisplayDialog(UR ur) {
- this(ur, null, false, false, false);
+ this(ur, null, false, false, QREncoding.UR);
+ }
+
+ public QRDisplayDialog(UR ur, BBQR bbqr, boolean addLegacyEncodingOption, boolean addScanButton, QREncoding defaultEncoding) {
+ this(ur, bbqr, null, addLegacyEncodingOption, addScanButton, defaultEncoding);
}
- public QRDisplayDialog(UR ur, BBQR bbqr, boolean addLegacyEncodingOption, boolean addScanButton, boolean selectBbqrButton) {
+ public QRDisplayDialog(UR ur, BBQR bbqr, String raw, boolean addLegacyEncodingOption, boolean addScanButton, QREncoding defaultEncoding) {
this.ur = ur;
this.bbqr = bbqr;
this.addLegacyEncodingOption = bbqr == null && addLegacyEncodingOption;
@@ -88,11 +96,14 @@ public class QRDisplayDialog extends Dialog<ButtonType> {
if(bbqr != null) {
this.bbqrEncoder = new BBQREncoder(bbqr.type(), DEFAULT_BBQR_ENCODING, bbqr.data(), Config.get().getQrDensity().getMaxBbqrFragmentLength(), 0);
- if(selectBbqrButton) {
- useBbqrEncoding = true;
+ if(defaultEncoding == QREncoding.BBQR || Config.get().getQrEncoding() == QREncoding.BBQR) {
+ encoding = QREncoding.BBQR;
}
}
+ this.raw = raw;
+ this.rawEncodable = raw != null && getQrCode(raw, true) != null;
+
final DialogPane dialogPane = new QRDisplayDialogPane();
setDialogPane(dialogPane);
AppServices.setStageIcon(dialogPane.getScene().getWindow());
@@ -156,8 +167,10 @@ public class QRDisplayDialog extends Dialog<ButtonType> {
public QRDisplayDialog(String data, boolean addScanButton) {
this.ur = null;
this.bbqr = null;
+ this.raw = data;
this.urEncoder = null;
this.bbqrEncoder = null;
+ this.rawEncodable = true;
final DialogPane dialogPane = new QRDisplayDialogPane();
setDialogPane(dialogPane);
@@ -205,7 +218,9 @@ public class QRDisplayDialog extends Dialog<ButtonType> {
}
private boolean isSinglePart() {
- if(useBbqrEncoding) {
+ if(encoding == QREncoding.RAW) {
+ return true;
+ } else if(encoding == QREncoding.BBQR) {
return bbqrEncoder.isSinglePart();
} else if(!useLegacyEncoding) {
return urEncoder.isSinglePart();
@@ -215,7 +230,9 @@ public class QRDisplayDialog extends Dialog<ButtonType> {
}
private void nextPart() {
- if(useBbqrEncoding) {
+ if(encoding == QREncoding.RAW) {
+ currentPart = raw;
+ } else if(encoding == QREncoding.BBQR) {
String fragment = bbqrEncoder.nextPart();
currentPart = fragment.toUpperCase(Locale.ROOT);
} else if(!useLegacyEncoding) {
@@ -231,6 +248,10 @@ public class QRDisplayDialog extends Dialog<ButtonType> {
}
protected Image getQrCode(String fragment) {
+ return getQrCode(fragment, false);
+ }
+
+ protected Image getQrCode(String fragment, boolean trial) {
try {
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix qrMatrix = qrCodeWriter.encode(fragment, BarcodeFormat.QR_CODE, qrSize, qrSize, Map.of(EncodeHintType.MARGIN, "2"));
@@ -241,7 +262,9 @@ public class QRDisplayDialog extends Dialog<ButtonType> {
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
return new Image(bais);
} catch(Exception e) {
- log.error("Error generating QR", e);
+ if(!trial) {
+ log.error("Error generating QR", e);
+ }
}
return null;
@@ -265,18 +288,13 @@ public class QRDisplayDialog extends Dialog<ButtonType> {
}
}
- public boolean isUseBbqrEncoding() {
- return useBbqrEncoding;
+ public QREncoding getEncoding() {
+ return encoding;
}
- private void setUseBbqrEncoding(boolean useBbqrEncoding) {
- if(useBbqrEncoding) {
- this.useBbqrEncoding = true;
- restartAnimation();
- } else {
- this.useBbqrEncoding = false;
- restartAnimation();
- }
+ private void setEncoding(QREncoding encoding) {
+ this.encoding = encoding;
+ restartAnimation();
}
private void changeQRDensity() {
@@ -373,18 +391,29 @@ public class QRDisplayDialog extends Dialog<ButtonType> {
return scanButton;
} else if(buttonType.getButtonData() == ButtonBar.ButtonData.BACK_PREVIOUS) {
- ToggleButton bbqr = new ToggleButton(buttonType.getText());
- bbqr.setGraphicTextGap(5);
- bbqr.setGraphic(getGlyph(FontAwesome5.Glyph.QRCODE));
- bbqr.setSelected(useBbqrEncoding);
- final ButtonBar.ButtonData buttonData = buttonType.getButtonData();
- ButtonBar.setButtonData(bbqr, buttonData);
+ ComboBox<QREncoding> encodingComboBox = new ComboBox<>();
+ if(ur != null) {
+ encodingComboBox.getItems().add(QREncoding.UR);
+ }
+ if(bbqr != null) {
+ encodingComboBox.getItems().add(QREncoding.BBQR);
+ }
+ if(raw != null) {
+ encodingComboBox.getItems().add(QREncoding.RAW);
+ }
- bbqr.selectedProperty().addListener((observable, oldValue, newValue) -> {
- setUseBbqrEncoding(newValue);
+ encodingComboBox.setCellFactory(_ -> new QREncodingListCell());
+ encodingComboBox.setButtonCell(new QREncodingButtonCell());
+ encodingComboBox.setValue(encoding);
+ encodingComboBox.setOnAction(_ -> {
+ setEncoding(encodingComboBox.getValue());
+ Config.get().setQrEncoding(encodingComboBox.getValue());
});
- return bbqr;
+ final ButtonBar.ButtonData buttonData = buttonType.getButtonData();
+ ButtonBar.setButtonData(encodingComboBox, buttonData);
+
+ return encodingComboBox;
}
return super.createButton(buttonType);
@@ -413,4 +442,38 @@ public class QRDisplayDialog extends Dialog<ButtonType> {
glyph.setFontSize(11);
return glyph;
}
+
+ private class QREncodingListCell extends ListCell<QREncoding> {
+ @Override
+ protected void updateItem(QREncoding item, boolean empty) {
+ super.updateItem(item, empty);
+ if(empty || item == null) {
+ setText(null);
+ setGraphic(null);
+ setDisable(false);
+ setOpacity(1.0);
+ } else {
+ setText(item.getName() + " Encoding");
+ setGraphic(item.getSVGImage());
+ setGraphicTextGap(8.0d);
+ setDisable(item == QREncoding.RAW && !rawEncodable);
+ setOpacity(isDisabled() ? 0.5 : 1.0);
+ }
+ }
+ }
+
+ private static class QREncodingButtonCell extends ListCell<QREncoding> {
+ @Override
+ protected void updateItem(QREncoding item, boolean empty) {
+ super.updateItem(item, empty);
+ if(item == null || empty) {
+ setText("");
+ setGraphic(null);
+ } else {
+ setText(item.getName());
+ setGraphic(item.getSVGImage());
+ setGraphicTextGap(8.0d);
+ }
+ }
+ }
}
diff --git a/src/main/java/com/sparrowwallet/sparrow/control/QREncoding.java b/src/main/java/com/sparrowwallet/sparrow/control/QREncoding.java
new file mode 100644
index 0000000..b309ec8
--- /dev/null
+++ b/src/main/java/com/sparrowwallet/sparrow/control/QREncoding.java
@@ -0,0 +1,49 @@
+package com.sparrowwallet.sparrow.control;
+
+import com.sparrowwallet.sparrow.AppServices;
+import com.sparrowwallet.sparrow.Theme;
+import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
+import com.sparrowwallet.sparrow.io.Config;
+import javafx.geometry.Insets;
+import javafx.scene.Node;
+import org.controlsfx.glyphfont.Glyph;
+import org.girod.javafx.svgimage.SVGLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URL;
+import java.util.Locale;
+
+public enum QREncoding {
+ UR("UR"), BBQR("BBQr"), RAW("Raw");
+
+ private static final Logger log = LoggerFactory.getLogger(QREncoding.class);
+
+ QREncoding(String name) {
+ this.name = name;
+ }
+
+ private final String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public Node getSVGImage() {
+ try {
+ URL url = AppServices.class.getResource("/image/qrencoding/" + getName().toLowerCase(Locale.ROOT) + "-icon" + (Config.get().getTheme() == Theme.DARK ? "-invert" : "") + ".svg");
+ if(url != null) {
+ return SVGLoader.load(url);
+ } else {
+ Glyph glyph = new Glyph(FontAwesome5.FONT_NAME, FontAwesome5.Glyph.QRCODE);
+ glyph.setFontSize(12);
+ glyph.setPadding(new Insets(0, 2, 0, 0));
+ return glyph;
+ }
+ } catch(Exception e) {
+ log.error("Could not load QR encoding source image for " + name);
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Config.java b/src/main/java/com/sparrowwallet/sparrow/io/Config.java
index 06843ef..06dda95 100644
--- a/src/main/java/com/sparrowwallet/sparrow/io/Config.java
+++ b/src/main/java/com/sparrowwallet/sparrow/io/Config.java
@@ -7,6 +7,7 @@ import com.sparrowwallet.sparrow.UnitFormat;
import com.sparrowwallet.sparrow.Mode;
import com.sparrowwallet.sparrow.Theme;
import com.sparrowwallet.sparrow.control.QRDensity;
+import com.sparrowwallet.sparrow.control.QREncoding;
import com.sparrowwallet.sparrow.control.WebcamResolution;
import com.sparrowwallet.sparrow.net.*;
import com.sparrowwallet.sparrow.wallet.FeeRatesSelection;
@@ -62,6 +63,7 @@ public class Config {
private long dustAttackThreshold = DUST_ATTACK_THRESHOLD_SATS;
private int enumerateHwPeriod = ENUMERATE_HW_PERIOD_SECS;
private QRDensity qrDensity;
+ private QREncoding qrEncoding;
private WebcamResolution webcamResolution;
private boolean mirrorCapture = true;
private boolean useZbar = true;
@@ -429,6 +431,15 @@ public class Config {
flush();
}
+ public QREncoding getQrEncoding() {
+ return qrEncoding;
+ }
+
+ public void setQrEncoding(QREncoding qrEncoding) {
+ this.qrEncoding = qrEncoding;
+ flush();
+ }
+
public WebcamResolution getWebcamResolution() {
return webcamResolution;
}
diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java
index 314991f..ce2bc47 100644
--- a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java
+++ b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java
@@ -982,7 +982,7 @@ public class HeadersController extends TransactionFormController implements Init
//TODO: Remove once Cobo Vault support has been removed
boolean addLegacyEncodingOption = headersForm.getSigningWallet().getKeystores().stream().anyMatch(keystore -> keystore.getWalletModel().showLegacyQR());
boolean addBbqrOption = headersForm.getSigningWallet().getKeystores().stream().anyMatch(keystore -> keystore.getWalletModel().showBbqr());
- boolean selectBbqrOption = headersForm.getSigningWallet().getKeystores().stream().allMatch(keystore -> keystore.getWalletModel().selectBbqr());
+ QREncoding encoding = headersForm.getSigningWallet().getKeystores().stream().allMatch(keystore -> keystore.getWalletModel().selectBbqr()) ? QREncoding.BBQR : QREncoding.UR;
//Don't include non witness utxo fields for segwit wallets when displaying the PSBT as a QR - it can add greatly to the time required for scanning
boolean includeNonWitnessUtxos = !Arrays.asList(ScriptType.WITNESS_TYPES).contains(headersForm.getSigningWallet().getScriptType());
@@ -990,7 +990,7 @@ public class HeadersController extends TransactionFormController implements Init
CryptoPSBT cryptoPSBT = new CryptoPSBT(psbtBytes);
BBQR bbqr = addBbqrOption ? new BBQR(BBQRType.PSBT, psbtBytes) : null;
- QRDisplayDialog qrDisplayDialog = new QRDisplayDialog(cryptoPSBT.toUR(), bbqr, addLegacyEncodingOption, true, selectBbqrOption);
+ QRDisplayDialog qrDisplayDialog = new QRDisplayDialog(cryptoPSBT.toUR(), bbqr, addLegacyEncodingOption, true, encoding);
qrDisplayDialog.initOwner(toggleButton.getScene().getWindow());
Optional<ButtonType> optButtonType = qrDisplayDialog.showAndWait();
if(optButtonType.isPresent() && optButtonType.get().getButtonData() == ButtonBar.ButtonData.OK_DONE) {
@@ -1360,7 +1360,8 @@ public class HeadersController extends TransactionFormController implements Init
byte[] txBytes = transaction.bitcoinSerialize();
UR ur = UR.fromBytes(txBytes);
BBQR bbqr = new BBQR(BBQRType.TXN, txBytes);
- QRDisplayDialog qrDisplayDialog = new QRDisplayDialog(ur, bbqr, false, false, false);
+ String raw = Utils.bytesToHex(txBytes);
+ QRDisplayDialog qrDisplayDialog = new QRDisplayDialog(ur, bbqr, raw, false, false, QREncoding.UR);
qrDisplayDialog.initOwner(showTransactionButton.getScene().getWindow());
qrDisplayDialog.showAndWait();
} catch (Exception exception) {
diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java
index adc9713..158515a 100644
--- a/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java
+++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SettingsController.java
@@ -383,11 +383,11 @@ public class SettingsController extends WalletFormController implements Initiali
}
boolean addBbqrOption = walletForm.getWallet().getKeystores().stream().anyMatch(keystore -> keystore.getWalletModel().showBbqr());
- boolean selectBbqrOption = walletForm.getWallet().getKeystores().stream().allMatch(keystore -> keystore.getWalletModel().selectBbqr());
+ QREncoding encoding = walletForm.getWallet().getKeystores().stream().allMatch(keystore -> keystore.getWalletModel().selectBbqr()) ? QREncoding.BBQR : QREncoding.UR;
UR cryptoOutputUR = cryptoOutput.toUR();
BBQR bbqr = addBbqrOption ? new BBQR(BBQRType.UNICODE, outputDescriptor.toString(true).getBytes(StandardCharsets.UTF_8)) : null;
- QRDisplayDialog qrDisplayDialog = new DescriptorQRDisplayDialog(walletForm.getWallet().getFullDisplayName(), outputDescriptor.toString(true), cryptoOutputUR, bbqr, selectBbqrOption);
+ QRDisplayDialog qrDisplayDialog = new DescriptorQRDisplayDialog(walletForm.getWallet().getFullDisplayName(), outputDescriptor.toString(true), cryptoOutputUR, bbqr, encoding);
qrDisplayDialog.initOwner(showDescriptorQR.getScene().getWindow());
qrDisplayDialog.showAndWait();
}
diff --git a/src/main/resources/image/qrencoding/bbqr-icon-invert.svg b/src/main/resources/image/qrencoding/bbqr-icon-invert.svg
new file mode 100644
index 0000000..6eb6df3
--- /dev/null
+++ b/src/main/resources/image/qrencoding/bbqr-icon-invert.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="15px" height="15px" viewBox="0 0 15 15" version="1.1">
+<g id="surface1">
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(83.92157%,82.352942%,82.352942%);fill-opacity:1;" d="M 1.433594 3.285156 C 1.433594 2.453125 2.109375 1.78125 2.9375 1.78125 L 12.0625 1.78125 C 12.890625 1.78125 13.566406 2.453125 13.566406 3.285156 L 13.566406 11.683594 C 13.566406 12.511719 12.890625 13.1875 12.0625 13.1875 L 2.9375 13.1875 C 2.109375 13.1875 1.433594 12.511719 1.433594 11.683594 Z M 1.433594 3.285156 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(14.117648%,14.117648%,14.117648%);fill-opacity:1;" d="M 4.351562 6.257812 L 6.125 6.257812 L 6.125 7.144531 L 3.460938 7.144531 L 3.460938 2.707031 L 6.125 2.707031 L 6.125 3.59375 L 4.351562 3.59375 L 4.351562 4.480469 L 6.125 4.480469 L 6.125 5.367188 L 4.351562 5.367188 Z M 7.011719 4.480469 L 6.125 4.480469 L 6.125 3.59375 L 7.011719 3.59375 Z M 7.011719 6.257812 L 6.125 6.257812 L 6.125 5.367188 L 7.011719 5.367188 Z M 7.011719 6.257812 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(14.117648%,14.117648%,14.117648%);fill-opacity:1;" d="M 9.019531 6.257812 L 10.792969 6.257812 L 10.792969 7.144531 L 8.132812 7.144531 L 8.132812 2.707031 L 10.792969 2.707031 L 10.792969 3.59375 L 9.019531 3.59375 L 9.019531 4.480469 L 10.792969 4.480469 L 10.792969 5.367188 L 9.019531 5.367188 Z M 11.683594 4.480469 L 10.792969 4.480469 L 10.792969 3.59375 L 11.683594 3.59375 Z M 11.683594 6.257812 L 10.792969 6.257812 L 10.792969 5.367188 L 11.683594 5.367188 Z M 11.683594 6.257812 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(14.117648%,14.117648%,14.117648%);fill-opacity:1;" d="M 4.351562 8.742188 L 4.351562 7.855469 L 6.125 7.855469 L 6.125 8.742188 Z M 4.351562 11.40625 L 3.460938 11.40625 L 3.460938 8.742188 L 4.351562 8.742188 Z M 6.125 10.519531 L 6.125 8.742188 L 7.011719 8.742188 L 7.011719 10.519531 Z M 6.125 11.40625 L 5.238281 11.40625 L 5.238281 10.519531 L 6.125 10.519531 Z M 5.238281 11.40625 L 5.238281 12.292969 L 4.351562 12.292969 L 4.351562 11.40625 Z M 6.125 12.292969 L 6.125 11.40625 L 7.011719 11.40625 L 7.011719 12.292969 Z M 6.125 12.292969 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(14.117648%,14.117648%,14.117648%);fill-opacity:1;" d="M 9.4375 12.292969 L 8.546875 12.292969 L 8.546875 8.742188 L 10.324219 8.742188 L 10.324219 9.632812 L 9.4375 9.632812 Z M 9.4375 12.292969 "/>
+</g>
+</svg>
diff --git a/src/main/resources/image/qrencoding/bbqr-icon.svg b/src/main/resources/image/qrencoding/bbqr-icon.svg
new file mode 100644
index 0000000..cfd6374
--- /dev/null
+++ b/src/main/resources/image/qrencoding/bbqr-icon.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="15px" height="15px" viewBox="0 0 15 15" version="1.1">
+<g id="surface1">
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(14.117648%,14.117648%,14.117648%);fill-opacity:1;" d="M 1.433594 3.285156 C 1.433594 2.453125 2.109375 1.78125 2.9375 1.78125 L 12.0625 1.78125 C 12.890625 1.78125 13.566406 2.453125 13.566406 3.285156 L 13.566406 11.683594 C 13.566406 12.511719 12.890625 13.1875 12.0625 13.1875 L 2.9375 13.1875 C 2.109375 13.1875 1.433594 12.511719 1.433594 11.683594 Z M 1.433594 3.285156 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 4.351562 6.257812 L 6.125 6.257812 L 6.125 7.144531 L 3.460938 7.144531 L 3.460938 2.707031 L 6.125 2.707031 L 6.125 3.59375 L 4.351562 3.59375 L 4.351562 4.480469 L 6.125 4.480469 L 6.125 5.367188 L 4.351562 5.367188 Z M 7.011719 4.480469 L 6.125 4.480469 L 6.125 3.59375 L 7.011719 3.59375 Z M 7.011719 6.257812 L 6.125 6.257812 L 6.125 5.367188 L 7.011719 5.367188 Z M 7.011719 6.257812 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 9.019531 6.257812 L 10.792969 6.257812 L 10.792969 7.144531 L 8.132812 7.144531 L 8.132812 2.707031 L 10.792969 2.707031 L 10.792969 3.59375 L 9.019531 3.59375 L 9.019531 4.480469 L 10.792969 4.480469 L 10.792969 5.367188 L 9.019531 5.367188 Z M 11.683594 4.480469 L 10.792969 4.480469 L 10.792969 3.59375 L 11.683594 3.59375 Z M 11.683594 6.257812 L 10.792969 6.257812 L 10.792969 5.367188 L 11.683594 5.367188 Z M 11.683594 6.257812 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 4.351562 8.742188 L 4.351562 7.855469 L 6.125 7.855469 L 6.125 8.742188 Z M 4.351562 11.40625 L 3.460938 11.40625 L 3.460938 8.742188 L 4.351562 8.742188 Z M 6.125 10.519531 L 6.125 8.742188 L 7.011719 8.742188 L 7.011719 10.519531 Z M 6.125 11.40625 L 5.238281 11.40625 L 5.238281 10.519531 L 6.125 10.519531 Z M 5.238281 11.40625 L 5.238281 12.292969 L 4.351562 12.292969 L 4.351562 11.40625 Z M 6.125 12.292969 L 6.125 11.40625 L 7.011719 11.40625 L 7.011719 12.292969 Z M 6.125 12.292969 "/>
+<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 9.496094 12.292969 L 8.605469 12.292969 L 8.605469 8.742188 L 10.382812 8.742188 L 10.382812 9.632812 L 9.496094 9.632812 Z M 9.496094 12.292969 "/>
+</g>
+</svg>
diff --git a/src/main/resources/image/qrencoding/ur-icon-invert.svg b/src/main/resources/image/qrencoding/ur-icon-invert.svg
new file mode 100644
index 0000000..89148e7
--- /dev/null
+++ b/src/main/resources/image/qrencoding/ur-icon-invert.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="15px" height="15px" viewBox="0 0 15 15" version="1.1">
+<g id="surface1">
+<path style="fill:none;stroke-width:18;stroke-linecap:butt;stroke-linejoin:miter;stroke:#d6d2d2;stroke-opacity:1;stroke-miterlimit:4;" d="M 295 155 C 295 102.5 252.5 60 200 60 C 147.5 60 105 102.5 105 155 C 105 207.5 147.5 250 200 250 C 252.5 250 295 207.5 295 155 Z M 295 155 " transform="matrix(0.0375,0,0,0.0375,0,0)"/>
+<path style="fill:none;stroke-width:18;stroke-linecap:butt;stroke-linejoin:miter;stroke:#d6d2d2;stroke-opacity:1;stroke-miterlimit:4;" d="M 235 245 C 235 192.5 192.5 150 140 150 C 87.5 150 45 192.5 45 245 C 45 297.5 87.5 340 140 340 C 192.5 340 235 297.5 235 245 Z M 235 245 " transform="matrix(0.0375,0,0,0.0375,0,0)"/>
+<path style="fill:none;stroke-width:18;stroke-linecap:butt;stroke-linejoin:miter;stroke:#d6d2d2;stroke-opacity:1;stroke-miterlimit:4;" d="M 355 245 C 355 192.5 312.5 150 260 150 C 207.5 150 165 192.5 165 245 C 165 297.5 207.5 340 260 340 C 312.5 340 355 297.5 355 245 Z M 355 245 " transform="matrix(0.0375,0,0,0.0375,0,0)"/>
+</g>
+</svg>
diff --git a/src/main/resources/image/qrencoding/ur-icon.svg b/src/main/resources/image/qrencoding/ur-icon.svg
new file mode 100644
index 0000000..2876b07
--- /dev/null
+++ b/src/main/resources/image/qrencoding/ur-icon.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="15px" height="15px" viewBox="0 0 15 15" version="1.1">
+<g id="surface1">
+<path style="fill:none;stroke-width:18;stroke-linecap:butt;stroke-linejoin:miter;stroke:#242424;stroke-opacity:1;stroke-miterlimit:4;" d="M 295 155 C 295 102.5 252.5 60 200 60 C 147.5 60 105 102.5 105 155 C 105 207.5 147.5 250 200 250 C 252.5 250 295 207.5 295 155 Z M 295 155 " transform="matrix(0.0375,0,0,0.0375,0,0)"/>
+<path style="fill:none;stroke-width:18;stroke-linecap:butt;stroke-linejoin:miter;stroke:#242424;stroke-opacity:1;stroke-miterlimit:4;" d="M 235 245 C 235 192.5 192.5 150 140 150 C 87.5 150 45 192.5 45 245 C 45 297.5 87.5 340 140 340 C 192.5 340 235 297.5 235 245 Z M 235 245 " transform="matrix(0.0375,0,0,0.0375,0,0)"/>
+<path style="fill:none;stroke-width:18;stroke-linecap:butt;stroke-linejoin:miter;stroke:#242424;stroke-opacity:1;stroke-miterlimit:4;" d="M 355 245 C 355 192.5 312.5 150 260 150 C 207.5 150 165 192.5 165 245 C 165 297.5 207.5 340 260 340 C 312.5 340 355 297.5 355 245 Z M 355 245 " transform="matrix(0.0375,0,0,0.0375,0,0)"/>
+</g>
+</svg>
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.