#include "firmware_page.h" #include #include "ui/widgets.h" #include "version.h" namespace roro { namespace { constexpr const char* kUpdatesFolder = "/updates"; bool endsWith(const std::string& s, const std::string& suffix) { return s.size() >= suffix.size() && s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0; } } // namespace void FirmwarePage::enter() { confirm_.reset(); { std::lock_guard g(lock_); files_.clear(); listed_ = false; } list_.setCount(kFixed); storage_.runJob([this]() { std::vector found; File dir = SD.open(kUpdatesFolder); if (dir && dir.isDirectory()) for (File f = dir.openNextFile(); f; f = dir.openNextFile()) if (!f.isDirectory() && endsWith(f.name(), ".ota")) found.push_back(std::string(kUpdatesFolder) + "/" + f.name()); std::lock_guard g(lock_); files_ = found; listed_ = true; }); } std::vector FirmwarePage::files() { std::lock_guard g(lock_); return files_; } bool FirmwarePage::onKey(const KeyEvent& e) { auto found = files(); if (confirm_) { confirm_->onKey(e); int pick = list_.selected() - kFixed; if (confirm_->result() == 1 && pick >= 0 && pick < static_cast(found.size())) update_.installFromSd(found[pick]); if (confirm_->result() != DialogModel::kPending) confirm_.reset(); return true; } list_.setCount(kFixed + static_cast(found.size())); switch (e.key) { case Key::Up: list_.up(); break; case Key::Down: list_.down(); break; case Key::Back: return false; case Key::Select: if (list_.selected() >= kFixed) confirm_.reset(new DialogModel({"Cancel", "Install"})); break; default: break; } return true; } void FirmwarePage::draw(Canvas& c) { auto found = files(); bool listed; { std::lock_guard g(lock_); listed = listed_; } list_.setCount(kFixed + static_cast(found.size())); std::string ip = wifi_.ip(); widgets::list( c, list_, theme::kContent, [&](int i) -> std::string { switch (i) { case kVersion: return "Version"; case kStatus: return "Status"; case kAddress: return "Push to"; case kSdHeader: return listed ? (found.empty() ? "No .ota files in /updates" : "On the SD card:") : "Looking on the SD card..."; default: return " " + found[i - kFixed].substr(std::string(kUpdatesFolder).size() + 1); } }, [&](int i) -> std::string { switch (i) { case kVersion: return versionString(); case kStatus: return update_.onProbation() ? "on probation" : "confirmed"; case kAddress: return ip.empty() ? "Wi-Fi not connected" : ip + ":" + std::to_string(UpdateService::kPort); case kSdHeader: return ""; default: return "install >"; } }); if (confirm_) { int pick = list_.selected() - kFixed; std::string name = pick >= 0 && pick < static_cast(found.size()) ? found[pick] : ""; widgets::dialog(c, "Install update?", name + " is checked before anything is written. The device restarts after.", *confirm_); } } } // namespace roro