mirror of
https://github.com/google/pebble.git
synced 2025-11-30 03:02:24 -05:00
264 lines
8.2 KiB
C
264 lines
8.2 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 <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);
|
|
}
|