#pragma once #include #include namespace roro::release { // Installing a release without being asked to (issue #52): once a newer one is known, wait for a // quiet moment (nothing recording, nothing written, no key for a while), then say so on the // screen for 30 seconds. Unless someone answers no, it is installed. Host-tested; the caller says // what "quiet" is and does the installing. class AutoUpdate { public: static constexpr uint32_t kIdleMs = 120000; // no key for this long: nobody is using the device static constexpr uint32_t kAskMs = 30000; // how long the question stays before it answers itself enum class Action : uint8_t { None, Ask, // the question goes up Dismiss, // the question comes down, nothing is installed Install, // the question comes down, install tag() }; // A newer release is known. A second offer while one is waiting or asked replaces its tag. void offer(const std::string& tag) { tag_ = tag; if (state_ == State::Idle) state_ = State::Waiting; } // The setting went off, or the release is no longer wanted. void withdraw() { withdrawn_ = true; } // The user's answer to the question. void answer(bool install) { if (state_ != State::Asking) return; answered_ = true; accepted_ = install; } // Call often. `busy`: something is recording or being written, and a restart would cut it. // `idleMs`: how long since the last key. Action step(uint32_t nowMs, bool busy, uint32_t idleMs) { bool withdrawn = withdrawn_; withdrawn_ = false; switch (state_) { case State::Idle: return Action::None; case State::Waiting: if (withdrawn) return state_ = State::Idle, Action::None; if (busy || idleMs < kIdleMs) return Action::None; state_ = State::Asking; askedMs_ = nowMs; answered_ = false; return Action::Ask; case State::Asking: if (withdrawn) return state_ = State::Idle, Action::Dismiss; if (answered_) return state_ = State::Idle, accepted_ ? Action::Install : Action::Dismiss; // Something started meanwhile (a Track from a console): the question goes, and comes back later. if (busy) return state_ = State::Waiting, Action::Dismiss; if (nowMs - askedMs_ >= kAskMs) return state_ = State::Idle, Action::Install; return Action::None; } return Action::None; } bool waiting() const { return state_ == State::Waiting; } bool asking() const { return state_ == State::Asking; } const std::string& tag() const { return tag_; } // Whole seconds until the question answers itself, rounded up. int secondsLeft(uint32_t nowMs) const { uint32_t gone = nowMs - askedMs_; return !asking() || gone >= kAskMs ? 0 : static_cast((kAskMs - gone + 999) / 1000); } private: enum class State : uint8_t { Idle, Waiting, Asking }; State state_ = State::Idle; std::string tag_; uint32_t askedMs_ = 0; bool answered_ = false, accepted_ = false, withdrawn_ = false; }; } // namespace roro::release