From 5795d43d0da67db71b67c6adf224b317dfc224e5 Mon Sep 17 00:00:00 2001 From: Karel Vervaeke Date: Thu, 19 Feb 2026 14:00:30 +0100 Subject: [PATCH] print qr code when producing a device link uri, but only if System.console is not null --- build.gradle.kts | 1 + gradle/libs.versions.toml | 1 + .../asamk/signal/commands/LinkCommand.java | 33 ++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 71b34c49..2ea0a7df 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -88,6 +88,7 @@ dependencies { implementation(libs.slf4j.api) implementation(libs.slf4j.jul) implementation(libs.logback) + implementation(libs.zxing) implementation(project(":libsignal-cli")) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 01a50e15..aaafef38 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,6 +7,7 @@ bouncycastle = "org.bouncycastle:bcprov-jdk18on:1.83" jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.20.2" argparse4j = "net.sourceforge.argparse4j:argparse4j:0.9.0" dbusjava = "com.github.hypfvieh:dbus-java-transport-native-unixsocket:5.0.0" +zxing = "com.google.zxing:core:3.5.4" slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-jul = { module = "org.slf4j:jul-to-slf4j", version.ref = "slf4j" } logback = "ch.qos.logback:logback-classic:1.5.29" diff --git a/src/main/java/org/asamk/signal/commands/LinkCommand.java b/src/main/java/org/asamk/signal/commands/LinkCommand.java index 7c29a3a9..eefab368 100644 --- a/src/main/java/org/asamk/signal/commands/LinkCommand.java +++ b/src/main/java/org/asamk/signal/commands/LinkCommand.java @@ -1,5 +1,10 @@ package org.asamk.signal.commands; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.QRCodeWriter; + import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; @@ -14,6 +19,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.net.URI; import java.util.concurrent.TimeoutException; public class LinkCommand implements ProvisioningCommand { @@ -44,7 +50,11 @@ public class LinkCommand implements ProvisioningCommand { deviceName = "cli"; } try { - writer.println("{}", m.getDeviceLinkUri()); + final URI deviceLinkUri = m.getDeviceLinkUri(); + if (System.console() != null) { + printQrCode(writer, deviceLinkUri); + } + writer.println("{}", deviceLinkUri); var number = m.finishDeviceLink(deviceName); writer.println("Associated with: {}", number); } catch (TimeoutException e) { @@ -59,4 +69,25 @@ public class LinkCommand implements ProvisioningCommand { + "\" before trying again."); } } + + private void printQrCode(final PlainTextWriter writer, final URI deviceLinkUri) { + try { + var bitMatrix = new QRCodeWriter().encode(deviceLinkUri.toString(), BarcodeFormat.QR_CODE, 0, 0); + for (int y = 0; y < bitMatrix.getHeight(); y += 2) { + writer.println(formatQRCodeLinePair(bitMatrix, y)); + } + } catch (WriterException e) { + logger.error("Failed to generate QR code", e); + } + } + + private static String formatQRCodeLinePair(final BitMatrix bitMatrix, final int y) { + var line = new StringBuilder(); + for (int x = 0; x < bitMatrix.getWidth(); x++) { + boolean upper = bitMatrix.get(x, y); + boolean lower = y + 1 < bitMatrix.getHeight() && bitMatrix.get(x, y + 1); + line.append((upper && lower) ? "█" : (upper ? "▀" : (lower ? "▄" : " "))); + } + return line.toString(); + } }