Files
pebble/src/fw/shell/normal/display_calibration_prompt.c
2025-01-27 11:38:16 -08:00

128 lines
5.0 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if PLATFORM_SPALDING
#include "display_calibration_prompt.h"
#include "applib/ui/dialogs/confirmation_dialog.h"
#include "apps/system_apps/settings/settings_display_calibration.h"
#include "kernel/event_loop.h"
#include "kernel/ui/modals/modal_manager.h"
#include "mfg/mfg_info.h"
#include "mfg/mfg_serials.h"
#include "resource/resource_ids.auto.h"
#include "services/common/i18n/i18n.h"
#include "services/common/new_timer/new_timer.h"
#include "shell/prefs.h"
#include "util/size.h"
// The calibration screen will be changing the screen offsets, so it's best that it remains on top
// of most other modals (generic, alerts, etc) to prevent confusion about the screen's alignment.
static const ModalPriority MODAL_PRIORITY = ModalPriorityCritical;
static void prv_calibrate_confirm_pop(ClickRecognizerRef recognizer, void *context) {
i18n_free_all(context);
confirmation_dialog_pop((ConfirmationDialog *)context);
}
static void prv_calibrate_confirm_cb(ClickRecognizerRef recognizer, void *context) {
settings_display_calibration_push(modal_manager_get_window_stack(MODAL_PRIORITY));
prv_calibrate_confirm_pop(recognizer, context);
}
static void prv_calibrate_click_config(void *context) {
window_single_click_subscribe(BUTTON_ID_UP, prv_calibrate_confirm_cb);
window_single_click_subscribe(BUTTON_ID_DOWN, prv_calibrate_confirm_pop);
window_single_click_subscribe(BUTTON_ID_BACK, prv_calibrate_confirm_pop);
}
static TimerID s_timer = TIMER_INVALID_ID;
static void prv_push_calibration_dialog(void *data) {
shell_prefs_set_should_prompt_display_calibration(false);
ConfirmationDialog *confirmation_dialog = confirmation_dialog_create("Calibrate Prompt");
Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog);
dialog_set_text(dialog, i18n_get("Your screen may need calibration. Calibrate it now?",
confirmation_dialog));
dialog_set_background_color(dialog, GColorMediumAquamarine);
dialog_set_icon(dialog, RESOURCE_ID_GENERIC_PIN_TINY);
confirmation_dialog_set_click_config_provider(confirmation_dialog,
prv_calibrate_click_config);
confirmation_dialog_push(confirmation_dialog,
modal_manager_get_window_stack(MODAL_PRIORITY));
}
static bool prv_display_has_user_offset(void) {
GPoint display_offset = shell_prefs_get_display_offset();
GPoint mfg_display_offset = mfg_info_get_disp_offsets();
return (!gpoint_equal(&display_offset, &mfg_display_offset));
}
static void prv_timer_callback(void *data) {
new_timer_delete(s_timer);
s_timer = TIMER_INVALID_ID;
// last check: make sure we need to display the prompt in case something changed in the
// time that the timer was waiting.
if (!shell_prefs_should_prompt_display_calibration()) {
return;
}
launcher_task_add_callback(prv_push_calibration_dialog, NULL);
}
T_STATIC bool prv_is_known_misaligned_serial_number(const char *serial) {
// Filter watches known to be misaligned based on the serial number. This is possible because
// Serial numbers are represented as strings as described in:
// https://pebbletechnology.atlassian.net/wiki/display/DEV/Hardware+Serial+Numbering
// All watches of the same model produced from the same manufacturer on the same date, on the
// same manufacturing line, will share the same first 8 characters of the serial number. In this
// way, batches which are misaligned can be identified by a string comparison on these characters.
//
// NOTE: This also conveniently excludes test automation boards, so the dialog should not
// appear during integration tests.
const char *ranges[] = { "Q402445E" };
for (size_t i = 0; i < ARRAY_LENGTH(ranges); i++) {
if (strncmp(serial, ranges[i], strlen(ranges[i])) == 0) {
return true;
}
}
return false;
}
static bool prv_is_potentially_misaligned_watch() {
return !prv_display_has_user_offset() &&
prv_is_known_misaligned_serial_number(mfg_get_serial_number());
}
void display_calibration_prompt_show_if_needed(void) {
if (!prv_is_potentially_misaligned_watch()) {
shell_prefs_set_should_prompt_display_calibration(false);
return;
}
if (shell_prefs_should_prompt_display_calibration()) {
s_timer = new_timer_create();
const uint32_t prompt_delay_time_ms = MS_PER_SECOND * SECONDS_PER_MINUTE;
new_timer_start(s_timer, prompt_delay_time_ms, prv_timer_callback, NULL, 0 /* flags */);
}
}
#endif