Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson
2025-01-27 11:38:16 -08:00
committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions
+137
View File
@@ -0,0 +1,137 @@
/*
* 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 "app_glance.h"
#include "applib/template_string.h"
#include "apps/system_apps/launcher/default/launcher_app_glance_generic.h"
#include "process_state/app_state/app_state.h"
#include "resource/resource_ids.auto.h"
#include "services/normal/blob_db/app_glance_db_private.h"
#include "services/normal/timeline/attribute.h"
#include "services/normal/timeline/timeline_resources.h"
#include "syscall/syscall.h"
#include "util/string.h"
#include "util/uuid.h"
AppGlanceResult app_glance_add_slice(AppGlanceReloadSession *session, AppGlanceSlice slice) {
#if !CAPABILITY_HAS_APP_GLANCES
return APP_GLANCE_RESULT_INVALID_SESSION;
#endif
AppGlanceResult result = APP_GLANCE_RESULT_SUCCESS;
AppGlance *glance = app_state_get_glance();
// We use the glance set in the session as a crude way of validating the session
// Bail out if the session is invalid
if (!session || (glance != session->glance)) {
result |= APP_GLANCE_RESULT_INVALID_SESSION;
return result;
}
// From here on to the end of the function we can accumulate multiple failures in `result`
// Check if this slice would put us over the max slices per glance
if (glance->num_slices >= APP_GLANCE_DB_MAX_SLICES_PER_GLANCE) {
result |= APP_GLANCE_RESULT_SLICE_CAPACITY_EXCEEDED;
}
// Check if this slice's icon is a valid resource (but only if it's not the value that specifies
// that the app's default icon should be used)
if (slice.layout.icon != APP_GLANCE_SLICE_DEFAULT_ICON) {
Uuid app_uuid;
sys_get_app_uuid(&app_uuid);
const TimelineResourceInfo timeline_resource_info = (TimelineResourceInfo) {
.app_id = &app_uuid,
.res_id = (TimelineResourceId)slice.layout.icon,
};
AppResourceInfo app_resource_info = (AppResourceInfo) {};
sys_timeline_resources_get_id(&timeline_resource_info,
LAUNCHER_APP_GLANCE_GENERIC_ICON_SIZE_TYPE, &app_resource_info);
if (app_resource_info.res_id == RESOURCE_ID_INVALID) {
result |= APP_GLANCE_RESULT_INVALID_ICON;
}
}
// Check if the template string is too long (if it's not NULL)
// Plus one for the null terminator, then plus one more to become too long
const size_t template_string_too_long_size = ATTRIBUTE_APP_GLANCE_SUBTITLE_MAX_LEN + 1 + 1;
if (slice.layout.subtitle_template_string &&
strnlen(slice.layout.subtitle_template_string,
template_string_too_long_size) == template_string_too_long_size) {
result |= APP_GLANCE_RESULT_TEMPLATE_STRING_TOO_LONG;
}
// Check if the provided subtitle string is a valid template string, if it's present.
const time_t current_time = sys_get_time();
if (slice.layout.subtitle_template_string) {
const TemplateStringVars template_string_vars = (TemplateStringVars) {
.current_time = current_time,
};
TemplateStringError template_string_error = {0};
if (!template_string_evaluate(slice.layout.subtitle_template_string, NULL, 0, NULL,
&template_string_vars, &template_string_error)) {
result |= APP_GLANCE_RESULT_INVALID_TEMPLATE_STRING;
}
}
// Check if the slice would expire in the past (if it's not APP_GLANCE_SLICE_NO_EXPIRATION)
if ((slice.expiration_time != APP_GLANCE_SLICE_NO_EXPIRATION) &&
slice.expiration_time <= current_time) {
result |= APP_GLANCE_RESULT_EXPIRES_IN_THE_PAST;
}
// If we haven't failed at this point, we're ready to add the slice to the glance!
if (result == APP_GLANCE_RESULT_SUCCESS) {
AppGlanceSliceInternal *slice_dest = &glance->slices[glance->num_slices];
*slice_dest = (AppGlanceSliceInternal) {
.expiration_time = slice.expiration_time,
.type = AppGlanceSliceType_IconAndSubtitle,
.icon_and_subtitle.icon_resource_id = slice.layout.icon,
};
// We already checked that the provided template string fits, so use strcpy instead of strncpy
if (slice.layout.subtitle_template_string) {
strcpy(slice_dest->icon_and_subtitle.template_string, slice.layout.subtitle_template_string);
}
glance->num_slices++;
}
return result;
}
void app_glance_reload(AppGlanceReloadCallback callback, void *context) {
#if !CAPABILITY_HAS_APP_GLANCES
return;
#endif
AppGlance *glance = app_state_get_glance();
Uuid current_app_uuid;
sys_get_app_uuid(&current_app_uuid);
// Zero out the glance
app_glance_service_init_glance(glance);
if (callback) {
// Create a "reload session" on the stack that wraps the glance, and then call the user callback
AppGlanceReloadSession session = (AppGlanceReloadSession) {
.glance = glance,
};
callback(&session, APP_GLANCE_DB_MAX_SLICES_PER_GLANCE, context);
}
sys_app_glance_update(&current_app_uuid, glance);
}