diff --git a/examples/companion_radio/ui-lvgl/QuickScreen.h b/examples/companion_radio/ui-lvgl/QuickScreen.h index c43867bd..6bbbd98b 100644 --- a/examples/companion_radio/ui-lvgl/QuickScreen.h +++ b/examples/companion_radio/ui-lvgl/QuickScreen.h @@ -46,8 +46,18 @@ static void onQuickEditPh(lv_event_t* e) { s_ui->quickEditInsert((int)(uintptr_ static void onQuickEditKb(lv_event_t* e) { s_ui->quickEditDone(lv_event_get_code(e) == LV_EVENT_READY); } static void onQuickList(lv_event_t* e) { (void)e; s_ui->showQuickMsgs(); } static void onAdvertGo(lv_event_t* e) { s_ui->advertSend((uintptr_t)lv_event_get_user_data(e) != 0); } +static void bluetoothHint(char* sub, size_t n) { + if (!s_ui->isSerialEnabled()) snprintf(sub, n, "Off"); + else if (s_ui->hasConnection()) snprintf(sub, n, "App connected"); + else if (the_mesh.getBLEPin()) snprintf(sub, n, "PIN %06lu", (unsigned long)the_mesh.getBLEPin()); + else snprintf(sub, n, "Waiting for the app"); +} static void onBtSwitch(lv_event_t* e) { - s_ui->setBluetooth(lv_obj_has_state((lv_obj_t*)lv_event_get_target(e), LV_STATE_CHECKED)); + lv_obj_t* sw = (lv_obj_t*)lv_event_get_target(e); + s_ui->setBluetooth(lv_obj_has_state(sw, LV_STATE_CHECKED)); + char sub[48]; + bluetoothHint(sub, sizeof(sub)); + rowHintSet(lv_obj_get_parent(sw), sub); } } // namespace qview @@ -210,25 +220,15 @@ void UITask::advertSend(bool flood) { static void bluetoothRow(lv_obj_t* body) { char sub[48]; - bool on = s_ui->isSerialEnabled(); - if (!on) snprintf(sub, sizeof(sub), "Off"); - else if (s_ui->hasConnection()) snprintf(sub, sizeof(sub), "App connected"); - else if (the_mesh.getBLEPin()) snprintf(sub, sizeof(sub), "PIN %06lu", (unsigned long)the_mesh.getBLEPin()); - else snprintf(sub, sizeof(sub), "Waiting for the app"); + qview::bluetoothHint(sub, sizeof(sub)); lv_obj_t* sw = switchRow(body, LV_SYMBOL_BLUETOOTH " Bluetooth", sub, nullptr); - if (on) lv_obj_add_state(sw, LV_STATE_CHECKED); + if (s_ui->isSerialEnabled()) lv_obj_add_state(sw, LV_STATE_CHECKED); lv_obj_add_event_cb(sw, qview::onBtSwitch, LV_EVENT_VALUE_CHANGED, NULL); } void UITask::setBluetooth(bool on) { if (on) enableSerial(); else disableSerial(); - showToast(on ? "Bluetooth on" : "Bluetooth off - USB still works", 1500); - if (_screen == SCR_SETTINGS) { // the row's subtitle - lv_obj_t* body = _body; - int32_t y = body ? lv_obj_get_scroll_y(body) : 0; - buildSettings(); - if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); } - } + if (!on) showToast("Bluetooth off - USB still works", 1500); refreshStatusBar(); } diff --git a/examples/companion_radio/ui-lvgl/UITask.cpp b/examples/companion_radio/ui-lvgl/UITask.cpp index 1abeb243..2d78d28c 100644 --- a/examples/companion_radio/ui-lvgl/UITask.cpp +++ b/examples/companion_radio/ui-lvgl/UITask.cpp @@ -373,6 +373,13 @@ static lv_obj_t* groupText(lv_obj_t* row, const char* text, const char* hint, ui // The title label of a settingRow() / listRow(), to recolour or rename it. static lv_obj_t* rowTitle(lv_obj_t* row) { return (lv_obj_t*)lv_obj_get_user_data(row); } +// Rewrites the hint under a row's title in place (the label made right after +// it) -- a rebuild would cut short the animation of the switch just tapped. +static void rowHintSet(lv_obj_t* row, const char* hint) { + lv_obj_t* h = lv_obj_get_sibling(rowTitle(row), 1); + if (h && lv_obj_check_type(h, &lv_label_class)) lv_label_set_text(h, hint); +} + // A settings row: the label left (a muted hint under it, cut at `hint_w`), // room on the right for a switch / dropdown / slider. static lv_obj_t* settingRow(lv_obj_t* parent, const char* text, const char* hint, int hint_w = 150) { @@ -2983,8 +2990,7 @@ void UITask::refreshThread() { if (n == 0 && _thread_skip > 0) { // the page went away (history trimmed) _thread_skip = 0; n = loadThreadPage(total); - } - if (total > _thread_skip + n) { + } if (total > _thread_skip + n) { char t[40]; snprintf(t, sizeof(t), LV_SYMBOL_UP " Older messages (%d)", total - _thread_skip - n); pageButton(_thread_list, t, 1); diff --git a/examples/companion_radio/ui-lvgl/WifiScreen.h b/examples/companion_radio/ui-lvgl/WifiScreen.h index b15b7417..73d98249 100644 --- a/examples/companion_radio/ui-lvgl/WifiScreen.h +++ b/examples/companion_radio/ui-lvgl/WifiScreen.h @@ -33,8 +33,19 @@ static void onWifiForget(lv_event_t* e) { s_wifi_forget_btn = nullptr; s_ui->wifiForget((int)(uintptr_t)lv_event_get_user_data(e)); } +static void wifiHint(char* sub, size_t n) { + int saved = lvport::wifiSavedCount(); + if (!lvport::wifiAllowed()) snprintf(sub, n, "Off"); + else if (saved == 1) snprintf(sub, n, "%s", lvport::wifiSaved(0)->ssid); + else if (saved > 1) snprintf(sub, n, "%d saved networks", saved); + else snprintf(sub, n, "Tap to pick a network"); +} static void onWifiAllowed(lv_event_t* e) { - s_ui->wifiSetAllowed(lv_obj_has_state((lv_obj_t*)lv_event_get_target(e), LV_STATE_CHECKED)); + lv_obj_t* sw = (lv_obj_t*)lv_event_get_target(e); + s_ui->wifiSetAllowed(lv_obj_has_state(sw, LV_STATE_CHECKED)); + char sub[48]; + wifiHint(sub, sizeof(sub)); + rowHintSet(lv_obj_get_parent(sw), sub); } static void onWifiField(lv_event_t* e) { s_ui->wifiEdit((lv_obj_t*)lv_event_get_target(e)); } static void onWifiKb(lv_event_t* e) { @@ -165,14 +176,9 @@ void UITask::wifiKeyboardHide() { // on / off, the row opens the network settings. static void wifiRow(lv_obj_t* body) { char sub[48]; - int n = lvport::wifiSavedCount(); - bool on = lvport::wifiAllowed(); - if (!on) snprintf(sub, sizeof(sub), "Off"); - else if (n == 1) snprintf(sub, sizeof(sub), "%s", lvport::wifiSaved(0)->ssid); - else if (n > 1) snprintf(sub, sizeof(sub), "%d saved networks", n); - else snprintf(sub, sizeof(sub), "Tap to pick a network"); + wifiHint(sub, sizeof(sub)); lv_obj_t* sw = switchRow(body, LV_SYMBOL_WIFI " WiFi", sub, nullptr); - if (on) lv_obj_add_state(sw, LV_STATE_CHECKED); + if (lvport::wifiAllowed()) lv_obj_add_state(sw, LV_STATE_CHECKED); lv_obj_add_event_cb(sw, onWifiAllowed, LV_EVENT_VALUE_CHANGED, NULL); lv_obj_t* row = lv_obj_get_parent(sw); lv_obj_add_flag(row, LV_OBJ_FLAG_CLICKABLE); @@ -189,12 +195,6 @@ void UITask::wifiSetAllowed(bool on) { if (mapview::s_dl.active()) mapDownloadStop(); if (_wifi_scanning) { _wifi_scanning = false; lvport::netEnd(); } } - if (_screen == SCR_SETTINGS) { // the row's subtitle - lv_obj_t* body = _body; - int32_t y = body ? lv_obj_get_scroll_y(body) : 0; - buildSettings(); - if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); } - } } void UITask::wifiScan() { diff --git a/examples/companion_radio/ui-lvgl/map/TileDownloader.h b/examples/companion_radio/ui-lvgl/map/TileDownloader.h index cc28f2ce..5b5b7c04 100644 --- a/examples/companion_radio/ui-lvgl/map/TileDownloader.h +++ b/examples/companion_radio/ui-lvgl/map/TileDownloader.h @@ -228,9 +228,11 @@ public: else if (isPlaceholder(data, len)) { lvport::fetchRelease(); finish(FAILED, "Server sends a placeholder (blocked / key?)"); return; } else if (!writeTile(_z, _x, _y, data, len)) { lvport::fetchRelease(); finish(FAILED, "Can't write to the SD card"); return; } else { _done++; _consec_fail = 0; } - } else if (!_retried && r != -403 && r != -401 && r != -404) { + } else if (!_retried && r != -403 && r != -401) { // One more try after a pause: a dropped keep-alive connection or a - // busy server usually answers the second time. + // busy server usually answers the second time. A 404 too: both tile + // servers answer every tile of the world, and OpenTopoMap sends the + // odd 404 for a tile it hasn't rendered yet. lvport::fetchRelease(); _retried = true; _last_start = millis() + 1000; @@ -247,7 +249,13 @@ public: // Skip tiles already on the card (a few stat()s per pass), then start the next GET. for (int i = 0; i < 16 && _state == RUNNING; i++) { - if (_z > _area.zmax) { finish(DONE, "Done"); return; } + if (_z > _area.zmax) { + // Tiles on the card are skipped, so the same download again fetches just the missing ones. + char m[48]; + if (_failed) snprintf(m, sizeof(m), "Done, %lu missing - download again", (unsigned long)_failed); + finish(DONE, _failed ? m : "Done"); + return; + } char path[96]; tilePath(path, sizeof(path), _z, _x, _y, _layer); struct stat st;