Review changes
This commit is contained in:
@@ -230,7 +230,7 @@ void HomeActivity::render(RenderLock&&) {
|
||||
// Build menu items dynamically
|
||||
std::vector<const char*> menuItems = {tr(STR_BROWSE_FILES), tr(STR_MENU_RECENT_BOOKS), tr(STR_FILE_TRANSFER),
|
||||
tr(STR_WEATHER), tr(STR_SETTINGS_TITLE)};
|
||||
std::vector<UIIcon> menuIcons = {Folder, Recent, Library, Transfer, Settings};
|
||||
std::vector<UIIcon> menuIcons = {Folder, Recent, Transfer, Library, Settings};
|
||||
|
||||
if (hasOpdsUrl) {
|
||||
// Insert OPDS Browser after Recents (before File Transfer)
|
||||
|
||||
@@ -232,6 +232,27 @@ void WeatherActivity::fetchWeather() {
|
||||
requestUpdate();
|
||||
}
|
||||
|
||||
void WeatherActivity::openSettingsActivity() {
|
||||
renderer.setOrientation(GfxRenderer::Orientation::Portrait);
|
||||
startActivityForResult(std::make_unique<WeatherSettingsActivity>(renderer, mappedInput),
|
||||
[this](const ActivityResult&) {
|
||||
renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise);
|
||||
forceRefresh = true;
|
||||
loadAndDisplay();
|
||||
});
|
||||
}
|
||||
|
||||
void WeatherActivity::triggerRefresh(const bool showPopup) {
|
||||
showRefreshPopup = showPopup;
|
||||
if (WiFi.status() == WL_CONNECTED && WiFi.localIP() != IPAddress(0, 0, 0, 0)) {
|
||||
state = State::FETCHING;
|
||||
requestUpdate(true);
|
||||
fetchWeather();
|
||||
} else {
|
||||
launchWifiSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void WeatherActivity::loop() {
|
||||
if (state == State::WIFI_SELECTION) {
|
||||
return; // Handled by WifiSelectionActivity
|
||||
@@ -239,24 +260,10 @@ void WeatherActivity::loop() {
|
||||
|
||||
if (state == State::ERROR) {
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
// Open weather settings (useful when no location is configured)
|
||||
renderer.setOrientation(GfxRenderer::Orientation::Portrait);
|
||||
startActivityForResult(std::make_unique<WeatherSettingsActivity>(renderer, mappedInput),
|
||||
[this](const ActivityResult&) {
|
||||
renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise);
|
||||
forceRefresh = true;
|
||||
loadAndDisplay();
|
||||
});
|
||||
openSettingsActivity();
|
||||
} else if (mappedInput.wasReleased(MappedInputManager::Button::Left) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Right)) {
|
||||
// Retry fetch
|
||||
if (WiFi.status() == WL_CONNECTED && WiFi.localIP() != IPAddress(0, 0, 0, 0)) {
|
||||
state = State::FETCHING;
|
||||
requestUpdate(true);
|
||||
fetchWeather();
|
||||
} else {
|
||||
launchWifiSelection();
|
||||
}
|
||||
triggerRefresh(false);
|
||||
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
onGoHome();
|
||||
}
|
||||
@@ -299,27 +306,10 @@ void WeatherActivity::loop() {
|
||||
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
||||
onGoHome();
|
||||
} else if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
||||
// Open weather settings
|
||||
renderer.setOrientation(GfxRenderer::Orientation::Portrait);
|
||||
startActivityForResult(std::make_unique<WeatherSettingsActivity>(renderer, mappedInput),
|
||||
[this](const ActivityResult&) {
|
||||
// Re-apply landscape after returning from settings
|
||||
renderer.setOrientation(GfxRenderer::Orientation::LandscapeClockwise);
|
||||
// Refresh after settings change
|
||||
forceRefresh = true;
|
||||
loadAndDisplay();
|
||||
});
|
||||
openSettingsActivity();
|
||||
} else if (mappedInput.wasReleased(MappedInputManager::Button::Left) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Right)) {
|
||||
// Manual refresh with popup feedback.
|
||||
showRefreshPopup = true;
|
||||
if (WiFi.status() == WL_CONNECTED && WiFi.localIP() != IPAddress(0, 0, 0, 0)) {
|
||||
state = State::FETCHING;
|
||||
requestUpdate(true);
|
||||
fetchWeather();
|
||||
} else {
|
||||
launchWifiSelection();
|
||||
}
|
||||
triggerRefresh(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,9 +377,9 @@ void WeatherActivity::render(RenderLock&&) {
|
||||
|
||||
if (state == State::FETCHING && showRefreshPopup) {
|
||||
GUI.drawPopup(renderer, tr(STR_LOADING_POPUP));
|
||||
} else {
|
||||
renderer.displayBuffer();
|
||||
}
|
||||
|
||||
renderer.displayBuffer();
|
||||
}
|
||||
|
||||
void WeatherActivity::renderCurrentConditions(int x, int y, int w, int h) {
|
||||
@@ -490,6 +480,11 @@ void WeatherActivity::renderDailyForecast(int x, int y, int w, int h) {
|
||||
const StrId dayNameIds[] = {StrId::STR_WEATHER_DAY_SUN, StrId::STR_WEATHER_DAY_MON, StrId::STR_WEATHER_DAY_TUE,
|
||||
StrId::STR_WEATHER_DAY_WED, StrId::STR_WEATHER_DAY_THU, StrId::STR_WEATHER_DAY_FRI,
|
||||
StrId::STR_WEATHER_DAY_SAT};
|
||||
const StrId monthNameIds[] = {
|
||||
StrId::STR_WEATHER_MONTH_JAN, StrId::STR_WEATHER_MONTH_FEB, StrId::STR_WEATHER_MONTH_MAR,
|
||||
StrId::STR_WEATHER_MONTH_APR, StrId::STR_WEATHER_MONTH_MAY, StrId::STR_WEATHER_MONTH_JUN,
|
||||
StrId::STR_WEATHER_MONTH_JUL, StrId::STR_WEATHER_MONTH_AUG, StrId::STR_WEATHER_MONTH_SEP,
|
||||
StrId::STR_WEATHER_MONTH_OCT, StrId::STR_WEATHER_MONTH_NOV, StrId::STR_WEATHER_MONTH_DEC};
|
||||
|
||||
const char* unitSuffix = WEATHER_SETTINGS.getTempUnit() == WeatherTempUnit::CELSIUS ? "C" : "F";
|
||||
int numDays = static_cast<int>(weatherData.daily.size());
|
||||
@@ -527,8 +522,8 @@ void WeatherActivity::renderDailyForecast(int x, int y, int w, int h) {
|
||||
|
||||
// Date (e.g. "Apr 3") - for all days
|
||||
char dateBuf[16];
|
||||
const char* monthNames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
snprintf(dateBuf, sizeof(dateBuf), "%s %d", monthNames[timeinfo.tm_mon], timeinfo.tm_mday);
|
||||
const char* monthName = I18N.get(monthNameIds[timeinfo.tm_mon]);
|
||||
snprintf(dateBuf, sizeof(dateBuf), "%s %d", monthName, timeinfo.tm_mday);
|
||||
int dateWidth = renderer.getTextWidth(SMALL_FONT_ID, dateBuf);
|
||||
renderer.drawText(SMALL_FONT_ID, cardX + (cardWidth - dateWidth) / 2, textY, dateBuf);
|
||||
textY += 18;
|
||||
|
||||
@@ -43,6 +43,8 @@ class WeatherActivity final : public Activity {
|
||||
void launchWifiSelection();
|
||||
void onWifiSelectionComplete(bool connected);
|
||||
void fetchWeather();
|
||||
void openSettingsActivity();
|
||||
void triggerRefresh(bool showPopup);
|
||||
|
||||
// Render sub-sections (landscape 800x480)
|
||||
void renderCurrentConditions(int x, int y, int w, int h);
|
||||
|
||||
@@ -125,21 +125,13 @@ void WeatherSettingsActivity::launchCitySearch() {
|
||||
[this, query = kb.text](const ActivityResult& wifiResult) {
|
||||
if (wifiResult.isCancelled) return;
|
||||
searchResults = WeatherClient::searchCity(query);
|
||||
if (searchResults.empty()) {
|
||||
requestUpdate();
|
||||
return;
|
||||
}
|
||||
showingSearchResults = true;
|
||||
showingSearchResults = !searchResults.empty();
|
||||
selectedIndex = 0;
|
||||
requestUpdate();
|
||||
});
|
||||
} else {
|
||||
searchResults = WeatherClient::searchCity(kb.text);
|
||||
if (searchResults.empty()) {
|
||||
requestUpdate();
|
||||
return;
|
||||
}
|
||||
showingSearchResults = true;
|
||||
showingSearchResults = !searchResults.empty();
|
||||
selectedIndex = 0;
|
||||
requestUpdate();
|
||||
}
|
||||
@@ -153,10 +145,14 @@ void WeatherSettingsActivity::launchLatitudeEntry() {
|
||||
[this](const ActivityResult& result) {
|
||||
if (!result.isCancelled) {
|
||||
const auto& kb = std::get<KeyboardResult>(result.data);
|
||||
float lat = strtof(kb.text.c_str(), nullptr);
|
||||
if (lat >= -90.0f && lat <= 90.0f) {
|
||||
WEATHER_SETTINGS.setLocation(lat, WEATHER_SETTINGS.getLongitude(), WEATHER_SETTINGS.getLocationName());
|
||||
char* end = nullptr;
|
||||
const float lat = strtof(kb.text.c_str(), &end);
|
||||
if (end != kb.text.c_str() && *end == '\0' && lat >= -90.0f && lat <= 90.0f) {
|
||||
if (lat != WEATHER_SETTINGS.getLatitude()) {
|
||||
WEATHER_SETTINGS.setLocation(lat, WEATHER_SETTINGS.getLongitude(), "");
|
||||
}
|
||||
WEATHER_SETTINGS.saveToFile();
|
||||
requestUpdate();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -169,10 +165,14 @@ void WeatherSettingsActivity::launchLongitudeEntry() {
|
||||
[this](const ActivityResult& result) {
|
||||
if (!result.isCancelled) {
|
||||
const auto& kb = std::get<KeyboardResult>(result.data);
|
||||
float lon = strtof(kb.text.c_str(), nullptr);
|
||||
if (lon >= -180.0f && lon <= 180.0f) {
|
||||
WEATHER_SETTINGS.setLocation(WEATHER_SETTINGS.getLatitude(), lon, WEATHER_SETTINGS.getLocationName());
|
||||
char* end = nullptr;
|
||||
const float lon = strtof(kb.text.c_str(), &end);
|
||||
if (end != kb.text.c_str() && *end == '\0' && lon >= -180.0f && lon <= 180.0f) {
|
||||
if (lon != WEATHER_SETTINGS.getLongitude()) {
|
||||
WEATHER_SETTINGS.setLocation(WEATHER_SETTINGS.getLatitude(), lon, "");
|
||||
}
|
||||
WEATHER_SETTINGS.saveToFile();
|
||||
requestUpdate();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ class WeatherSettingsActivity final : public Activity {
|
||||
std::vector<GeocodingResult> searchResults;
|
||||
bool showingSearchResults = false;
|
||||
|
||||
static constexpr int MENU_ITEMS = 6; // Location, Lat, Lon, TempUnit, WindUnit, PrecipUnit
|
||||
static constexpr size_t MENU_ITEMS = 6; // Location, Lat, Lon, TempUnit, WindUnit, PrecipUnit
|
||||
|
||||
void handleSelection();
|
||||
void launchCitySearch();
|
||||
|
||||
@@ -55,52 +55,37 @@ class FileWriteStream final : public Stream {
|
||||
bool HttpDownloader::fetchUrl(const std::string& url, Stream& outContent) {
|
||||
// Use NetworkClientSecure for HTTPS, regular NetworkClient for HTTP
|
||||
std::unique_ptr<NetworkClient> client;
|
||||
LOG_DBG("HTTP", "fetchUrl[1] start (https=%d)", UrlUtils::isHttpsUrl(url) ? 1 : 0);
|
||||
if (UrlUtils::isHttpsUrl(url)) {
|
||||
auto* secureClient = new NetworkClientSecure();
|
||||
LOG_DBG("HTTP", "fetchUrl[2] created NetworkClientSecure");
|
||||
secureClient->setInsecure();
|
||||
LOG_DBG("HTTP", "fetchUrl[3] setInsecure done");
|
||||
client.reset(secureClient);
|
||||
} else {
|
||||
client.reset(new NetworkClient());
|
||||
LOG_DBG("HTTP", "fetchUrl[2] created NetworkClient");
|
||||
}
|
||||
HTTPClient http;
|
||||
|
||||
LOG_DBG("HTTP", "Fetching: %s", url.c_str());
|
||||
|
||||
LOG_DBG("HTTP", "fetchUrl[4] begin() before");
|
||||
http.begin(*client, url.c_str());
|
||||
LOG_DBG("HTTP", "fetchUrl[5] begin() after");
|
||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
LOG_DBG("HTTP", "fetchUrl[6] redirects configured");
|
||||
http.addHeader("User-Agent", "CrossPoint-ESP32-" CROSSPOINT_VERSION);
|
||||
LOG_DBG("HTTP", "fetchUrl[7] user-agent header added");
|
||||
|
||||
// Add Basic HTTP auth if credentials are configured
|
||||
if (strlen(SETTINGS.opdsUsername) > 0 && strlen(SETTINGS.opdsPassword) > 0) {
|
||||
std::string credentials = std::string(SETTINGS.opdsUsername) + ":" + SETTINGS.opdsPassword;
|
||||
String encoded = base64::encode(credentials.c_str());
|
||||
http.addHeader("Authorization", "Basic " + encoded);
|
||||
LOG_DBG("HTTP", "fetchUrl[8] auth header added");
|
||||
}
|
||||
|
||||
LOG_DBG("HTTP", "fetchUrl[9] GET() before");
|
||||
const int httpCode = http.GET();
|
||||
LOG_DBG("HTTP", "fetchUrl[10] GET() after code=%d", httpCode);
|
||||
if (httpCode != HTTP_CODE_OK) {
|
||||
LOG_ERR("HTTP", "Fetch failed: %d", httpCode);
|
||||
LOG_DBG("HTTP", "fetchUrl[11] end() after GET failure");
|
||||
http.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DBG("HTTP", "fetchUrl[12] writeToStream() before");
|
||||
http.writeToStream(&outContent);
|
||||
LOG_DBG("HTTP", "fetchUrl[13] writeToStream() after");
|
||||
|
||||
LOG_DBG("HTTP", "fetchUrl[14] end() before success return");
|
||||
http.end();
|
||||
|
||||
LOG_DBG("HTTP", "Fetch success");
|
||||
@@ -120,41 +105,30 @@ HttpDownloader::DownloadError HttpDownloader::downloadToFile(const std::string&
|
||||
ProgressCallback progress) {
|
||||
// Use NetworkClientSecure for HTTPS, regular NetworkClient for HTTP
|
||||
std::unique_ptr<NetworkClient> client;
|
||||
LOG_DBG("HTTP", "downloadToFile[1] start (https=%d)", UrlUtils::isHttpsUrl(url) ? 1 : 0);
|
||||
if (UrlUtils::isHttpsUrl(url)) {
|
||||
auto* secureClient = new NetworkClientSecure();
|
||||
LOG_DBG("HTTP", "downloadToFile[2] created NetworkClientSecure");
|
||||
secureClient->setInsecure();
|
||||
LOG_DBG("HTTP", "downloadToFile[3] setInsecure done");
|
||||
client.reset(secureClient);
|
||||
} else {
|
||||
client.reset(new NetworkClient());
|
||||
LOG_DBG("HTTP", "downloadToFile[2] created NetworkClient");
|
||||
}
|
||||
HTTPClient http;
|
||||
|
||||
LOG_DBG("HTTP", "Downloading: %s", url.c_str());
|
||||
LOG_DBG("HTTP", "Destination: %s", destPath.c_str());
|
||||
|
||||
LOG_DBG("HTTP", "downloadToFile[4] begin() before");
|
||||
http.begin(*client, url.c_str());
|
||||
LOG_DBG("HTTP", "downloadToFile[5] begin() after");
|
||||
http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
|
||||
LOG_DBG("HTTP", "downloadToFile[6] redirects configured");
|
||||
http.addHeader("User-Agent", "CrossPoint-ESP32-" CROSSPOINT_VERSION);
|
||||
LOG_DBG("HTTP", "downloadToFile[7] user-agent header added");
|
||||
|
||||
// Add Basic HTTP auth if credentials are configured
|
||||
if (strlen(SETTINGS.opdsUsername) > 0 && strlen(SETTINGS.opdsPassword) > 0) {
|
||||
std::string credentials = std::string(SETTINGS.opdsUsername) + ":" + SETTINGS.opdsPassword;
|
||||
String encoded = base64::encode(credentials.c_str());
|
||||
http.addHeader("Authorization", "Basic " + encoded);
|
||||
LOG_DBG("HTTP", "downloadToFile[8] auth header added");
|
||||
}
|
||||
|
||||
LOG_DBG("HTTP", "downloadToFile[9] GET() before");
|
||||
const int httpCode = http.GET();
|
||||
LOG_DBG("HTTP", "downloadToFile[10] GET() after code=%d", httpCode);
|
||||
if (httpCode != HTTP_CODE_OK) {
|
||||
LOG_ERR("HTTP", "Download failed: %d", httpCode);
|
||||
http.end();
|
||||
@@ -184,14 +158,10 @@ HttpDownloader::DownloadError HttpDownloader::downloadToFile(const std::string&
|
||||
|
||||
// Let HTTPClient handle chunked decoding and stream body bytes into the file.
|
||||
FileWriteStream fileStream(file, contentLength, progress);
|
||||
LOG_DBG("HTTP", "downloadToFile[11] writeToStream() before");
|
||||
const int writeResult = http.writeToStream(&fileStream);
|
||||
LOG_DBG("HTTP", "downloadToFile[12] writeToStream() after result=%d", writeResult);
|
||||
|
||||
file.close();
|
||||
LOG_DBG("HTTP", "downloadToFile[13] file closed");
|
||||
http.end();
|
||||
LOG_DBG("HTTP", "downloadToFile[14] http end done");
|
||||
|
||||
if (writeResult < 0) {
|
||||
LOG_ERR("HTTP", "writeToStream error: %d", writeResult);
|
||||
|
||||
Reference in New Issue
Block a user