Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

View file

@ -0,0 +1,123 @@
/*
* 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 "applib/app_sync/app_sync.h"
#include "syscall/syscall.h"
#include "system/passert.h"
#include <string.h>
static void delegate_errors(AppSync *s, DictionaryResult dict_result,
AppMessageResult app_message_result) {
if (dict_result == DICT_OK && app_message_result == APP_MSG_OK) {
return;
}
if (s->callback.error) {
s->callback.error(dict_result, app_message_result, s->callback.context);
}
}
static void update_key_callback(const uint32_t key, const Tuple *new_tuple,
const Tuple *old_tuple, void *context) {
AppSync *s = context;
if (s->callback.value_changed) {
s->callback.value_changed(key, new_tuple, old_tuple, s->callback.context);
}
}
static void pass_initial_values_app_task_callback(void *data) {
AppSync *s = data;
Tuple *tuple = dict_read_first(&s->current_iter);
while (tuple) {
update_key_callback(tuple->key, tuple, NULL, s);
tuple = dict_read_next(&s->current_iter);
}
}
static void update_callback(DictionaryIterator *updated_iter, void *context) {
AppSync *s = context;
uint32_t size = s->buffer_size;
const bool update_existing_keys_only = true;
DictionaryResult result = dict_merge(&s->current_iter, &size,
updated_iter,
update_existing_keys_only,
update_key_callback, s);
delegate_errors(s, result, APP_MSG_OK);
}
static void out_failed_callback(DictionaryIterator *failed, AppMessageResult reason,
void *context) {
AppSync *s = context;
delegate_errors(s, DICT_OK, reason);
}
static void in_dropped_callback(AppMessageResult reason, void *context) {
AppSync *s = context;
delegate_errors(s, DICT_OK, reason);
}
// FIXME PBL-1709: this should return an AppMessageResult ...
void app_sync_init(AppSync *s,
uint8_t *buffer, const uint16_t buffer_size,
const Tuplet * const keys_and_initial_values, const uint8_t count,
AppSyncTupleChangedCallback tuple_changed_callback,
AppSyncErrorCallback error_callback,
void *context) {
PBL_ASSERTN(buffer != NULL);
PBL_ASSERTN(buffer_size > 0);
s->buffer = buffer;
s->buffer_size = buffer_size;
s->callback.value_changed = tuple_changed_callback;
s->callback.error = error_callback;
s->callback.context = context;
uint32_t in_out_size = buffer_size;
const DictionaryResult dict_result = dict_serialize_tuplets_to_buffer_with_iter(
&s->current_iter, keys_and_initial_values, count, s->buffer, &in_out_size);
app_message_set_context(s);
app_message_register_outbox_sent(update_callback);
app_message_register_outbox_failed(out_failed_callback);
app_message_register_inbox_received(update_callback);
app_message_register_inbox_dropped(in_dropped_callback);
sys_current_process_schedule_callback(pass_initial_values_app_task_callback, s);
delegate_errors(s, dict_result, APP_MSG_OK);
}
void app_sync_deinit(AppSync *s) {
app_message_set_context(NULL);
app_message_register_outbox_sent(NULL);
app_message_register_outbox_failed(NULL);
app_message_register_inbox_received(NULL);
app_message_register_inbox_dropped(NULL);
s->current = NULL;
}
AppMessageResult app_sync_set(AppSync *s, const Tuplet * const updated_keys_and_values,
const uint8_t count) {
DictionaryIterator *iter;
AppMessageResult result = app_message_outbox_begin(&iter);
if (iter == NULL) {
return result;
}
for (unsigned int i = 0; i < count; ++i) {
dict_write_tuplet(iter, &updated_keys_and_values[i]);
}
dict_write_end(iter);
return app_message_outbox_send();
}
const Tuple * app_sync_get(const AppSync *s, const uint32_t key) {
return dict_find(&s->current_iter, key);
}

View file

@ -0,0 +1,164 @@
/*
* 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.
*/
#pragma once
#include "util/dict.h"
#include "applib/app_message/app_message.h"
//! @file app_sync.h
//! @addtogroup Foundation
//! @{
//! @addtogroup AppSync
//! \brief UI synchronization layer for AppMessage
//!
//! AppSync is a convenience layer that resides on top of \ref AppMessage, and serves
//! as a UI synchronization layer for AppMessage. In so doing, AppSync makes it easier
//! to drive the information displayed in the watchapp UI with messages sent by a phone app.
//!
//! AppSync maintains and updates a Dictionary, and provides your app with a callback
//! (AppSyncTupleChangedCallback) routine that is called whenever the Dictionary changes
//! and the app's UI is updated. Note that the app UI is not updated automatically.
//! To update the UI, you need to implement the callback.
//!
//! Pebble OS provides support for data serialization utilities, like Dictionary,
//! Tuple and Tuplet data structures and their accompanying functions. You use Tuplets to create
//! a Dictionary with Tuple structures.
//!
//! AppSync manages the storage and bookkeeping chores of the current Tuple values. AppSync copies
//! incoming AppMessage Tuples into this "current" Dictionary, so that the key/values remain
//! available for the UI to use. For example, it is safe to use a C-string value provided by AppSync
//! and use it directly in a text_layer_set_text() call.
//!
//! Your app needs to supply the buffer that AppSync uses for the "current" Dictionary when
//! initializing AppSync.
//!
//! Refer to the
//! <a href="https://developer.getpebble.com/guides/pebble-apps/communications/appsync/">
//! Synchronizing App UI</a>
//! guide for a conceptual overview and code usage.
//! @{
struct AppSync;
//! Called whenever a Tuple changes. This does not necessarily mean the value in
//! the Tuple has changed. When the internal "current" dictionary gets updated,
//! existing Tuples might get shuffled around in the backing buffer, even though
//! the values stay the same. In this callback, the client code gets the chance
//! to remove the old reference and start using the new one.
//! In this callback, your application MUST clean up any references to the
//! `old_tuple` of a PREVIOUS call to this callback (and replace it with the
//! `new_tuple` that is passed in with the current call).
//! @param key The key for which the Tuple was changed.
//! @param new_tuple The new tuple. The tuple points to the actual, updated
//! "current" dictionary, as backed by the buffer internal to the AppSync
//! struct. Therefore the Tuple can be used after the callback returns, until
//! the AppSync is deinited. In case there was an error (e.g. storage shortage),
//! this `new_tuple` can be `NULL_TUPLE`.
//! @param old_tuple The values that will be replaced with `new_tuple`. The key,
//! value and type will be equal to the previous tuple in the old destination
//! dictionary; however, the `old_tuple` points to a stack-allocated copy of the
//! old data. This value will be `NULL_TUPLE` when the initial values are
//! being set.
//! @param context Pointer to application specific data, as set using
//! \ref app_sync_init()
//! @see \ref app_sync_init()
typedef void (*AppSyncTupleChangedCallback)(const uint32_t key, const Tuple *new_tuple,
const Tuple *old_tuple, void *context);
//! Called whenever there was an error.
//! @param dict_error The dictionary result error code, if the error was
//! dictionary related.
//! @param app_message_error The app_message result error code, if the error
//! was app_message related.
//! @param context Pointer to application specific data, as set using
//! \ref app_sync_init()
//! @see \ref app_sync_init()
typedef void (*AppSyncErrorCallback)(DictionaryResult dict_error,
AppMessageResult app_message_error, void *context);
//! Initialized an AppSync system with specific buffer size and initial keys and
//! values. The `callback.value_changed` callback will be called
//! __asynchronously__ with the initial keys and values, as to avoid duplicating
//! code to update your app's UI.
//! @param s The AppSync context to initialize
//! @param buffer The buffer that AppSync should use
//! @param buffer_size The size of the backing storage of the "current"
//! dictionary. Use \ref dict_calc_buffer_size_from_tuplets() to estimate the
//! size you need.
//! @param keys_and_initial_values An array of Tuplets with the initial keys and
//! values.
//! @param count The number of Tuplets in the `keys_and_initial_values` array.
//! @param tuple_changed_callback The callback that will handle changed
//! key/value pairs
//! @param error_callback The callback that will handle errors
//! @param context Pointer to app specific data that will get passed into calls
//! to the callbacks
//! @note Only updates for the keys specified in this initial array will be
//! accepted by AppSync, updates for other keys that might come in will just be
//! ignored.
void app_sync_init(struct AppSync *s, uint8_t *buffer, const uint16_t buffer_size,
const Tuplet * const keys_and_initial_values, const uint8_t count,
AppSyncTupleChangedCallback tuple_changed_callback,
AppSyncErrorCallback error_callback, void *context);
//! Cleans up an AppSync system.
//! It frees the buffer allocated by an \ref app_sync_init() call and
//! deregisters itself from the \ref AppMessage subsystem.
//! @param s The AppSync context to deinit.
void app_sync_deinit(struct AppSync *s);
//! Updates key/value pairs using an array of Tuplets.
//! @note The call will attempt to send the updated keys and values to the
//! application on the other end.
//! Only after the other end has acknowledged the update, the `.value_changed`
//! callback will be called to confirm the update has completed and your
//! application code can update its user interface.
//! @param s The AppSync context
//! @param keys_and_values_to_update An array of Tuplets with the keys and
//! values to update. The data in the Tuplets are copied during the call, so the
//! array can be stack-allocated.
//! @param count The number of Tuplets in the `keys_and_values_to_update` array.
//! @return The result code from the \ref AppMessage subsystem.
//! Can be \ref APP_MSG_OK, \ref APP_MSG_BUSY or \ref APP_MSG_INVALID_ARGS
AppMessageResult app_sync_set(struct AppSync *s, const Tuplet * const keys_and_values_to_update,
const uint8_t count);
//! Finds and gets a tuple in the "current" dictionary.
//! @param s The AppSync context
//! @param key The key for which to find a Tuple
//! @return Pointer to a found Tuple, or NULL if there was no Tuple with the
//! specified key.
const Tuple * app_sync_get(const struct AppSync *s, const uint32_t key);
//! @} // end addtogroup AppSync
//! @} // end addtogroup Foundation
typedef struct AppSync {
DictionaryIterator current_iter;
union {
Dictionary *current;
uint8_t *buffer;
};
uint16_t buffer_size;
struct {
AppSyncTupleChangedCallback value_changed;
AppSyncErrorCallback error;
void *context;
} callback;
} AppSync;