Fix title overflow in confirm/info screens; log checkForUpdate() diagnostics
confirmWarningScreen() and infoScreen() only fell back from text size 2 to size 1 for long titles, but some titles (e.g. "Prüfung fehlgeschlagen. WLAN prüfen.") still overflowed the screen edge at size 1. Both now wrap the title across up to 3 lines via the new wrapTitleLines() helper, and VIEW_TOP shifts down to match the actual number of title lines instead of always assuming one. checkForUpdate() now logs the same kind of diagnostic detail over serial that performUpdate() already had (start info, and the specific failure reason - HTTP error/status, JSON parse error, missing tag_name, missing firmware.bin asset - instead of just returning silently). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -207,6 +207,37 @@ namespace {
|
|||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Zerlegt einen Titel in bis zu maxLines Zeilen nach dem gleichen
|
||||||
|
// Wortumbruch-Prinzip wie layoutWrapped() - ein Titel wird aber nie
|
||||||
|
// gescrollt, sondern muss immer komplett sichtbar sein, daher eine
|
||||||
|
// eigene, einfachere Variante ohne Scroll-Unterstuetzung.
|
||||||
|
int wrapTitleLines(TFT_eSPI& tft, const String& text, int16_t maxWidth, String* outLines, int maxLines) {
|
||||||
|
int count = 0;
|
||||||
|
int32_t start = 0;
|
||||||
|
int32_t len = text.length();
|
||||||
|
while (start < len && count < maxLines) {
|
||||||
|
while (start < len && text[start] == ' ') start++;
|
||||||
|
if (start >= len) break;
|
||||||
|
|
||||||
|
String line = text.substring(start, len);
|
||||||
|
bool isLastAllowedLine = (count == maxLines - 1);
|
||||||
|
if (!isLastAllowedLine) {
|
||||||
|
while (tft.textWidth(line) > maxWidth) {
|
||||||
|
int32_t lastSpace = line.lastIndexOf(' ');
|
||||||
|
if (lastSpace <= 0) break;
|
||||||
|
line = line.substring(0, lastSpace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outLines[count++] = line;
|
||||||
|
start += line.length();
|
||||||
|
}
|
||||||
|
if (count == 0) {
|
||||||
|
outLines[0] = text;
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
// Warn-Ueberlage, die praktisch den kompletten Bildschirm einnimmt (nur
|
// Warn-Ueberlage, die praktisch den kompletten Bildschirm einnimmt (nur
|
||||||
// ein paar Pixel Rand) - urspruenglich nur fuers Einschalten des
|
// ein paar Pixel Rand) - urspruenglich nur fuers Einschalten des
|
||||||
// Flugbuchs gebaut (erklaert, warum es sich nach 24h automatisch
|
// Flugbuchs gebaut (erklaert, warum es sich nach 24h automatisch
|
||||||
@@ -238,8 +269,29 @@ namespace {
|
|||||||
constexpr int16_t TEXT_MAX_WIDTH = BOX_W - 20;
|
constexpr int16_t TEXT_MAX_WIDTH = BOX_W - 20;
|
||||||
constexpr int16_t LINE_H = 16;
|
constexpr int16_t LINE_H = 16;
|
||||||
constexpr int16_t TITLE_Y = BOX_Y + 16;
|
constexpr int16_t TITLE_Y = BOX_Y + 16;
|
||||||
// Eine Leerzeile Abstand zwischen "Achtung!!!" und dem Fliesstext.
|
|
||||||
constexpr int16_t VIEW_TOP = TITLE_Y + 12 + LINE_H;
|
// Titel-Text vorab in so viele Zeilen umbrechen, wie bei Größe 2
|
||||||
|
// (oder bei zu langem Text Größe 1) nötig sind - siehe
|
||||||
|
// wrapTitleLines() oben. VIEW_TOP hängt dadurch von der
|
||||||
|
// tatsächlichen Zeilenzahl des Titels ab, ist also kein constexpr
|
||||||
|
// mehr wie vorher (wo immer nur eine Titelzeile angenommen wurde).
|
||||||
|
tft.setTextSize(2);
|
||||||
|
uint8_t titleTextSize = 2;
|
||||||
|
if (tft.textWidth(title) > TEXT_MAX_WIDTH) {
|
||||||
|
tft.setTextSize(1);
|
||||||
|
titleTextSize = 1;
|
||||||
|
}
|
||||||
|
constexpr int MAX_TITLE_LINES = 3;
|
||||||
|
String titleLines[MAX_TITLE_LINES];
|
||||||
|
int titleLineCount = wrapTitleLines(tft, title, TEXT_MAX_WIDTH, titleLines, MAX_TITLE_LINES);
|
||||||
|
// Fließtext wird immer bei Größe 1 vermessen/gezeichnet (siehe
|
||||||
|
// layoutWrapped()-Aufrufe unten) - Größe hier zurücksetzen, falls
|
||||||
|
// obiger Titel-Breitentest sie auf 2 stehen gelassen hat, sonst
|
||||||
|
// würde die gleich folgende totalH-Berechnung (vor dem ersten
|
||||||
|
// redraw()) mit falscher (zu breiter) Schriftgröße rechnen.
|
||||||
|
tft.setTextSize(1);
|
||||||
|
// Eine Leerzeile Abstand zwischen Titel und Fließtext.
|
||||||
|
int16_t VIEW_TOP = (int16_t)(TITLE_Y + titleLineCount * LINE_H + 12);
|
||||||
|
|
||||||
constexpr int16_t BTN_H = 36;
|
constexpr int16_t BTN_H = 36;
|
||||||
constexpr int16_t BTN_GAP = 8;
|
constexpr int16_t BTN_GAP = 8;
|
||||||
@@ -285,11 +337,10 @@ namespace {
|
|||||||
// links/rechts ueber den Bildschirmrand hinaus. Deshalb hier
|
// links/rechts ueber den Bildschirmrand hinaus. Deshalb hier
|
||||||
// die Breite bei Groesse 2 pruefen und bei Bedarf auf Groesse 1
|
// die Breite bei Groesse 2 pruefen und bei Bedarf auf Groesse 1
|
||||||
// zurueckfallen, statt eine feste Groesse anzunehmen.
|
// zurueckfallen, statt eine feste Groesse anzunehmen.
|
||||||
tft.setTextSize(2);
|
tft.setTextSize(titleTextSize);
|
||||||
if (tft.textWidth(title) > (BOX_W - 20)) {
|
for (int i = 0; i < titleLineCount; i++) {
|
||||||
tft.setTextSize(1);
|
tft.drawString(titleLines[i], BOX_X + BOX_W / 2, (int16_t)(TITLE_Y + i * LINE_H));
|
||||||
}
|
}
|
||||||
tft.drawString(title, BOX_X + BOX_W / 2, TITLE_Y);
|
|
||||||
tft.setTextSize(1);
|
tft.setTextSize(1);
|
||||||
tft.setTextDatum(TL_DATUM);
|
tft.setTextDatum(TL_DATUM);
|
||||||
|
|
||||||
@@ -344,7 +395,29 @@ namespace {
|
|||||||
constexpr int16_t TEXT_MAX_WIDTH = BOX_W - 20;
|
constexpr int16_t TEXT_MAX_WIDTH = BOX_W - 20;
|
||||||
constexpr int16_t LINE_H = 16;
|
constexpr int16_t LINE_H = 16;
|
||||||
constexpr int16_t TITLE_Y = BOX_Y + 16;
|
constexpr int16_t TITLE_Y = BOX_Y + 16;
|
||||||
constexpr int16_t VIEW_TOP = TITLE_Y + 12 + LINE_H;
|
|
||||||
|
// Titel-Text vorab in so viele Zeilen umbrechen, wie bei Größe 2
|
||||||
|
// (oder bei zu langem Text Größe 1) nötig sind - siehe
|
||||||
|
// wrapTitleLines() oben. VIEW_TOP hängt dadurch von der
|
||||||
|
// tatsächlichen Zeilenzahl des Titels ab, ist also kein constexpr
|
||||||
|
// mehr wie vorher (wo immer nur eine Titelzeile angenommen wurde).
|
||||||
|
tft.setTextSize(2);
|
||||||
|
uint8_t titleTextSize = 2;
|
||||||
|
if (tft.textWidth(title) > TEXT_MAX_WIDTH) {
|
||||||
|
tft.setTextSize(1);
|
||||||
|
titleTextSize = 1;
|
||||||
|
}
|
||||||
|
constexpr int MAX_TITLE_LINES = 3;
|
||||||
|
String titleLines[MAX_TITLE_LINES];
|
||||||
|
int titleLineCount = wrapTitleLines(tft, title, TEXT_MAX_WIDTH, titleLines, MAX_TITLE_LINES);
|
||||||
|
// Fließtext wird immer bei Größe 1 vermessen/gezeichnet (siehe
|
||||||
|
// layoutWrapped()-Aufrufe unten) - Größe hier zurücksetzen, falls
|
||||||
|
// obiger Titel-Breitentest sie auf 2 stehen gelassen hat, sonst
|
||||||
|
// würde die gleich folgende totalH-Berechnung (vor dem ersten
|
||||||
|
// redraw()) mit falscher (zu breiter) Schriftgröße rechnen.
|
||||||
|
tft.setTextSize(1);
|
||||||
|
// Eine Leerzeile Abstand zwischen Titel und Fließtext.
|
||||||
|
int16_t VIEW_TOP = (int16_t)(TITLE_Y + titleLineCount * LINE_H + 12);
|
||||||
|
|
||||||
constexpr int16_t BTN_H = 40;
|
constexpr int16_t BTN_H = 40;
|
||||||
constexpr int16_t BOTTOM_MARGIN = 10;
|
constexpr int16_t BOTTOM_MARGIN = 10;
|
||||||
@@ -377,11 +450,10 @@ namespace {
|
|||||||
|
|
||||||
tft.setTextDatum(MC_DATUM);
|
tft.setTextDatum(MC_DATUM);
|
||||||
tft.setTextColor(accentColor, TFT_BLACK);
|
tft.setTextColor(accentColor, TFT_BLACK);
|
||||||
tft.setTextSize(2);
|
tft.setTextSize(titleTextSize);
|
||||||
if (tft.textWidth(title) > (BOX_W - 20)) {
|
for (int i = 0; i < titleLineCount; i++) {
|
||||||
tft.setTextSize(1);
|
tft.drawString(titleLines[i], BOX_X + BOX_W / 2, (int16_t)(TITLE_Y + i * LINE_H));
|
||||||
}
|
}
|
||||||
tft.drawString(title, BOX_X + BOX_W / 2, TITLE_Y);
|
|
||||||
tft.setTextSize(1);
|
tft.setTextSize(1);
|
||||||
tft.setTextDatum(TL_DATUM);
|
tft.setTextDatum(TL_DATUM);
|
||||||
|
|
||||||
|
|||||||
@@ -43,17 +43,29 @@ namespace {
|
|||||||
CheckInfo checkForUpdate() {
|
CheckInfo checkForUpdate() {
|
||||||
CheckInfo info;
|
CheckInfo info;
|
||||||
|
|
||||||
|
Serial.printf("[OTA] Pruefe auf Update: url=%s freeHeap=%u RSSI=%ddBm\n", RELEASES_API_URL,
|
||||||
|
(unsigned)ESP.getFreeHeap(), WiFi.RSSI());
|
||||||
|
|
||||||
WiFiClientSecure client;
|
WiFiClientSecure client;
|
||||||
client.setInsecure();
|
client.setInsecure();
|
||||||
client.setTimeout(8000);
|
client.setTimeout(8000);
|
||||||
|
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
http.setTimeout(8000);
|
http.setTimeout(8000);
|
||||||
if (!http.begin(client, RELEASES_API_URL)) return info;
|
if (!http.begin(client, RELEASES_API_URL)) {
|
||||||
|
Serial.println("[OTA] Pruefung fehlgeschlagen: http.begin() lieferte false.");
|
||||||
|
return info;
|
||||||
|
}
|
||||||
http.addHeader("User-Agent", USER_AGENT);
|
http.addHeader("User-Agent", USER_AGENT);
|
||||||
|
|
||||||
int code = http.GET();
|
int code = http.GET();
|
||||||
if (code != HTTP_CODE_OK) {
|
if (code != HTTP_CODE_OK) {
|
||||||
|
if (code < 0) {
|
||||||
|
Serial.printf("[OTA] Pruefung fehlgeschlagen: HTTP-Fehler=%d (%s)\n", code,
|
||||||
|
HTTPClient::errorToString(code).c_str());
|
||||||
|
} else {
|
||||||
|
Serial.printf("[OTA] Pruefung fehlgeschlagen: HTTP-Status=%d\n", code);
|
||||||
|
}
|
||||||
http.end();
|
http.end();
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
@@ -61,17 +73,21 @@ CheckInfo checkForUpdate() {
|
|||||||
http.end();
|
http.end();
|
||||||
|
|
||||||
JsonDocument doc;
|
JsonDocument doc;
|
||||||
if (deserializeJson(doc, body)) return info;
|
DeserializationError jsonErr = deserializeJson(doc, body);
|
||||||
|
if (jsonErr) {
|
||||||
|
Serial.printf("[OTA] Pruefung fehlgeschlagen: JSON-Fehler (%s)\n", jsonErr.c_str());
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
const char* tag = doc["tag_name"] | "";
|
const char* tag = doc["tag_name"] | "";
|
||||||
if (!tag[0]) return info;
|
if (!tag[0]) {
|
||||||
|
Serial.println("[OTA] Pruefung fehlgeschlagen: kein tag_name im Release-JSON.");
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
strncpy(info.latestVersion, (tag[0] == 'v' || tag[0] == 'V') ? tag + 1 : tag,
|
strncpy(info.latestVersion, (tag[0] == 'v' || tag[0] == 'V') ? tag + 1 : tag,
|
||||||
sizeof(info.latestVersion) - 1);
|
sizeof(info.latestVersion) - 1);
|
||||||
|
|
||||||
// Der Release-Workflow (siehe CLAUDE.md im Repo) laedt das rohe
|
|
||||||
// Build-Artefakt unveraendert als "firmware.bin" ins Release hoch -
|
|
||||||
// keine Umbenennung mehr noetig.
|
|
||||||
JsonArray assets = doc["assets"];
|
JsonArray assets = doc["assets"];
|
||||||
for (JsonObject asset : assets) {
|
for (JsonObject asset : assets) {
|
||||||
const char* name = asset["name"] | "";
|
const char* name = asset["name"] | "";
|
||||||
@@ -82,10 +98,17 @@ CheckInfo checkForUpdate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!info.downloadUrl[0]) return info; // Release ohne firmware.bin-Anhang
|
if (!info.downloadUrl[0]) {
|
||||||
|
Serial.printf("[OTA] Pruefung fehlgeschlagen: Release v%s hat keinen firmware.bin-Anhang.\n",
|
||||||
|
info.latestVersion);
|
||||||
|
return info; // Release ohne firmware.bin-Anhang
|
||||||
|
}
|
||||||
|
|
||||||
int cmp = compareVersions(info.latestVersion, Config::APP_VERSION);
|
int cmp = compareVersions(info.latestVersion, Config::APP_VERSION);
|
||||||
info.result = (cmp > 0) ? CheckResult::UpdateAvailable : CheckResult::UpToDate;
|
info.result = (cmp > 0) ? CheckResult::UpdateAvailable : CheckResult::UpToDate;
|
||||||
|
Serial.printf("[OTA] Pruefung erfolgreich: installiert=v%s neuestes=v%s -> %s\n", Config::APP_VERSION,
|
||||||
|
info.latestVersion,
|
||||||
|
info.result == CheckResult::UpdateAvailable ? "Update verfuegbar" : "bereits aktuell");
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user