fix(settings): preserve quick resume timeout preference (#2101)
## Summary ### **What is the goal of this PR?** This fixes an unintended settings side effect when cycling the `Sleep Screen` option through `Quick Resume`. Previously, selecting `Sleep Screen = Quick Resume` globally forced `Quick Resume on Timeout = ON` and left it enabled even after the user toggled `Sleep Screen` to another option within the same settings session. Now the auto-enable behavior is scoped to the Settings screen session: - If `Quick Resume on Timeout` was already `ON` when entering Settings, it stays `ON`. - If it was `OFF`, selecting `Sleep Screen = Quick Resume` temporarily turns it `ON`. - If the user then switches away from `Quick Resume`, it turns back `OFF`. ### **What changes are included?** - Removes the global logic that permanently forced `Quick Resume on Timeout` to `ON` whenever `Sleep Screen` was set to `Quick Resume`, even if it was just due to toggling through the options. - Adds Settings-screen session tracking so `Quick Resume on Timeout` is only auto-enabled while the user has `Sleep Screen = Quick Resume`. - Restores `Quick Resume on Timeout` back to `OFF` when the user switches away, but only if it was `OFF` when they entered Settings. - Preserves existing `ON` timeout preferences. - Same behavior applies to the web settings ## Additional Context - Tested this on device and via the settings UI --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**< YES >**_
This commit is contained in:
@@ -79,12 +79,6 @@ void CrossPointSettings::validateFrontButtonMapping(CrossPointSettings& settings
|
||||
}
|
||||
}
|
||||
|
||||
void CrossPointSettings::normalizeDependentSettings(CrossPointSettings& settings) {
|
||||
if (settings.sleepScreen == SLEEP_SCREEN_MODE::QUICK_RESUME) {
|
||||
settings.quickResumeSleepScreen = QUICK_RESUME_SLEEP_SCREEN::QUICK_RESUME_AFTER_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
bool CrossPointSettings::saveToFile() const {
|
||||
Storage.mkdir("/.crosspoint");
|
||||
return JsonSettingsIO::saveSettings(*this, SETTINGS_FILE_JSON);
|
||||
|
||||
@@ -275,7 +275,6 @@ class CrossPointSettings {
|
||||
bool loadFromFile();
|
||||
|
||||
static void validateFrontButtonMapping(CrossPointSettings& settings);
|
||||
static void normalizeDependentSettings(CrossPointSettings& settings);
|
||||
|
||||
private:
|
||||
bool loadFromBinaryFile();
|
||||
|
||||
@@ -223,10 +223,6 @@ bool JsonSettingsIO::loadSettings(CrossPointSettings& s, const char* json, bool*
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t quickResumeBeforeNormalize = s.quickResumeSleepScreen;
|
||||
CrossPointSettings::normalizeDependentSettings(s);
|
||||
if (s.quickResumeSleepScreen != quickResumeBeforeNormalize && needsResave) *needsResave = true;
|
||||
|
||||
// Front button remap — managed by RemapFrontButtons sub-activity, not in SettingsList.
|
||||
using S = CrossPointSettings;
|
||||
s.frontButtonBack =
|
||||
|
||||
@@ -86,6 +86,10 @@ void SettingsActivity::onEnter() {
|
||||
// Reset selection to first category
|
||||
selectedCategoryIndex = 0;
|
||||
selectedSettingIndex = 0;
|
||||
preserveQuickResumeTimeoutOn =
|
||||
SETTINGS.quickResumeSleepScreen == CrossPointSettings::QUICK_RESUME_SLEEP_SCREEN::QUICK_RESUME_AFTER_TIMEOUT;
|
||||
quickResumeTimeoutAutoEnabled = false;
|
||||
syncQuickResumeTimeoutForSleepScreen(/*sleepScreenChanged=*/true, /*quickResumeTimeoutChanged=*/false);
|
||||
|
||||
rebuildSettingsLists();
|
||||
|
||||
@@ -176,6 +180,8 @@ void SettingsActivity::toggleCurrentSetting() {
|
||||
}
|
||||
|
||||
const auto& setting = (*currentSettings)[selectedSetting];
|
||||
const bool sleepScreenChanged = setting.valuePtr == &CrossPointSettings::sleepScreen;
|
||||
const bool quickResumeTimeoutChanged = setting.valuePtr == &CrossPointSettings::quickResumeSleepScreen;
|
||||
|
||||
if (setting.type == SettingType::TOGGLE && setting.valuePtr != nullptr) {
|
||||
// Toggle the boolean value using the member pointer
|
||||
@@ -253,10 +259,33 @@ void SettingsActivity::toggleCurrentSetting() {
|
||||
return;
|
||||
}
|
||||
|
||||
CrossPointSettings::normalizeDependentSettings(SETTINGS);
|
||||
syncQuickResumeTimeoutForSleepScreen(sleepScreenChanged, quickResumeTimeoutChanged);
|
||||
SETTINGS.saveToFile();
|
||||
}
|
||||
|
||||
void SettingsActivity::syncQuickResumeTimeoutForSleepScreen(bool sleepScreenChanged, bool quickResumeTimeoutChanged) {
|
||||
if (quickResumeTimeoutChanged) {
|
||||
preserveQuickResumeTimeoutOn =
|
||||
SETTINGS.quickResumeSleepScreen == CrossPointSettings::QUICK_RESUME_SLEEP_SCREEN::QUICK_RESUME_AFTER_TIMEOUT;
|
||||
quickResumeTimeoutAutoEnabled = false;
|
||||
}
|
||||
|
||||
if (SETTINGS.sleepScreen == CrossPointSettings::SLEEP_SCREEN_MODE::QUICK_RESUME) {
|
||||
if (SETTINGS.quickResumeSleepScreen != CrossPointSettings::QUICK_RESUME_SLEEP_SCREEN::QUICK_RESUME_AFTER_TIMEOUT) {
|
||||
SETTINGS.quickResumeSleepScreen = CrossPointSettings::QUICK_RESUME_SLEEP_SCREEN::QUICK_RESUME_AFTER_TIMEOUT;
|
||||
quickResumeTimeoutAutoEnabled = !preserveQuickResumeTimeoutOn;
|
||||
} else if (sleepScreenChanged && !preserveQuickResumeTimeoutOn) {
|
||||
quickResumeTimeoutAutoEnabled = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (sleepScreenChanged && quickResumeTimeoutAutoEnabled && !preserveQuickResumeTimeoutOn) {
|
||||
SETTINGS.quickResumeSleepScreen = CrossPointSettings::QUICK_RESUME_SLEEP_SCREEN::QUICK_RESUME_NEVER;
|
||||
quickResumeTimeoutAutoEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsActivity::render(RenderLock&&) {
|
||||
renderer.clearScreen();
|
||||
|
||||
|
||||
@@ -156,12 +156,16 @@ class SettingsActivity final : public Activity {
|
||||
std::vector<SettingInfo> systemSettings;
|
||||
const std::vector<SettingInfo>* currentSettings = nullptr;
|
||||
|
||||
bool preserveQuickResumeTimeoutOn = false;
|
||||
bool quickResumeTimeoutAutoEnabled = false;
|
||||
|
||||
static constexpr int categoryCount = 4;
|
||||
static const StrId categoryNames[categoryCount];
|
||||
|
||||
void enterCategory(int categoryIndex);
|
||||
void toggleCurrentSetting();
|
||||
void rebuildSettingsLists();
|
||||
void syncQuickResumeTimeoutForSleepScreen(bool sleepScreenChanged, bool quickResumeTimeoutChanged);
|
||||
|
||||
public:
|
||||
explicit SettingsActivity(GfxRenderer& renderer, MappedInputManager& mappedInput)
|
||||
|
||||
@@ -1265,7 +1265,6 @@ void CrossPointWebServer::handlePostSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
CrossPointSettings::normalizeDependentSettings(SETTINGS);
|
||||
SETTINGS.saveToFile();
|
||||
|
||||
LOG_DBG("WEB", "Applied %d setting(s)", applied);
|
||||
|
||||
@@ -312,6 +312,11 @@
|
||||
<script>
|
||||
let allSettings = [];
|
||||
let originalValues = {};
|
||||
let preserveQuickResumeTimeoutOn = false;
|
||||
let quickResumeTimeoutAutoEnabled = false;
|
||||
const SLEEP_SCREEN_MODE = {
|
||||
QUICK_RESUME: 6
|
||||
};
|
||||
|
||||
function escapeHtml(unsafe) {
|
||||
return unsafe
|
||||
@@ -336,12 +341,12 @@
|
||||
if (setting.type === 'toggle') {
|
||||
const checked = setting.value ? 'checked' : '';
|
||||
return '<label class="toggle-switch">' +
|
||||
'<input type="checkbox" id="' + id + '" ' + checked + ' onchange="markChanged()">' +
|
||||
'<input type="checkbox" id="' + id + '" ' + checked + ' onchange="handleSettingChanged(\'' + setting.key + '\')">' +
|
||||
'<span class="toggle-slider"></span></label>';
|
||||
}
|
||||
|
||||
if (setting.type === 'enum') {
|
||||
let html = '<select id="' + id + '" onchange="markChanged()">';
|
||||
let html = '<select id="' + id + '" onchange="handleSettingChanged(\'' + setting.key + '\')">';
|
||||
setting.options.forEach(function(opt, idx) {
|
||||
const selected = idx === setting.value ? ' selected' : '';
|
||||
html += '<option value="' + idx + '"' + selected + '>' + escapeHtml(opt) + '</option>';
|
||||
@@ -353,14 +358,14 @@
|
||||
if (setting.type === 'value') {
|
||||
return '<input type="number" id="' + id + '" value="' + setting.value + '"' +
|
||||
' min="' + setting.min + '" max="' + setting.max + '" step="' + setting.step + '"' +
|
||||
' onchange="markChanged()">';
|
||||
' onchange="handleSettingChanged(\'' + setting.key + '\')">';
|
||||
}
|
||||
|
||||
if (setting.type === 'string') {
|
||||
const inputType = setting.name.toLowerCase().includes('password') ? 'password' : 'text';
|
||||
const val = setting.value || '';
|
||||
return '<input type="' + inputType + '" id="' + id + '" value="' + escapeHtml(val) + '"' +
|
||||
' oninput="markChanged()">';
|
||||
' oninput="handleSettingChanged(\'' + setting.key + '\')">';
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -389,6 +394,96 @@
|
||||
document.getElementById('saveBtn').disabled = false;
|
||||
}
|
||||
|
||||
function findSetting(key) {
|
||||
return allSettings.find(function(s) { return s.key === key; });
|
||||
}
|
||||
|
||||
function getControl(key) {
|
||||
return document.getElementById('setting-' + key);
|
||||
}
|
||||
|
||||
function getControlValue(key) {
|
||||
const setting = findSetting(key);
|
||||
const el = getControl(key);
|
||||
if (!setting || !el) return undefined;
|
||||
|
||||
if (setting.type === 'toggle') {
|
||||
return el.checked ? 1 : 0;
|
||||
}
|
||||
if (setting.type === 'enum' || setting.type === 'value') {
|
||||
return parseInt(el.value, 10);
|
||||
}
|
||||
return el.value;
|
||||
}
|
||||
|
||||
function setControlValue(key, value) {
|
||||
const setting = findSetting(key);
|
||||
const el = getControl(key);
|
||||
if (!setting || !el) return false;
|
||||
|
||||
if (setting.type === 'toggle') {
|
||||
const checked = !!value;
|
||||
if (el.checked === checked) return false;
|
||||
el.checked = checked;
|
||||
return true;
|
||||
}
|
||||
|
||||
const nextValue = String(value);
|
||||
if (el.value === nextValue) return false;
|
||||
el.value = nextValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
function isQuickResumeSleepScreenSelected() {
|
||||
const sleepScreen = findSetting('sleepScreen');
|
||||
const sleepScreenValue = getControlValue('sleepScreen');
|
||||
if (!sleepScreen || sleepScreenValue === undefined || !Number.isFinite(sleepScreenValue)) return false;
|
||||
|
||||
let selectedValue = sleepScreenValue;
|
||||
if (Array.isArray(sleepScreen.values)) {
|
||||
selectedValue = Number(sleepScreen.values[sleepScreenValue]);
|
||||
} else if (Array.isArray(sleepScreen.options)) {
|
||||
const selectedOption = sleepScreen.options[sleepScreenValue];
|
||||
if (selectedOption && typeof selectedOption === 'object' && 'value' in selectedOption) {
|
||||
selectedValue = Number(selectedOption.value);
|
||||
}
|
||||
}
|
||||
|
||||
return selectedValue === SLEEP_SCREEN_MODE.QUICK_RESUME;
|
||||
}
|
||||
|
||||
function syncQuickResumeTimeoutForSleepScreen(sleepScreenChanged, quickResumeTimeoutChanged) {
|
||||
const timeoutValue = getControlValue('quickResumeSleepScreen');
|
||||
if (timeoutValue === undefined) return false;
|
||||
|
||||
let changed = false;
|
||||
if (quickResumeTimeoutChanged) {
|
||||
preserveQuickResumeTimeoutOn = timeoutValue === 1;
|
||||
quickResumeTimeoutAutoEnabled = false;
|
||||
}
|
||||
|
||||
if (isQuickResumeSleepScreenSelected()) {
|
||||
if (timeoutValue !== 1) {
|
||||
changed = setControlValue('quickResumeSleepScreen', 1);
|
||||
quickResumeTimeoutAutoEnabled = !preserveQuickResumeTimeoutOn;
|
||||
} else if (sleepScreenChanged && !preserveQuickResumeTimeoutOn) {
|
||||
quickResumeTimeoutAutoEnabled = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
if (sleepScreenChanged && quickResumeTimeoutAutoEnabled && !preserveQuickResumeTimeoutOn) {
|
||||
changed = setControlValue('quickResumeSleepScreen', 0);
|
||||
quickResumeTimeoutAutoEnabled = false;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
function handleSettingChanged(key) {
|
||||
syncQuickResumeTimeoutForSleepScreen(key === 'sleepScreen', key === 'quickResumeSleepScreen');
|
||||
markChanged();
|
||||
}
|
||||
|
||||
async function loadSettings() {
|
||||
try {
|
||||
const response = await fetch('/api/settings');
|
||||
@@ -427,6 +522,11 @@
|
||||
container.innerHTML = html;
|
||||
document.getElementById('save-container').style.display = '';
|
||||
document.getElementById('saveBtn').disabled = true;
|
||||
preserveQuickResumeTimeoutOn = getControlValue('quickResumeSleepScreen') === 1;
|
||||
quickResumeTimeoutAutoEnabled = false;
|
||||
if (syncQuickResumeTimeoutForSleepScreen(true, false)) {
|
||||
markChanged();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
document.getElementById('settings-container').innerHTML =
|
||||
|
||||
Reference in New Issue
Block a user