mirror of
https://github.com/google/pebble.git
synced 2025-05-25 04:34:52 +00:00
1178 lines
37 KiB
C
1178 lines
37 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.
|
|
*/
|
|
|
|
#include "shell/normal/quick_launch.h"
|
|
#include "shell/normal/watchface.h"
|
|
#include "shell/prefs.h"
|
|
#include "shell/prefs_private.h"
|
|
#include "shell/system_theme.h"
|
|
|
|
#include "apps/system_apps/toggle/quiet_time.h"
|
|
#include "board/board.h"
|
|
#include "drivers/backlight.h"
|
|
#include "mfg/mfg_info.h"
|
|
#include "os/mutex.h"
|
|
#include "popups/timeline/peek.h"
|
|
#include "process_management/app_install_manager.h"
|
|
#include "process_management/process_manager.h"
|
|
#include "services/common/hrm/hrm_manager.h"
|
|
#include "services/common/i18n/i18n.h"
|
|
#include "services/normal/bluetooth/ble_hrm.h"
|
|
#include "services/normal/settings/settings_file.h"
|
|
#include "services/normal/timeline/peek.h"
|
|
#include "system/logging.h"
|
|
#include "system/passert.h"
|
|
#include "util/size.h"
|
|
#include "util/uuid.h"
|
|
|
|
#if CAPABILITY_HAS_HEALTH_TRACKING
|
|
#include "services/normal/activity/activity.h"
|
|
#include "services/normal/activity/activity_insights.h"
|
|
#endif
|
|
|
|
#include <stdbool.h>
|
|
|
|
static PebbleMutex *s_mutex;
|
|
|
|
#define PREF_KEY_CLOCK_24H "clock24h"
|
|
static bool s_clock_24h = false;
|
|
|
|
#define PREF_KEY_CLOCK_TIMEZONE_SOURCE_IS_MANUAL "timezoneSource"
|
|
static bool s_clock_timezone_source_is_manual = false;
|
|
|
|
#define PREF_KEY_CLOCK_PHONE_TIMEZONE_ID "automaticTimezoneID"
|
|
static int16_t s_clock_phone_timezone_id = -1;
|
|
|
|
#define PREF_KEY_UNITS_DISTANCE "unitsDistance"
|
|
static uint8_t s_units_distance = UnitsDistance_Miles;
|
|
|
|
#define PREF_KEY_BACKLIGHT_BEHAVIOUR_DEPRECATED "lightBehaviour"
|
|
#define PREF_KEY_BACKLIGHT_ENABLED "lightEnabled"
|
|
static bool s_backlight_enabled = true;
|
|
#define PREF_KEY_BACKLIGHT_AMBIENT_SENSOR_ENABLED "lightAmbientSensorEnabled"
|
|
static bool s_backlight_ambient_sensor_enabled = true;
|
|
|
|
#define PREF_KEY_BACKLIGHT_TIMEOUT_MS "lightTimeoutMs"
|
|
static uint32_t s_backlight_timeout_ms = DEFAULT_BACKLIGHT_TIMEOUT_MS;
|
|
#define PREF_KEY_BACKLIGHT_INTENSITY "lightIntensity"
|
|
static uint16_t s_backlight_intensity; // default pulled from BOARD_CONFIGs in shell_prefs_init()
|
|
|
|
#define PREF_KEY_BACKLIGHT_MOTION "lightMotion"
|
|
static bool s_backlight_motion_enabled = true;
|
|
|
|
#define PREF_KEY_STATIONARY "stationaryMode"
|
|
#if RELEASE && !PLATFORM_SPALDING
|
|
static bool s_stationary_mode_enabled = false;
|
|
#else
|
|
static bool s_stationary_mode_enabled = true;
|
|
#endif
|
|
|
|
#define PREF_KEY_DEFAULT_WORKER "workerId"
|
|
static Uuid s_default_worker = UUID_INVALID_INIT;
|
|
|
|
// We use "textStyle" to indicate the content size
|
|
#define PREF_KEY_TEXT_STYLE "textStyle"
|
|
static uint8_t s_text_style = PreferredContentSizeDefault;
|
|
#if !UNITTEST
|
|
_Static_assert(sizeof(PreferredContentSize) == sizeof(s_text_style),
|
|
"sizeof(PreferredContentSize) grew, pref needs to be migrated!");
|
|
#endif
|
|
|
|
#define PREF_KEY_LANG_ENGLISH "langEnglish"
|
|
static bool s_language_english = false;
|
|
|
|
typedef struct QuickLaunchPreference {
|
|
bool enabled;
|
|
Uuid uuid;
|
|
} QuickLaunchPreference;
|
|
|
|
#define PREF_KEY_QUICK_LAUNCH_UP "qlUp"
|
|
#define PREF_KEY_QUICK_LAUNCH_DOWN "qlDown"
|
|
#define PREF_KEY_QUICK_LAUNCH_SELECT "qlSelect"
|
|
#define PREF_KEY_QUICK_LAUNCH_BACK "qlBack"
|
|
|
|
static QuickLaunchPreference s_quick_launch_up = {
|
|
.enabled = true,
|
|
.uuid = UUID_INVALID_INIT,
|
|
};
|
|
|
|
static QuickLaunchPreference s_quick_launch_down = {
|
|
.enabled = true,
|
|
.uuid = UUID_INVALID_INIT,
|
|
};
|
|
|
|
static QuickLaunchPreference s_quick_launch_select = {
|
|
.enabled = true,
|
|
.uuid = UUID_INVALID_INIT,
|
|
};
|
|
|
|
static QuickLaunchPreference s_quick_launch_back = {
|
|
.enabled = true,
|
|
.uuid = QUIET_TIME_TOGGLE_UUID,
|
|
};
|
|
|
|
#define PREF_KEY_QUICK_LAUNCH_SETUP_OPENED "qlSetupOpened"
|
|
static uint8_t s_quick_launch_setup_opened = 0;
|
|
|
|
#define PREF_KEY_DEFAULT_WATCHFACE "watchface"
|
|
static Uuid s_default_watchface = UUID_INVALID_INIT;
|
|
|
|
#define PREF_KEY_WELCOME_VERSION "welcomeVersion"
|
|
static uint8_t s_welcome_version = 0;
|
|
|
|
#if CAPABILITY_HAS_HEALTH_TRACKING
|
|
#define PREF_KEY_ACTIVITY_PREFERENCES "activityPreferences"
|
|
static ActivitySettings s_activity_preferences = ACTIVITY_DEFAULT_PREFERENCES;
|
|
|
|
#define PREF_KEY_ACTIVITY_ACTIVATED_TIMESTAMP "activityActivated"
|
|
static time_t s_activity_activation_timestamp = 0;
|
|
|
|
#define PREF_KEY_ACTIVITY_ACTIVATION_DELAY_INSIGHT "activityActivationDelayInsights"
|
|
static uint32_t s_activity_activation_delay_insight = 0;
|
|
|
|
#define PREF_KEY_ACTIVITY_HEALTH_APP_OPENED "activityHealthAppOpened"
|
|
static uint8_t s_activity_prefs_health_app_opened = 0;
|
|
|
|
#define PREF_KEY_ACTIVITY_WORKOUT_APP_OPENED "activityWorkoutAppOpened"
|
|
static uint8_t s_activity_prefs_workout_app_opened = 0;
|
|
|
|
#define PREF_KEY_ALARMS_APP_OPENED "alarmsAppOpened"
|
|
static uint8_t s_alarms_app_opened = 0;
|
|
|
|
#define PREF_KEY_ACTIVITY_HRM_PREFERENCES "hrmPreferences"
|
|
static ActivityHRMSettings s_activity_hrm_preferences = ACTIVITY_HRM_DEFAULT_PREFERENCES;
|
|
|
|
#define PREF_KEY_ACTIVITY_HEART_RATE_PREFERENCES "heartRatePreferences"
|
|
static HeartRatePreferences s_activity_hr_preferences = ACTIVITY_HEART_RATE_DEFAULT_PREFERENCES;
|
|
#endif // CAPABILITY_HAS_HEALTH_TRACKING
|
|
|
|
#if PLATFORM_SPALDING
|
|
#define PREF_KEY_DISPLAY_USER_OFFSET "displayUserOffset"
|
|
static GPoint s_display_user_offset = {0, 0};
|
|
#define PREF_KEY_SHOULD_PROMPT_DISPLAY_CALIBRATION "promptDisplayCal"
|
|
static bool s_should_prompt_display_calibration = true;
|
|
#endif
|
|
|
|
#if CAPABILITY_HAS_TIMELINE_PEEK
|
|
#define PREF_KEY_TIMELINE_SETTINGS_OPENED "timelineSettingsOpened"
|
|
static uint8_t s_timeline_settings_opened = 0;
|
|
|
|
#define PREF_KEY_TIMELINE_PEEK_ENABLED "timelineQuickViewEnabled"
|
|
static bool s_timeline_peek_enabled = true;
|
|
|
|
#define PREF_KEY_TIMELINE_PEEK_BEFORE_TIME_M "timelineQuickViewBeforeTimeMin"
|
|
static uint16_t s_timeline_peek_before_time_m =
|
|
(TIMELINE_PEEK_DEFAULT_SHOW_BEFORE_TIME_S / SECONDS_PER_MINUTE);
|
|
#endif
|
|
|
|
// ============================================================================================
|
|
// Handlers for each pref that validate the new setting and store the new value in our globals.
|
|
// This handler will be called when the setting is changed from inside the firmware using one of
|
|
// the "set" calls or when a pref is changed via a blob_db insert operation from the mobile
|
|
// (after we receive the blob_db update event).
|
|
//
|
|
// If changing of the setting requires more than just setting a global, this handler is the
|
|
// place to perform those other actions.
|
|
//
|
|
// If the the handler gets passed a invalid new value, set its s_* global to a default value,
|
|
// and return false. This will trigger a rewrite of the s_* global to the backing file.
|
|
//
|
|
// Each of these functions MUST be named using the following pattern because they are called
|
|
// programmatically via the PREFS_MACRO macro defined below:
|
|
// static bool prv_set_<static_var_name>(<static_var_type> new_value)
|
|
static bool prv_set_s_clock_24h(bool *new_value) {
|
|
s_clock_24h = *new_value;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_clock_timezone_source_is_manual(bool *new_value) {
|
|
s_clock_timezone_source_is_manual = *new_value;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_clock_phone_timezone_id(int16_t *new_value) {
|
|
s_clock_phone_timezone_id = *new_value;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_units_distance(uint8_t *new_unit) {
|
|
if (*new_unit >= UnitsDistanceCount) {
|
|
s_units_distance = UnitsDistance_Miles;
|
|
return false;
|
|
}
|
|
s_units_distance = *new_unit;
|
|
return true;
|
|
};
|
|
|
|
static bool prv_set_s_backlight_enabled(bool *enabled) {
|
|
s_backlight_enabled = *enabled;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_backlight_ambient_sensor_enabled(bool *enabled) {
|
|
s_backlight_ambient_sensor_enabled = *enabled;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_backlight_timeout_ms(uint32_t *timeout_ms) {
|
|
if (*timeout_ms > 0) {
|
|
s_backlight_timeout_ms = *timeout_ms;
|
|
return true;
|
|
}
|
|
s_backlight_timeout_ms = DEFAULT_BACKLIGHT_TIMEOUT_MS;
|
|
return false;
|
|
}
|
|
|
|
static uint16_t prv_convert_backlight_percent_to_intensity(uint32_t percent_intensity);
|
|
|
|
static bool prv_set_s_backlight_intensity(uint16_t *intensity) {
|
|
if (*intensity > BACKLIGHT_BRIGHTNESS_OFF) {
|
|
s_backlight_intensity = *intensity;
|
|
return true;
|
|
}
|
|
s_backlight_intensity =
|
|
prv_convert_backlight_percent_to_intensity(BOARD_CONFIG.backlight_on_percent);
|
|
return false;
|
|
}
|
|
|
|
static bool prv_set_s_backlight_motion_enabled(bool *enabled) {
|
|
s_backlight_motion_enabled = *enabled;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_stationary_mode_enabled(bool *enabled) {
|
|
s_stationary_mode_enabled = *enabled;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_default_worker(Uuid *uuid) {
|
|
s_default_worker = *uuid;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_text_style(uint8_t *style) {
|
|
s_text_style = *style;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_language_english(bool *english) {
|
|
s_language_english = *english;
|
|
i18n_enable(!s_language_english);
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_quick_launch_up(QuickLaunchPreference *pref) {
|
|
s_quick_launch_up = *pref;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_quick_launch_down(QuickLaunchPreference *pref) {
|
|
s_quick_launch_down = *pref;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_quick_launch_select(QuickLaunchPreference *pref) {
|
|
s_quick_launch_select = *pref;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_quick_launch_back(QuickLaunchPreference *pref) {
|
|
s_quick_launch_back = *pref;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_quick_launch_setup_opened(uint8_t *version) {
|
|
s_quick_launch_setup_opened = *version;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_default_watchface(Uuid *uuid) {
|
|
s_default_watchface = *uuid;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_welcome_version(uint8_t *version) {
|
|
s_welcome_version = *version;
|
|
return true;
|
|
}
|
|
|
|
#if CAPABILITY_HAS_HEALTH_TRACKING
|
|
static bool prv_set_s_activity_preferences(ActivitySettings *new_settings) {
|
|
bool invalid_data = false;
|
|
|
|
if (new_settings->height_mm <= 0) {
|
|
new_settings->height_mm = ACTIVITY_DEFAULT_HEIGHT_MM;
|
|
invalid_data = true;
|
|
}
|
|
|
|
if (new_settings->weight_dag <= 0) {
|
|
new_settings->weight_dag = ACTIVITY_DEFAULT_WEIGHT_DAG;
|
|
invalid_data = true;
|
|
}
|
|
|
|
if (!(new_settings->gender == ActivityGenderMale ||
|
|
new_settings->gender == ActivityGenderFemale ||
|
|
new_settings->gender == ActivityGenderOther)) {
|
|
new_settings->gender = ACTIVITY_DEFAULT_GENDER;
|
|
invalid_data = true;
|
|
}
|
|
|
|
if (new_settings->age_years <= 0) {
|
|
new_settings->age_years = ACTIVITY_DEFAULT_AGE_YEARS;
|
|
invalid_data = true;
|
|
}
|
|
|
|
if (new_settings->tracking_enabled) {
|
|
activity_start_tracking(false);
|
|
} else {
|
|
activity_stop_tracking();
|
|
}
|
|
|
|
s_activity_preferences = *new_settings;
|
|
|
|
// If we received invalid data, we return false, so that prefs_private_handle_blob_db_event
|
|
// will rewrite s_activity_preferences to the backing file
|
|
return !invalid_data;
|
|
}
|
|
|
|
static bool prv_set_s_activity_activation_timestamp(time_t *timestamp) {
|
|
s_activity_activation_timestamp = *timestamp;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_activity_activation_delay_insight(uint32_t *insight_bitmask) {
|
|
s_activity_activation_delay_insight = *insight_bitmask;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_activity_prefs_health_app_opened(uint8_t *version) {
|
|
s_activity_prefs_health_app_opened = *version;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_activity_prefs_workout_app_opened(uint8_t *version) {
|
|
s_activity_prefs_workout_app_opened = *version;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_alarms_app_opened(uint8_t *version) {
|
|
s_alarms_app_opened = *version;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_activity_hr_preferences(HeartRatePreferences *new_settings) {
|
|
if (new_settings->resting_hr > new_settings->elevated_hr ||
|
|
new_settings->elevated_hr > new_settings->max_hr) {
|
|
return false;
|
|
}
|
|
if (new_settings->zone1_threshold > new_settings->zone2_threshold ||
|
|
new_settings->zone2_threshold > new_settings->zone3_threshold) {
|
|
return false;
|
|
}
|
|
|
|
s_activity_hr_preferences = *new_settings;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_activity_hrm_preferences(ActivityHRMSettings *new_settings) {
|
|
// Set the preference before calling `hrm_manager_enable` because it actually queries
|
|
// for the setting
|
|
s_activity_hrm_preferences = *new_settings;
|
|
|
|
#if CAPABILITY_HAS_BUILTIN_HRM
|
|
hrm_manager_handle_prefs_changed();
|
|
#endif // CAPABILITY_HAS_BUILTIN_HRM
|
|
#if BLE_HRM_SERVICE
|
|
ble_hrm_handle_activity_prefs_heart_rate_is_enabled(new_settings->enabled);
|
|
#endif // BLE_HRM_SERVICE
|
|
return true;
|
|
}
|
|
#endif /* CAPABILITY_HAS_HEALTH_TRACKING */
|
|
|
|
|
|
#if PLATFORM_SPALDING
|
|
static bool prv_set_s_display_user_offset(GPoint *offset) {
|
|
s_display_user_offset = *offset;
|
|
return true;
|
|
}
|
|
static bool prv_set_s_should_prompt_display_calibration(bool should_prompt) {
|
|
s_should_prompt_display_calibration = should_prompt;
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#if CAPABILITY_HAS_TIMELINE_PEEK
|
|
static uint8_t prv_set_s_timeline_settings_opened(uint8_t *version) {
|
|
s_timeline_settings_opened = *version;
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_timeline_peek_enabled(bool *enabled) {
|
|
s_timeline_peek_enabled = *enabled;
|
|
timeline_peek_set_enabled(*enabled);
|
|
return true;
|
|
}
|
|
|
|
static bool prv_set_s_timeline_peek_before_time_m(uint16_t *before_time_m) {
|
|
s_timeline_peek_before_time_m = *before_time_m;
|
|
timeline_peek_set_show_before_time(*before_time_m * SECONDS_PER_MINUTE);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
// Table of all prefs
|
|
typedef bool (*PrefSetHandler)(const void *value, size_t val_len);
|
|
typedef struct {
|
|
const char *key;
|
|
void *value;
|
|
uint16_t value_len;
|
|
PrefSetHandler handler;
|
|
} PrefsTableEntry;
|
|
|
|
// The PREFS_DECLARE_HANDLER creates a springboard function with a generic signature
|
|
// (of type PrefSetHandler) which simply calls into the specialized function prv_set_<var_name>
|
|
// after dereferencing the void* argument using the right type for that pref
|
|
#define PREFS_MACRO(name, var) \
|
|
static bool prv_set_ ## var ## _cb(const void *value, size_t val_len) { \
|
|
return prv_set_ ## var ((__typeof__(var) *)value); \
|
|
}
|
|
#include "prefs_values.h.inc"
|
|
#undef PREFS_MACRO
|
|
|
|
// Create a time containing the key name and global variable name for each pref
|
|
#define PREFS_MACRO(key, var) {key, &var, sizeof(var), prv_set_ ## var ## _cb},
|
|
static const PrefsTableEntry s_prefs_table[] = {
|
|
#include "prefs_values.h.inc"
|
|
};
|
|
#undef PREFS_MACRO
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
// FIXME PBL-22272. We back convert this value in
|
|
// settings_display.c:prv_get_scaled_brightness() We should really just store
|
|
// the percent intensity or a setting level name and leave it up to the light
|
|
// module to do the conversion
|
|
static uint16_t prv_convert_backlight_percent_to_intensity(uint32_t percent_intensity) {
|
|
return (BACKLIGHT_BRIGHTNESS_MAX * (uint32_t)percent_intensity) / 100;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
static void prv_convert_deprecated_backlight_behaviour_key(SettingsFile *file) {
|
|
// if present, convert deprecated BACKLIGHT_BEHAVIOUR key to the two new separate keys
|
|
if (settings_file_exists(file, PREF_KEY_BACKLIGHT_BEHAVIOUR_DEPRECATED,
|
|
sizeof(PREF_KEY_BACKLIGHT_BEHAVIOUR_DEPRECATED))) {
|
|
bool temp;
|
|
BacklightBehaviour backlight_behaviour = BacklightBehaviour_Auto;
|
|
settings_file_get(file, PREF_KEY_BACKLIGHT_BEHAVIOUR_DEPRECATED,
|
|
sizeof(PREF_KEY_BACKLIGHT_BEHAVIOUR_DEPRECATED),
|
|
&backlight_behaviour, sizeof(backlight_behaviour));
|
|
temp = (backlight_behaviour != BacklightBehaviour_Off);
|
|
settings_file_set(file, PREF_KEY_BACKLIGHT_ENABLED,
|
|
sizeof(PREF_KEY_BACKLIGHT_ENABLED), &temp, sizeof(temp));
|
|
temp = (backlight_behaviour != BacklightBehaviour_On);
|
|
settings_file_set(file, PREF_KEY_BACKLIGHT_AMBIENT_SENSOR_ENABLED,
|
|
sizeof(PREF_KEY_BACKLIGHT_AMBIENT_SENSOR_ENABLED), &temp, sizeof(temp));
|
|
settings_file_delete(file, PREF_KEY_BACKLIGHT_BEHAVIOUR_DEPRECATED,
|
|
sizeof(PREF_KEY_BACKLIGHT_BEHAVIOUR_DEPRECATED));
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
void shell_prefs_init(void) {
|
|
s_backlight_intensity =
|
|
prv_convert_backlight_percent_to_intensity(BOARD_CONFIG.backlight_on_percent);
|
|
s_mutex = mutex_create();
|
|
|
|
SettingsFile file = {{0}};
|
|
if (settings_file_open(&file, SHELL_PREFS_FILE_NAME, SHELL_PREFS_FILE_LEN) != S_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
prv_convert_deprecated_backlight_behaviour_key(&file);
|
|
|
|
// Init state for each pref from our backing store
|
|
uint32_t num_entries = ARRAY_LENGTH(s_prefs_table);
|
|
const PrefsTableEntry *entry = s_prefs_table;
|
|
for (uint32_t i = 0; i < num_entries; i++, entry++) {
|
|
// Keys in the backing store include the null terminator, so we add 1 to key_len
|
|
size_t key_len = strlen(entry->key) + 1;
|
|
if (settings_file_get_len(&file, entry->key, key_len) == entry->value_len) {
|
|
settings_file_get(&file, entry->key, key_len, entry->value, entry->value_len);
|
|
}
|
|
}
|
|
|
|
settings_file_close(&file);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
// Find the PrefsTableEntry for the given key
|
|
static const PrefsTableEntry *prv_prefs_entry(const uint8_t *key, size_t key_len) {
|
|
uint32_t num_entries = ARRAY_LENGTH(s_prefs_table);
|
|
const PrefsTableEntry *entry = s_prefs_table;
|
|
for (uint32_t i = 0; i < num_entries; i++, entry++) {
|
|
if (!strncmp((const char *)key, entry->key, key_len)) {
|
|
return entry;
|
|
}
|
|
}
|
|
PBL_LOG(LOG_LEVEL_WARNING, "Unrecognized key: %s", (const char *)key);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
// Set the backing store for a pref
|
|
static bool prv_set_pref_backing(const PrefsTableEntry *entry, const void *value, int value_len) {
|
|
if (value_len != entry->value_len) {
|
|
PBL_LOG(LOG_LEVEL_WARNING, "Attempt to set %s using invalid value_len of %"PRIu32"",
|
|
entry->key, (uint32_t)value_len);
|
|
return false;
|
|
}
|
|
|
|
status_t rv = E_ERROR;
|
|
mutex_lock(s_mutex);
|
|
{
|
|
SettingsFile file = {{0}};
|
|
if (settings_file_open(&file, SHELL_PREFS_FILE_NAME, SHELL_PREFS_FILE_LEN) == S_SUCCESS) {
|
|
// Keys in the backing store include the null terminator, so we add 1 to key_len
|
|
rv = settings_file_set(&file, entry->key, strlen(entry->key) + 1, value, value_len);
|
|
if (rv != S_SUCCESS) {
|
|
PBL_LOG(LOG_LEVEL_WARNING, "Failed to set pref '%s' (%"PRIi32")", entry->key, (int32_t)rv);
|
|
}
|
|
settings_file_close(&file);
|
|
}
|
|
}
|
|
mutex_unlock(s_mutex);
|
|
return (rv == S_SUCCESS);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
// Convenience function used to update the state AND set the backing for a pref. This is
|
|
// used by the functions below that are called by the firmware to change prefs (i.e.
|
|
// shell_prefs_set.*, backlight_set.*, etc.).
|
|
static void prv_pref_set(const char* key, const void *value, size_t val_len) {
|
|
// Find the entry for this key
|
|
const PrefsTableEntry *entry = prv_prefs_entry((const uint8_t *)key, strlen(key));
|
|
|
|
// validate the key and value length
|
|
PBL_ASSERT(entry != NULL, "Key %s not found", key);
|
|
PBL_ASSERT(val_len == entry->value_len, "Attempt to set %s using invalid value_len of %"PRIu32"",
|
|
entry->key, (uint32_t)val_len);
|
|
|
|
// Call the update handler
|
|
bool success = entry->handler(value, val_len);
|
|
PBL_ASSERT(success, "Failure to store new value for %s in settings file", key);
|
|
|
|
// Update the backing store
|
|
if (success) {
|
|
prv_set_pref_backing(entry, value, val_len);
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
// Exported function used by blob_db API to set the backing store for a specific key
|
|
bool prefs_private_write_backing(const uint8_t *key, size_t key_len, const void *value,
|
|
int value_len) {
|
|
const PrefsTableEntry *entry = prv_prefs_entry(key, key_len);
|
|
if (!entry) {
|
|
return false;
|
|
}
|
|
|
|
return prv_set_pref_backing(entry, value, value_len);
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
// Exported function used by blob_db API to get the length of a value in our backing store
|
|
int prefs_private_get_backing_len(const uint8_t *key, size_t key_len) {
|
|
const PrefsTableEntry *entry = prv_prefs_entry(key, key_len);
|
|
if (!entry) {
|
|
return 0;
|
|
}
|
|
return entry->value_len;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
// Exported function used by blob_db API to read our backing store
|
|
bool prefs_private_read_backing(const uint8_t *key, size_t key_len, void *value, int value_len) {
|
|
const PrefsTableEntry *entry = prv_prefs_entry(key, key_len);
|
|
if (!entry) {
|
|
return false;
|
|
}
|
|
|
|
if (value_len != entry->value_len) {
|
|
PBL_LOG(LOG_LEVEL_WARNING, "Attempt to read %s using invalid value_len of %"PRIu32"",
|
|
entry->key, (uint32_t)value_len);
|
|
return false;
|
|
}
|
|
|
|
bool success = false;
|
|
mutex_lock(s_mutex);
|
|
{
|
|
SettingsFile file = {{0}};
|
|
if (settings_file_open(&file, SHELL_PREFS_FILE_NAME, SHELL_PREFS_FILE_LEN) == S_SUCCESS) {
|
|
// Keys in the backing store include the null terminator, so we add 1 to key_len
|
|
success = (settings_file_get(&file, entry->key, key_len + 1, value, value_len)
|
|
== S_SUCCESS);
|
|
settings_file_close(&file);
|
|
}
|
|
}
|
|
mutex_unlock(s_mutex);
|
|
return success;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
// Called from KernelMain when we get a blob DB event. We take this opportunity to update the state
|
|
// of the given pref
|
|
void prefs_private_handle_blob_db_event(PebbleBlobDBEvent *event) {
|
|
if (event->type != BlobDBEventTypeInsert) {
|
|
return;
|
|
}
|
|
|
|
const PrefsTableEntry *entry = prv_prefs_entry(event->key, event->key_len);
|
|
if (!entry) {
|
|
return;
|
|
}
|
|
|
|
// Read in the updated value from the backing store
|
|
bool success = prefs_private_read_backing(event->key, event->key_len, entry->value,
|
|
entry->value_len);
|
|
if (success) {
|
|
// Call the state update handler in case this pref needs to take other action besides
|
|
// just updating the global
|
|
if (!entry->handler(entry->value, entry->value_len)) {
|
|
// If the handler returns false, that means it reset the global back to the default,
|
|
// so we should write the new value to the backing
|
|
prefs_private_write_backing(event->key, event->key_len, entry->value, entry->value_len);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ========================================================================================
|
|
// Exported functions used by the firmware to read/change a preference.
|
|
// IMPORTANT: When implementing the *set* call, be sure to call prv_pref_set(). This does
|
|
// two things:
|
|
// 1.) It validates that the stored global matches the type of the passed in argument
|
|
// 2.) It insures that the flow will also work correctly for setting a pref from the
|
|
// mobile side using a blob_db insert operation.
|
|
bool shell_prefs_get_clock_24h_style(void) {
|
|
return s_clock_24h;
|
|
}
|
|
|
|
UnitsDistance shell_prefs_get_units_distance(void) {
|
|
return s_units_distance;
|
|
}
|
|
|
|
void shell_prefs_set_units_distance(UnitsDistance new_unit) {
|
|
uint8_t uint_new_unit = new_unit;
|
|
prv_pref_set(PREF_KEY_UNITS_DISTANCE, &uint_new_unit, sizeof(uint_new_unit));
|
|
}
|
|
|
|
void shell_prefs_set_clock_24h_style(bool is24h) {
|
|
prv_pref_set(PREF_KEY_CLOCK_24H, &is24h, sizeof(is24h));
|
|
}
|
|
|
|
bool shell_prefs_is_timezone_source_manual(void) {
|
|
return s_clock_timezone_source_is_manual;
|
|
}
|
|
|
|
void shell_prefs_set_timezone_source_manual(bool manual) {
|
|
prv_pref_set(PREF_KEY_CLOCK_TIMEZONE_SOURCE_IS_MANUAL, &manual, sizeof(manual));
|
|
}
|
|
|
|
void shell_prefs_set_automatic_timezone_id(int16_t timezone_id) {
|
|
prv_pref_set(PREF_KEY_CLOCK_PHONE_TIMEZONE_ID, &timezone_id, sizeof(timezone_id));
|
|
}
|
|
|
|
int16_t shell_prefs_get_automatic_timezone_id(void) {
|
|
return s_clock_phone_timezone_id;
|
|
}
|
|
|
|
// Emulate the old BacklightBehaviour type for analytics.
|
|
// This is a deprecated method and should not be called by new code.
|
|
BacklightBehaviour backlight_get_behaviour(void) {
|
|
if (s_backlight_enabled) {
|
|
if (s_backlight_ambient_sensor_enabled) {
|
|
return BacklightBehaviour_Auto;
|
|
} else {
|
|
return BacklightBehaviour_On;
|
|
}
|
|
} else {
|
|
return BacklightBehaviour_Off;
|
|
}
|
|
}
|
|
|
|
bool backlight_is_enabled(void) {
|
|
return s_backlight_enabled;
|
|
}
|
|
|
|
void backlight_set_enabled(bool enabled) {
|
|
prv_pref_set(PREF_KEY_BACKLIGHT_ENABLED, &enabled, sizeof(enabled));
|
|
}
|
|
|
|
bool backlight_is_ambient_sensor_enabled(void) {
|
|
#if INFINITE_BACKLIGHT
|
|
return false;
|
|
#endif
|
|
return s_backlight_ambient_sensor_enabled;
|
|
}
|
|
|
|
void backlight_set_ambient_sensor_enabled(bool enabled) {
|
|
prv_pref_set(PREF_KEY_BACKLIGHT_AMBIENT_SENSOR_ENABLED, &enabled, sizeof(enabled));
|
|
}
|
|
|
|
uint32_t backlight_get_timeout_ms(void) {
|
|
#if INFINITE_BACKLIGHT
|
|
return UINT32_MAX;
|
|
#endif
|
|
return s_backlight_timeout_ms;
|
|
}
|
|
|
|
void backlight_set_timeout_ms(uint32_t timeout_ms) {
|
|
prv_pref_set(PREF_KEY_BACKLIGHT_TIMEOUT_MS, &timeout_ms, sizeof(timeout_ms));
|
|
}
|
|
|
|
uint16_t backlight_get_intensity(void) {
|
|
return s_backlight_intensity;
|
|
}
|
|
|
|
uint8_t backlight_get_intensity_percent(void) {
|
|
return (backlight_get_intensity() * 100) / BACKLIGHT_BRIGHTNESS_MAX;
|
|
}
|
|
|
|
void backlight_set_intensity_percent(uint8_t percent_intensity) {
|
|
PBL_ASSERTN(percent_intensity > 0 && percent_intensity <= 100);
|
|
uint16_t intensity = prv_convert_backlight_percent_to_intensity(percent_intensity);
|
|
PBL_ASSERTN(intensity > BACKLIGHT_BRIGHTNESS_OFF);
|
|
prv_pref_set(PREF_KEY_BACKLIGHT_INTENSITY, &intensity, sizeof(intensity));
|
|
}
|
|
|
|
bool backlight_is_motion_enabled(void) {
|
|
return s_backlight_motion_enabled;
|
|
}
|
|
|
|
void backlight_set_motion_enabled(bool enable) {
|
|
prv_pref_set(PREF_KEY_BACKLIGHT_MOTION, &enable, sizeof(enable));
|
|
}
|
|
|
|
bool shell_prefs_get_stationary_enabled(void) {
|
|
return s_stationary_mode_enabled;
|
|
}
|
|
|
|
void shell_prefs_set_stationary_enabled(bool enabled) {
|
|
prv_pref_set(PREF_KEY_STATIONARY, &enabled, sizeof(enabled));
|
|
}
|
|
|
|
AppInstallId worker_preferences_get_default_worker(void) {
|
|
return app_install_get_id_for_uuid(&s_default_worker);
|
|
}
|
|
|
|
void worker_preferences_set_default_worker(AppInstallId app_id) {
|
|
Uuid uuid;
|
|
app_install_get_uuid_for_install_id(app_id, &uuid);
|
|
prv_pref_set(PREF_KEY_DEFAULT_WORKER, &uuid, sizeof(uuid));
|
|
}
|
|
|
|
bool quick_launch_is_enabled(ButtonId button) {
|
|
switch (button) {
|
|
case BUTTON_ID_UP:
|
|
return s_quick_launch_up.enabled;
|
|
case BUTTON_ID_DOWN:
|
|
return s_quick_launch_down.enabled;
|
|
case BUTTON_ID_SELECT:
|
|
return s_quick_launch_select.enabled;
|
|
case BUTTON_ID_BACK:
|
|
return s_quick_launch_back.enabled;
|
|
case NUM_BUTTONS:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
AppInstallId quick_launch_get_app(ButtonId button) {
|
|
Uuid *uuid = NULL;
|
|
switch (button) {
|
|
case BUTTON_ID_UP:
|
|
uuid = &s_quick_launch_up.uuid;
|
|
break;
|
|
case BUTTON_ID_DOWN:
|
|
uuid = &s_quick_launch_down.uuid;
|
|
break;
|
|
case BUTTON_ID_SELECT:
|
|
uuid = &s_quick_launch_select.uuid;
|
|
break;
|
|
case BUTTON_ID_BACK:
|
|
uuid = &s_quick_launch_back.uuid;
|
|
break;
|
|
case NUM_BUTTONS:
|
|
break;
|
|
}
|
|
PBL_ASSERTN(uuid);
|
|
return app_install_get_id_for_uuid(uuid);
|
|
}
|
|
|
|
void quick_launch_set_app(ButtonId button, AppInstallId app_id) {
|
|
QuickLaunchPreference pref = (QuickLaunchPreference) {
|
|
.enabled = true,
|
|
};
|
|
app_install_get_uuid_for_install_id(app_id, &pref.uuid);
|
|
|
|
const char *key = NULL;
|
|
switch (button) {
|
|
case BUTTON_ID_UP:
|
|
key = PREF_KEY_QUICK_LAUNCH_UP;
|
|
break;
|
|
case BUTTON_ID_DOWN:
|
|
key = PREF_KEY_QUICK_LAUNCH_DOWN;
|
|
break;
|
|
case BUTTON_ID_SELECT:
|
|
key = PREF_KEY_QUICK_LAUNCH_SELECT;
|
|
break;
|
|
case BUTTON_ID_BACK:
|
|
key = PREF_KEY_QUICK_LAUNCH_BACK;
|
|
break;
|
|
case NUM_BUTTONS:
|
|
break;
|
|
}
|
|
PBL_ASSERTN(key);
|
|
prv_pref_set(key, &pref, sizeof(pref));
|
|
}
|
|
|
|
void quick_launch_set_enabled(ButtonId button, bool enabled) {
|
|
QuickLaunchPreference pref;
|
|
|
|
const char *key = NULL;
|
|
switch (button) {
|
|
case BUTTON_ID_UP:
|
|
pref = s_quick_launch_up;
|
|
key = PREF_KEY_QUICK_LAUNCH_UP;
|
|
break;
|
|
case BUTTON_ID_DOWN:
|
|
pref = s_quick_launch_down;
|
|
key = PREF_KEY_QUICK_LAUNCH_DOWN;
|
|
break;
|
|
case BUTTON_ID_SELECT:
|
|
pref = s_quick_launch_select;
|
|
key = PREF_KEY_QUICK_LAUNCH_SELECT;
|
|
break;
|
|
case BUTTON_ID_BACK:
|
|
pref = s_quick_launch_back;
|
|
key = PREF_KEY_QUICK_LAUNCH_BACK;
|
|
break;
|
|
case NUM_BUTTONS:
|
|
break;
|
|
}
|
|
PBL_ASSERTN(key);
|
|
pref.enabled = enabled;
|
|
prv_pref_set(key, &pref, sizeof(pref));
|
|
}
|
|
|
|
void quick_launch_set_quick_launch_setup_opened(uint8_t version) {
|
|
if (s_quick_launch_setup_opened != version) {
|
|
s_quick_launch_setup_opened = version;
|
|
prv_pref_set(PREF_KEY_QUICK_LAUNCH_SETUP_OPENED, &s_quick_launch_setup_opened,
|
|
sizeof(s_quick_launch_setup_opened));
|
|
}
|
|
}
|
|
|
|
uint8_t quick_launch_get_quick_launch_setup_opened(void) {
|
|
return s_quick_launch_setup_opened;
|
|
}
|
|
|
|
void watchface_set_default_install_id(AppInstallId app_id) {
|
|
Uuid uuid;
|
|
app_install_get_uuid_for_install_id(app_id, &uuid);
|
|
prv_pref_set(PREF_KEY_DEFAULT_WATCHFACE, &uuid, sizeof(uuid));
|
|
}
|
|
|
|
void welcome_set_welcome_version(uint8_t version) {
|
|
if (s_welcome_version != version) {
|
|
s_welcome_version = version;
|
|
prv_pref_set(PREF_KEY_WELCOME_VERSION, &version, sizeof(version));
|
|
}
|
|
}
|
|
|
|
uint8_t welcome_get_welcome_version(void) {
|
|
return s_welcome_version;
|
|
}
|
|
|
|
static bool prv_set_default_any_watchface_enumerate_callback(AppInstallEntry *entry, void *data) {
|
|
if (!app_install_entry_is_watchface(entry)
|
|
|| app_install_entry_is_hidden(entry)) {
|
|
return true; // continue search
|
|
}
|
|
|
|
watchface_set_default_install_id(entry->install_id);
|
|
return false;
|
|
}
|
|
|
|
AppInstallId watchface_get_default_install_id(void) {
|
|
AppInstallId app_id = app_install_get_id_for_uuid(&s_default_watchface);
|
|
AppInstallEntry entry;
|
|
if (app_id == INSTALL_ID_INVALID ||
|
|
!app_install_get_entry_for_install_id(app_id, &entry) ||
|
|
!app_install_entry_is_watchface(&entry)) {
|
|
app_install_enumerate_entries(
|
|
prv_set_default_any_watchface_enumerate_callback, NULL);
|
|
app_id = app_install_get_id_for_uuid(&s_default_watchface);
|
|
}
|
|
return app_id;
|
|
}
|
|
|
|
void system_theme_set_content_size(PreferredContentSize content_size) {
|
|
if (content_size >= NumPreferredContentSizes) {
|
|
PBL_LOG(LOG_LEVEL_WARNING, "Ignoring attempt to set content size to invalid size %d",
|
|
content_size);
|
|
return;
|
|
}
|
|
const uint8_t content_size_uint = content_size;
|
|
prv_pref_set(PREF_KEY_TEXT_STYLE, &content_size_uint, sizeof(content_size_uint));
|
|
}
|
|
|
|
PreferredContentSize system_theme_get_content_size(void) {
|
|
return system_theme_convert_host_content_size_to_runtime_platform(
|
|
(PreferredContentSize)s_text_style);
|
|
}
|
|
|
|
bool shell_prefs_get_language_english(void) {
|
|
return s_language_english;
|
|
}
|
|
|
|
void shell_prefs_set_language_english(bool english) {
|
|
prv_pref_set(PREF_KEY_LANG_ENGLISH, &english, sizeof(english));
|
|
}
|
|
|
|
void shell_prefs_toggle_language_english(void) {
|
|
shell_prefs_set_language_english(!shell_prefs_get_language_english());
|
|
}
|
|
|
|
#if CAPABILITY_HAS_HEALTH_TRACKING
|
|
static void prv_activity_pref_set(void) {
|
|
prv_pref_set(PREF_KEY_ACTIVITY_PREFERENCES, &s_activity_preferences,
|
|
sizeof(s_activity_preferences));
|
|
}
|
|
|
|
time_t activity_prefs_get_activation_time(void) {
|
|
return s_activity_activation_timestamp;
|
|
}
|
|
|
|
void activity_prefs_set_activated(void) {
|
|
if (s_activity_activation_timestamp == 0) {
|
|
s_activity_activation_timestamp = rtc_get_time();
|
|
prv_pref_set(PREF_KEY_ACTIVITY_ACTIVATED_TIMESTAMP, &s_activity_activation_timestamp,
|
|
sizeof(s_activity_activation_timestamp));
|
|
}
|
|
}
|
|
|
|
bool activity_prefs_has_activation_delay_insight_fired(ActivationDelayInsightType type) {
|
|
return (s_activity_activation_delay_insight & (1 << type));
|
|
}
|
|
|
|
void activity_prefs_set_activation_delay_insight_fired(ActivationDelayInsightType type) {
|
|
s_activity_activation_delay_insight |= (1 << type);
|
|
prv_pref_set(PREF_KEY_ACTIVITY_ACTIVATION_DELAY_INSIGHT, &s_activity_activation_delay_insight,
|
|
sizeof(s_activity_activation_delay_insight));
|
|
}
|
|
|
|
uint8_t activity_prefs_get_health_app_opened_version(void) {
|
|
return s_activity_prefs_health_app_opened;
|
|
}
|
|
|
|
void activity_prefs_set_health_app_opened_version(uint8_t version) {
|
|
if (s_activity_prefs_health_app_opened != version) {
|
|
s_activity_prefs_health_app_opened = version;
|
|
prv_pref_set(PREF_KEY_ACTIVITY_HEALTH_APP_OPENED, &s_activity_prefs_health_app_opened,
|
|
sizeof(s_activity_prefs_health_app_opened));
|
|
}
|
|
}
|
|
|
|
uint8_t activity_prefs_get_workout_app_opened_version(void) {
|
|
return s_activity_prefs_workout_app_opened;
|
|
}
|
|
|
|
void activity_prefs_set_workout_app_opened_version(uint8_t version) {
|
|
if (s_activity_prefs_workout_app_opened != version) {
|
|
s_activity_prefs_workout_app_opened = version;
|
|
prv_pref_set(PREF_KEY_ACTIVITY_WORKOUT_APP_OPENED, &s_activity_prefs_workout_app_opened,
|
|
sizeof(s_activity_prefs_workout_app_opened));
|
|
}
|
|
}
|
|
|
|
bool activity_prefs_activity_insights_are_enabled(void) {
|
|
return s_activity_preferences.activity_insights_enabled;
|
|
}
|
|
|
|
void activity_prefs_activity_insights_set_enabled(bool enable) {
|
|
s_activity_preferences.activity_insights_enabled = enable;
|
|
prv_activity_pref_set();
|
|
}
|
|
|
|
bool activity_prefs_sleep_insights_are_enabled(void) {
|
|
return s_activity_preferences.sleep_insights_enabled;
|
|
}
|
|
|
|
void activity_prefs_sleep_insights_set_enabled(bool enable) {
|
|
s_activity_preferences.sleep_insights_enabled = enable;
|
|
prv_activity_pref_set();
|
|
}
|
|
|
|
bool activity_prefs_tracking_is_enabled(void) {
|
|
return s_activity_preferences.tracking_enabled;
|
|
}
|
|
|
|
void activity_prefs_tracking_set_enabled(bool enable) {
|
|
s_activity_preferences.tracking_enabled = enable;
|
|
prv_activity_pref_set();
|
|
}
|
|
|
|
void activity_prefs_set_height_mm(uint16_t height_mm) {
|
|
s_activity_preferences.height_mm = height_mm;
|
|
prv_activity_pref_set();
|
|
}
|
|
|
|
uint16_t activity_prefs_get_height_mm(void) {
|
|
return s_activity_preferences.height_mm;
|
|
}
|
|
|
|
void activity_prefs_set_weight_dag(uint16_t weight_dag) {
|
|
s_activity_preferences.weight_dag = weight_dag;
|
|
prv_activity_pref_set();
|
|
}
|
|
|
|
uint16_t activity_prefs_get_weight_dag(void) {
|
|
return s_activity_preferences.weight_dag;
|
|
}
|
|
|
|
void activity_prefs_set_gender(ActivityGender gender) {
|
|
s_activity_preferences.gender = gender;
|
|
prv_activity_pref_set();
|
|
}
|
|
|
|
ActivityGender activity_prefs_get_gender(void) {
|
|
return s_activity_preferences.gender;
|
|
}
|
|
|
|
void activity_prefs_set_age_years(uint8_t age_years) {
|
|
s_activity_preferences.age_years = age_years;
|
|
prv_activity_pref_set();
|
|
}
|
|
|
|
uint8_t activity_prefs_get_age_years(void) {
|
|
return s_activity_preferences.age_years;
|
|
}
|
|
|
|
uint8_t activity_prefs_heart_get_resting_hr(void) {
|
|
return s_activity_hr_preferences.resting_hr;
|
|
}
|
|
|
|
uint8_t activity_prefs_heart_get_elevated_hr(void) {
|
|
return s_activity_hr_preferences.elevated_hr;
|
|
}
|
|
|
|
uint8_t activity_prefs_heart_get_max_hr(void) {
|
|
return s_activity_hr_preferences.max_hr;
|
|
}
|
|
|
|
uint8_t activity_prefs_heart_get_zone1_threshold(void) {
|
|
return s_activity_hr_preferences.zone1_threshold;
|
|
}
|
|
|
|
uint8_t activity_prefs_heart_get_zone2_threshold(void) {
|
|
return s_activity_hr_preferences.zone2_threshold;
|
|
}
|
|
|
|
uint8_t activity_prefs_heart_get_zone3_threshold(void) {
|
|
return s_activity_hr_preferences.zone3_threshold;
|
|
}
|
|
|
|
bool activity_prefs_heart_rate_is_enabled(void) {
|
|
return s_activity_hrm_preferences.enabled;
|
|
}
|
|
|
|
void alarm_prefs_set_alarms_app_opened(uint8_t version) {
|
|
if (s_alarms_app_opened != version) {
|
|
s_alarms_app_opened = version;
|
|
prv_pref_set(PREF_KEY_ALARMS_APP_OPENED, &s_alarms_app_opened, sizeof(s_alarms_app_opened));
|
|
}
|
|
}
|
|
|
|
uint8_t alarm_prefs_get_alarms_app_opened(void) {
|
|
return s_alarms_app_opened;
|
|
}
|
|
#endif /* CAPABILITY_HAS_HEALTH_TRACKING */
|
|
|
|
#if PLATFORM_SPALDING
|
|
void shell_prefs_set_display_offset(GPoint offset) {
|
|
const GPoint user_offset = gpoint_sub(offset, mfg_info_get_disp_offsets());
|
|
prv_pref_set(PREF_KEY_DISPLAY_USER_OFFSET, &user_offset, sizeof(user_offset));
|
|
}
|
|
|
|
GPoint shell_prefs_get_display_offset(void) {
|
|
return gpoint_add(s_display_user_offset, mfg_info_get_disp_offsets());
|
|
}
|
|
|
|
void shell_prefs_display_offset_init(void) {
|
|
display_set_offset(shell_prefs_get_display_offset());
|
|
}
|
|
|
|
bool shell_prefs_should_prompt_display_calibration(void) {
|
|
return s_should_prompt_display_calibration;
|
|
}
|
|
|
|
void shell_prefs_set_should_prompt_display_calibration(bool should_prompt) {
|
|
s_should_prompt_display_calibration = should_prompt;
|
|
prv_pref_set(PREF_KEY_SHOULD_PROMPT_DISPLAY_CALIBRATION, &s_should_prompt_display_calibration,
|
|
sizeof(s_should_prompt_display_calibration));
|
|
}
|
|
#endif
|
|
|
|
#if CAPABILITY_HAS_TIMELINE_PEEK
|
|
void timeline_prefs_set_settings_opened(uint8_t version) {
|
|
prv_pref_set(PREF_KEY_TIMELINE_SETTINGS_OPENED, &version, sizeof(version));
|
|
}
|
|
|
|
uint8_t timeline_prefs_get_settings_opened(void) {
|
|
return s_timeline_settings_opened;
|
|
}
|
|
|
|
void timeline_peek_prefs_set_enabled(bool enabled) {
|
|
prv_pref_set(PREF_KEY_TIMELINE_PEEK_ENABLED, &enabled, sizeof(enabled));
|
|
}
|
|
|
|
bool timeline_peek_prefs_get_enabled(void) {
|
|
return s_timeline_peek_enabled;
|
|
}
|
|
|
|
void timeline_peek_prefs_set_before_time(uint16_t before_time_m) {
|
|
prv_pref_set(PREF_KEY_TIMELINE_PEEK_BEFORE_TIME_M, &before_time_m, sizeof(before_time_m));
|
|
}
|
|
|
|
uint16_t timeline_peek_prefs_get_before_time(void) {
|
|
return s_timeline_peek_before_time_m;
|
|
}
|
|
#else
|
|
uint16_t timeline_peek_prefs_get_before_time(void) {
|
|
return TIMELINE_PEEK_DEFAULT_SHOW_BEFORE_TIME_S;
|
|
}
|
|
#endif
|