Kiosk: apply schedule engine in playback and add System Settings toggle
ScrollerPage now resolves the active Block from ScheduleManager (reusing the existing stream-switch path), honoring dynamicAudio true/false and falling back to local selection for gaps, no schedule, or toggle off. Adds a 'Schedule' On/Off entry (default On) in System Settings persisted in config.yaml, and config plumbing (proclamation.schedule_enabled). All on-device schedule times remain 12h AM/PM.
This commit is contained in:
+39
-26
@@ -214,15 +214,32 @@ void ScrollerPage::buildStreams(AppState &state) {
|
||||
streamIndex_ = 0;
|
||||
scheduleChangePending_ = false;
|
||||
|
||||
// Schedule state: only an active schedule with a currently-matching block
|
||||
// changes the play list. The block boundary is handled in advanceStream(),
|
||||
// so rebuilding mid-playback never truncates the current proclamation.
|
||||
// Schedule state: resolve the active Block from the schedule manager (which
|
||||
// owns the fetched/cached payload and the local on/off toggle). The block
|
||||
// boundary is handled in advanceStream(), so rebuilding mid-playback never
|
||||
// truncates the current proclamation.
|
||||
const radiantwave::config::AppConfig cfg = state.config.get();
|
||||
scheduleActive_ = cfg.proclamation.schedule_active;
|
||||
timezone_ = cfg.timezone;
|
||||
scheduleBlocks_ = scheduleActive_ ? state.db.getScheduleBlocks()
|
||||
: std::vector<radiantwave::db::ScheduleBlockRecord>{};
|
||||
activeBlockIndex_ = currentScheduleBlockIndex();
|
||||
// Keep the engine's local toggle in step with config (it may have been
|
||||
// changed in System Settings or hot-reloaded).
|
||||
state.scheduleManager.setEnabled(cfg.proclamation.schedule_enabled);
|
||||
|
||||
const int now = radiantwave::timezone::currentLocalMinutes(timezone_);
|
||||
const radiantwave::schedule::Weekday weekday =
|
||||
radiantwave::schedule::weekdayFromIndex(
|
||||
radiantwave::timezone::currentLocalWeekday(timezone_));
|
||||
const radiantwave::schedule::Decision decision =
|
||||
state.scheduleManager.activeBlock(now, weekday);
|
||||
scheduleActive_ = state.scheduleManager.enabled();
|
||||
if (decision.hasBlock) {
|
||||
activeBlockProclamationIds_ = decision.block.proclamation_ids;
|
||||
activeBlockDynamicAudio_ = decision.block.dynamic_audio;
|
||||
activeBlockIndex_ = 1; // any non-negative marker: a block is active
|
||||
} else {
|
||||
activeBlockProclamationIds_.clear();
|
||||
activeBlockDynamicAudio_ = false;
|
||||
activeBlockIndex_ = -1;
|
||||
}
|
||||
|
||||
auto proclamations = state.db.getActiveProclamations();
|
||||
if (proclamations.empty()) {
|
||||
@@ -264,9 +281,8 @@ void ScrollerPage::buildStreams(AppState &state) {
|
||||
|
||||
const std::vector<std::string>* scheduledIds = nullptr;
|
||||
std::vector<std::string> scheduledOrder;
|
||||
if (activeBlockIndex_ >= 0 &&
|
||||
activeBlockIndex_ < static_cast<int>(scheduleBlocks_.size())) {
|
||||
scheduledOrder = scheduleBlocks_[activeBlockIndex_].proclamation_ids;
|
||||
if (activeBlockIndex_ >= 0 && !activeBlockProclamationIds_.empty()) {
|
||||
scheduledOrder = activeBlockProclamationIds_;
|
||||
scheduledIds = &scheduledOrder;
|
||||
}
|
||||
|
||||
@@ -275,26 +291,25 @@ void ScrollerPage::buildStreams(AppState &state) {
|
||||
DEFAULT_PROPLEAMATION_ID, scheduledIds);
|
||||
}
|
||||
|
||||
int ScrollerPage::currentScheduleBlockIndex() const {
|
||||
if (!scheduleActive_ || scheduleBlocks_.empty()) {
|
||||
return -1;
|
||||
bool ScrollerPage::scheduleBlockActiveNow() const {
|
||||
if (!state_) {
|
||||
return false;
|
||||
}
|
||||
const int now = radiantwave::timezone::currentLocalMinutes(timezone_);
|
||||
for (size_t i = 0; i < scheduleBlocks_.size(); ++i) {
|
||||
const radiantwave::db::ScheduleBlockRecord &block = scheduleBlocks_[i];
|
||||
if (radiantwave::scroller::minuteInScheduleBlock(
|
||||
now, block.start_minutes, block.end_minutes, block.overnight)) {
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
const radiantwave::schedule::Weekday weekday =
|
||||
radiantwave::schedule::weekdayFromIndex(
|
||||
radiantwave::timezone::currentLocalWeekday(timezone_));
|
||||
return state_->scheduleManager.activeBlock(now, weekday).hasBlock;
|
||||
}
|
||||
|
||||
void ScrollerPage::pollScheduleChange() {
|
||||
if (!scheduleActive_) {
|
||||
// The manager owns the local toggle; when off, there is never an active block.
|
||||
if (!state_) {
|
||||
return;
|
||||
}
|
||||
if (currentScheduleBlockIndex() != activeBlockIndex_) {
|
||||
const bool activeNow = scheduleBlockActiveNow();
|
||||
const bool hadBlock = activeBlockIndex_ >= 0;
|
||||
if (activeNow != hadBlock) {
|
||||
scheduleChangePending_ = true;
|
||||
}
|
||||
}
|
||||
@@ -303,9 +318,7 @@ bool ScrollerPage::effectiveDynamicAudio() const {
|
||||
// dynamicAudio=true on the active block forces dynamic audio, overriding
|
||||
// whatever the device selected. false means "don't override": the device's
|
||||
// own selection (dynamic or a fixed track) applies as usual.
|
||||
if (scheduleActive_ && activeBlockIndex_ >= 0 &&
|
||||
activeBlockIndex_ < static_cast<int>(scheduleBlocks_.size()) &&
|
||||
scheduleBlocks_[activeBlockIndex_].dynamic_audio) {
|
||||
if (scheduleActive_ && activeBlockIndex_ >= 0 && activeBlockDynamicAudio_) {
|
||||
return true;
|
||||
}
|
||||
return state_ ? state_->config.get().audio.dynamic_audio : false;
|
||||
|
||||
Reference in New Issue
Block a user