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:
@@ -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 {
|
||||
|
||||
@@ -52,13 +52,28 @@ final class PairingClient {
|
||||
/** Thrown when pairing is switched off on the server (no APPROVAL_PASSWORD). */
|
||||
static final class NotEnabledException extends Exception { }
|
||||
|
||||
/** The server answered with a status the flow does not expect. */
|
||||
static final class HttpStatusException extends java.io.IOException {
|
||||
final int status;
|
||||
|
||||
HttpStatusException(int status) {
|
||||
super("HTTP " + status);
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
|
||||
/** What the server says when APPROVAL_PASSWORD is unset; any other 404 is a missing route. */
|
||||
static final String NOT_ENABLED_MESSAGE = "pairing is not enabled";
|
||||
|
||||
private PairingClient() { }
|
||||
|
||||
static Request start(String serverUrl, String installationId) throws Exception {
|
||||
Response r = post(serverUrl + "/v1/pair/start",
|
||||
new JSONObject().put("installationId", installationId));
|
||||
if (r.status == 404) throw new NotEnabledException();
|
||||
if (r.status != 201) throw new java.io.IOException("HTTP " + r.status);
|
||||
// Only the server's own answer means "switched off". A 404 without it is a server (or
|
||||
// proxy) that has no pairing route at all, e.g. one older than 2.1.0.
|
||||
if (r.status == 404 && r.body.contains(NOT_ENABLED_MESSAGE)) throw new NotEnabledException();
|
||||
if (r.status != 201) throw new HttpStatusException(r.status);
|
||||
JSONObject json = new JSONObject(r.body);
|
||||
long now = System.currentTimeMillis();
|
||||
return new Request(
|
||||
@@ -77,7 +92,7 @@ final class PairingClient {
|
||||
case 202: return new PollResult(Outcome.PENDING, null);
|
||||
case 403: return new PollResult(Outcome.DENIED, null);
|
||||
case 410: return new PollResult(Outcome.EXPIRED, null);
|
||||
default: throw new java.io.IOException("HTTP " + r.status);
|
||||
default: throw new HttpStatusException(r.status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user