From 46dccee1dedeb8b6fb69ce796b7a8886dc534d5e Mon Sep 17 00:00:00 2001 From: Nils Stinnesbeck Date: Sat, 17 Oct 2026 21:12:21 +0200 Subject: [PATCH] App 2.4.3: pairing code visible on the car screen The code below the QR code was not visible on the head unit: the dialog stacked the text, a 240 dp QR code, the code and the status, and its content could not scroll, so on the wide, low screen the lower part was cut off. On wide screens the QR code now sits left of the text, the content scrolls, and the code is also part of the instruction sentence, so it is readable wherever the text is. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki --- app/build.gradle | 4 +- .../java/com/evsuite/abrp/MainActivity.java | 47 +++++++++++++++---- app/src/main/res/values/strings.xml | 2 +- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c2c0330..09ee60c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,8 +34,8 @@ android { applicationId "com.stinnesbeck.nilsmg4" minSdk 27 targetSdk 36 - versionCode 60 - versionName "2.4.2" + versionCode 61 + versionName "2.4.3" // Nothing about the server ships in the APK: its URL and ingest token are entered in // the app's Server tab (ServerConfig), and the database is only reachable by the server. diff --git a/app/src/main/java/com/evsuite/abrp/MainActivity.java b/app/src/main/java/com/evsuite/abrp/MainActivity.java index 3fe59c7..fb7d9d6 100644 --- a/app/src/main/java/com/evsuite/abrp/MainActivity.java +++ b/app/src/main/java/com/evsuite/abrp/MainActivity.java @@ -483,10 +483,17 @@ public class MainActivity extends AppCompatActivity { float density = getResources().getDisplayMetrics().density; int pad = (int) (24 * density); + // On the head unit's wide, low screen the QR code sits left of the text: stacked, the + // code and status below a 240 dp QR code were cut off. Scrollable either way, so + // nothing can be clipped on any screen. + android.util.DisplayMetrics metrics = getResources().getDisplayMetrics(); + boolean wide = metrics.widthPixels > metrics.heightPixels; android.widget.LinearLayout panel = new android.widget.LinearLayout(this); - panel.setOrientation(android.widget.LinearLayout.VERTICAL); - panel.setGravity(android.view.Gravity.CENTER_HORIZONTAL); + panel.setOrientation(wide ? android.widget.LinearLayout.HORIZONTAL : android.widget.LinearLayout.VERTICAL); + panel.setGravity(wide ? android.view.Gravity.CENTER_VERTICAL : android.view.Gravity.CENTER_HORIZONTAL); panel.setPadding(pad, pad, pad, pad); + android.widget.LinearLayout column = new android.widget.LinearLayout(this); + column.setOrientation(android.widget.LinearLayout.VERTICAL); TextView instructions = new TextView(this); instructions.setTextAppearance(R.style.Text_EV_Secondary); ImageView qr = new ImageView(this); @@ -501,17 +508,37 @@ public class MainActivity extends AppCompatActivity { TextView status = new TextView(this); status.setTextAppearance(R.style.Text_EV_Body); status.setText(R.string.pair_starting); - panel.addView(instructions); android.widget.LinearLayout.LayoutParams qrParams = new android.widget.LinearLayout.LayoutParams(qrSize, qrSize); - qrParams.topMargin = pad / 2; - panel.addView(qr, qrParams); - panel.addView(code); - panel.addView(status); + android.widget.LinearLayout.LayoutParams columnParams; + if (wide) { + qrParams.rightMargin = pad; + columnParams = new android.widget.LinearLayout.LayoutParams( + 0, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, 1f); + panel.addView(qr, qrParams); + panel.addView(column, columnParams); + column.addView(instructions); + } else { + qrParams.topMargin = pad / 2; + qrParams.bottomMargin = pad / 2; + columnParams = new android.widget.LinearLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.MATCH_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT); + panel.addView(instructions); + panel.addView(qr, qrParams); + panel.addView(column, columnParams); + } + android.widget.LinearLayout.LayoutParams codeParams = new android.widget.LinearLayout.LayoutParams( + android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT); + codeParams.topMargin = pad / 2; + codeParams.bottomMargin = pad / 2; + column.addView(code, codeParams); + column.addView(status); + android.widget.ScrollView scroll = new android.widget.ScrollView(this); + scroll.addView(panel); final boolean[] cancelled = {false}; androidx.appcompat.app.AlertDialog dialog = new MaterialAlertDialogBuilder(this) .setTitle(R.string.pair_title) - .setView(panel) + .setView(scroll) .setNegativeButton(R.string.pair_cancel, null) .create(); // Shown only once the server has answered, so a failure leaves no empty QR frame. @@ -527,7 +554,9 @@ public class MainActivity extends AppCompatActivity { PairingClient.Request request = PairingClient.start(url, installationId); android.graphics.Bitmap bitmap = QrCode.generate(request.verificationUriComplete, 416); runOnUiThread(() -> { - instructions.setText(getString(R.string.pair_scan, request.verificationUri)); + // The code is in the sentence as well as below it, so it is readable + // wherever the text is. + instructions.setText(getString(R.string.pair_scan, request.verificationUri, request.userCode)); if (bitmap != null) { qr.setImageBitmap(bitmap); qr.setVisibility(View.VISIBLE); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f942d80..b92d940 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -58,7 +58,7 @@ Pair with phone Shows a QR code: scan it with your phone and approve with the approval password. The token below is then filled in for you. Pair with phone - Scan with your phone, or open %1$s and enter this code: + Scan the QR code with your phone, or open %1$s and enter the code %2$s: Waiting for approval… Contacting the server… Paired — this car now has its own token