mirror of
https://github.com/google/pebble.git
synced 2025-12-01 19:52:23 -05:00
Import of the watch repository from Pebble
This commit is contained in:
263
src/fw/services/normal/activity/insights_settings.c
Normal file
263
src/fw/services/normal/activity/insights_settings.c
Normal file
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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 <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "activity.h"
|
||||
#include "insights_settings.h"
|
||||
#include "os/mutex.h"
|
||||
#include "services/normal/filesystem/pfs.h"
|
||||
#include "services/normal/settings/settings_file.h"
|
||||
#include "system/logging.h"
|
||||
#include "util/size.h"
|
||||
|
||||
#define ACTIVITY_INSIGHTS_SETTINGS_FILENAME "insights"
|
||||
#define ACTIVITY_INSIGHTS_SETTINGS_DEFAULT_FILE_SIZE 4096
|
||||
|
||||
|
||||
#define ACTIVITY_INSIGHTS_SETTINGS_VERSION_KEY "version"
|
||||
#define ACTIVITY_INSIGHTS_SETTINGS_DEFAULT_VERSION 0
|
||||
|
||||
#define ACTIVITY_INSIGHTS_SETTINGS_CURRENT_STRUCT_VERSION 4
|
||||
|
||||
static PebbleMutex *s_insight_settings_mutex;
|
||||
|
||||
#define ACTIVITY_INSIGHTS_SETTINGS_SLEEP_REWARD_DEFAULT { \
|
||||
.version = ACTIVITY_INSIGHTS_SETTINGS_CURRENT_STRUCT_VERSION, \
|
||||
.enabled = false, \
|
||||
.reward = { \
|
||||
.min_days_data = 6, \
|
||||
.continuous_min_days_data = 2, \
|
||||
.target_qualifying_days = 2, \
|
||||
.target_percent_of_median = 120, \
|
||||
.notif_min_interval_seconds = 7 * SECONDS_PER_DAY, \
|
||||
.sleep.trigger_after_wakeup_seconds = 2 * SECONDS_PER_HOUR \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ACTIVITY_INSIGHTS_SETTINGS_SLEEP_SUMMARY_DEFAULT { \
|
||||
.version = ACTIVITY_INSIGHTS_SETTINGS_CURRENT_STRUCT_VERSION, \
|
||||
.enabled = true, \
|
||||
.summary = { \
|
||||
.above_avg_threshold = 10, \
|
||||
.below_avg_threshold = -10, \
|
||||
.fail_threshold = -50, \
|
||||
.sleep = { \
|
||||
.max_fail_minutes = 7 * MINUTES_PER_HOUR, \
|
||||
.trigger_notif_seconds = 30 * SECONDS_PER_MINUTE, \
|
||||
.trigger_notif_activity = 20, \
|
||||
.trigger_notif_active_minutes = 5 \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ACTIVITY_INSIGHTS_SETTINGS_ACTIVITY_REWARD_DEFAULT { \
|
||||
.version = ACTIVITY_INSIGHTS_SETTINGS_CURRENT_STRUCT_VERSION, \
|
||||
.enabled = false, \
|
||||
.reward = {\
|
||||
.min_days_data = 6, \
|
||||
.continuous_min_days_data = 0, \
|
||||
.target_qualifying_days = 0, \
|
||||
.target_percent_of_median = 150, \
|
||||
.notif_min_interval_seconds = 1 * SECONDS_PER_DAY, \
|
||||
.activity = { \
|
||||
.trigger_active_minutes = 2, \
|
||||
.trigger_steps_per_minute = 50 \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ACTIVITY_INSIGHTS_SETTINGS_ACTIVITY_SUMMARY_DEFAULT { \
|
||||
.version = ACTIVITY_INSIGHTS_SETTINGS_CURRENT_STRUCT_VERSION, \
|
||||
.enabled = true, \
|
||||
.summary = { \
|
||||
.above_avg_threshold = 10, \
|
||||
.below_avg_threshold = -10, \
|
||||
.fail_threshold = -50, \
|
||||
.activity = { \
|
||||
.trigger_minute = (20 * MINUTES_PER_HOUR) + 30, \
|
||||
.update_threshold_steps = 1000, \
|
||||
.update_max_interval_seconds = 30 * SECONDS_PER_MINUTE, \
|
||||
.show_notification = true, \
|
||||
.max_fail_steps = 10000, \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define ACTIVITY_INSIGHTS_SETTINGS_ACTIVITY_SESSION_DEFAULT { \
|
||||
.version = ACTIVITY_INSIGHTS_SETTINGS_CURRENT_STRUCT_VERSION, \
|
||||
.enabled = true, \
|
||||
.session = { \
|
||||
.show_notification = true, \
|
||||
.activity = { \
|
||||
.trigger_elapsed_minutes = 20, \
|
||||
.trigger_cooldown_minutes = 10, \
|
||||
}, \
|
||||
} \
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *key;
|
||||
ActivityInsightSettings default_val;
|
||||
} AISDefault;
|
||||
|
||||
static const AISDefault AIS_DEFAULTS[] = {
|
||||
{
|
||||
.key = ACTIVITY_INSIGHTS_SETTINGS_SLEEP_REWARD,
|
||||
.default_val = ACTIVITY_INSIGHTS_SETTINGS_SLEEP_REWARD_DEFAULT
|
||||
},
|
||||
{
|
||||
.key = ACTIVITY_INSIGHTS_SETTINGS_SLEEP_SUMMARY,
|
||||
.default_val = ACTIVITY_INSIGHTS_SETTINGS_SLEEP_SUMMARY_DEFAULT
|
||||
},
|
||||
{
|
||||
.key = ACTIVITY_INSIGHTS_SETTINGS_ACTIVITY_REWARD,
|
||||
.default_val = ACTIVITY_INSIGHTS_SETTINGS_ACTIVITY_REWARD_DEFAULT
|
||||
},
|
||||
{
|
||||
.key = ACTIVITY_INSIGHTS_SETTINGS_ACTIVITY_SUMMARY,
|
||||
.default_val = ACTIVITY_INSIGHTS_SETTINGS_ACTIVITY_SUMMARY_DEFAULT
|
||||
},
|
||||
{
|
||||
.key = ACTIVITY_INSIGHTS_SETTINGS_ACTIVITY_SESSION,
|
||||
.default_val = ACTIVITY_INSIGHTS_SETTINGS_ACTIVITY_SESSION_DEFAULT
|
||||
},
|
||||
};
|
||||
|
||||
// Return true if we successfully opened the file
|
||||
static bool prv_open_settings_and_lock(SettingsFile *file) {
|
||||
mutex_lock(s_insight_settings_mutex);
|
||||
if (settings_file_open(file, ACTIVITY_INSIGHTS_SETTINGS_FILENAME,
|
||||
ACTIVITY_INSIGHTS_SETTINGS_DEFAULT_FILE_SIZE) == S_SUCCESS) {
|
||||
return true;
|
||||
} else {
|
||||
mutex_unlock(s_insight_settings_mutex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Close the settings file and release the lock
|
||||
static void prv_close_settings_and_unlock(SettingsFile *file) {
|
||||
settings_file_close(file);
|
||||
mutex_unlock(s_insight_settings_mutex);
|
||||
}
|
||||
|
||||
void activity_insights_settings_init(void) {
|
||||
// Create our mutex
|
||||
s_insight_settings_mutex = mutex_create();
|
||||
|
||||
SettingsFile file;
|
||||
if (settings_file_open(&file,
|
||||
ACTIVITY_INSIGHTS_SETTINGS_FILENAME,
|
||||
ACTIVITY_INSIGHTS_SETTINGS_DEFAULT_FILE_SIZE) == S_SUCCESS) {
|
||||
if (!settings_file_exists(&file,
|
||||
ACTIVITY_INSIGHTS_SETTINGS_VERSION_KEY,
|
||||
strlen(ACTIVITY_INSIGHTS_SETTINGS_VERSION_KEY))) {
|
||||
// init version to 0
|
||||
const uint16_t default_version = ACTIVITY_INSIGHTS_SETTINGS_DEFAULT_VERSION;
|
||||
settings_file_set(&file,
|
||||
ACTIVITY_INSIGHTS_SETTINGS_VERSION_KEY,
|
||||
strlen(ACTIVITY_INSIGHTS_SETTINGS_VERSION_KEY),
|
||||
&default_version,
|
||||
sizeof(uint16_t));
|
||||
}
|
||||
|
||||
settings_file_close(&file);
|
||||
return;
|
||||
}
|
||||
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Failed to create activity insights settings file");
|
||||
}
|
||||
|
||||
uint16_t activity_insights_settings_get_version(void) {
|
||||
uint16_t version = ACTIVITY_INSIGHTS_SETTINGS_DEFAULT_VERSION;
|
||||
SettingsFile file;
|
||||
if (prv_open_settings_and_lock(&file)) {
|
||||
settings_file_get(&file,
|
||||
ACTIVITY_INSIGHTS_SETTINGS_VERSION_KEY,
|
||||
strlen(ACTIVITY_INSIGHTS_SETTINGS_VERSION_KEY),
|
||||
&version,
|
||||
sizeof(uint16_t));
|
||||
prv_close_settings_and_unlock(&file);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
bool activity_insights_settings_read(const char *insight_name,
|
||||
ActivityInsightSettings *settings_out) {
|
||||
bool rv = false;
|
||||
*settings_out = (ActivityInsightSettings) {};
|
||||
|
||||
SettingsFile file;
|
||||
if (prv_open_settings_and_lock(&file)) {
|
||||
if (settings_file_get(&file,
|
||||
insight_name, strlen(insight_name),
|
||||
settings_out, sizeof(*settings_out)) != S_SUCCESS) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Didn't find insight with key %s", insight_name);
|
||||
goto close;
|
||||
}
|
||||
|
||||
if (settings_out->version != ACTIVITY_INSIGHTS_SETTINGS_CURRENT_STRUCT_VERSION) {
|
||||
// versions don't match, bail out!
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "activity insights struct version mismatch");
|
||||
goto close;
|
||||
}
|
||||
|
||||
rv = true;
|
||||
close:
|
||||
prv_close_settings_and_unlock(&file);
|
||||
}
|
||||
|
||||
if (!rv) {
|
||||
// Use default value if we didn't find anything else
|
||||
for (unsigned i = 0; i < ARRAY_LENGTH(AIS_DEFAULTS); ++i) {
|
||||
if (strcmp(insight_name, AIS_DEFAULTS[i].key) == 0) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Using default for insight %s", insight_name);
|
||||
*settings_out = AIS_DEFAULTS[i].default_val;
|
||||
rv = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool activity_insights_settings_write(const char *insight_name,
|
||||
ActivityInsightSettings *settings) {
|
||||
bool rv = false;
|
||||
|
||||
SettingsFile file;
|
||||
if (prv_open_settings_and_lock(&file)) {
|
||||
if (settings_file_set(&file,
|
||||
insight_name, strlen(insight_name),
|
||||
settings, sizeof(*settings)) != S_SUCCESS) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Unable to save insight setting with key %s", insight_name);
|
||||
} else {
|
||||
rv = true;
|
||||
}
|
||||
prv_close_settings_and_unlock(&file);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
PFSCallbackHandle activity_insights_settings_watch(PFSFileChangedCallback callback) {
|
||||
return pfs_watch_file(ACTIVITY_INSIGHTS_SETTINGS_FILENAME, callback, FILE_CHANGED_EVENT_CLOSED,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void activity_insights_settings_unwatch(PFSCallbackHandle cb_handle) {
|
||||
pfs_unwatch_file(cb_handle);
|
||||
}
|
||||
Reference in New Issue
Block a user