App 2.4.2: pairing dialog shows errors instead of closing

- On a failure the pairing dialog stays open and shows the reason in
  red (Cancel becomes Close); before, it closed at once and the reason
  was only in the Server tab's status line.
- "Pairing is not enabled" only when the server says so. Any other 404
  (a server older than 2.1.0, or a proxy's page) is reported as a
  server without pairing.
- Pairing and Test connection name the problem: a server name that
  does not resolve (Test connection used to say "No internet
  connection"), a server that does not answer, or the HTTP status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
This commit is contained in:
2026-10-17 20:56:02 +02:00
co-authored by Claude Opus 5.5
parent 6f57351866
commit 8a5813fa16
6 changed files with 84 additions and 11 deletions
@@ -455,7 +455,7 @@ public class MainActivity extends AppCompatActivity {
message = getString(R.string.conn_err_timeout);
color = COLOR_ERROR;
} catch (Exception e) {
message = getString(R.string.conn_err_no_internet);
message = describeNetworkError(e, url);
color = COLOR_ERROR;
}
final String m = message;
@@ -514,6 +514,9 @@ public class MainActivity extends AppCompatActivity {
.setView(panel)
.setNegativeButton(R.string.pair_cancel, null)
.create();
// Shown only once the server has answered, so a failure leaves no empty QR frame.
qr.setVisibility(View.GONE);
code.setVisibility(View.GONE);
dialog.setOnDismissListener(d -> cancelled[0] = true);
dialog.show();
@@ -525,8 +528,12 @@ public class MainActivity extends AppCompatActivity {
android.graphics.Bitmap bitmap = QrCode.generate(request.verificationUriComplete, 416);
runOnUiThread(() -> {
instructions.setText(getString(R.string.pair_scan, request.verificationUri));
if (bitmap != null) qr.setImageBitmap(bitmap);
if (bitmap != null) {
qr.setImageBitmap(bitmap);
qr.setVisibility(View.VISIBLE);
}
code.setText(request.userCode);
code.setVisibility(View.VISIBLE);
status.setText(R.string.pair_waiting);
});
PairingClient.PollResult result = null;
@@ -552,26 +559,58 @@ public class MainActivity extends AppCompatActivity {
}
} catch (PairingClient.NotEnabledException e) {
outcome = getString(R.string.pair_not_enabled);
} catch (PairingClient.HttpStatusException e) {
outcome = e.status == 404 ? getString(R.string.pair_err_old_server)
: getString(R.string.err_http, e.status);
} catch (InterruptedException e) {
return;
} catch (Exception e) {
outcome = getString(R.string.pair_error, e.getClass().getSimpleName());
outcome = describeNetworkError(e, url);
}
final String message = outcome;
final int c = color;
runOnUiThread(() -> {
if (!cancelled[0]) dialog.dismiss();
setConnectionStatus(c, message);
if (c == COLOR_OK) {
if (consumptionCloudClient != null) consumptionCloudClient.invalidateHistory();
showServerConfig();
serverTokenLayout.setError(null);
refreshStatus();
if (!cancelled[0]) dialog.dismiss();
return;
}
setConnectionStatus(c, message);
if (cancelled[0]) return;
// Keep the dialog up with the reason, instead of closing it on a failure the
// driver would otherwise only see as a flicker.
qr.setVisibility(View.GONE);
code.setVisibility(View.GONE);
instructions.setVisibility(View.GONE);
status.setText(message);
status.setTextColor(c);
android.widget.Button close = dialog.getButton(android.content.DialogInterface.BUTTON_NEGATIVE);
if (close != null) close.setText(R.string.pair_close);
});
}, "server-pairing").start();
}
/**
* A network failure in words the driver can act on: a name that does not resolve, a
* server that does not answer, or a status. Shared by Test connection and pairing.
*/
private String describeNetworkError(Exception e, String url) {
String host = Uri.parse(url).getHost();
if (host == null) host = url;
if (e instanceof java.net.UnknownHostException) return getString(R.string.err_dns, host);
if (e instanceof java.net.SocketTimeoutException || e instanceof java.net.ConnectException
|| e instanceof java.net.NoRouteToHostException) {
return getString(R.string.err_unreachable, host);
}
if (e instanceof PairingClient.HttpStatusException) {
return getString(R.string.err_http, ((PairingClient.HttpStatusException) e).status);
}
return getString(R.string.pair_error, e.getClass().getSimpleName());
}
private static int httpGet(String url, String token) throws java.io.IOException {
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) new java.net.URL(url).openConnection();
try {