mirror of
https://github.com/google/pebble.git
synced 2025-05-24 20:24:53 +00:00
Import of the watch repository from Pebble
This commit is contained in:
commit
3b92768480
10334 changed files with 2564465 additions and 0 deletions
92
src/fw/shell/normal/app_idle_timeout.c
Normal file
92
src/fw/shell/normal/app_idle_timeout.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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_idle_timeout.h"
|
||||
|
||||
#include "kernel/event_loop.h"
|
||||
#include "os/tick.h"
|
||||
#include "services/common/new_timer/new_timer.h"
|
||||
#include "shell/normal/watchface.h"
|
||||
#include "shell/shell.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
|
||||
static const int WATCHFACE_TIMEOUT_MS = 30000;
|
||||
|
||||
TimerID s_timer;
|
||||
bool s_app_paused = false;
|
||||
bool s_app_started = false;
|
||||
|
||||
#ifndef NO_WATCH_TIMEOUT
|
||||
static void prv_kernel_callback_watchface_launch(void* data) {
|
||||
watchface_launch_default(shell_get_watchface_compositor_animation(true /* watchface_is_dest */));
|
||||
}
|
||||
|
||||
static void prv_timeout_expired(void *cb_data) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "App idle timeout hit! launching watchface");
|
||||
launcher_task_add_callback(prv_kernel_callback_watchface_launch, NULL);
|
||||
}
|
||||
|
||||
static void prv_start_timer(bool create) {
|
||||
if (create) {
|
||||
s_timer = new_timer_create();
|
||||
}
|
||||
|
||||
if (s_timer != TIMER_INVALID_ID && !s_app_paused && s_app_started) {
|
||||
bool success = new_timer_start(s_timer, WATCHFACE_TIMEOUT_MS, prv_timeout_expired,
|
||||
NULL, 0 /* flags */);
|
||||
PBL_ASSERTN(success);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void app_idle_timeout_start(void) {
|
||||
PBL_ASSERTN(s_timer == TIMER_INVALID_ID);
|
||||
|
||||
s_app_started = true;
|
||||
#ifndef NO_WATCH_TIMEOUT
|
||||
prv_start_timer(true /* create a timer */);
|
||||
#endif
|
||||
}
|
||||
|
||||
void app_idle_timeout_stop(void) {
|
||||
if (s_timer != TIMER_INVALID_ID) {
|
||||
new_timer_delete(s_timer);
|
||||
s_timer = TIMER_INVALID_ID;
|
||||
s_app_started = false;
|
||||
}
|
||||
}
|
||||
|
||||
void app_idle_timeout_pause(void) {
|
||||
if (s_timer != TIMER_INVALID_ID) {
|
||||
new_timer_stop(s_timer);
|
||||
}
|
||||
s_app_paused = true;
|
||||
}
|
||||
|
||||
void app_idle_timeout_resume(void) {
|
||||
s_app_paused = false;
|
||||
#ifndef NO_WATCH_TIMEOUT
|
||||
prv_start_timer(false /* do not create a timer */);
|
||||
#endif
|
||||
}
|
||||
|
||||
void app_idle_timeout_refresh(void) {
|
||||
#ifndef NO_WATCH_TIMEOUT
|
||||
prv_start_timer(false /* do not create a timer */);
|
||||
#endif
|
||||
}
|
36
src/fw/shell/normal/app_idle_timeout.h
Normal file
36
src/fw/shell/normal/app_idle_timeout.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
//! Start using the idle timeout for the current app.
|
||||
void app_idle_timeout_start(void);
|
||||
|
||||
//! Stop using the idle timeout for the current app. This is safe to call even if the idle timeout wasn't running.
|
||||
void app_idle_timeout_stop(void);
|
||||
|
||||
//! Pause the idle timeout for the current app. This is safe to call even if the idle timeout wasn't running
|
||||
//! previously.
|
||||
void app_idle_timeout_pause(void);
|
||||
|
||||
//! Resume the idle timeout for the current app. This is safe to call even if the idle timeout wasn't running
|
||||
//! previously.
|
||||
void app_idle_timeout_resume(void);
|
||||
|
||||
//! Reset the timeout. Call this whenever there is activity that should prevent the idle timeout from firing. This
|
||||
//! is safe to call even if the idle timeout wasn't running previously.
|
||||
void app_idle_timeout_refresh(void);
|
||||
|
188
src/fw/shell/normal/battery_ui.c
Normal file
188
src/fw/shell/normal/battery_ui.c
Normal file
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* 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 "battery_ui.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "applib/ui/dialogs/dialog_private.h"
|
||||
#include "applib/ui/dialogs/simple_dialog.h"
|
||||
#include "applib/ui/window_stack.h"
|
||||
#include "applib/ui/ui.h"
|
||||
#include "kernel/event_loop.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "kernel/ui/kernel_ui.h"
|
||||
#include "kernel/ui/modals/modal_manager.h"
|
||||
#include "process_management/app_manager.h"
|
||||
#include "resource/resource_ids.auto.h"
|
||||
#include "services/common/battery/battery_curve.h"
|
||||
#include "services/common/clock.h"
|
||||
#include "services/common/i18n/i18n.h"
|
||||
#include "services/common/light.h"
|
||||
#include "system/logging.h"
|
||||
#include "util/time/time.h"
|
||||
|
||||
typedef void (*DialogUpdateFn)(Dialog *, void *);
|
||||
|
||||
static Dialog *s_dialog = NULL;
|
||||
|
||||
typedef struct {
|
||||
uint32_t percent;
|
||||
GColor background_color;
|
||||
ResourceId warning_icon;
|
||||
} BatteryWarningDisplayData;
|
||||
|
||||
// UI Callbacks
|
||||
///////////////////////
|
||||
|
||||
static const GColor s_warning_color[] = {
|
||||
{ .argb = GColorLightGrayARGB8 },
|
||||
{ .argb = GColorRedARGB8 },
|
||||
};
|
||||
|
||||
static const ResourceId s_warning_icon[] = {
|
||||
RESOURCE_ID_BATTERY_ICON_LOW_LARGE,
|
||||
RESOURCE_ID_BATTERY_ICON_VERY_LOW_LARGE
|
||||
};
|
||||
|
||||
static void prv_update_ui_fully_charged(Dialog *dialog, void *ignored) {
|
||||
dialog_set_text(dialog, i18n_get("Fully Charged", dialog));
|
||||
dialog_set_background_color(dialog, GColorKellyGreen);
|
||||
dialog_set_icon(dialog, RESOURCE_ID_BATTERY_ICON_FULL_LARGE);
|
||||
}
|
||||
|
||||
static void prv_update_ui_charging(Dialog *dialog, void *ignored) {
|
||||
dialog_set_text(dialog, i18n_get("Charging", dialog));
|
||||
dialog_set_background_color(dialog, GColorLightGray);
|
||||
dialog_set_icon(dialog, RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE);
|
||||
}
|
||||
|
||||
static void prv_update_ui_warning(Dialog *dialog, void *context) {
|
||||
const BatteryWarningDisplayData *data = context;
|
||||
const uint32_t percent = data->percent;
|
||||
dialog_set_background_color(dialog, data->background_color);
|
||||
const size_t warning_length = 64;
|
||||
char buffer[warning_length];
|
||||
const uint32_t battery_hours_left = battery_curve_get_hours_remaining(percent);
|
||||
const char *message = clock_get_relative_daypart_string(rtc_get_time(), battery_hours_left);
|
||||
|
||||
if (message) {
|
||||
snprintf(buffer, warning_length, i18n_get("Powered 'til %s", dialog),
|
||||
i18n_get(message, dialog));
|
||||
dialog_set_text(dialog, buffer);
|
||||
}
|
||||
|
||||
dialog_set_icon(dialog, data->warning_icon);
|
||||
}
|
||||
|
||||
static void prv_dialog_on_unload(void *context) {
|
||||
Dialog *dialog = context;
|
||||
i18n_free_all(dialog);
|
||||
if (dialog == s_dialog) {
|
||||
s_dialog = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_display_modal(WindowStack *stack, DialogUpdateFn update_fn, void *data) {
|
||||
if (s_dialog) {
|
||||
update_fn(s_dialog, data);
|
||||
return;
|
||||
}
|
||||
|
||||
SimpleDialog *new_simple_dialog = simple_dialog_create(
|
||||
WINDOW_NAME("Battery Status"));
|
||||
|
||||
Dialog *new_dialog = simple_dialog_get_dialog(new_simple_dialog);
|
||||
dialog_set_callbacks(new_dialog, &(DialogCallbacks) {
|
||||
.unload = prv_dialog_on_unload,
|
||||
}, NULL);
|
||||
update_fn(new_dialog, data);
|
||||
|
||||
Dialog *old_dialog = s_dialog;
|
||||
s_dialog = new_dialog;
|
||||
simple_dialog_push(new_simple_dialog, stack);
|
||||
|
||||
#if PBL_ROUND
|
||||
// For circular display, to fit some battery_ui messages requires 3 lines
|
||||
// Simple dialog only allows up to 2 lines, so adjust here
|
||||
// This has to occur after the dialog push has been called
|
||||
TextLayer *text_layer = &new_simple_dialog->dialog.text_layer;
|
||||
GContext *ctx = graphics_context_get_current_context();
|
||||
const int font_height = fonts_get_font_height(text_layer->font);
|
||||
const int text_cap_height = fonts_get_font_cap_offset(text_layer->font);
|
||||
const int max_text_height = 2 * font_height + text_cap_height;
|
||||
const int32_t text_height = text_layer_get_content_size(ctx, text_layer).h;
|
||||
if (text_height > max_text_height) {
|
||||
// Values used below were to improve visual aesthetics and were reviewed by design
|
||||
const int num_lines = 3;
|
||||
const int line_spacing_delta = -4;
|
||||
const int text_shift_y = -2;
|
||||
const int text_box_height = (font_height + text_cap_height) * num_lines +
|
||||
line_spacing_delta * (num_lines - 1);
|
||||
const int text_flow_inset = 6; // Modify to allow longer central lines
|
||||
text_layer_enable_screen_text_flow_and_paging(text_layer, text_flow_inset);
|
||||
text_layer_set_size(text_layer, GSize(DISP_COLS, text_box_height));
|
||||
text_layer->layer.frame.origin.y += text_shift_y;
|
||||
text_layer_set_line_spacing_delta(text_layer, line_spacing_delta);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (old_dialog) {
|
||||
dialog_pop(old_dialog);
|
||||
}
|
||||
}
|
||||
|
||||
// Public API
|
||||
////////////////////
|
||||
|
||||
void battery_ui_display_plugged(void) {
|
||||
// If we're plugged in for charging, we want to alert the user of this,
|
||||
// but we don't want to overlay ourselves over anything they may have
|
||||
// on the screen at the moment.
|
||||
WindowStack *stack = modal_manager_get_window_stack(ModalPriorityGeneric);
|
||||
prv_display_modal(stack, prv_update_ui_charging, NULL);
|
||||
}
|
||||
|
||||
void battery_ui_display_fully_charged(void) {
|
||||
// If we're plugged in (charged), we want to alert the user of this,
|
||||
// but we don't want to overlay ourselves over anything they may have
|
||||
// on the screen at the moment.
|
||||
WindowStack *stack = modal_manager_get_window_stack(ModalPriorityGeneric);
|
||||
prv_display_modal(stack, prv_update_ui_fully_charged, NULL);
|
||||
}
|
||||
|
||||
void battery_ui_display_warning(uint32_t percent, BatteryUIWarningLevel warning_level) {
|
||||
// If we're not plugged in, that means we hit a critical power notification,
|
||||
// so we want to alert the user, subverting any non-critical windows they
|
||||
// have on the screen.
|
||||
WindowStack *stack = modal_manager_get_window_stack(ModalPriorityAlert);
|
||||
|
||||
BatteryWarningDisplayData display_data = {
|
||||
.percent = percent,
|
||||
.background_color = s_warning_color[warning_level],
|
||||
.warning_icon = s_warning_icon[warning_level],
|
||||
};
|
||||
prv_display_modal(stack, prv_update_ui_warning, &display_data);
|
||||
}
|
||||
|
||||
void battery_ui_dismiss_modal(void) {
|
||||
if (s_dialog) {
|
||||
dialog_pop(s_dialog);
|
||||
s_dialog = NULL;
|
||||
}
|
||||
}
|
48
src/fw/shell/normal/battery_ui.h
Normal file
48
src/fw/shell/normal/battery_ui.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 "applib/graphics/gtypes.h"
|
||||
#include "services/common/battery/battery_monitor.h"
|
||||
|
||||
typedef enum BatteryUIWarningLevel {
|
||||
BatteryUIWarningLevel_None = -1,
|
||||
BatteryUIWarningLevel_Low,
|
||||
BatteryUIWarningLevel_VeryLow
|
||||
} BatteryUIWarningLevel;
|
||||
|
||||
//! Process the incoming battery state change notification
|
||||
void battery_ui_handle_state_change_event(PreciseBatteryChargeState new_state);
|
||||
|
||||
//! Handle shutting down the watch.
|
||||
//!
|
||||
//! If the watch is plugged in at the time, a "shut down while charging" UI is
|
||||
//! displayed to give the user feedback on the charge state. Standby will be
|
||||
//! entered once the watch is unplugged.
|
||||
void battery_ui_handle_shut_down(void);
|
||||
|
||||
//! Show the 'battery charging' modal dialog
|
||||
void battery_ui_display_plugged(void);
|
||||
|
||||
//! Show the 'battery charged' modal dialog
|
||||
void battery_ui_display_fully_charged(void);
|
||||
|
||||
//! Show the 'battery critical' modal dialog
|
||||
void battery_ui_display_warning(uint32_t percent, BatteryUIWarningLevel warning_level);
|
||||
|
||||
//! Dismiss the battery UI modal window.
|
||||
void battery_ui_dismiss_modal(void);
|
292
src/fw/shell/normal/battery_ui_fsm.c
Normal file
292
src/fw/shell/normal/battery_ui_fsm.c
Normal file
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* 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 "battery_ui.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "applib/ui/vibes.h"
|
||||
#include "apps/system_app_ids.h"
|
||||
#include "apps/system_apps/battery_critical_app.h"
|
||||
#include "kernel/low_power.h"
|
||||
#include "kernel/ui/modals/modal_manager.h"
|
||||
#include "kernel/util/standby.h"
|
||||
#include "process_management/app_manager.h"
|
||||
#include "services/common/battery/battery_curve.h"
|
||||
#include "services/common/status_led.h"
|
||||
#include "services/common/vibe_pattern.h"
|
||||
#include "services/normal/notifications/do_not_disturb.h"
|
||||
#include "services/normal/vibes/vibe_intensity.h"
|
||||
#include "shell/normal/watchface.h"
|
||||
#include "util/ratio.h"
|
||||
#include "util/size.h"
|
||||
|
||||
// The Battery UI state machine keeps track of when to notify the user of a
|
||||
// change in battery charge state, and when to automatically dismiss the status
|
||||
// modal window.
|
||||
|
||||
#define MAX_TRANSITIONS 6
|
||||
|
||||
typedef void (*EntryFunc)(void *);
|
||||
typedef void (*ExitFunc)(void);
|
||||
|
||||
typedef enum BatteryUIStateID {
|
||||
BatteryInvalid,
|
||||
BatteryGood,
|
||||
BatteryWarning,
|
||||
BatteryLowPower,
|
||||
BatteryCritical,
|
||||
BatteryCharging,
|
||||
BatteryFullyCharged, // plugged but not charging (aka 100%)
|
||||
BatteryShutdownCharging
|
||||
} BatteryUIStateID;
|
||||
|
||||
typedef struct BatteryUIState {
|
||||
EntryFunc enter;
|
||||
ExitFunc exit;
|
||||
BatteryUIStateID next_state[MAX_TRANSITIONS];
|
||||
} BatteryUIState;
|
||||
|
||||
static void prv_display_warning(void *data);
|
||||
static void prv_dismiss_warning(void);
|
||||
static void prv_enter_low_power(void *ignored);
|
||||
static void prv_exit_low_power(void);
|
||||
static void prv_enter_critical(void *ignored);
|
||||
static void prv_exit_critical(void);
|
||||
static void prv_display_plugged(void *data);
|
||||
static void prv_dismiss_plugged(void);
|
||||
static void prv_display_fully_charged(void *data);
|
||||
static void prv_dismiss_fully_charged(void);
|
||||
// TODO PBL-39883: Replace w/ QUIRK_RESET_ON_SHUTDOWN_WHILE_CHARGING once arbitrary prefixes land
|
||||
#if PLATFORM_TINTIN || PLATFORM_SILK
|
||||
static void prv_shutdown(void *ignored);
|
||||
#else
|
||||
static void prv_enter_shutdown_charging(void *ignored);
|
||||
#endif
|
||||
|
||||
static const BatteryUIState ui_states[] = {
|
||||
[BatteryGood] = { .next_state = {
|
||||
BatteryWarning, BatteryLowPower, BatteryCritical, BatteryCharging, BatteryFullyCharged
|
||||
}},
|
||||
[BatteryWarning] = { .enter = prv_display_warning, .exit = prv_dismiss_warning, .next_state = {
|
||||
BatteryGood, BatteryWarning, BatteryLowPower, BatteryCharging
|
||||
}},
|
||||
[BatteryLowPower] = { .enter = prv_enter_low_power, .exit = prv_exit_low_power, .next_state = {
|
||||
BatteryWarning, BatteryCritical, BatteryCharging
|
||||
}},
|
||||
[BatteryCritical] = { .enter = prv_enter_critical, .exit = prv_exit_critical, .next_state = {
|
||||
BatteryLowPower, BatteryCharging
|
||||
}},
|
||||
[BatteryCharging] = { .enter = prv_display_plugged, .exit = prv_dismiss_plugged, .next_state = {
|
||||
BatteryGood, BatteryWarning, BatteryLowPower,
|
||||
BatteryCritical, BatteryFullyCharged, BatteryShutdownCharging
|
||||
}},
|
||||
[BatteryFullyCharged] = { .enter = prv_display_fully_charged, .exit = prv_dismiss_fully_charged,
|
||||
.next_state = {
|
||||
BatteryGood, BatteryWarning, BatteryLowPower, BatteryCritical, BatteryShutdownCharging
|
||||
}},
|
||||
// TODO PBL-39883: Replace w/ QUIRK_RESET_ON_SHUTDOWN_WHILE_CHARGING once arbitrary prefixes land
|
||||
#if PLATFORM_TINTIN || PLATFORM_SILK
|
||||
[BatteryShutdownCharging] = { .enter = prv_shutdown }
|
||||
#else
|
||||
[BatteryShutdownCharging] = { .enter = prv_enter_shutdown_charging }
|
||||
#endif
|
||||
};
|
||||
|
||||
static BatteryUIStateID s_state = BatteryGood;
|
||||
static BatteryUIWarningLevel s_warning_points_index = -1;
|
||||
|
||||
#if PLATFORM_SPALDING
|
||||
/* first warning for S4 is at 12 hours remaining, second at 6 hours remaining */
|
||||
static const uint8_t s_warning_points[] = { 12, 6 };
|
||||
#else
|
||||
/* first warning is at 18 hours remaining, second at 12 hours remaining */
|
||||
static const uint8_t s_warning_points[] = { 18, 12 };
|
||||
#endif
|
||||
|
||||
// State functions
|
||||
|
||||
static void prv_display_warning(void *data) {
|
||||
const uint8_t percent = ratio32_to_percent(((PreciseBatteryChargeState *)data)->charge_percent);
|
||||
bool new_warning = false;
|
||||
const BatteryUIWarningLevel num_points = ARRAY_LENGTH(s_warning_points) - 1;
|
||||
|
||||
while (s_warning_points_index < num_points && (percent <=
|
||||
battery_curve_get_percent_remaining(s_warning_points[s_warning_points_index + 1]))) {
|
||||
s_warning_points_index++;
|
||||
new_warning = true;
|
||||
}
|
||||
|
||||
if (new_warning) {
|
||||
if (!do_not_disturb_is_active()) {
|
||||
vibes_short_pulse();
|
||||
}
|
||||
battery_ui_display_warning(percent, s_warning_points_index);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_dismiss_warning(void) {
|
||||
battery_ui_dismiss_modal();
|
||||
s_warning_points_index = -1;
|
||||
}
|
||||
|
||||
static void prv_enter_low_power(void *ignored) {
|
||||
#ifndef RECOVERY_FW
|
||||
watchface_start_low_power();
|
||||
modal_manager_pop_all_below_priority(ModalPriorityAlarm);
|
||||
modal_manager_set_min_priority(ModalPriorityAlarm);
|
||||
// Override the vibe intensity to Medium in low-power mode
|
||||
vibes_set_default_vibe_strength(get_strength_for_intensity(VibeIntensityMedium));
|
||||
#else
|
||||
app_manager_launch_new_app(&(AppLaunchConfig) {
|
||||
.md = prf_low_power_app_get_info(),
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
static void prv_exit_low_power(void) {
|
||||
#ifndef RECOVERY_FW
|
||||
modal_manager_set_min_priority(ModalPriorityMin);
|
||||
watchface_launch_default(NULL);
|
||||
vibe_intensity_set(vibe_intensity_get());
|
||||
#else
|
||||
app_manager_close_current_app(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void prv_enter_critical(void *ignored) {
|
||||
if (!do_not_disturb_is_active()) {
|
||||
vibes_short_pulse();
|
||||
}
|
||||
// in case there is a warning on screen
|
||||
modal_manager_pop_all();
|
||||
modal_manager_set_min_priority(ModalPriorityMax);
|
||||
app_manager_put_launch_app_event(&(AppLaunchEventConfig) {
|
||||
.id = APP_ID_BATTERY_CRITICAL,
|
||||
});
|
||||
}
|
||||
|
||||
static void prv_exit_critical(void) {
|
||||
app_manager_close_current_app(true);
|
||||
modal_manager_set_min_priority(ModalPriorityMin);
|
||||
}
|
||||
|
||||
static void prv_display_plugged(void *data) {
|
||||
if (!do_not_disturb_is_active()) {
|
||||
vibes_short_pulse();
|
||||
}
|
||||
battery_ui_display_plugged();
|
||||
|
||||
status_led_set(StatusLedState_Charging);
|
||||
}
|
||||
|
||||
static void prv_dismiss_plugged(void) {
|
||||
battery_ui_dismiss_modal();
|
||||
|
||||
status_led_set(StatusLedState_Off);
|
||||
}
|
||||
|
||||
static void prv_display_fully_charged(void *data) {
|
||||
battery_ui_display_fully_charged();
|
||||
status_led_set(StatusLedState_FullyCharged);
|
||||
}
|
||||
|
||||
static void prv_dismiss_fully_charged(void) {
|
||||
battery_ui_dismiss_modal();
|
||||
|
||||
status_led_set(StatusLedState_Off);
|
||||
}
|
||||
|
||||
// TODO PBL-39883: Replace w/ QUIRK_RESET_ON_SHUTDOWN_WHILE_CHARGING once arbitrary prefixes land
|
||||
#if PLATFORM_TINTIN || PLATFORM_SILK
|
||||
static void prv_shutdown(void *ignored) {
|
||||
battery_ui_handle_shut_down();
|
||||
}
|
||||
#else
|
||||
static void prv_enter_shutdown_charging(void *ignored) {
|
||||
app_manager_put_launch_app_event(&(AppLaunchEventConfig) {
|
||||
.id = APP_ID_SHUTDOWN_CHARGING,
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
// Internals
|
||||
|
||||
static void prv_transition(BatteryUIStateID next_state, void *data) {
|
||||
if (s_state != next_state) {
|
||||
// All self-transitions are internal.
|
||||
// A state's entry function is a its only valid action.
|
||||
// The exit function is only called on actual state changes.
|
||||
if (ui_states[s_state].exit) {
|
||||
ui_states[s_state].exit();
|
||||
}
|
||||
s_state = next_state;
|
||||
}
|
||||
if (ui_states[s_state].enter) {
|
||||
ui_states[s_state].enter(data);
|
||||
}
|
||||
}
|
||||
|
||||
static bool prv_is_valid_transition(BatteryUIStateID next_state) {
|
||||
const uint8_t count = ARRAY_LENGTH(ui_states[s_state].next_state);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (ui_states[s_state].next_state[i] == next_state) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static BatteryUIStateID prv_get_state(PreciseBatteryChargeState *state) {
|
||||
// TODO: Refactor?
|
||||
if (state->is_plugged) {
|
||||
// Don't use the PreciseBatteryChargeState definition of is_charging, as it maps to the
|
||||
// result of @see battery_charge_controller_thinks_we_are_charging instead of the actual
|
||||
// user-facing definition of charging.
|
||||
const uint32_t is_charging = battery_get_charge_state().is_charging;
|
||||
return is_charging ? BatteryCharging : BatteryFullyCharged;
|
||||
} else if (battery_monitor_critical_lockout()) {
|
||||
return BatteryCritical;
|
||||
} else if (low_power_is_active()) {
|
||||
return BatteryLowPower;
|
||||
} else if (ratio32_to_percent(state->charge_percent) <=
|
||||
battery_curve_get_percent_remaining(s_warning_points[0])) {
|
||||
return BatteryWarning;
|
||||
} else {
|
||||
return BatteryGood;
|
||||
}
|
||||
}
|
||||
|
||||
void battery_ui_handle_state_change_event(PreciseBatteryChargeState charge_state) {
|
||||
BatteryUIStateID next_state = prv_get_state(&charge_state);
|
||||
if (prv_is_valid_transition(next_state)) {
|
||||
prv_transition(next_state, &charge_state);
|
||||
}
|
||||
}
|
||||
|
||||
void battery_ui_handle_shut_down(void) {
|
||||
if (s_state != BatteryCharging) {
|
||||
enter_standby(RebootReasonCode_ShutdownMenuItem);
|
||||
} else {
|
||||
prv_transition(BatteryShutdownCharging, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void battery_ui_reset_fsm_for_tests(void) {
|
||||
s_state = BatteryGood;
|
||||
s_warning_points_index = -1;
|
||||
}
|
127
src/fw/shell/normal/display_calibration_prompt.c
Normal file
127
src/fw/shell/normal/display_calibration_prompt.c
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#if PLATFORM_SPALDING
|
||||
|
||||
#include "display_calibration_prompt.h"
|
||||
|
||||
#include "applib/ui/dialogs/confirmation_dialog.h"
|
||||
#include "apps/system_apps/settings/settings_display_calibration.h"
|
||||
#include "kernel/event_loop.h"
|
||||
#include "kernel/ui/modals/modal_manager.h"
|
||||
#include "mfg/mfg_info.h"
|
||||
#include "mfg/mfg_serials.h"
|
||||
#include "resource/resource_ids.auto.h"
|
||||
#include "services/common/i18n/i18n.h"
|
||||
#include "services/common/new_timer/new_timer.h"
|
||||
#include "shell/prefs.h"
|
||||
#include "util/size.h"
|
||||
|
||||
// The calibration screen will be changing the screen offsets, so it's best that it remains on top
|
||||
// of most other modals (generic, alerts, etc) to prevent confusion about the screen's alignment.
|
||||
static const ModalPriority MODAL_PRIORITY = ModalPriorityCritical;
|
||||
|
||||
static void prv_calibrate_confirm_pop(ClickRecognizerRef recognizer, void *context) {
|
||||
i18n_free_all(context);
|
||||
confirmation_dialog_pop((ConfirmationDialog *)context);
|
||||
}
|
||||
|
||||
static void prv_calibrate_confirm_cb(ClickRecognizerRef recognizer, void *context) {
|
||||
settings_display_calibration_push(modal_manager_get_window_stack(MODAL_PRIORITY));
|
||||
prv_calibrate_confirm_pop(recognizer, context);
|
||||
}
|
||||
|
||||
static void prv_calibrate_click_config(void *context) {
|
||||
window_single_click_subscribe(BUTTON_ID_UP, prv_calibrate_confirm_cb);
|
||||
window_single_click_subscribe(BUTTON_ID_DOWN, prv_calibrate_confirm_pop);
|
||||
window_single_click_subscribe(BUTTON_ID_BACK, prv_calibrate_confirm_pop);
|
||||
}
|
||||
|
||||
static TimerID s_timer = TIMER_INVALID_ID;
|
||||
|
||||
static void prv_push_calibration_dialog(void *data) {
|
||||
shell_prefs_set_should_prompt_display_calibration(false);
|
||||
|
||||
ConfirmationDialog *confirmation_dialog = confirmation_dialog_create("Calibrate Prompt");
|
||||
Dialog *dialog = confirmation_dialog_get_dialog(confirmation_dialog);
|
||||
|
||||
dialog_set_text(dialog, i18n_get("Your screen may need calibration. Calibrate it now?",
|
||||
confirmation_dialog));
|
||||
dialog_set_background_color(dialog, GColorMediumAquamarine);
|
||||
dialog_set_icon(dialog, RESOURCE_ID_GENERIC_PIN_TINY);
|
||||
confirmation_dialog_set_click_config_provider(confirmation_dialog,
|
||||
prv_calibrate_click_config);
|
||||
confirmation_dialog_push(confirmation_dialog,
|
||||
modal_manager_get_window_stack(MODAL_PRIORITY));
|
||||
}
|
||||
|
||||
static bool prv_display_has_user_offset(void) {
|
||||
GPoint display_offset = shell_prefs_get_display_offset();
|
||||
GPoint mfg_display_offset = mfg_info_get_disp_offsets();
|
||||
return (!gpoint_equal(&display_offset, &mfg_display_offset));
|
||||
}
|
||||
|
||||
static void prv_timer_callback(void *data) {
|
||||
new_timer_delete(s_timer);
|
||||
s_timer = TIMER_INVALID_ID;
|
||||
|
||||
// last check: make sure we need to display the prompt in case something changed in the
|
||||
// time that the timer was waiting.
|
||||
if (!shell_prefs_should_prompt_display_calibration()) {
|
||||
return;
|
||||
}
|
||||
|
||||
launcher_task_add_callback(prv_push_calibration_dialog, NULL);
|
||||
}
|
||||
|
||||
T_STATIC bool prv_is_known_misaligned_serial_number(const char *serial) {
|
||||
// Filter watches known to be misaligned based on the serial number. This is possible because
|
||||
// Serial numbers are represented as strings as described in:
|
||||
// https://pebbletechnology.atlassian.net/wiki/display/DEV/Hardware+Serial+Numbering
|
||||
// All watches of the same model produced from the same manufacturer on the same date, on the
|
||||
// same manufacturing line, will share the same first 8 characters of the serial number. In this
|
||||
// way, batches which are misaligned can be identified by a string comparison on these characters.
|
||||
//
|
||||
// NOTE: This also conveniently excludes test automation boards, so the dialog should not
|
||||
// appear during integration tests.
|
||||
const char *ranges[] = { "Q402445E" };
|
||||
for (size_t i = 0; i < ARRAY_LENGTH(ranges); i++) {
|
||||
if (strncmp(serial, ranges[i], strlen(ranges[i])) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool prv_is_potentially_misaligned_watch() {
|
||||
return !prv_display_has_user_offset() &&
|
||||
prv_is_known_misaligned_serial_number(mfg_get_serial_number());
|
||||
}
|
||||
|
||||
void display_calibration_prompt_show_if_needed(void) {
|
||||
if (!prv_is_potentially_misaligned_watch()) {
|
||||
shell_prefs_set_should_prompt_display_calibration(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_prefs_should_prompt_display_calibration()) {
|
||||
s_timer = new_timer_create();
|
||||
const uint32_t prompt_delay_time_ms = MS_PER_SECOND * SECONDS_PER_MINUTE;
|
||||
new_timer_start(s_timer, prompt_delay_time_ms, prv_timer_callback, NULL, 0 /* flags */);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
19
src/fw/shell/normal/display_calibration_prompt.h
Normal file
19
src/fw/shell/normal/display_calibration_prompt.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
void display_calibration_prompt_show_if_needed(void);
|
43
src/fw/shell/normal/language_ui.c
Normal file
43
src/fw/shell/normal/language_ui.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 "language_ui.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "applib/ui/dialogs/simple_dialog.h"
|
||||
#include "kernel/event_loop.h"
|
||||
#include "kernel/ui/modals/modal_manager.h"
|
||||
#include "resource/resource_ids.auto.h"
|
||||
#include "services/common/i18n/i18n.h"
|
||||
#include "shell/normal/watchface.h"
|
||||
|
||||
static void prv_push_language_changed_dialog(void *data) {
|
||||
const char *lang_name = (const char *)data;
|
||||
SimpleDialog *simple_dialog = simple_dialog_create("LangFileChanged");
|
||||
Dialog *dialog = simple_dialog_get_dialog(simple_dialog);
|
||||
dialog_set_text(dialog, lang_name);
|
||||
dialog_set_icon(dialog, RESOURCE_ID_GENERIC_CONFIRMATION_LARGE);
|
||||
dialog_set_background_color(dialog, GColorJaegerGreen);
|
||||
dialog_set_timeout(dialog, DIALOG_TIMEOUT_DEFAULT);
|
||||
simple_dialog_push(simple_dialog, modal_manager_get_window_stack(ModalPriorityAlert));
|
||||
// after dialog closes, launch the watchface
|
||||
watchface_launch_default(NULL);
|
||||
}
|
||||
|
||||
void language_ui_display_changed(const char *lang_name) {
|
||||
launcher_task_add_callback(prv_push_language_changed_dialog, (void *)lang_name);
|
||||
}
|
20
src/fw/shell/normal/language_ui.h
Normal file
20
src/fw/shell/normal/language_ui.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
// Show a dialog indicating the language has changed
|
||||
void language_ui_display_changed(const char *lang_name);
|
1178
src/fw/shell/normal/prefs.c
Normal file
1178
src/fw/shell/normal/prefs.c
Normal file
File diff suppressed because it is too large
Load diff
40
src/fw/shell/normal/prefs_values.h.inc
Normal file
40
src/fw/shell/normal/prefs_values.h.inc
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Included by prefs.c to declare the list of preferences and their keys
|
||||
PREFS_MACRO(PREF_KEY_CLOCK_24H, s_clock_24h)
|
||||
PREFS_MACRO(PREF_KEY_CLOCK_TIMEZONE_SOURCE_IS_MANUAL, s_clock_timezone_source_is_manual)
|
||||
PREFS_MACRO(PREF_KEY_CLOCK_PHONE_TIMEZONE_ID, s_clock_phone_timezone_id)
|
||||
PREFS_MACRO(PREF_KEY_UNITS_DISTANCE, s_units_distance)
|
||||
PREFS_MACRO(PREF_KEY_BACKLIGHT_ENABLED, s_backlight_enabled)
|
||||
PREFS_MACRO(PREF_KEY_BACKLIGHT_AMBIENT_SENSOR_ENABLED, s_backlight_ambient_sensor_enabled)
|
||||
PREFS_MACRO(PREF_KEY_BACKLIGHT_TIMEOUT_MS, s_backlight_timeout_ms)
|
||||
PREFS_MACRO(PREF_KEY_BACKLIGHT_INTENSITY, s_backlight_intensity)
|
||||
PREFS_MACRO(PREF_KEY_BACKLIGHT_MOTION, s_backlight_motion_enabled)
|
||||
PREFS_MACRO(PREF_KEY_STATIONARY, s_stationary_mode_enabled)
|
||||
PREFS_MACRO(PREF_KEY_DEFAULT_WORKER, s_default_worker)
|
||||
PREFS_MACRO(PREF_KEY_TEXT_STYLE, s_text_style)
|
||||
PREFS_MACRO(PREF_KEY_LANG_ENGLISH, s_language_english)
|
||||
PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_UP, s_quick_launch_up)
|
||||
PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_DOWN, s_quick_launch_down)
|
||||
PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_SELECT, s_quick_launch_select)
|
||||
PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_BACK, s_quick_launch_back)
|
||||
PREFS_MACRO(PREF_KEY_QUICK_LAUNCH_SETUP_OPENED, s_quick_launch_setup_opened)
|
||||
PREFS_MACRO(PREF_KEY_DEFAULT_WATCHFACE, s_default_watchface)
|
||||
PREFS_MACRO(PREF_KEY_WELCOME_VERSION, s_welcome_version)
|
||||
#if CAPABILITY_HAS_HEALTH_TRACKING
|
||||
PREFS_MACRO(PREF_KEY_ACTIVITY_PREFERENCES, s_activity_preferences)
|
||||
PREFS_MACRO(PREF_KEY_ACTIVITY_ACTIVATED_TIMESTAMP, s_activity_activation_timestamp)
|
||||
PREFS_MACRO(PREF_KEY_ACTIVITY_ACTIVATION_DELAY_INSIGHT, s_activity_activation_delay_insight)
|
||||
PREFS_MACRO(PREF_KEY_ACTIVITY_HEALTH_APP_OPENED, s_activity_prefs_health_app_opened)
|
||||
PREFS_MACRO(PREF_KEY_ACTIVITY_WORKOUT_APP_OPENED, s_activity_prefs_workout_app_opened)
|
||||
PREFS_MACRO(PREF_KEY_ALARMS_APP_OPENED, s_alarms_app_opened)
|
||||
PREFS_MACRO(PREF_KEY_ACTIVITY_HRM_PREFERENCES, s_activity_hrm_preferences)
|
||||
PREFS_MACRO(PREF_KEY_ACTIVITY_HEART_RATE_PREFERENCES, s_activity_hr_preferences)
|
||||
#endif
|
||||
#if PLATFORM_SPALDING
|
||||
PREFS_MACRO(PREF_KEY_DISPLAY_USER_OFFSET, s_display_user_offset)
|
||||
PREFS_MACRO(PREF_KEY_SHOULD_PROMPT_DISPLAY_CALIBRATION, s_should_prompt_display_calibration)
|
||||
#endif
|
||||
#if CAPABILITY_HAS_TIMELINE_PEEK
|
||||
PREFS_MACRO(PREF_KEY_TIMELINE_SETTINGS_OPENED, s_timeline_settings_opened)
|
||||
PREFS_MACRO(PREF_KEY_TIMELINE_PEEK_ENABLED, s_timeline_peek_enabled)
|
||||
PREFS_MACRO(PREF_KEY_TIMELINE_PEEK_BEFORE_TIME_M, s_timeline_peek_before_time_m)
|
||||
#endif
|
28
src/fw/shell/normal/quick_launch.h
Normal file
28
src/fw/shell/normal/quick_launch.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 "drivers/button_id.h"
|
||||
#include "process_management/app_install_types.h"
|
||||
#include "util/uuid.h"
|
||||
|
||||
bool quick_launch_is_enabled(ButtonId button);
|
||||
AppInstallId quick_launch_get_app(ButtonId button);
|
||||
void quick_launch_set_app(ButtonId button, AppInstallId app_id);
|
||||
void quick_launch_set_enabled(ButtonId button, bool enabled);
|
||||
void quick_launch_set_quick_launch_setup_opened(uint8_t version);
|
||||
uint8_t quick_launch_get_quick_launch_setup_opened(void);
|
125
src/fw/shell/normal/shell.c
Normal file
125
src/fw/shell/normal/shell.c
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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/shell.h"
|
||||
|
||||
#include "apps/system_app_ids.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "process_management/app_install_manager.h"
|
||||
#include "process_management/app_install_types.h"
|
||||
#include "process_management/app_manager.h"
|
||||
#include "services/common/compositor/compositor_transitions.h"
|
||||
|
||||
#define WATCHFACE_SHUTTER_COLOR GColorWhite
|
||||
#define HEALTH_SHUTTER_COLOR PBL_IF_COLOR_ELSE(GColorBlack, GColorWhite)
|
||||
#define ACTION_SHUTTER_COLOR PBL_IF_COLOR_ELSE(GColorLightGray, GColorWhite)
|
||||
|
||||
static const CompositorTransition *prv_get_watchface_compositor_animation(
|
||||
CompositorTransitionDirection direction) {
|
||||
return PBL_IF_RECT_ELSE(compositor_shutter_transition_get(direction, WATCHFACE_SHUTTER_COLOR),
|
||||
compositor_port_hole_transition_app_get(direction));
|
||||
}
|
||||
|
||||
static const CompositorTransition *prv_get_health_compositor_animation(
|
||||
CompositorTransitionDirection direction) {
|
||||
return PBL_IF_RECT_ELSE(compositor_shutter_transition_get(direction, HEALTH_SHUTTER_COLOR),
|
||||
compositor_port_hole_transition_app_get(direction));
|
||||
}
|
||||
|
||||
static const CompositorTransition *prv_get_action_compositor_animation(
|
||||
CompositorTransitionDirection direction) {
|
||||
return PBL_IF_RECT_ELSE(compositor_shutter_transition_get(direction, ACTION_SHUTTER_COLOR),
|
||||
NULL);
|
||||
}
|
||||
|
||||
const CompositorTransition *shell_get_watchface_compositor_animation(
|
||||
bool watchface_is_destination) {
|
||||
const CompositorTransitionDirection direction = watchface_is_destination ?
|
||||
CompositorTransitionDirectionLeft : CompositorTransitionDirectionRight;
|
||||
return prv_get_watchface_compositor_animation(direction);
|
||||
}
|
||||
|
||||
static const CompositorTransition *prv_app_launcher_transition_animation(
|
||||
CompositorTransitionDirection direction) {
|
||||
const bool app_is_destination = (direction == CompositorTransitionDirectionRight);
|
||||
return PBL_IF_RECT_ELSE(compositor_launcher_app_transition_get(app_is_destination),
|
||||
compositor_port_hole_transition_app_get(direction));
|
||||
}
|
||||
|
||||
const CompositorTransition *shell_get_close_compositor_animation(AppInstallId current_app_id,
|
||||
AppInstallId next_app_id) {
|
||||
const CompositorTransition *res = NULL;
|
||||
AppInstallEntry *app_entry = kernel_zalloc_check(sizeof(AppInstallEntry));
|
||||
|
||||
if (app_install_get_entry_for_install_id(next_app_id, app_entry) &&
|
||||
app_install_entry_is_watchface(app_entry)) {
|
||||
if (current_app_id == APP_ID_LAUNCHER_MENU) {
|
||||
res = prv_get_watchface_compositor_animation(CompositorTransitionDirectionLeft);
|
||||
goto done;
|
||||
} else if (current_app_id == APP_ID_HEALTH_APP) {
|
||||
res = prv_get_health_compositor_animation(CompositorTransitionDirectionDown);
|
||||
goto done;
|
||||
} else {
|
||||
res = prv_get_action_compositor_animation(CompositorTransitionDirectionLeft);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (next_app_id == APP_ID_LAUNCHER_MENU) {
|
||||
res = prv_app_launcher_transition_animation(CompositorTransitionDirectionLeft);
|
||||
goto done;
|
||||
}
|
||||
|
||||
// If we get here, we don't use a compositor animation for the transition
|
||||
|
||||
done:
|
||||
kernel_free(app_entry);
|
||||
return res;
|
||||
}
|
||||
|
||||
const CompositorTransition *shell_get_open_compositor_animation(AppInstallId current_app_id,
|
||||
AppInstallId next_app_id) {
|
||||
const CompositorTransition *res = NULL;
|
||||
AppInstallEntry *app_entry = kernel_zalloc_check(sizeof(AppInstallEntry));
|
||||
|
||||
if (app_install_get_entry_for_install_id(current_app_id, app_entry)) {
|
||||
if (app_install_entry_is_watchface(app_entry)) {
|
||||
if (next_app_id == APP_ID_LAUNCHER_MENU) {
|
||||
res = prv_get_watchface_compositor_animation(CompositorTransitionDirectionRight);
|
||||
goto done;
|
||||
} else if (next_app_id == APP_ID_HEALTH_APP) {
|
||||
res = prv_get_health_compositor_animation(CompositorTransitionDirectionUp);
|
||||
goto done;
|
||||
}
|
||||
} else if ((current_app_id == APP_ID_HEALTH_APP) &&
|
||||
app_install_get_entry_for_install_id(next_app_id, app_entry) &&
|
||||
app_install_entry_is_watchface(app_entry)) {
|
||||
res = prv_get_health_compositor_animation(CompositorTransitionDirectionDown);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (current_app_id == APP_ID_LAUNCHER_MENU) {
|
||||
res = prv_app_launcher_transition_animation(CompositorTransitionDirectionRight);
|
||||
goto done;
|
||||
}
|
||||
|
||||
// If we get here, we don't use a compositor animation for the transition
|
||||
|
||||
done:
|
||||
kernel_free(app_entry);
|
||||
return res;
|
||||
}
|
204
src/fw/shell/normal/shell_event_loop.c
Normal file
204
src/fw/shell/normal/shell_event_loop.c
Normal file
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* 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 <kernel/events.h>
|
||||
#include "shell/shell_event_loop.h"
|
||||
#include "shell/prefs_private.h"
|
||||
|
||||
#include "apps/system_app_ids.h"
|
||||
#include "apps/system_apps/app_fetch_ui.h"
|
||||
#include "apps/system_apps/settings/settings_quick_launch.h"
|
||||
#include "apps/system_apps/timeline/timeline.h"
|
||||
#include "kernel/low_power.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "popups/alarm_popup.h"
|
||||
#include "popups/bluetooth_pairing_ui.h"
|
||||
#include "popups/notifications/notification_window.h"
|
||||
#include "popups/timeline/peek.h"
|
||||
#include "process_management/app_install_manager.h"
|
||||
#include "process_management/app_manager.h"
|
||||
#include "process_management/process_manager.h"
|
||||
#include "services/common/analytics/analytics.h"
|
||||
#include "services/common/bluetooth/bluetooth_persistent_storage.h"
|
||||
#include "services/common/shared_prf_storage/shared_prf_storage.h"
|
||||
#include "services/normal/activity/activity.h"
|
||||
#include "services/normal/activity/workout_service.h"
|
||||
#include "services/normal/app_inbox_service.h"
|
||||
#include "services/normal/app_outbox_service.h"
|
||||
#include "services/normal/music.h"
|
||||
#include "services/normal/music_endpoint.h"
|
||||
#include "services/normal/notifications/do_not_disturb.h"
|
||||
#include "services/normal/stationary.h"
|
||||
#include "services/normal/timeline/event.h"
|
||||
#include "shell/normal/app_idle_timeout.h"
|
||||
#include "shell/normal/battery_ui.h"
|
||||
#include "shell/normal/display_calibration_prompt.h"
|
||||
#include "shell/normal/quick_launch.h"
|
||||
#include "shell/normal/watchface.h"
|
||||
#include "shell/normal/welcome.h"
|
||||
#include "shell/prefs.h"
|
||||
#include "system/logging.h"
|
||||
|
||||
|
||||
extern void shell_prefs_init(void);
|
||||
|
||||
void shell_event_loop_init(void) {
|
||||
shell_prefs_init();
|
||||
#if PLATFORM_SPALDING
|
||||
shell_prefs_display_offset_init();
|
||||
display_calibration_prompt_show_if_needed();
|
||||
#endif
|
||||
notification_window_service_init();
|
||||
app_inbox_service_init();
|
||||
app_outbox_service_init();
|
||||
app_message_sender_init();
|
||||
watchface_init();
|
||||
timeline_peek_init();
|
||||
#if CAPABILITY_HAS_HEALTH_TRACKING
|
||||
// Start activity tracking if enabled
|
||||
if (activity_prefs_tracking_is_enabled()) {
|
||||
activity_start_tracking(false /*test_mode*/);
|
||||
}
|
||||
workout_service_init();
|
||||
#endif
|
||||
|
||||
bool factory_reset_or_first_use = !shared_prf_storage_get_getting_started_complete();
|
||||
// We are almost done booting, welcome the user if applicable. This _must_ occur before setting
|
||||
// the getting started completed below.
|
||||
welcome_push_notification(factory_reset_or_first_use);
|
||||
if (factory_reset_or_first_use) {
|
||||
bt_persistent_storage_set_unfaithful(true);
|
||||
}
|
||||
|
||||
// As soon as we boot normally for the first time, we've therefore completed first use mode and
|
||||
// we don't need to go through it again until we factory reset.
|
||||
shared_prf_storage_set_getting_started_complete(true /* complete */);
|
||||
}
|
||||
|
||||
void shell_event_loop_handle_event(PebbleEvent *e) {
|
||||
switch (e->type) {
|
||||
case PEBBLE_APP_FETCH_REQUEST_EVENT:
|
||||
app_manager_handle_app_fetch_request_event(&e->app_fetch_request);
|
||||
return;
|
||||
|
||||
case PEBBLE_ALARM_CLOCK_EVENT:
|
||||
analytics_inc(ANALYTICS_DEVICE_METRIC_ALARM_SOUNDED_COUNT, AnalyticsClient_System);
|
||||
PBL_LOG(LOG_LEVEL_INFO, "Alarm event in the shell event loop");
|
||||
stationary_wake_up();
|
||||
alarm_popup_push_window(&e->alarm_clock);
|
||||
return;
|
||||
|
||||
case PEBBLE_BT_PAIRING_EVENT:
|
||||
bluetooth_pairing_ui_handle_event(&e->bluetooth.pair);
|
||||
return;
|
||||
|
||||
case PEBBLE_APP_WILL_CHANGE_FOCUS_EVENT:
|
||||
if (e->app_focus.in_focus) {
|
||||
app_idle_timeout_resume();
|
||||
} else {
|
||||
app_idle_timeout_pause();
|
||||
}
|
||||
return;
|
||||
|
||||
case PEBBLE_SYS_NOTIFICATION_EVENT:
|
||||
// This handles incoming Notifications and actions on Notifications and Reminders
|
||||
notification_window_handle_notification(&e->sys_notification);
|
||||
return;
|
||||
|
||||
case PEBBLE_CALENDAR_EVENT:
|
||||
do_not_disturb_handle_calendar_event(&e->calendar);
|
||||
return;
|
||||
|
||||
case PEBBLE_TIMELINE_PEEK_EVENT:
|
||||
timeline_peek_handle_peek_event(&e->timeline_peek);
|
||||
return;
|
||||
|
||||
case PEBBLE_BLOBDB_EVENT:
|
||||
{
|
||||
// Calendar should only handle pin_db events
|
||||
PebbleBlobDBEvent *blobdb_event = &e->blob_db;
|
||||
if (blobdb_event->db_id == BlobDBIdPins) {
|
||||
timeline_event_handle_blobdb_event();
|
||||
} else if (blobdb_event->db_id == BlobDBIdPrefs) {
|
||||
prefs_private_handle_blob_db_event(blobdb_event);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case PEBBLE_DO_NOT_DISTURB_EVENT:
|
||||
notification_window_handle_dnd_event(&e->do_not_disturb);
|
||||
return;
|
||||
|
||||
case PEBBLE_REMINDER_EVENT:
|
||||
// This handles incoming Reminders
|
||||
notification_window_handle_reminder(&e->reminder);
|
||||
return;
|
||||
|
||||
case PEBBLE_BATTERY_STATE_CHANGE_EVENT:
|
||||
battery_ui_handle_state_change_event(e->battery_state.new_state);
|
||||
return;
|
||||
|
||||
case PEBBLE_COMM_SESSION_EVENT:
|
||||
music_endpoint_handle_mobile_app_event(&e->bluetooth.comm_session_event);
|
||||
return;
|
||||
|
||||
// Sent by the comm layer once we get a response from the mobile app to a phone version request
|
||||
case PEBBLE_REMOTE_APP_INFO_EVENT:
|
||||
music_endpoint_handle_mobile_app_info_event(&e->bluetooth.app_info_event);
|
||||
analytics_inc(ANALYTICS_DEVICE_METRIC_PHONE_APP_INFO_COUNT, AnalyticsClient_System);
|
||||
return;
|
||||
|
||||
case PEBBLE_MEDIA_EVENT:
|
||||
if (e->media.playback_state == MusicPlayStatePlaying) {
|
||||
app_install_mark_prioritized(APP_ID_MUSIC, true /* can_expire */);
|
||||
}
|
||||
return;
|
||||
|
||||
case PEBBLE_HEALTH_SERVICE_EVENT:
|
||||
workout_service_health_event_handler(&e->health_event);
|
||||
return;
|
||||
|
||||
case PEBBLE_ACTIVITY_EVENT:
|
||||
workout_service_activity_event_handler(&e->activity_event);
|
||||
return;
|
||||
|
||||
case PEBBLE_WORKOUT_EVENT: {
|
||||
// If a workout is ongoing, keep the app at the top of the launcher.
|
||||
// When a workout is stopped it will return to it's normal position after the
|
||||
// default timeout.
|
||||
PebbleWorkoutEvent *workout_e = &e->workout;
|
||||
bool can_expire = true;
|
||||
switch (workout_e->type) {
|
||||
case PebbleWorkoutEvent_Started:
|
||||
case PebbleWorkoutEvent_Paused:
|
||||
can_expire = false;
|
||||
break;
|
||||
case PebbleWorkoutEvent_Stopped:
|
||||
can_expire = true;
|
||||
break;
|
||||
case PebbleWorkoutEvent_FrontendOpened:
|
||||
case PebbleWorkoutEvent_FrontendClosed:
|
||||
break;
|
||||
}
|
||||
app_install_mark_prioritized(APP_ID_WORKOUT, can_expire);
|
||||
workout_service_workout_event_handler(workout_e);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break; // don't care
|
||||
}
|
||||
}
|
||||
|
172
src/fw/shell/normal/shutdown_charging.c
Normal file
172
src/fw/shell/normal/shutdown_charging.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "applib/app.h"
|
||||
#include "applib/app_timer.h"
|
||||
#include "applib/battery_state_service.h"
|
||||
#include "applib/ui/dialogs/simple_dialog.h"
|
||||
#include "applib/ui/window.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "kernel/util/standby.h"
|
||||
#include "process_management/pebble_process_md.h"
|
||||
#include "process_management/worker_manager.h"
|
||||
#include "process_state/app_state/app_state.h"
|
||||
#include "resource/resource.h"
|
||||
#include "resource/resource_ids.auto.h"
|
||||
#include "services/runlevel.h"
|
||||
#include "services/common/status_led.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
#include "system/reboot_reason.h"
|
||||
#include "system/reset.h"
|
||||
|
||||
static const uint32_t CHARGER_DISCONNECT_TIMEOUT_MS = 3000;
|
||||
|
||||
typedef enum DialogState {
|
||||
DialogState_Uninitialized = 0,
|
||||
DialogState_Charging,
|
||||
DialogState_FullyCharged,
|
||||
} DialogState;
|
||||
|
||||
|
||||
struct AppData {
|
||||
SimpleDialog *dialog;
|
||||
AppTimer *poweroff_timer;
|
||||
DialogState last_dialog_state;
|
||||
bool was_plugged;
|
||||
};
|
||||
|
||||
static void prv_reboot_on_click(ClickRecognizerRef recognizer, void *data) {
|
||||
// Don't try to return to normal functioning; just reboot the watch. The user
|
||||
// thinks the watch is already off anyway.
|
||||
RebootReason reboot_reason = { RebootReasonCode_ShutdownMenuItem };
|
||||
reboot_reason_set(&reboot_reason);
|
||||
system_reset();
|
||||
}
|
||||
|
||||
static void prv_config_provider(void *context) {
|
||||
for (int i = 0; i < NUM_BUTTONS; ++i) {
|
||||
window_long_click_subscribe(i, 0, prv_reboot_on_click, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_power_off_timer_expired(void *data) {
|
||||
enter_standby(RebootReasonCode_ShutdownMenuItem);
|
||||
}
|
||||
|
||||
static void prv_battery_state_handler(BatteryChargeState charge) {
|
||||
struct AppData *data = app_state_get_user_data();
|
||||
if (charge.is_plugged && !data->was_plugged) {
|
||||
app_timer_cancel(data->poweroff_timer);
|
||||
} else if (!charge.is_plugged && data->was_plugged) {
|
||||
data->poweroff_timer = app_timer_register(
|
||||
CHARGER_DISCONNECT_TIMEOUT_MS, prv_power_off_timer_expired, NULL);
|
||||
}
|
||||
|
||||
DialogState next_dialog_state = DialogState_Uninitialized;
|
||||
|
||||
if (charge.is_charging) {
|
||||
next_dialog_state = DialogState_Charging;
|
||||
} else if (charge.is_plugged) {
|
||||
next_dialog_state = DialogState_FullyCharged;
|
||||
} else {
|
||||
// Unplugged. We'll be shutting down in a couple seconds if the user doesn't
|
||||
// plug the charger back in, so don't change the dialog.
|
||||
next_dialog_state = data->last_dialog_state;
|
||||
}
|
||||
|
||||
Dialog *dialog = simple_dialog_get_dialog(data->dialog);
|
||||
|
||||
if (next_dialog_state != data->last_dialog_state) {
|
||||
// Setting the dialog icon to itself restarts the animation, which looks
|
||||
// bad, so we want to avoid that if we can help it.
|
||||
switch (next_dialog_state) {
|
||||
case DialogState_FullyCharged:
|
||||
dialog_set_text(dialog, i18n_get("Fully Charged", data));
|
||||
dialog_set_icon(dialog, RESOURCE_ID_BATTERY_ICON_FULL_LARGE_INVERTED);
|
||||
break;
|
||||
case DialogState_Charging:
|
||||
default:
|
||||
dialog_set_text(dialog, i18n_get("Charging", data));
|
||||
dialog_set_icon(dialog, RESOURCE_ID_BATTERY_ICON_CHARGING_LARGE_INVERTED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (charge.is_plugged) {
|
||||
if (charge.is_charging) {
|
||||
status_led_set(StatusLedState_Charging);
|
||||
} else {
|
||||
status_led_set(StatusLedState_FullyCharged);
|
||||
}
|
||||
} else {
|
||||
status_led_set(StatusLedState_Off);
|
||||
}
|
||||
|
||||
data->was_plugged = charge.is_plugged;
|
||||
data->last_dialog_state = next_dialog_state;
|
||||
}
|
||||
|
||||
static void prv_handle_init(void) {
|
||||
struct AppData *data = app_malloc_check(sizeof(struct AppData));
|
||||
*data = (struct AppData){};
|
||||
app_state_set_user_data(data);
|
||||
|
||||
data->dialog = simple_dialog_create(WINDOW_NAME("Shutdown Charging"));
|
||||
Dialog *dialog = simple_dialog_get_dialog(data->dialog);
|
||||
dialog_set_background_color(dialog, GColorBlack);
|
||||
dialog_set_text_color(dialog, GColorWhite);
|
||||
window_set_click_config_provider(&dialog->window, prv_config_provider);
|
||||
|
||||
// The assumption is that this app is launched when the charger is connected
|
||||
// and the shutdown menu item is selected.
|
||||
data->was_plugged = true;
|
||||
data->last_dialog_state = DialogState_Uninitialized;
|
||||
battery_state_service_subscribe(prv_battery_state_handler);
|
||||
// Handle the edge-case where the charger is disconnected between the user
|
||||
// selecting shut down and this app subscribing to battery state events.
|
||||
// Also set the initial battery charge level.
|
||||
prv_battery_state_handler(battery_state_service_peek());
|
||||
|
||||
app_simple_dialog_push(data->dialog);
|
||||
// TODO: have the runlevel machinery disable bluetooth and worker.
|
||||
services_set_runlevel(RunLevel_BareMinimum);
|
||||
worker_manager_disable();
|
||||
}
|
||||
|
||||
|
||||
static void s_main(void) {
|
||||
prv_handle_init();
|
||||
app_event_loop();
|
||||
}
|
||||
|
||||
const PebbleProcessMd* shutdown_charging_get_app_info(void) {
|
||||
static const PebbleProcessMdSystem s_app_md = {
|
||||
.common = {
|
||||
.main_func = s_main,
|
||||
.visibility = ProcessVisibilityHidden,
|
||||
// UUID: 48fa66c4-4e6f-4b32-bf75-a16e12d630c3
|
||||
.uuid = {0x48, 0xfa, 0x66, 0xc4, 0x4e, 0x6f, 0x4b, 0x32,
|
||||
0xbf, 0x75, 0xa1, 0x6e, 0x12, 0xd6, 0x30, 0xc3},
|
||||
},
|
||||
.name = "Shutdown Charging",
|
||||
};
|
||||
return (const PebbleProcessMd*) &s_app_md;
|
||||
}
|
621
src/fw/shell/normal/system_app_registry_list.json
Normal file
621
src/fw/shell/normal/system_app_registry_list.json
Normal file
|
@ -0,0 +1,621 @@
|
|||
{
|
||||
"warning_one": [
|
||||
" 1. DO NOT CHANGE OR REUSE THE ID OF ANY APPLICATION IN THE LIST ",
|
||||
" 2. Read the directions "
|
||||
],
|
||||
"directions": [
|
||||
" - The System Apps are the applications that are actually coded",
|
||||
" into the firmware with static PebbleProcessMd's. ",
|
||||
" - The Resource Apps are the applications that are stored in ",
|
||||
" the resource pack included with the firmware. ",
|
||||
" - The section 'system_apps' only lists the PebbleProcessMd* ",
|
||||
" functions ",
|
||||
" - Resource app entry requires a UUID, then bin_resource_id ",
|
||||
" then an icon_resource_id ",
|
||||
" - To enable only certain applications on a certain ",
|
||||
" add the particular DEFINE variable into the 'ifdefs' field ",
|
||||
" Doing so will add that application to the generated list ",
|
||||
" - To disable applications entirely, add a 'DISABLED' define to",
|
||||
" the list of defines for the application "
|
||||
],
|
||||
"warning_two": [
|
||||
" 1. DO NOT CHANGE OR REUSE THE ID OF ANY APPLICATION IN THE LIST ",
|
||||
" 2. Read the directions "
|
||||
],
|
||||
"system_apps": [
|
||||
{
|
||||
"id": -69,
|
||||
"enum": "TICTOC",
|
||||
"md_fn": "tictoc_get_app_info"
|
||||
},
|
||||
{
|
||||
"id": -98,
|
||||
"enum": "KICKSTART",
|
||||
"md_fn": "kickstart_get_app_info",
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding",
|
||||
"silk",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -2,
|
||||
"enum": "LOW_POWER_FACE",
|
||||
"md_fn": "low_power_face_get_app_info"
|
||||
},
|
||||
{
|
||||
"id": -7,
|
||||
"enum": "SETTINGS",
|
||||
"md_fn": "settings_get_app_info",
|
||||
"color_argb8": "GColorLightGrayARGB8"
|
||||
},
|
||||
{
|
||||
"id": -3,
|
||||
"enum": "MUSIC",
|
||||
"md_fn": "music_app_get_info",
|
||||
"color_argb8": "GColorOrangeARGB8"
|
||||
},
|
||||
{
|
||||
"id": -4,
|
||||
"enum": "NOTIFICATIONS",
|
||||
"md_fn": "notifications_app_get_info",
|
||||
"color_argb8": "GColorSunsetOrangeARGB8"
|
||||
},
|
||||
{
|
||||
"id": -5,
|
||||
"enum": "ALARMS",
|
||||
"md_fn": "alarms_app_get_info",
|
||||
"color_argb8": "GColorJaegerGreenARGB8"
|
||||
},
|
||||
{
|
||||
"id": -6,
|
||||
"enum": "WATCHFACES",
|
||||
"md_fn": "watchfaces_get_app_info",
|
||||
"color_argb8": "GColorJazzberryJamARGB8"
|
||||
},
|
||||
{
|
||||
"id": -9,
|
||||
"enum": "QUICK_LAUNCH_SETUP",
|
||||
"md_fn": "quick_launch_setup_get_app_info"
|
||||
},
|
||||
{
|
||||
"id": -10,
|
||||
"enum": "TIMELINE",
|
||||
"md_fn": "timeline_get_app_info"
|
||||
},
|
||||
{
|
||||
"id": -12,
|
||||
"enum": "BOUNCING_BOX_DEMO",
|
||||
"md_fn": "bouncing_box_demo_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"],
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -13,
|
||||
"enum": "PEBBLE_COLORS",
|
||||
"md_fn": "pebble_colors_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -14,
|
||||
"enum": "PEBBLE_SHAPES",
|
||||
"md_fn": "pebble_shapes_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -17,
|
||||
"enum": "MOVABLE_LINE",
|
||||
"md_fn": "movable_line_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -18,
|
||||
"enum": "GFX_TESTS",
|
||||
"md_fn": "gfx_tests_get_app_info",
|
||||
"ifdefs": ["PERFORMANCE_TESTS"]
|
||||
},
|
||||
{
|
||||
"id": -19,
|
||||
"enum": "TEST_ARGS_RECEIVER",
|
||||
"md_fn": "test_args_receiver_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -20,
|
||||
"enum": "TEST_ARGS_SENDER",
|
||||
"md_fn": "test_args_sender_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -21,
|
||||
"enum": "FLASH_DIAGNOSTIC",
|
||||
"md_fn": "flash_diagnostic_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -22,
|
||||
"enum": "FS_RESOURCES",
|
||||
"md_fn": "fs_resources_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -23,
|
||||
"enum": "EXIT",
|
||||
"md_fn": "exit_app_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -25,
|
||||
"enum": "APP_HEAP_DEMO",
|
||||
"md_fn": "app_heap_demo_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -26,
|
||||
"enum": "DATA_LOGGING_TEST",
|
||||
"md_fn": "data_logging_test_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -27,
|
||||
"enum": "KILL_BT",
|
||||
"md_fn": "kill_bt_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -28,
|
||||
"enum": "TRIGGER_ALARM",
|
||||
"md_fn": "trigger_alarm_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -29,
|
||||
"enum": "ANIMATED_DEMO",
|
||||
"md_fn": "animated_demo_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -30,
|
||||
"enum": "GRENADE_LAUNCHER",
|
||||
"md_fn": "grenade_launcher_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -31,
|
||||
"enum": "TEXT_LAYOUT",
|
||||
"md_fn": "text_layout_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -33,
|
||||
"enum": "MENU",
|
||||
"md_fn": "menu_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -34,
|
||||
"enum": "SIMPLE_MENU",
|
||||
"md_fn": "simple_menu_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -35,
|
||||
"enum": "SCROLL",
|
||||
"md_fn": "scroll_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -36,
|
||||
"enum": "CLICK",
|
||||
"md_fn": "click_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -37,
|
||||
"enum": "PROGRESS",
|
||||
"md_fn": "progress_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -38,
|
||||
"enum": "NUMBER_FIELD",
|
||||
"md_fn": "number_field_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -41,
|
||||
"enum": "EVENT_SERVICE",
|
||||
"md_fn": "event_service_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -43,
|
||||
"enum": "PERSIST",
|
||||
"md_fn": "persist_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -44,
|
||||
"enum": "TIMER",
|
||||
"md_fn": "timer_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -45,
|
||||
"enum": "TEST_SYS_TIMER",
|
||||
"md_fn": "test_sys_timer_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -46,
|
||||
"enum": "TEST_CORE_DUMP",
|
||||
"md_fn": "test_core_dump_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -47,
|
||||
"enum": "FLASH_PROF",
|
||||
"md_fn": "flash_prof_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -48,
|
||||
"enum": "TEST_BLUETOOTH",
|
||||
"md_fn": "test_bluetooth_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -50,
|
||||
"enum": "VIBE_AND_LOGS",
|
||||
"md_fn": "vibe_and_logs_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -51,
|
||||
"enum": "MENU_OVERFLOW",
|
||||
"md_fn": "menu_overflow_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -54,
|
||||
"enum": "LAUNCHER_MENU",
|
||||
"md_fn": "launcher_menu_app_get_app_info"
|
||||
},
|
||||
{
|
||||
"id": -55,
|
||||
"enum": "TEXT_CLIPPING",
|
||||
"md_fn": "text_clipping_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -56,
|
||||
"enum": "LIGHT_CONFIG",
|
||||
"md_fn": "light_config_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -57,
|
||||
"enum": "STROKE_WIDTH",
|
||||
"md_fn": "stroke_width_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"],
|
||||
"target_platforms": [
|
||||
"snowy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -58,
|
||||
"enum": "AMB_LIGHT_READ",
|
||||
"md_fn": "ambient_light_reading_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -59,
|
||||
"enum": "WEATHER",
|
||||
"md_fn": "weather_app_get_info",
|
||||
"color_argb8": "GColorBlueMoonARGB8",
|
||||
"target_platforms":[
|
||||
"snowy",
|
||||
"spalding",
|
||||
"silk",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -95,
|
||||
"enum": "WORKOUT",
|
||||
"md_fn": "workout_app_get_info",
|
||||
"color_argb8": "GColorYellowARGB8",
|
||||
"target_platforms": [
|
||||
"silk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -60,
|
||||
"enum": "SHUTDOWN_CHARGING",
|
||||
"md_fn": "shutdown_charging_get_app_info",
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -61,
|
||||
"enum": "SWAP_LAYER",
|
||||
"md_fn": "swap_layer_demo_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -62,
|
||||
"enum": "BATTERY_CRITICAL",
|
||||
"md_fn": "battery_critical_get_app_info"
|
||||
},
|
||||
{
|
||||
"id": -63,
|
||||
"enum": "TEXT_SPACING",
|
||||
"md_fn": "text_spacing_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -64,
|
||||
"enum": "KINO_LAYER",
|
||||
"md_fn": "kino_layer_demo_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -65,
|
||||
"enum": "DIALOGS",
|
||||
"md_fn": "dialogs_demo_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -66,
|
||||
"enum": "STATUSBAR",
|
||||
"md_fn": "statusbar_demo_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -68,
|
||||
"enum": "MORPH_SQUARE",
|
||||
"md_fn": "morph_square_demo_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -70,
|
||||
"enum": "MENU_RIGHT_ICON",
|
||||
"md_fn": "menu_layer_right_icon_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -71,
|
||||
"enum": "VIBE_STRENGTH",
|
||||
"md_fn": "vibe_strength_demo_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -72,
|
||||
"enum": "ACTION_MENU",
|
||||
"md_fn": "action_menu_demo_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -73,
|
||||
"enum": "OPTION_MENU",
|
||||
"md_fn": "option_menu_demo_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -74,
|
||||
"enum": "PROFILE_MUTEXES",
|
||||
"md_fn": "profile_mutexes_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS", "DISABLED"]
|
||||
},
|
||||
{
|
||||
"id": -75,
|
||||
"enum": "DEADLOCK",
|
||||
"md_fn": "deadlock_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -76,
|
||||
"enum": "TIMELINE_PINS",
|
||||
"md_fn": "timeline_pins_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -77,
|
||||
"enum": "TEXT_FLOW",
|
||||
"md_fn": "text_flow_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -78,
|
||||
"enum": "MENU_ROUND",
|
||||
"md_fn": "menu_round_app_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"],
|
||||
"target_platforms": [
|
||||
"spalding"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -79,
|
||||
"enum": "ACTIVITY_DEMO",
|
||||
"md_fn": "activity_demo_get_app_info",
|
||||
"ifdefs": ["SHOW_ACTIVITY_DEMO"],
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding",
|
||||
"silk",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -80,
|
||||
"enum": "ACTIVITY_TEST",
|
||||
"md_fn": "activity_test_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"],
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding",
|
||||
"silk",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -81,
|
||||
"enum": "DOUBLE_TAP_TEST",
|
||||
"md_fn": "double_tap_test_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"]
|
||||
},
|
||||
{
|
||||
"id": -82,
|
||||
"enum": "HEALTH_APP",
|
||||
"md_fn": "health_app_get_info",
|
||||
"color_argb8": "GColorSunsetOrangeARGB8",
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding",
|
||||
"silk",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -83,
|
||||
"enum": "SEND_TEXT",
|
||||
"md_fn": "send_text_app_get_info",
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding",
|
||||
"silk",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -84,
|
||||
"enum": "VIBE_SCORE",
|
||||
"md_fn": "vibe_score_demo_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"],
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -86,
|
||||
"enum": "IDL",
|
||||
"md_fn": "idl_demo_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"],
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -87,
|
||||
"enum": "TEMPERATURE_DEMO",
|
||||
"md_fn": "temperature_demo_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"],
|
||||
"target_platforms": [
|
||||
"snowy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -88,
|
||||
"enum": "GDRAWMASK_DEMO",
|
||||
"md_fn": "gdrawmask_demo_get_app_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"],
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -90,
|
||||
"enum": "REMINDERS",
|
||||
"md_fn": "reminder_app_get_info",
|
||||
"color_argb8": "GColorChromeYellowARGB8",
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding",
|
||||
"silk",
|
||||
"robert"
|
||||
],
|
||||
"ifdefs": ["CAPABILITY_HAS_MICROPHONE=1"]
|
||||
},
|
||||
{
|
||||
"id": -91,
|
||||
"enum": "MPU_TEST",
|
||||
"md_fn": "test_mpu_cache_get_info",
|
||||
"ifdefs": ["ENABLE_TEST_APPS"],
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding",
|
||||
"silk",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -92,
|
||||
"enum": "QUIET_TIME_TOGGLE",
|
||||
"md_fn": "quiet_time_toggle_get_app_info",
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding",
|
||||
"silk",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -94,
|
||||
"enum": "MOTION_BACKLIGHT_TOGGLE",
|
||||
"md_fn": "motion_backlight_toggle_get_app_info",
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding",
|
||||
"silk",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -93,
|
||||
"enum": "AIRPLANE_MODE_TOGGLE",
|
||||
"md_fn": "airplane_mode_toggle_get_app_info",
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding",
|
||||
"silk",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -96,
|
||||
"enum": "TIMELINE_PAST",
|
||||
"md_fn": "timeline_past_get_app_info",
|
||||
"target_platforms": [
|
||||
"snowy",
|
||||
"spalding",
|
||||
"silk",
|
||||
"robert"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": -97,
|
||||
"enum": "SPORTS",
|
||||
"md_fn": "sports_app_get_info"
|
||||
}
|
||||
],
|
||||
"resource_apps": [
|
||||
{
|
||||
"id": -52,
|
||||
"enum": "GOLF",
|
||||
"name": "Golf",
|
||||
"uuid": "cf1e816a-9db0-4511-bbb8-f60c48ca8fac",
|
||||
"bin_resource_id": "RESOURCE_ID_STORED_APP_GOLF",
|
||||
"icon_resource_id": "DEFAULT_MENU_ICON"
|
||||
}
|
||||
]
|
||||
}
|
133
src/fw/shell/normal/system_app_state_machine.c
Normal file
133
src/fw/shell/normal/system_app_state_machine.c
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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/system_app_state_machine.h"
|
||||
|
||||
#include "apps/core_apps/panic_window_app.h"
|
||||
#include "apps/system_apps/battery_critical_app.h"
|
||||
#include "apps/system_app_ids.h"
|
||||
#include "apps/system_apps/launcher/launcher_app.h"
|
||||
#include "apps/watch/low_power/low_power_face.h"
|
||||
#include "shell/normal/watchface.h"
|
||||
#include "kernel/low_power.h"
|
||||
#include "kernel/panic.h"
|
||||
#include "resource/resource.h"
|
||||
#include "services/common/battery/battery_monitor.h"
|
||||
#include "system/bootbits.h"
|
||||
#include "system/logging.h"
|
||||
#include "process_management/app_manager.h"
|
||||
|
||||
//! @file system_app_state_machine.c
|
||||
//!
|
||||
//! This file implements our app to app flow that makes up our normal shell. It defines
|
||||
//! which app first runs at start up and what app should be launched to replace the current
|
||||
//! app if the current app wants to close.
|
||||
//!
|
||||
//! The logic for which app should replace closing apps is a little tricky. Apps can be launched
|
||||
//! in various ways, either due to direct user interaction (selecting an app in the launcher) or
|
||||
//! through the phone app using pebble protocol (for example, a new app being installed or a
|
||||
//! companion app launching its watchapp in response to an event). What we want to happen is
|
||||
//! the user can then close that app and end up in a rough approximation of where they came from.
|
||||
//!
|
||||
//! The way we implement this is by having two apps that make up roots of the graph. If you're
|
||||
//! in the launcher and you launch an app, closing that app will return to the launcher. If you
|
||||
//! attempt to nest further (you launch an app from the launcher and that app in turn launches
|
||||
//! another app), closing any app will still return you to the launcher. This is done to prevent
|
||||
//! the stack from growing too deep and having to exit a ton of apps to get back to where you want.
|
||||
//! The watchface is also a root (closing an app that launched while you were in a watchface
|
||||
//! will return to you to the watchface). Finally, closing the launcher will return you to the
|
||||
//! watchface, and closing the watchface (either by pressing select or the watchface crashing)
|
||||
//! should take you to the launcher.
|
||||
//!
|
||||
//! Launching any watchface for any reason will put you in the "root watchface" state.
|
||||
//!
|
||||
//! Below is a pretty ASCII picture to describe the states we can be in. What happens when you
|
||||
//! close an app is illustrated with the arrow with the X.
|
||||
//!
|
||||
//! +---------------------+----+ +-------------------------+-----+
|
||||
//! | Remote Launched App | | | Remote Launched App | |
|
||||
//! +---------------+-----+ <--+ | Launcher Launched App | <---+
|
||||
//! X +---------------+---------+
|
||||
//! ^ | X
|
||||
//! | v ^ |
|
||||
//! | | v
|
||||
//! +----+----------------+ +X-----> +------+------------------+
|
||||
//! | Watchface | | Launcher |
|
||||
//! +---------------------+ <-----X+ +-------------------------+
|
||||
//!
|
||||
|
||||
//! As per the above block comment, are we currently rooted in the watchface stack or the
|
||||
//! launcher stack?
|
||||
static bool s_rooted_in_watchface = false;
|
||||
|
||||
const PebbleProcessMd* system_app_state_machine_system_start(void) {
|
||||
// start critical battery app when necessary
|
||||
if (battery_monitor_critical_lockout()) {
|
||||
return battery_critical_get_app_info();
|
||||
}
|
||||
|
||||
if (low_power_is_active()) {
|
||||
return low_power_face_get_app_info();
|
||||
}
|
||||
|
||||
if (launcher_panic_get_current_error() != 0) {
|
||||
return panic_app_get_app_info();
|
||||
}
|
||||
|
||||
return launcher_menu_app_get_app_info();
|
||||
}
|
||||
|
||||
//! @return True if the currently running app is an installed watchface
|
||||
static bool prv_current_app_is_watchface(void) {
|
||||
return app_install_is_watchface(app_manager_get_current_app_id());
|
||||
}
|
||||
|
||||
AppInstallId system_app_state_machine_get_last_registered_app(void) {
|
||||
// If we're rooted in the watchface but we're not the watchface itself, or the launcher
|
||||
// is closing, we should launch the watchface.
|
||||
if ((s_rooted_in_watchface && !prv_current_app_is_watchface())
|
||||
|| (app_manager_get_current_app_md() == launcher_menu_app_get_app_info())) {
|
||||
return watchface_get_default_install_id();
|
||||
}
|
||||
|
||||
return APP_ID_LAUNCHER_MENU;
|
||||
}
|
||||
|
||||
const PebbleProcessMd* system_app_state_machine_get_default_app(void) {
|
||||
return launcher_menu_app_get_app_info();
|
||||
}
|
||||
|
||||
void system_app_state_machine_register_app_launch(AppInstallId app_id) {
|
||||
if (app_id == APP_ID_LAUNCHER_MENU) {
|
||||
s_rooted_in_watchface = false;
|
||||
} else if (app_install_is_watchface(app_id)) {
|
||||
s_rooted_in_watchface = true;
|
||||
}
|
||||
|
||||
// Other app launches don't modify our root so just ignore them.
|
||||
}
|
||||
|
||||
void system_app_state_machine_panic(void) {
|
||||
if (app_manager_is_initialized()) {
|
||||
app_manager_launch_new_app(&(AppLaunchConfig) {
|
||||
.md = panic_app_get_app_info(),
|
||||
});
|
||||
}
|
||||
|
||||
// Else, just wait for the app_manager to initialize to show the panic app using
|
||||
// system_app_state_machine_system_start().
|
||||
}
|
||||
|
202
src/fw/shell/normal/watchface.c
Normal file
202
src/fw/shell/normal/watchface.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* 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 "watchface.h"
|
||||
|
||||
#include "apps/system_app_ids.h"
|
||||
#include "apps/system_apps/launcher/launcher_app.h"
|
||||
#include "apps/system_apps/settings/settings_quick_launch.h"
|
||||
#include "apps/system_apps/settings/settings_quick_launch_app_menu.h"
|
||||
#include "apps/system_apps/settings/settings_quick_launch_setup_menu.h"
|
||||
#include "apps/system_apps/timeline/timeline.h"
|
||||
#include "apps/watch/low_power/low_power_face.h"
|
||||
#include "kernel/event_loop.h"
|
||||
#include "kernel/low_power.h"
|
||||
#include "kernel/ui/modals/modal_manager.h"
|
||||
#include "popups/timeline/peek.h"
|
||||
#include "process_management/app_manager.h"
|
||||
#include "process_management/pebble_process_md.h"
|
||||
#include "services/common/analytics/analytics.h"
|
||||
#include "services/common/compositor/compositor_transitions.h"
|
||||
#include "services/normal/notifications/do_not_disturb.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
#define QUICK_LAUNCH_HOLD_MS (400)
|
||||
|
||||
static ClickManager s_click_manager;
|
||||
|
||||
static bool prv_should_ignore_button_click(void) {
|
||||
if (app_manager_get_task_context()->closing_state != ProcessRunState_Running) {
|
||||
// Ignore if the app is not running (such as if it is in the process of closing)
|
||||
return true;
|
||||
}
|
||||
if (low_power_is_active()) {
|
||||
// If we're in low power mode we dont allow any interaction
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void prv_launch_app_via_button(AppLaunchEventConfig *config,
|
||||
ClickRecognizerRef recognizer) {
|
||||
config->common.button = click_recognizer_get_button_id(recognizer);
|
||||
app_manager_put_launch_app_event(config);
|
||||
}
|
||||
|
||||
static void prv_quick_launch_handler(ClickRecognizerRef recognizer, void *data) {
|
||||
ButtonId button = click_recognizer_get_button_id(recognizer);
|
||||
if (!quick_launch_is_enabled(button)) {
|
||||
return;
|
||||
}
|
||||
AppInstallId app_id = quick_launch_get_app(button);
|
||||
if (app_id == INSTALL_ID_INVALID) {
|
||||
app_id = app_install_get_id_for_uuid(&quick_launch_setup_get_app_info()->uuid);
|
||||
}
|
||||
prv_launch_app_via_button(&(AppLaunchEventConfig) {
|
||||
.id = app_id,
|
||||
.common.reason = APP_LAUNCH_QUICK_LAUNCH,
|
||||
}, recognizer);
|
||||
}
|
||||
|
||||
static void prv_launch_timeline(ClickRecognizerRef recognizer, void *data) {
|
||||
static TimelineArgs s_timeline_args;
|
||||
const bool is_up = (click_recognizer_get_button_id(recognizer) == BUTTON_ID_UP);
|
||||
if (is_up) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Launching timeline in past mode.");
|
||||
s_timeline_args.direction = TimelineIterDirectionPast;
|
||||
analytics_inc(ANALYTICS_DEVICE_METRIC_TIMELINE_PAST_LAUNCH_COUNT, AnalyticsClient_System);
|
||||
} else {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Launching timeline in future mode.");
|
||||
s_timeline_args.direction = TimelineIterDirectionFuture;
|
||||
analytics_inc(ANALYTICS_DEVICE_METRIC_TIMELINE_FUTURE_LAUNCH_COUNT, AnalyticsClient_System);
|
||||
}
|
||||
s_timeline_args.launch_into_pin = true;
|
||||
s_timeline_args.stay_in_list_view = true;
|
||||
timeline_peek_get_item_id(&s_timeline_args.pin_id);
|
||||
|
||||
const CompositorTransition *animation = NULL;
|
||||
const bool is_future = (s_timeline_args.direction == TimelineIterDirectionFuture);
|
||||
const bool timeline_is_destination = true;
|
||||
#if PBL_ROUND
|
||||
animation = compositor_dot_transition_timeline_get(is_future, timeline_is_destination);
|
||||
#else
|
||||
const bool jump = (!uuid_is_invalid(&s_timeline_args.pin_id) && !timeline_peek_is_first_event());
|
||||
animation = jump ? compositor_peek_transition_timeline_get() :
|
||||
compositor_slide_transition_timeline_get(is_future, timeline_is_destination,
|
||||
timeline_peek_is_future_empty());
|
||||
#endif
|
||||
prv_launch_app_via_button(&(AppLaunchEventConfig) {
|
||||
.id = APP_ID_TIMELINE,
|
||||
.common.args = &s_timeline_args,
|
||||
.common.transition = animation,
|
||||
}, recognizer);
|
||||
}
|
||||
|
||||
static void prv_configure_click_handler(ButtonId button_id, ClickHandler single_click_handler) {
|
||||
ClickConfig *cfg = &s_click_manager.recognizers[button_id].config;
|
||||
cfg->long_click.delay_ms = QUICK_LAUNCH_HOLD_MS;
|
||||
cfg->long_click.handler = prv_quick_launch_handler;
|
||||
cfg->click.handler = single_click_handler;
|
||||
}
|
||||
|
||||
static void prv_launch_launcher_app(ClickRecognizerRef recognizer, void *data) {
|
||||
static const LauncherMenuArgs s_launcher_args = { .reset_scroll = true };
|
||||
prv_launch_app_via_button(&(AppLaunchEventConfig) {
|
||||
.id = APP_ID_LAUNCHER_MENU,
|
||||
.common.args = &s_launcher_args,
|
||||
}, recognizer);
|
||||
}
|
||||
|
||||
#if CAPABILITY_HAS_CORE_NAVIGATION4
|
||||
static void prv_launch_health_app(ClickRecognizerRef recognizer, void *data) {
|
||||
prv_launch_app_via_button(&(AppLaunchEventConfig) {
|
||||
.id = APP_ID_HEALTH_APP,
|
||||
}, recognizer);
|
||||
}
|
||||
#endif // CAPABILITY_HAS_CORE_NAVIGATION4
|
||||
|
||||
static ClickHandler prv_get_up_click_handler(void) {
|
||||
#if CAPABILITY_HAS_CORE_NAVIGATION4
|
||||
return prv_launch_health_app;
|
||||
#else
|
||||
return prv_launch_timeline;
|
||||
#endif // CAPABILITY_HAS_CORE_NAVIGATION4
|
||||
}
|
||||
|
||||
static void prv_dismiss_timeline_peek(ClickRecognizerRef recognizer, void *data) {
|
||||
timeline_peek_dismiss();
|
||||
}
|
||||
|
||||
static void prv_watchface_configure_click_handlers(void) {
|
||||
prv_configure_click_handler(BUTTON_ID_UP, prv_get_up_click_handler());
|
||||
prv_configure_click_handler(BUTTON_ID_DOWN, prv_launch_timeline);
|
||||
prv_configure_click_handler(BUTTON_ID_SELECT, prv_launch_launcher_app);
|
||||
prv_configure_click_handler(BUTTON_ID_BACK, prv_dismiss_timeline_peek);
|
||||
}
|
||||
|
||||
void watchface_init(void) {
|
||||
click_manager_init(&s_click_manager);
|
||||
prv_watchface_configure_click_handlers();
|
||||
}
|
||||
|
||||
void watchface_handle_button_event(PebbleEvent *e) {
|
||||
if (prv_should_ignore_button_click()) {
|
||||
return;
|
||||
}
|
||||
switch (e->type) {
|
||||
case PEBBLE_BUTTON_DOWN_EVENT:
|
||||
click_recognizer_handle_button_down(&s_click_manager.recognizers[e->button.button_id]);
|
||||
break;
|
||||
case PEBBLE_BUTTON_UP_EVENT:
|
||||
click_recognizer_handle_button_up(&s_click_manager.recognizers[e->button.button_id]);
|
||||
break;
|
||||
default:
|
||||
PBL_CROAK("Invalid event type: %u", e->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_watchface_launch_low_power(void) {
|
||||
PBL_LOG(LOG_LEVEL_DEBUG, "Switching default watchface to low_power_mode watchface");
|
||||
app_manager_put_launch_app_event(&(AppLaunchEventConfig) {
|
||||
.id = APP_ID_LOW_POWER_FACE,
|
||||
});
|
||||
}
|
||||
|
||||
void watchface_launch_default(const CompositorTransition *animation) {
|
||||
app_manager_put_launch_app_event(&(AppLaunchEventConfig) {
|
||||
.id = watchface_get_default_install_id(),
|
||||
.common.transition = animation,
|
||||
});
|
||||
}
|
||||
|
||||
static void kernel_callback_watchface_launch(void* data) {
|
||||
watchface_launch_default(NULL);
|
||||
}
|
||||
|
||||
void command_watch(void) {
|
||||
launcher_task_add_callback(kernel_callback_watchface_launch, NULL);
|
||||
}
|
||||
|
||||
void watchface_start_low_power(void) {
|
||||
app_manager_set_minimum_run_level(ProcessAppRunLevelNormal);
|
||||
prv_watchface_launch_low_power();
|
||||
}
|
||||
|
||||
void watchface_reset_click_manager(void) {
|
||||
click_manager_reset(&s_click_manager);
|
||||
}
|
35
src/fw/shell/normal/watchface.h
Normal file
35
src/fw/shell/normal/watchface.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 "kernel/events.h"
|
||||
#include "process_management/app_install_manager.h"
|
||||
#include "services/common/compositor/compositor.h"
|
||||
|
||||
void watchface_init(void);
|
||||
|
||||
void watchface_handle_button_event(PebbleEvent *e);
|
||||
|
||||
void watchface_set_default_install_id(AppInstallId id);
|
||||
|
||||
AppInstallId watchface_get_default_install_id(void);
|
||||
|
||||
void watchface_launch_default(const CompositorTransition *animation);
|
||||
|
||||
void watchface_start_low_power(void);
|
||||
|
||||
void watchface_reset_click_manager(void);
|
89
src/fw/shell/normal/welcome.c
Normal file
89
src/fw/shell/normal/welcome.c
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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 "welcome.h"
|
||||
|
||||
#include "kernel/event_loop.h"
|
||||
#include "resource/timeline_resource_ids.auto.h"
|
||||
#include "services/common/evented_timer.h"
|
||||
#include "services/common/i18n/i18n.h"
|
||||
#include "services/common/shared_prf_storage/shared_prf_storage.h"
|
||||
#include "shell/prefs.h"
|
||||
#include "system/logging.h"
|
||||
#include "util/attributes.h"
|
||||
#include "util/size.h"
|
||||
|
||||
static void prv_push_welcome_notification(void *UNUSED data) {
|
||||
AttributeList notif_attr_list = {};
|
||||
attribute_list_add_uint32(¬if_attr_list, AttributeIdIconTiny,
|
||||
TIMELINE_RESOURCE_NOTIFICATION_FLAG);
|
||||
attribute_list_add_cstring(¬if_attr_list, AttributeIdTitle,
|
||||
/// Welcome title text welcoming a 3.x user to 4.x
|
||||
i18n_get("Pebble Updated!", ¬if_attr_list));
|
||||
/// Welcome body text welcoming a 3.x user to 4.x.
|
||||
const char *welcome_text = i18n_get(
|
||||
"For activity and sleep tracking, press up from your watch face.\n\n"
|
||||
"Press down for current and future events.\n\n"
|
||||
"Read more at blog.pebble.com",
|
||||
¬if_attr_list);
|
||||
attribute_list_add_cstring(¬if_attr_list, AttributeIdBody, welcome_text);
|
||||
attribute_list_add_uint8(¬if_attr_list, AttributeIdBgColor, GColorOrangeARGB8);
|
||||
|
||||
AttributeList dismiss_action_attr_list = {};
|
||||
attribute_list_add_cstring(&dismiss_action_attr_list, AttributeIdTitle,
|
||||
i18n_get("Dismiss", ¬if_attr_list));
|
||||
|
||||
int action_id = 0;
|
||||
TimelineItemAction actions[] = {
|
||||
{
|
||||
.id = action_id++,
|
||||
.type = TimelineItemActionTypeDismiss,
|
||||
.attr_list = dismiss_action_attr_list,
|
||||
},
|
||||
};
|
||||
TimelineItemActionGroup action_group = {
|
||||
.num_actions = ARRAY_LENGTH(actions),
|
||||
.actions = actions,
|
||||
};
|
||||
|
||||
const time_t now = rtc_get_time();
|
||||
TimelineItem *item = timeline_item_create_with_attributes(
|
||||
now, 0, TimelineItemTypeNotification, LayoutIdNotification, ¬if_attr_list, &action_group);
|
||||
i18n_free_all(¬if_attr_list);
|
||||
attribute_list_destroy_list(¬if_attr_list);
|
||||
attribute_list_destroy_list(&dismiss_action_attr_list);
|
||||
|
||||
if (!item) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Failed to welcome the user.");
|
||||
return;
|
||||
}
|
||||
|
||||
item->header.from_watch = true;
|
||||
notifications_add_notification(item);
|
||||
timeline_item_destroy(item);
|
||||
welcome_set_welcome_version(WelcomeVersionCurrent);
|
||||
}
|
||||
|
||||
void welcome_push_notification(bool factory_reset_or_first_use) {
|
||||
const WelcomeVersion version = welcome_get_welcome_version();
|
||||
// This check only works if it is called before getting started complete is set
|
||||
if (!factory_reset_or_first_use && (version < WelcomeVersion_4xNormalFirmware)) {
|
||||
// This has completed getting started on a previous normal firmware, welcome them if the
|
||||
// version is before 4.x
|
||||
// We wait some time since notification storage takes time to initialize
|
||||
launcher_task_add_callback(prv_push_welcome_notification, NULL);
|
||||
}
|
||||
}
|
44
src/fw/shell/normal/welcome.h
Normal file
44
src/fw/shell/normal/welcome.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//! Version of the welcoming of the user to the normal firmware
|
||||
typedef enum WelcomeVersion {
|
||||
//! Initial version or never launched normal firmware
|
||||
WelcomeVersion_InitialVersion = 0,
|
||||
//! 4.x Normal Firmware
|
||||
WelcomeVersion_4xNormalFirmware = 1,
|
||||
|
||||
WelcomeVersionCount,
|
||||
//! WelcomeVersion is an increasing version number. WelcomeVersionCurrent must
|
||||
//! not decrement. This should ensure that the current version is always the latest.
|
||||
WelcomeVersionCurrent = WelcomeVersionCount - 1,
|
||||
} WelcomeVersion;
|
||||
|
||||
//! Welcomes the user to a newer normal firmware they have not used yet if they have used an older
|
||||
//! normal firmware and the newer normal firmware warrants a notification.
|
||||
//! @note This must be called before getting started completed is set in shared prf storage.
|
||||
void welcome_push_notification(bool factory_reset_or_first_use);
|
||||
|
||||
//! Set the welcome version. This is persisted in shell prefs.
|
||||
void welcome_set_welcome_version(uint8_t version);
|
||||
|
||||
//! Get the welcome version
|
||||
uint8_t welcome_get_welcome_version(void);
|
117
src/fw/shell/prefs.h
Normal file
117
src/fw/shell/prefs.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
// Shell Preferences
|
||||
//
|
||||
// These are preferences which must be available for querying across all shells
|
||||
// and which must be implemented differently depending on the shell compiled in.
|
||||
// Only preferences which are used by common services and kernel code meet these
|
||||
// criteria.
|
||||
//
|
||||
// NEW PREFERENCES DO __NOT__ BELONG HERE WITHOUT A VERY GOOD REASON.
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
#include "process_management/app_install_types.h"
|
||||
#include "shell/system_theme.h"
|
||||
#include "util/uuid.h"
|
||||
|
||||
|
||||
// The clock 12h/24h setting is required by services/common/clock.c.
|
||||
bool shell_prefs_get_clock_24h_style(void);
|
||||
void shell_prefs_set_clock_24h_style(bool is24h);
|
||||
|
||||
// The timezone source setting is required by services/common/clock.c.
|
||||
// When the source is manual, we don't override our timezone with the phone's timezone info
|
||||
bool shell_prefs_is_timezone_source_manual(void);
|
||||
void shell_prefs_set_timezone_source_manual(bool manual);
|
||||
|
||||
// The timezone id setting is required by services/common/clock.c.
|
||||
// The automatic timezone id is what we get from the phone
|
||||
int16_t shell_prefs_get_automatic_timezone_id(void);
|
||||
void shell_prefs_set_automatic_timezone_id(int16_t timezone_id);
|
||||
|
||||
// Preferences for choosing the units that are displayed in various places in the UI
|
||||
typedef enum UnitsDistance {
|
||||
UnitsDistance_KM,
|
||||
UnitsDistance_Miles,
|
||||
UnitsDistanceCount
|
||||
} UnitsDistance;
|
||||
|
||||
UnitsDistance shell_prefs_get_units_distance(void);
|
||||
void shell_prefs_set_units_distance(UnitsDistance newUnit);
|
||||
|
||||
// The backlight preferences are required in all shells, but the settings are
|
||||
// hardcoded when running PRF.
|
||||
|
||||
// The backlight behaviour enum value is used by the light service analytics.
|
||||
// This type has been deprecated for any other use, replaced by the enabled
|
||||
// and ambient_sensor_enabled booleans.
|
||||
typedef enum BacklightBehaviour {
|
||||
BacklightBehaviour_On = 0,
|
||||
BacklightBehaviour_Off = 1,
|
||||
BacklightBehaviour_Auto = 2,
|
||||
NumBacklightBehaviours
|
||||
} BacklightBehaviour;
|
||||
|
||||
bool backlight_is_enabled(void);
|
||||
void backlight_set_enabled(bool enabled);
|
||||
|
||||
bool backlight_is_ambient_sensor_enabled(void);
|
||||
void backlight_set_ambient_sensor_enabled(bool enabled);
|
||||
|
||||
#define DEFAULT_BACKLIGHT_TIMEOUT_MS 3000
|
||||
uint32_t backlight_get_timeout_ms(void);
|
||||
void backlight_set_timeout_ms(uint32_t timeout_ms);
|
||||
|
||||
uint16_t backlight_get_intensity(void);
|
||||
|
||||
uint8_t backlight_get_intensity_percent(void);
|
||||
void backlight_set_intensity_percent(uint8_t intensity_percent);
|
||||
|
||||
// The backlight motion enabled setting is used by the kernel event loop.
|
||||
bool backlight_is_motion_enabled(void);
|
||||
void backlight_set_motion_enabled(bool enable);
|
||||
|
||||
// Stationary mode will put the watch in a low power state. Disabling will
|
||||
// prevent the watch from turning off any features.
|
||||
bool shell_prefs_get_stationary_enabled(void);
|
||||
void shell_prefs_set_stationary_enabled(bool enabled);
|
||||
|
||||
// The default worker setting is used by process_management.
|
||||
AppInstallId worker_preferences_get_default_worker(void);
|
||||
void worker_preferences_set_default_worker(AppInstallId id);
|
||||
|
||||
bool shell_prefs_get_language_english(void);
|
||||
void shell_prefs_set_language_english(bool english);
|
||||
void shell_prefs_toggle_language_english(void);
|
||||
|
||||
// Manage display offset
|
||||
void shell_prefs_set_display_offset(GPoint offset);
|
||||
GPoint shell_prefs_get_display_offset(void);
|
||||
void shell_prefs_display_offset_init(void);
|
||||
bool shell_prefs_should_prompt_display_calibration(void);
|
||||
void shell_prefs_set_should_prompt_display_calibration(bool should_prompt);
|
||||
|
||||
uint8_t timeline_prefs_get_settings_opened(void);
|
||||
void timeline_prefs_set_settings_opened(uint8_t version);
|
||||
void timeline_peek_prefs_set_enabled(bool enabled);
|
||||
bool timeline_peek_prefs_get_enabled(void);
|
||||
void timeline_peek_prefs_set_before_time(uint16_t before_time_m);
|
||||
uint16_t timeline_peek_prefs_get_before_time(void);
|
57
src/fw/shell/prefs_private.h
Normal file
57
src/fw/shell/prefs_private.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 "flash_region/flash_region.h"
|
||||
#include "kernel/events.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define SHELL_PREFS_FILE_NAME "shellpref"
|
||||
// TODO: PBL-42170 Improve settings file to better utilize large sector sizes
|
||||
// We want to use a full page, but requesting such a size can end up using two pages
|
||||
#define SHELL_PREFS_FILE_LEN (2048)
|
||||
|
||||
|
||||
//! Update the backing store for the given preference.
|
||||
//! @param[in] key the preference's name, as defined in prefs.c
|
||||
//! @param[in] key_len the length of key
|
||||
//! @param[in] value pointer to the new value
|
||||
//! @param[in] length of the value
|
||||
//! @return true on success, false if failure
|
||||
bool prefs_private_write_backing(const uint8_t *key, size_t key_len, const void *value,
|
||||
int value_len);
|
||||
|
||||
//! Get the length of a preference's value as stored in the backing store
|
||||
//! @param[in] key the preference's name, as defined in prefs.c
|
||||
//! @param[in] key_len the length of key
|
||||
//! @return the >0 length upon success, or 0 if the preference was not found
|
||||
int prefs_private_get_backing_len(const uint8_t *key, size_t key_len);
|
||||
|
||||
//! Read the value of a preference from the backing store.
|
||||
//! @param[in] key the preference's name, as defined in prefs.c
|
||||
//! @param[in] key_len the length of key
|
||||
//! @param[out] value the value will be written into this pointer
|
||||
//! @param[in] length of the value
|
||||
//! @return true on success, false if failure
|
||||
bool prefs_private_read_backing(const uint8_t *key, size_t key_len, void *value, int value_len);
|
||||
|
||||
//! Process a blobDB event issued for the prefs DB (BlobDBIdPrefs). For BlobDBEventTypeInsert
|
||||
//! events, this method will update the internal global copy of that preference based on the
|
||||
//! new value that was placed into the backing store.
|
||||
//! @param[in] event pointer to the blob DB event
|
||||
void prefs_private_handle_blob_db_event(PebbleBlobDBEvent *event);
|
23
src/fw/shell/prefs_syscalls.c
Normal file
23
src/fw/shell/prefs_syscalls.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 "prefs_syscalls.h"
|
||||
|
||||
#include "syscall/syscall_internal.h"
|
||||
|
||||
DEFINE_SYSCALL(UnitsDistance, sys_shell_prefs_get_units_distance, void) {
|
||||
return shell_prefs_get_units_distance();
|
||||
}
|
21
src/fw/shell/prefs_syscalls.h
Normal file
21
src/fw/shell/prefs_syscalls.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* 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 "shell/prefs.h"
|
||||
|
||||
UnitsDistance sys_shell_prefs_get_units_distance(void);
|
31
src/fw/shell/prf/shell.c
Normal file
31
src/fw/shell/prf/shell.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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/shell.h"
|
||||
|
||||
#include "process_management/app_install_types.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
const CompositorTransition* shell_get_close_compositor_animation(AppInstallId current_app_id,
|
||||
AppInstallId next_app_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const CompositorTransition* shell_get_open_compositor_animation(AppInstallId current_app_id,
|
||||
AppInstallId next_app_id) {
|
||||
return NULL;
|
||||
}
|
76
src/fw/shell/prf/shell_event_loop.c
Normal file
76
src/fw/shell/prf/shell_event_loop.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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/shell_event_loop.h"
|
||||
|
||||
#include <bluetooth/reconnect.h>
|
||||
|
||||
#include "popups/bluetooth_pairing_ui.h"
|
||||
|
||||
#include "services/common/analytics/analytics.h"
|
||||
#include "services/prf/idle_watchdog.h"
|
||||
|
||||
static bool s_paused_reconnect_because_repairing = false;
|
||||
|
||||
static void prv_pause_reconnect_if_needed(void) {
|
||||
// See https://pebbletechnology.atlassian.net/browse/PBL-13231
|
||||
// iOS has a really annoying bug that causes it to automatically start pairing if it has no
|
||||
// pairing yet, but it does not present the confirmation UI, unless the user is in Bluetooth
|
||||
// Settings, OR, if the user has tapped the device from the EAAccessory device picker.
|
||||
// However, chances are neither are the case... When this happens, a pairing UI will show up
|
||||
// on Pebble, but nothing will show up on the iOS end.
|
||||
// This situation will occur if the user got into PRF and forgets the pairing in iOS (or the
|
||||
// other way around. Unfortunately, when PRF initiates the reconnection, there is no way to know
|
||||
// whether iOS still has the pairing (the user might have removed it). When a pairing event is
|
||||
// received, Pebble can also not know whether the confirmation UI is showing on iOS. However, it
|
||||
// probably means the other side forgot the previous pairing, so make Pebble stop
|
||||
// auto-reconnecting until reboot, so that the number of times the bug is hit is at least limited
|
||||
// to one time... :((((
|
||||
if (!s_paused_reconnect_because_repairing) {
|
||||
bt_driver_reconnect_pause();
|
||||
s_paused_reconnect_because_repairing = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void prv_resume_reconnect_if_needed(void) {
|
||||
if (s_paused_reconnect_because_repairing) {
|
||||
bt_driver_reconnect_resume();
|
||||
s_paused_reconnect_because_repairing = false;
|
||||
}
|
||||
}
|
||||
|
||||
void shell_event_loop_init(void) {
|
||||
#ifndef MANUFACTURING_FW
|
||||
prf_idle_watchdog_start();
|
||||
#endif
|
||||
}
|
||||
|
||||
void shell_event_loop_handle_event(PebbleEvent *e) {
|
||||
switch(e->type) {
|
||||
case PEBBLE_BT_PAIRING_EVENT:
|
||||
if (e->bluetooth.pair.type == PebbleBluetoothPairEventTypePairingComplete) {
|
||||
prv_resume_reconnect_if_needed();
|
||||
} else {
|
||||
prv_pause_reconnect_if_needed();
|
||||
}
|
||||
bluetooth_pairing_ui_handle_event(&e->bluetooth.pair);
|
||||
return;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
267
src/fw/shell/prf/stubs.c
Normal file
267
src/fw/shell/prf/stubs.c
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
//! @file stubs.c
|
||||
//!
|
||||
//! This file stubs out functionality that we don't want in PRF. Ideally this file wouldn't have
|
||||
//! to exist because systems that were common to both PRF and normal firmware wouldn't try to
|
||||
//! use something that only exists in normal, but we're not quite there yet.
|
||||
|
||||
#include "util/uuid.h"
|
||||
#include "drivers/backlight.h"
|
||||
#include "kernel/events.h"
|
||||
#include "popups/crashed_ui.h"
|
||||
#include "popups/notifications/notification_window.h"
|
||||
#include "process_management/app_install_manager.h"
|
||||
#include "process_management/pebble_process_md.h"
|
||||
#include "resource/resource_storage.h"
|
||||
#include "resource/resource_storage_file.h"
|
||||
#include "services/common/light.h"
|
||||
#include "services/normal/notifications/do_not_disturb.h"
|
||||
#include "services/normal/notifications/alerts_private.h"
|
||||
#include "services/normal/persist.h"
|
||||
#include "shell/prefs.h"
|
||||
|
||||
void app_fetch_binaries(const Uuid *uuid, AppInstallId app_id, bool has_worker) {
|
||||
}
|
||||
|
||||
const char *app_custom_get_title(AppInstallId app_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void crashed_ui_show_worker_crash(AppInstallId install_id) {
|
||||
}
|
||||
|
||||
void crashed_ui_show_pebble_reset(void) {
|
||||
}
|
||||
|
||||
void crashed_ui_show_bluetooth_stuck(void) {
|
||||
}
|
||||
|
||||
void app_idle_timeout_stop(void) {
|
||||
}
|
||||
|
||||
void wakeup_popup_window(uint8_t missed_apps_count, uint8_t *missed_apps_banks) {
|
||||
}
|
||||
|
||||
void watchface_set_default_install_id(AppInstallId id) {
|
||||
}
|
||||
|
||||
void watchface_handle_button_event(PebbleEvent *e) {
|
||||
}
|
||||
|
||||
void app_idle_timeout_refresh(void) {
|
||||
}
|
||||
|
||||
PebblePhoneCaller* phone_call_util_create_caller(const char *number, const char *name) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void alarm_set_snooze_delay(int delay_ms) {
|
||||
}
|
||||
|
||||
const void* const g_pbl_system_tbl[] = {};
|
||||
|
||||
const FileResourceData g_file_resource_stores[] = {};
|
||||
const uint32_t g_num_file_resource_stores = 0;
|
||||
|
||||
void persist_service_client_open(const Uuid *uuid) {
|
||||
}
|
||||
|
||||
void persist_service_client_close(const Uuid *uuid) {
|
||||
}
|
||||
|
||||
SettingsFile * persist_service_lock_and_get_store(const Uuid *uuid) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status_t persist_service_delete_file(const Uuid *uuid) {
|
||||
return E_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
void wakeup_enable(bool enable) {
|
||||
}
|
||||
|
||||
bool phone_call_is_using_ANCS(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#include "services/normal/notifications/alerts.h"
|
||||
|
||||
#include "services/normal/blob_db/app_db.h"
|
||||
#include "services/normal/app_cache.h"
|
||||
#include "services/normal/blob_db/pin_db.h"
|
||||
|
||||
status_t pin_db_delete_with_parent(const TimelineItemId *parent_id) {
|
||||
return E_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
status_t app_cache_add_entry(AppInstallId app_id, uint32_t total_size) {
|
||||
return E_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
status_t app_cache_remove_entry(AppInstallId id) {
|
||||
return E_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
status_t app_db_insert(const uint8_t *key, int key_len, const uint8_t *val, int val_len) {
|
||||
return E_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
status_t app_db_delete(const uint8_t *key, int key_len) {
|
||||
return E_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
AppInstallId app_db_check_next_unique_id(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void app_db_enumerate_entries(AppDBEnumerateCb cb, void *data) {
|
||||
}
|
||||
|
||||
AppInstallId app_db_get_install_id_for_uuid(const Uuid *uuid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
status_t app_db_get_app_entry_for_install_id(AppInstallId app_id, AppDBEntry *entry) {
|
||||
return E_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
bool app_db_exists_install_id(AppInstallId app_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void timeline_item_destroy(TimelineItem* item) {
|
||||
}
|
||||
|
||||
AppInstallId worker_preferences_get_default_worker(void) {
|
||||
return INSTALL_ID_INVALID;
|
||||
}
|
||||
|
||||
#include "process_management/process_loader.h"
|
||||
void * process_loader_load(const PebbleProcessMd *app_md, PebbleTask task,
|
||||
MemorySegment *destination) {
|
||||
return app_md->main_func;
|
||||
}
|
||||
|
||||
#include "services/normal/process_management/app_storage.h"
|
||||
AppStorageGetAppInfoResult app_storage_get_process_info(PebbleProcessInfo* app_info,
|
||||
uint8_t *build_id_out,
|
||||
AppInstallId app_id,
|
||||
PebbleTask task) {
|
||||
return GET_APP_INFO_COULD_NOT_READ_FORMAT;
|
||||
}
|
||||
|
||||
void app_storage_get_file_name(char *name, size_t buf_length, AppInstallId app_id,
|
||||
PebbleTask task) {
|
||||
// Empty string
|
||||
*name = 0;
|
||||
}
|
||||
|
||||
bool shell_prefs_get_clock_24h_style(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void shell_prefs_set_clock_24h_style(bool is_24h_style) {
|
||||
}
|
||||
|
||||
bool shell_prefs_is_timezone_source_manual(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void shell_prefs_set_timezone_source_manual(bool manual) {
|
||||
}
|
||||
|
||||
void shell_prefs_set_automatic_timezone_id(int16_t timezone_id) {
|
||||
}
|
||||
|
||||
int16_t shell_prefs_get_automatic_timezone_id(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
AlertMask alerts_get_mask(void) {
|
||||
return AlertMaskAllOff;
|
||||
}
|
||||
|
||||
bool do_not_disturb_is_active(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
BacklightBehaviour backlight_get_behaviour(void) {
|
||||
return BacklightBehaviour_On;
|
||||
}
|
||||
|
||||
bool backlight_is_enabled(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool backlight_is_ambient_sensor_enabled(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool backlight_is_motion_enabled(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bt_persistent_storage_get_airplane_mode_enabled(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void bt_persistent_storage_set_airplane_mode_enabled(bool *state) {
|
||||
}
|
||||
|
||||
uint32_t backlight_get_timeout_ms(void) {
|
||||
return DEFAULT_BACKLIGHT_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
uint16_t backlight_get_intensity(void) {
|
||||
return BACKLIGHT_BRIGHTNESS_MAX;
|
||||
}
|
||||
uint8_t backlight_get_intensity_percent(void) {
|
||||
return (backlight_get_intensity() * 100) / BACKLIGHT_BRIGHTNESS_MAX;
|
||||
}
|
||||
|
||||
bool shell_prefs_get_language_english(void) {
|
||||
return true;
|
||||
}
|
||||
void shell_prefs_set_language_english(bool english) {
|
||||
}
|
||||
void shell_prefs_toggle_language_english(void) {
|
||||
}
|
||||
|
||||
FontInfo *fonts_get_system_emoji_font_for_size(unsigned int font_height) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void analytics_event_app_crash(const Uuid *uuid, uint32_t pc, uint32_t lr,
|
||||
const uint8_t *build_id, bool is_rocky_app) {
|
||||
}
|
||||
|
||||
void analytics_event_bt_chip_boot(uint8_t build_id[BUILD_ID_EXPECTED_LEN],
|
||||
uint32_t crash_lr, uint32_t reboot_reason_code) {
|
||||
}
|
||||
|
||||
int16_t timeline_peek_get_origin_y(void) {
|
||||
return DISP_ROWS;
|
||||
}
|
||||
|
||||
int16_t timeline_peek_get_obstruction_origin_y(void) {
|
||||
return DISP_ROWS;
|
||||
}
|
||||
|
||||
void timeline_peek_handle_process_start(void) { }
|
||||
|
||||
void timeline_peek_handle_process_kill(void) { }
|
38
src/fw/shell/prf/system_app_registry_list.json
Normal file
38
src/fw/shell/prf/system_app_registry_list.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"warning_one": [
|
||||
" 1. DO NOT CHANGE OR REUSE THE ID OF ANY APPLICATION IN THE LIST ",
|
||||
" 2. Read the directions "
|
||||
],
|
||||
"directions": [
|
||||
" - The System Apps are the applications that are actually coded",
|
||||
" into the firmware with static PebbleProcessMd's. ",
|
||||
" - The Resource Apps are the applications that are stored in ",
|
||||
" the resource pack included with the firmware. ",
|
||||
" - The section 'system_apps' only lists the PebbleProcessMd* ",
|
||||
" functions ",
|
||||
" - Resource app entry requires a UUID, then bin_resource_id ",
|
||||
" then an icon_resource_id ",
|
||||
" - To enable only certain applications on a certain ",
|
||||
" add the particular DEFINE variable into the 'ifdefs' field ",
|
||||
" Doing so will add that application to the generated list ",
|
||||
" - To disable applications entirely, add a 'DISABLED' define to",
|
||||
" the list of defines for the application "
|
||||
],
|
||||
"warning_two": [
|
||||
" 1. DO NOT CHANGE OR REUSE THE ID OF ANY APPLICATION IN THE LIST ",
|
||||
" 2. Read the directions "
|
||||
],
|
||||
"system_apps": [
|
||||
{
|
||||
"id": -1,
|
||||
"enum": "MFG_MENU",
|
||||
"md_fn": "mfg_menu_app_get_info"
|
||||
},
|
||||
{
|
||||
"id": -2,
|
||||
"enum": "RECOVERY_FIRST_USE",
|
||||
"md_fn": "recovery_first_use_app_get_app_info"
|
||||
}
|
||||
],
|
||||
"resource_apps": []
|
||||
}
|
68
src/fw/shell/prf/system_app_state_machine.c
Normal file
68
src/fw/shell/prf/system_app_state_machine.c
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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/system_app_state_machine.h"
|
||||
|
||||
#include "apps/system_app_ids.h"
|
||||
#include "apps/core_apps/panic_window_app.h"
|
||||
#include "apps/prf_apps/mfg_menu_app.h"
|
||||
#include "apps/prf_apps/recovery_first_use_app/recovery_first_use_app.h"
|
||||
#include "kernel/panic.h"
|
||||
#include "mfg/mfg_mode/mfg_factory_mode.h"
|
||||
#include "process_management/app_manager.h"
|
||||
#include "services/prf/accessory/accessory_imaging.h"
|
||||
|
||||
const PebbleProcessMd* system_app_state_machine_system_start(void) {
|
||||
if (launcher_panic_get_current_error() != 0) {
|
||||
return panic_app_get_app_info();
|
||||
}
|
||||
|
||||
#ifdef MANUFACTURING_FW
|
||||
accessory_imaging_enable();
|
||||
mfg_enter_mfg_mode();
|
||||
return mfg_menu_app_get_info();
|
||||
#endif
|
||||
|
||||
return recovery_first_use_app_get_app_info();
|
||||
}
|
||||
|
||||
AppInstallId system_app_state_machine_get_last_registered_app(void) {
|
||||
if (mfg_is_mfg_mode()) {
|
||||
return APP_ID_MFG_MENU;
|
||||
}
|
||||
|
||||
return APP_ID_RECOVERY_FIRST_USE;
|
||||
}
|
||||
|
||||
const PebbleProcessMd* system_app_state_machine_get_default_app(void) {
|
||||
if (mfg_is_mfg_mode()) {
|
||||
return mfg_menu_app_get_info();
|
||||
}
|
||||
|
||||
return recovery_first_use_app_get_app_info();
|
||||
}
|
||||
|
||||
void system_app_state_machine_register_app_launch(AppInstallId app_id) {
|
||||
}
|
||||
|
||||
void system_app_state_machine_panic(void) {
|
||||
if (app_manager_is_first_app_launched()) {
|
||||
app_manager_launch_new_app(&(AppLaunchConfig) {
|
||||
.md = panic_app_get_app_info(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
279
src/fw/shell/sdk/prefs.c
Normal file
279
src/fw/shell/sdk/prefs.c
Normal file
|
@ -0,0 +1,279 @@
|
|||
/*
|
||||
* 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/preferred_content_size.h"
|
||||
#include "apps/system_app_ids.h"
|
||||
#include "board/board.h"
|
||||
#include "os/mutex.h"
|
||||
#include "process_management/app_install_manager.h"
|
||||
#include "process_management/process_manager.h"
|
||||
#include "services/normal/activity/activity.h"
|
||||
#include "services/normal/activity/activity_insights.h"
|
||||
#include "services/normal/activity/insights_settings.h"
|
||||
#include "services/normal/settings/settings_file.h"
|
||||
#include "shell/prefs.h"
|
||||
#include "shell/prefs_private.h"
|
||||
|
||||
static PebbleMutex *s_mutex;
|
||||
|
||||
#define PREF_KEY_CLOCK_24H "clock24h"
|
||||
static bool s_is_24h_style;
|
||||
|
||||
#define PREF_KEY_DEFAULT_WATCHFACE "watchface"
|
||||
static Uuid s_default_watchface = UUID_INVALID_INIT;
|
||||
|
||||
#define PREF_KEY_CONTENT_SIZE "contentSize"
|
||||
static uint8_t s_content_size;
|
||||
#if !UNITTEST
|
||||
_Static_assert(sizeof(PreferredContentSize) == sizeof(s_content_size),
|
||||
"sizeof(PreferredContentSize) grew, pref needs to be migrated!");
|
||||
#endif
|
||||
|
||||
void shell_prefs_init(void) {
|
||||
s_mutex = mutex_create();
|
||||
mutex_lock(s_mutex);
|
||||
SettingsFile file = {};
|
||||
if (settings_file_open(&file, SHELL_PREFS_FILE_NAME, SHELL_PREFS_FILE_LEN) != S_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
if (settings_file_get(&file, PREF_KEY_CLOCK_24H, sizeof(PREF_KEY_CLOCK_24H),
|
||||
&s_is_24h_style, sizeof(s_is_24h_style)) != S_SUCCESS) {
|
||||
// The setting likely doesn't exist yet so set it to the default (true)
|
||||
s_is_24h_style = true;
|
||||
}
|
||||
if (settings_file_get(&file, PREF_KEY_DEFAULT_WATCHFACE, sizeof(PREF_KEY_DEFAULT_WATCHFACE),
|
||||
&s_default_watchface, sizeof(s_default_watchface)) != S_SUCCESS) {
|
||||
s_default_watchface = UUID_INVALID;
|
||||
}
|
||||
if (settings_file_get(&file, PREF_KEY_CONTENT_SIZE, sizeof(PREF_KEY_CONTENT_SIZE),
|
||||
&s_content_size, sizeof(s_content_size)) != S_SUCCESS) {
|
||||
s_content_size = PreferredContentSizeDefault;
|
||||
}
|
||||
settings_file_close(&file);
|
||||
cleanup:
|
||||
mutex_unlock(s_mutex);
|
||||
}
|
||||
|
||||
static bool prv_pref_set(const char *key, const void *val, size_t val_len) {
|
||||
SettingsFile file = {};
|
||||
status_t rv;
|
||||
if ((rv = settings_file_open(&file, SHELL_PREFS_FILE_NAME, SHELL_PREFS_FILE_LEN)) != S_SUCCESS) {
|
||||
goto cleanup;
|
||||
}
|
||||
rv = settings_file_set(&file, key, strnlen(key, SETTINGS_KEY_MAX_LEN) + 1, val, val_len);
|
||||
settings_file_close(&file);
|
||||
cleanup:
|
||||
return (rv == S_SUCCESS);
|
||||
}
|
||||
|
||||
bool shell_prefs_get_clock_24h_style(void) {
|
||||
return s_is_24h_style;
|
||||
}
|
||||
|
||||
void shell_prefs_set_clock_24h_style(bool is_24h_style) {
|
||||
mutex_lock(s_mutex);
|
||||
if (prv_pref_set(PREF_KEY_CLOCK_24H, &s_is_24h_style, sizeof(s_is_24h_style))) {
|
||||
s_is_24h_style = is_24h_style;
|
||||
}
|
||||
mutex_unlock(s_mutex);
|
||||
}
|
||||
|
||||
bool shell_prefs_is_timezone_source_manual(void) {
|
||||
// Force things to automatic
|
||||
return false;
|
||||
}
|
||||
|
||||
void shell_prefs_set_timezone_source_manual(bool manual) {
|
||||
}
|
||||
|
||||
int16_t shell_prefs_get_automatic_timezone_id(void) {
|
||||
// Invalid
|
||||
return -1;
|
||||
}
|
||||
|
||||
void shell_prefs_set_automatic_timezone_id(int16_t timezone_id) {
|
||||
}
|
||||
|
||||
|
||||
// Exported function used by blob_db API to set the backing store for a specific key.
|
||||
// Not used by the SDK shell
|
||||
bool prefs_private_write_backing(const uint8_t *key, size_t key_len, const void *value,
|
||||
int value_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Exported function used by blob_db API to get the length of a value in our backing store
|
||||
// Not used by the SDK shell
|
||||
int prefs_private_get_backing_len(const uint8_t *key, size_t key_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Exported function used by blob_db API to read our backing store
|
||||
// Not used by the SDK shell
|
||||
bool prefs_private_read_backing(const uint8_t *key, size_t key_len, void *value, int value_len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
void watchface_set_default_install_id(AppInstallId app_id) {
|
||||
mutex_lock(s_mutex);
|
||||
Uuid uuid;
|
||||
app_install_get_uuid_for_install_id(app_id, &uuid);
|
||||
if (prv_pref_set(PREF_KEY_DEFAULT_WATCHFACE, &uuid, sizeof(uuid))) {
|
||||
s_default_watchface = uuid;
|
||||
}
|
||||
mutex_unlock(s_mutex);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
#else
|
||||
AppInstallId watchface_get_default_install_id(void) {
|
||||
return APP_ID_SDK;
|
||||
}
|
||||
|
||||
void watchface_set_default_install_id(AppInstallId id) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void system_theme_set_content_size(PreferredContentSize content_size) {
|
||||
mutex_lock(s_mutex);
|
||||
const uint8_t content_size_uint = content_size;
|
||||
if (content_size >= NumPreferredContentSizes) {
|
||||
PBL_LOG(LOG_LEVEL_WARNING, "Ignoring attempt to set content size to invalid size %d",
|
||||
content_size);
|
||||
} else if (prv_pref_set(PREF_KEY_CONTENT_SIZE, &content_size_uint, sizeof(content_size_uint))) {
|
||||
s_content_size = content_size;
|
||||
}
|
||||
mutex_unlock(s_mutex);
|
||||
}
|
||||
|
||||
PreferredContentSize system_theme_get_content_size(void) {
|
||||
return system_theme_convert_host_content_size_to_runtime_platform(
|
||||
(PreferredContentSize)s_content_size);
|
||||
}
|
||||
|
||||
bool activity_prefs_tracking_is_enabled(void) {
|
||||
#if CAPABILITY_HAS_HEALTH_TRACKING
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if CAPABILITY_HAS_HEALTH_TRACKING
|
||||
void activity_prefs_tracking_set_enabled(bool enable) {
|
||||
}
|
||||
|
||||
bool activity_prefs_activity_insights_are_enabled(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void activity_prefs_activity_insights_set_enabled(bool enable) {
|
||||
}
|
||||
|
||||
bool activity_prefs_sleep_insights_are_enabled(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void activity_prefs_sleep_insights_set_enabled(bool enable) {
|
||||
}
|
||||
|
||||
uint8_t activity_prefs_get_health_app_opened_version(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void activity_prefs_set_height_mm(uint16_t height_mm) {
|
||||
}
|
||||
|
||||
uint16_t activity_prefs_get_height_mm(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void activity_prefs_set_weight_dag(uint16_t weight_dag) {
|
||||
}
|
||||
|
||||
uint16_t activity_prefs_get_weight_dag(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void activity_prefs_set_gender(ActivityGender gender) {
|
||||
}
|
||||
|
||||
ActivityGender activity_prefs_get_gender(void) {
|
||||
return ActivityGenderOther;
|
||||
}
|
||||
|
||||
void activity_prefs_set_age_years(uint8_t age_years) {
|
||||
}
|
||||
|
||||
uint8_t activity_prefs_get_age_years(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool activity_prefs_heart_rate_is_enabled(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ActivityInsightSettings *activity_prefs_get_sleep_reward_settings(void) {
|
||||
static ActivityInsightSettings s_settings = { 0 };
|
||||
return &s_settings;
|
||||
}
|
||||
|
||||
void activity_prefs_set_activation_delay_insight_fired(ActivationDelayInsightType type) {
|
||||
}
|
||||
|
||||
bool activity_prefs_has_activation_delay_insight_fired(ActivationDelayInsightType type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool activity_prefs_get_health_app_opened(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void activity_prefs_set_activated(void) {
|
||||
}
|
||||
|
||||
time_t activity_prefs_get_activation_time(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
UnitsDistance shell_prefs_get_units_distance(void) {
|
||||
return UnitsDistance_Miles;
|
||||
}
|
||||
|
||||
#endif
|
30
src/fw/shell/sdk/shell.c
Normal file
30
src/fw/shell/sdk/shell.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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/shell.h"
|
||||
|
||||
#include "kernel/events.h"
|
||||
#include "process_management/app_install_types.h"
|
||||
|
||||
const CompositorTransition* shell_get_close_compositor_animation(AppInstallId current_app_id,
|
||||
AppInstallId next_app_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const CompositorTransition* shell_get_open_compositor_animation(AppInstallId current_app_id,
|
||||
AppInstallId next_app_id) {
|
||||
return NULL;
|
||||
}
|
68
src/fw/shell/sdk/shell_event_loop.c
Normal file
68
src/fw/shell/sdk/shell_event_loop.c
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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 "apps/system_app_ids.h"
|
||||
#include "apps/system_apps/timeline/timeline.h"
|
||||
#include "kernel/events.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "popups/notifications/notification_window.h"
|
||||
#include "popups/timeline/peek.h"
|
||||
#include "process_management/app_manager.h"
|
||||
#include "services/common/compositor/compositor.h"
|
||||
#include "services/normal/activity/activity.h"
|
||||
#include "services/normal/app_inbox_service.h"
|
||||
#include "services/normal/app_outbox_service.h"
|
||||
#include "shell/prefs.h"
|
||||
#include "shell/sdk/watchface.h"
|
||||
#include "shell/shell_event_loop.h"
|
||||
|
||||
extern void shell_prefs_init(void);
|
||||
|
||||
void shell_event_loop_init(void) {
|
||||
shell_prefs_init();
|
||||
notification_window_service_init();
|
||||
app_inbox_service_init();
|
||||
app_outbox_service_init();
|
||||
app_message_sender_init();
|
||||
watchface_init();
|
||||
timeline_peek_init();
|
||||
#if CAPABILITY_HAS_HEALTH_TRACKING
|
||||
// Start activity tracking if enabled
|
||||
if (activity_prefs_tracking_is_enabled()) {
|
||||
activity_start_tracking(false /*test_mode*/);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void shell_event_loop_handle_event(PebbleEvent *e) {
|
||||
switch (e->type) {
|
||||
case PEBBLE_APP_FETCH_REQUEST_EVENT:
|
||||
app_manager_handle_app_fetch_request_event(&e->app_fetch_request);
|
||||
return;
|
||||
|
||||
case PEBBLE_SYS_NOTIFICATION_EVENT:
|
||||
notification_window_handle_notification(&e->sys_notification);
|
||||
return;
|
||||
|
||||
case PEBBLE_REMINDER_EVENT:
|
||||
// This handles incoming Reminders
|
||||
notification_window_handle_reminder(&e->reminder);
|
||||
return;
|
||||
|
||||
default:
|
||||
break; // don't care
|
||||
}
|
||||
}
|
33
src/fw/shell/sdk/shell_sdk.c
Normal file
33
src/fw/shell/sdk/shell_sdk.c
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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_sdk.h"
|
||||
|
||||
#include "process_management/app_install_manager.h"
|
||||
|
||||
AppInstallId s_last_installed_app = INSTALL_ID_INVALID;
|
||||
|
||||
AppInstallId shell_sdk_get_last_installed_app(void) {
|
||||
return s_last_installed_app;
|
||||
}
|
||||
|
||||
void shell_sdk_set_last_installed_app(AppInstallId app_id) {
|
||||
s_last_installed_app = app_id;
|
||||
}
|
||||
|
||||
bool shell_sdk_last_installed_app_is_watchface() {
|
||||
return app_install_is_watchface(shell_sdk_get_last_installed_app());
|
||||
}
|
28
src/fw/shell/sdk/shell_sdk.h
Normal file
28
src/fw/shell/sdk/shell_sdk.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 "process_management/app_install_types.h"
|
||||
|
||||
//! @file shell_sdk.h
|
||||
//!
|
||||
//! Hooks into system_app_state_machine.c to watch for installed apps to be launched. Latches
|
||||
//! so we can figure out what was the installed app that we've launched most recently.
|
||||
|
||||
AppInstallId shell_sdk_get_last_installed_app(void);
|
||||
|
||||
void shell_sdk_set_last_installed_app(AppInstallId app_id);
|
||||
|
||||
bool shell_sdk_last_installed_app_is_watchface();
|
163
src/fw/shell/sdk/stubs.c
Normal file
163
src/fw/shell/sdk/stubs.c
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* 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 "drivers/backlight.h"
|
||||
#include "process_management/pebble_process_md.h"
|
||||
#include "services/normal/activity/activity.h"
|
||||
#include "services/normal/timeline/peek.h"
|
||||
#include "shell/prefs.h"
|
||||
#include "util/uuid.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void app_idle_timeout_start(void) {
|
||||
}
|
||||
|
||||
void app_idle_timeout_refresh(void) {
|
||||
}
|
||||
|
||||
void app_idle_timeout_stop(void) {
|
||||
}
|
||||
|
||||
void watchface_start_low_power(bool enable) {
|
||||
}
|
||||
|
||||
uint32_t backlight_get_timeout_ms(void) {
|
||||
return DEFAULT_BACKLIGHT_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
#define SDKSHELL_BACKLIGHT_ON_PERCENT 25 // Same as snowy bb2
|
||||
|
||||
uint16_t backlight_get_intensity(void) {
|
||||
return (BACKLIGHT_BRIGHTNESS_MAX * SDKSHELL_BACKLIGHT_ON_PERCENT / 100);
|
||||
}
|
||||
uint8_t backlight_get_intensity_percent(void) {
|
||||
return (backlight_get_intensity() * 100) / BACKLIGHT_BRIGHTNESS_MAX;
|
||||
}
|
||||
|
||||
void backlight_set_timeout_ms(uint32_t timeout_ms) {
|
||||
}
|
||||
|
||||
BacklightBehaviour backlight_get_behaviour(void) {
|
||||
return BacklightBehaviour_On;
|
||||
}
|
||||
|
||||
bool backlight_is_enabled(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool backlight_is_ambient_sensor_enabled(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool backlight_is_motion_enabled(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#include "process_management/app_install_types.h"
|
||||
void worker_preferences_set_default_worker(AppInstallId id) {
|
||||
}
|
||||
|
||||
AppInstallId worker_preferences_get_default_worker(void) {
|
||||
return INSTALL_ID_INVALID;
|
||||
}
|
||||
|
||||
#if !CAPABILITY_HAS_SDK_SHELL4
|
||||
const char *app_custom_get_title(AppInstallId id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void customizable_app_protocol_msg_callback(void *session, const uint8_t* data,
|
||||
size_t length) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// Used by the alarm service to add alarm pins to the timeline
|
||||
const PebbleProcessMd* alarms_app_get_info(void) {
|
||||
static const PebbleProcessMdSystem s_alarms_app_info = {
|
||||
.common = {
|
||||
.main_func = NULL,
|
||||
// UUID: 67a32d95-ef69-46d4-a0b9-854cc62f97f9
|
||||
.uuid = {0x67, 0xa3, 0x2d, 0x95, 0xef, 0x69, 0x46, 0xd4,
|
||||
0xa0, 0xb9, 0x85, 0x4c, 0xc6, 0x2f, 0x97, 0xf9},
|
||||
},
|
||||
.name = "Alarms",
|
||||
};
|
||||
return (const PebbleProcessMd*) &s_alarms_app_info;
|
||||
}
|
||||
|
||||
// This stub isn't needed on tintin as it uses a different launcher
|
||||
#if !PLATFORM_TINTIN && !CAPABILITY_HAS_SDK_SHELL4
|
||||
#include "apps/system_apps/launcher/launcher_app.h"
|
||||
const LauncherDrawState *launcher_app_get_draw_state(void) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
void analytics_external_update(void) {
|
||||
}
|
||||
|
||||
bool shell_prefs_get_stationary_enabled(void) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool shell_prefs_get_language_english(void) {
|
||||
return true;
|
||||
}
|
||||
void shell_prefs_set_language_english(bool english) {
|
||||
}
|
||||
void shell_prefs_toggle_language_english(void) {
|
||||
}
|
||||
|
||||
void language_ui_display_changed(const char *lang_name) {
|
||||
}
|
||||
|
||||
void timeline_peek_prefs_set_enabled(bool enabled) {}
|
||||
bool timeline_peek_prefs_get_enabled(void) {
|
||||
return true;
|
||||
}
|
||||
void timeline_peek_prefs_set_before_time(uint16_t before_time_m) {}
|
||||
uint16_t timeline_peek_prefs_get_before_time(void) {
|
||||
return (TIMELINE_PEEK_DEFAULT_SHOW_BEFORE_TIME_S / SECONDS_PER_MINUTE);
|
||||
}
|
||||
|
||||
bool workout_utils_find_ongoing_activity_session(ActivitySession *session_out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t activity_prefs_heart_get_resting_hr(void) {
|
||||
return 70;
|
||||
}
|
||||
|
||||
uint8_t activity_prefs_heart_get_elevated_hr(void) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
uint8_t activity_prefs_heart_get_max_hr(void) {
|
||||
return 190;
|
||||
}
|
||||
|
||||
uint8_t activity_prefs_heart_get_zone1_threshold(void) {
|
||||
return 130;
|
||||
}
|
||||
|
||||
uint8_t activity_prefs_heart_get_zone2_threshold(void) {
|
||||
return 154;
|
||||
}
|
||||
|
||||
uint8_t activity_prefs_heart_get_zone3_threshold(void) {
|
||||
return 172;
|
||||
}
|
51
src/fw/shell/sdk/system_app_registry_list.json
Normal file
51
src/fw/shell/sdk/system_app_registry_list.json
Normal file
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"warning_one": [
|
||||
" 1. DO NOT CHANGE OR REUSE THE ID OF ANY APPLICATION IN THE LIST ",
|
||||
" 2. Read the directions "
|
||||
],
|
||||
"directions": [
|
||||
" - The System Apps are the applications that are actually coded",
|
||||
" into the firmware with static PebbleProcessMd's. ",
|
||||
" - The Resource Apps are the applications that are stored in ",
|
||||
" the resource pack included with the firmware. ",
|
||||
" - The section 'system_apps' only lists the PebbleProcessMd* ",
|
||||
" functions ",
|
||||
" - Resource app entry requires a UUID, then bin_resource_id ",
|
||||
" then an icon_resource_id ",
|
||||
" - To enable only certain applications on a certain ",
|
||||
" add the particular DEFINE variable into the 'ifdefs' field ",
|
||||
" Doing so will add that application to the generated list ",
|
||||
" - To disable applications entirely, add a 'DISABLED' define to",
|
||||
" the list of defines for the application "
|
||||
],
|
||||
"warning_two": [
|
||||
" 1. DO NOT CHANGE OR REUSE THE ID OF ANY APPLICATION IN THE LIST ",
|
||||
" 2. Read the directions "
|
||||
],
|
||||
"system_apps": [
|
||||
{
|
||||
"id": -1,
|
||||
"enum": "SDK",
|
||||
"md_fn": "sdk_app_get_info"
|
||||
},
|
||||
{
|
||||
"id": -2,
|
||||
"enum": "TIMELINE",
|
||||
"md_fn": "timeline_get_app_info"
|
||||
},
|
||||
{
|
||||
"id": -3,
|
||||
"enum": "LAUNCHER_MENU",
|
||||
"md_fn": "launcher_menu_app_get_app_info",
|
||||
"ifdefs": ["CAPABILITY_HAS_SDK_SHELL4=1"]
|
||||
},
|
||||
{
|
||||
"id": -4,
|
||||
"enum": "WATCHFACES",
|
||||
"md_fn": "watchfaces_get_app_info",
|
||||
"ifdefs": ["CAPABILITY_HAS_SDK_SHELL4=1"],
|
||||
"color_argb8": "GColorJazzberryJamARGB8"
|
||||
}
|
||||
],
|
||||
"resource_apps": []
|
||||
}
|
99
src/fw/shell/sdk/system_app_state_machine.c
Normal file
99
src/fw/shell/sdk/system_app_state_machine.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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_sdk.h"
|
||||
|
||||
#include "apps/core_apps/panic_window_app.h"
|
||||
#include "apps/sdk/sdk_app.h"
|
||||
#include "apps/system_app_ids.h"
|
||||
#include "apps/system_apps/launcher/launcher_app.h"
|
||||
#include "kernel/panic.h"
|
||||
#include "process_management/app_install_manager.h"
|
||||
#include "process_management/app_manager.h"
|
||||
#include "shell/system_app_state_machine.h"
|
||||
#include "shell/sdk/watchface.h"
|
||||
|
||||
//! Whether to return to the watchface instead of the launcher upon exiting an app.
|
||||
static bool s_rooted_in_watchface = false;
|
||||
|
||||
const PebbleProcessMd *system_app_state_machine_system_start(void) {
|
||||
if (launcher_panic_get_current_error() != 0) {
|
||||
return panic_app_get_app_info();
|
||||
}
|
||||
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
const AppInstallId watchface_app_id = watchface_get_default_install_id();
|
||||
if (watchface_app_id != INSTALL_ID_INVALID) {
|
||||
return app_install_get_md(watchface_app_id, false /* worker */);
|
||||
}
|
||||
#endif
|
||||
|
||||
return sdk_app_get_info();
|
||||
}
|
||||
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
//! @return True if the currently running app is an installed watchface
|
||||
static bool prv_current_app_is_watchface(void) {
|
||||
return app_install_is_watchface(app_manager_get_current_app_id());
|
||||
}
|
||||
|
||||
AppInstallId system_app_state_machine_get_last_registered_app(void) {
|
||||
// If we're rooted in the watchface but we're not the watchface itself, or the launcher
|
||||
// is closing, we should launch the watchface.
|
||||
if ((s_rooted_in_watchface && !prv_current_app_is_watchface()) ||
|
||||
(app_manager_get_current_app_md() == launcher_menu_app_get_app_info())) {
|
||||
return watchface_get_default_install_id();
|
||||
}
|
||||
|
||||
return APP_ID_LAUNCHER_MENU;
|
||||
}
|
||||
|
||||
const PebbleProcessMd* system_app_state_machine_get_default_app(void) {
|
||||
return launcher_menu_app_get_app_info();
|
||||
}
|
||||
|
||||
#else
|
||||
AppInstallId system_app_state_machine_get_last_registered_app(void) {
|
||||
return APP_ID_SDK;
|
||||
}
|
||||
|
||||
const PebbleProcessMd* system_app_state_machine_get_default_app(void) {
|
||||
return sdk_app_get_info();
|
||||
}
|
||||
#endif
|
||||
|
||||
void system_app_state_machine_register_app_launch(AppInstallId app_id) {
|
||||
if (app_install_id_from_app_db(app_id)) {
|
||||
shell_sdk_set_last_installed_app(app_id);
|
||||
}
|
||||
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
if (app_id == APP_ID_LAUNCHER_MENU) {
|
||||
s_rooted_in_watchface = false;
|
||||
} else if (app_install_is_watchface(app_id)) {
|
||||
s_rooted_in_watchface = true;
|
||||
}
|
||||
// Other app launches don't modify our root so just ignore them.
|
||||
#endif
|
||||
}
|
||||
|
||||
void system_app_state_machine_panic(void) {
|
||||
if (app_manager_is_initialized()) {
|
||||
app_manager_launch_new_app(&(AppLaunchConfig) {
|
||||
.md = panic_app_get_app_info(),
|
||||
});
|
||||
}
|
||||
}
|
140
src/fw/shell/sdk/watchface.c
Normal file
140
src/fw/shell/sdk/watchface.c
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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 "watchface.h"
|
||||
|
||||
#include "applib/ui/dialogs/expandable_dialog.h"
|
||||
#include "apps/system_app_ids.h"
|
||||
#include "apps/system_apps/launcher/launcher_app.h"
|
||||
#include "apps/system_apps/timeline/timeline.h"
|
||||
#include "kernel/event_loop.h"
|
||||
#include "kernel/ui/modals/modal_manager.h"
|
||||
#include "popups/timeline/peek.h"
|
||||
#include "process_management/app_manager.h"
|
||||
#include "process_management/pebble_process_md.h"
|
||||
#include "services/common/analytics/analytics.h"
|
||||
#include "services/common/compositor/compositor_transitions.h"
|
||||
#include "shell/sdk/shell_sdk.h"
|
||||
#include "shell/system_app_state_machine.h"
|
||||
#include "system/logging.h"
|
||||
#include "system/passert.h"
|
||||
|
||||
typedef struct WatchfaceData {
|
||||
ClickManager click_manager;
|
||||
ButtonId button_pressed;
|
||||
AppInstallId active_watchface;
|
||||
} WatchfaceData;
|
||||
|
||||
static WatchfaceData s_watchface_data;
|
||||
|
||||
void watchface_launch_default(const CompositorTransition *animation) {
|
||||
app_manager_put_launch_app_event(&(AppLaunchEventConfig) {
|
||||
.id = watchface_get_default_install_id(),
|
||||
.common.transition = animation,
|
||||
});
|
||||
}
|
||||
|
||||
static void prv_launch_app_via_button(AppLaunchEventConfig *config,
|
||||
ClickRecognizerRef recognizer) {
|
||||
config->common.button = click_recognizer_get_button_id(recognizer);
|
||||
app_manager_put_launch_app_event(config);
|
||||
}
|
||||
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
static void prv_launch_launcher(ClickRecognizerRef recognizer, void *data) {
|
||||
static const LauncherMenuArgs s_launcher_args = { .reset_scroll = true };
|
||||
prv_launch_app_via_button(&(AppLaunchEventConfig) {
|
||||
.id = APP_ID_LAUNCHER_MENU,
|
||||
.common.args = &s_launcher_args,
|
||||
}, recognizer);
|
||||
}
|
||||
|
||||
static void prv_launch_timeline(ClickRecognizerRef recognizer, void *data) {
|
||||
static TimelineArgs s_timeline_args = {
|
||||
.pin_id = UUID_INVALID_INIT,
|
||||
};
|
||||
switch (click_recognizer_get_button_id(recognizer)) {
|
||||
case BUTTON_ID_DOWN:
|
||||
s_timeline_args.direction = TimelineIterDirectionFuture;
|
||||
break;
|
||||
case BUTTON_ID_UP:
|
||||
s_timeline_args.direction = TimelineIterDirectionPast;
|
||||
break;
|
||||
default:
|
||||
WTF;
|
||||
}
|
||||
|
||||
prv_launch_app_via_button(&(AppLaunchEventConfig) {
|
||||
.id = APP_ID_TIMELINE,
|
||||
.common.args = &s_timeline_args,
|
||||
}, recognizer);
|
||||
}
|
||||
|
||||
static void prv_configure_click(ButtonId button_id, ClickHandler click_handler) {
|
||||
WatchfaceData *data = &s_watchface_data;
|
||||
ClickConfig *cfg = &data->click_manager.recognizers[button_id].config;
|
||||
cfg->click.handler = click_handler;
|
||||
}
|
||||
|
||||
static void prv_watchface_configure_click_handlers(void) {
|
||||
prv_configure_click(BUTTON_ID_SELECT, prv_launch_launcher);
|
||||
prv_configure_click(BUTTON_ID_DOWN, prv_launch_timeline);
|
||||
prv_configure_click(BUTTON_ID_UP, prv_launch_timeline);
|
||||
}
|
||||
#endif
|
||||
|
||||
void watchface_init(void) {
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
WatchfaceData *data = &s_watchface_data;
|
||||
click_manager_init(&data->click_manager);
|
||||
prv_watchface_configure_click_handlers();
|
||||
#endif
|
||||
}
|
||||
|
||||
void watchface_handle_button_event(PebbleEvent *e) {
|
||||
#if CAPABILITY_HAS_SDK_SHELL4
|
||||
// Only handle button press if app state indicates that the app is still running
|
||||
// which is not in the process of closing
|
||||
WatchfaceData *data = &s_watchface_data;
|
||||
if (app_manager_get_task_context()->closing_state == ProcessRunState_Running) {
|
||||
data->button_pressed = e->button.button_id;
|
||||
switch (e->type) {
|
||||
case PEBBLE_BUTTON_DOWN_EVENT:
|
||||
click_recognizer_handle_button_down(&data->click_manager.recognizers[e->button.button_id]);
|
||||
break;
|
||||
case PEBBLE_BUTTON_UP_EVENT:
|
||||
click_recognizer_handle_button_up(&data->click_manager.recognizers[e->button.button_id]);
|
||||
break;
|
||||
default:
|
||||
PBL_CROAK("Invalid event type: %u", e->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if ((e->button.button_id == BUTTON_ID_SELECT) && (e->type == PEBBLE_BUTTON_DOWN_EVENT)) {
|
||||
app_manager_put_launch_app_event(&(AppLaunchEventConfig) {
|
||||
.id = system_app_state_machine_get_last_registered_app(),
|
||||
.common.reason = APP_LAUNCH_USER,
|
||||
.common.button = e->button.button_id,
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void watchface_reset_click_manager(void) {
|
||||
WatchfaceData *data = &s_watchface_data;
|
||||
click_manager_reset(&data->click_manager);
|
||||
}
|
33
src/fw/shell/sdk/watchface.h
Normal file
33
src/fw/shell/sdk/watchface.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 "kernel/events.h"
|
||||
#include "process_management/app_install_manager.h"
|
||||
#include "services/common/compositor/compositor.h"
|
||||
|
||||
void watchface_init(void);
|
||||
|
||||
void watchface_handle_button_event(PebbleEvent *e);
|
||||
|
||||
AppInstallId watchface_get_default_install_id(void);
|
||||
|
||||
void watchface_launch_default(const CompositorTransition *animation);
|
||||
|
||||
void watchface_set_timeline_peek_enabled(bool visible);
|
||||
|
||||
void watchface_reset_click_manager(void);
|
29
src/fw/shell/shell.h
Normal file
29
src/fw/shell/shell.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* 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 "process_management/app_install_types.h"
|
||||
|
||||
typedef struct CompositorTransition CompositorTransition;
|
||||
|
||||
const CompositorTransition *shell_get_watchface_compositor_animation(bool watchface_is_destination);
|
||||
|
||||
const CompositorTransition *shell_get_close_compositor_animation(AppInstallId current_app_id,
|
||||
AppInstallId next_app_id);
|
||||
|
||||
const CompositorTransition *shell_get_open_compositor_animation(AppInstallId current_app_id,
|
||||
AppInstallId next_app_id);
|
25
src/fw/shell/shell_event_loop.h
Normal file
25
src/fw/shell/shell_event_loop.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 "kernel/events.h"
|
||||
|
||||
void shell_event_loop_init(void);
|
||||
|
||||
//! Handle events relating to the base firmware UI
|
||||
void NOINLINE shell_event_loop_handle_event(PebbleEvent *event);
|
||||
|
36
src/fw/shell/system_app_state_machine.h
Normal file
36
src/fw/shell/system_app_state_machine.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 "process_management/pebble_process_md.h"
|
||||
#include "process_management/app_install_types.h"
|
||||
|
||||
//! Get the very first app we should run at startup.
|
||||
const PebbleProcessMd* system_app_state_machine_system_start(void);
|
||||
|
||||
//! Retrieve the app that was last launched prior to the current one.
|
||||
AppInstallId system_app_state_machine_get_last_registered_app(void);
|
||||
|
||||
//! Get the app we should launch after an app has crashed or has been force quit.
|
||||
const PebbleProcessMd* system_app_state_machine_get_default_app(void);
|
||||
|
||||
//! Tell the state machine we just launched an app.
|
||||
void system_app_state_machine_register_app_launch(AppInstallId app_id);
|
||||
|
||||
//! Stop the normal state machine! Just show the panic app.
|
||||
void system_app_state_machine_panic(void);
|
||||
|
228
src/fw/shell/system_theme.c
Normal file
228
src/fw/shell/system_theme.c
Normal file
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* 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 "system_theme.h"
|
||||
|
||||
#include "applib/fonts/fonts.h"
|
||||
#include "apps/system_apps/settings/settings_notifications_private.h"
|
||||
#include "process_management/process_manager.h"
|
||||
#include "services/common/analytics/analytics.h"
|
||||
#include "shell/prefs.h"
|
||||
#include "syscall/syscall_internal.h"
|
||||
#include "system/passert.h"
|
||||
#include "util/size.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
typedef struct SystemThemeTextStyle {
|
||||
const char *fonts[TextStyleFontCount];
|
||||
} SystemThemeTextStyle;
|
||||
|
||||
////////////////////
|
||||
// Themes
|
||||
|
||||
static const SystemThemeTextStyle s_text_styles[NumPreferredContentSizes] = {
|
||||
[PreferredContentSizeSmall] = {
|
||||
.fonts = {
|
||||
[TextStyleFont_Header] = FONT_KEY_GOTHIC_18_BOLD,
|
||||
#if !RECOVERY_FW
|
||||
[TextStyleFont_Title] = FONT_KEY_GOTHIC_18_BOLD,
|
||||
[TextStyleFont_Body] = FONT_KEY_GOTHIC_18,
|
||||
#endif
|
||||
[TextStyleFont_Subtitle] = FONT_KEY_GOTHIC_18_BOLD,
|
||||
[TextStyleFont_Caption] = FONT_KEY_GOTHIC_14,
|
||||
[TextStyleFont_Footer] = FONT_KEY_GOTHIC_14,
|
||||
//! @note this is the same as the Title key (as that's what it's cloned from) until Small
|
||||
//! is designed
|
||||
[TextStyleFont_MenuCellTitle] = FONT_KEY_GOTHIC_18_BOLD,
|
||||
//! @note this is the same as Medium until Small is designed
|
||||
[TextStyleFont_MenuCellSubtitle] = FONT_KEY_GOTHIC_18,
|
||||
#if !RECOVERY_FW
|
||||
//! @note this is the same as Medium until Small is designed
|
||||
[TextStyleFont_TimeHeaderNumbers] = FONT_KEY_LECO_20_BOLD_NUMBERS,
|
||||
#endif
|
||||
//! @note this is the same as Medium until Small is designed
|
||||
[TextStyleFont_TimeHeaderWords] = FONT_KEY_GOTHIC_14_BOLD,
|
||||
//! @note this is the same as Medium until Small is designed
|
||||
[TextStyleFont_PinSubtitle] = FONT_KEY_GOTHIC_18,
|
||||
//! @note this is the same as Medium until Small is designed
|
||||
[TextStyleFont_ParagraphHeader] = FONT_KEY_GOTHIC_14,
|
||||
},
|
||||
},
|
||||
[PreferredContentSizeMedium] = {
|
||||
.fonts = {
|
||||
[TextStyleFont_Header] = FONT_KEY_GOTHIC_18_BOLD,
|
||||
#if !RECOVERY_FW
|
||||
[TextStyleFont_Title] = FONT_KEY_GOTHIC_24_BOLD,
|
||||
[TextStyleFont_Body] = FONT_KEY_GOTHIC_24_BOLD,
|
||||
#endif
|
||||
[TextStyleFont_Subtitle] = FONT_KEY_GOTHIC_24_BOLD,
|
||||
[TextStyleFont_Caption] = FONT_KEY_GOTHIC_14,
|
||||
[TextStyleFont_Footer] = FONT_KEY_GOTHIC_18,
|
||||
[TextStyleFont_MenuCellTitle] = FONT_KEY_GOTHIC_24_BOLD,
|
||||
[TextStyleFont_MenuCellSubtitle] = FONT_KEY_GOTHIC_18,
|
||||
#if !RECOVERY_FW
|
||||
[TextStyleFont_TimeHeaderNumbers] = FONT_KEY_LECO_20_BOLD_NUMBERS,
|
||||
#endif
|
||||
[TextStyleFont_TimeHeaderWords] = FONT_KEY_GOTHIC_14_BOLD,
|
||||
[TextStyleFont_PinSubtitle] = FONT_KEY_GOTHIC_18,
|
||||
[TextStyleFont_ParagraphHeader] = FONT_KEY_GOTHIC_14,
|
||||
},
|
||||
},
|
||||
[PreferredContentSizeLarge] = {
|
||||
.fonts = {
|
||||
[TextStyleFont_Header] = FONT_KEY_GOTHIC_24_BOLD,
|
||||
#if !RECOVERY_FW
|
||||
[TextStyleFont_Title] = FONT_KEY_GOTHIC_28_BOLD,
|
||||
[TextStyleFont_Body] = FONT_KEY_GOTHIC_28,
|
||||
#endif
|
||||
[TextStyleFont_Subtitle] = FONT_KEY_GOTHIC_28,
|
||||
[TextStyleFont_Caption] = FONT_KEY_GOTHIC_18,
|
||||
[TextStyleFont_Footer] = FONT_KEY_GOTHIC_18,
|
||||
[TextStyleFont_MenuCellTitle] = FONT_KEY_GOTHIC_28,
|
||||
[TextStyleFont_MenuCellSubtitle] = FONT_KEY_GOTHIC_24_BOLD,
|
||||
#if !RECOVERY_FW
|
||||
[TextStyleFont_TimeHeaderNumbers] = FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM,
|
||||
#endif
|
||||
[TextStyleFont_TimeHeaderWords] = FONT_KEY_GOTHIC_18_BOLD,
|
||||
[TextStyleFont_PinSubtitle] = FONT_KEY_GOTHIC_24,
|
||||
[TextStyleFont_ParagraphHeader] = FONT_KEY_GOTHIC_18_BOLD,
|
||||
},
|
||||
},
|
||||
[PreferredContentSizeExtraLarge] = {
|
||||
.fonts = {
|
||||
[TextStyleFont_Header] = FONT_KEY_GOTHIC_28_BOLD,
|
||||
#if !RECOVERY_FW
|
||||
[TextStyleFont_Title] = FONT_KEY_GOTHIC_36_BOLD,
|
||||
[TextStyleFont_Body] = FONT_KEY_GOTHIC_36,
|
||||
#endif
|
||||
//! @note this is the same as Large until ExtraLarge is designed
|
||||
[TextStyleFont_Subtitle] = FONT_KEY_GOTHIC_28,
|
||||
[TextStyleFont_Caption] = FONT_KEY_GOTHIC_24,
|
||||
[TextStyleFont_Footer] = FONT_KEY_GOTHIC_24,
|
||||
//! @note this is the same as Large until ExtraLarge is designed
|
||||
[TextStyleFont_MenuCellTitle] = FONT_KEY_GOTHIC_28,
|
||||
//! @note this is the same as Large until ExtraLarge is designed
|
||||
[TextStyleFont_MenuCellSubtitle] = FONT_KEY_GOTHIC_24_BOLD,
|
||||
#if !RECOVERY_FW
|
||||
//! @note this is the same as Large until ExtraLarge is designed
|
||||
[TextStyleFont_TimeHeaderNumbers] = FONT_KEY_LECO_26_BOLD_NUMBERS_AM_PM,
|
||||
#endif
|
||||
//! @note this is the same as Large until ExtraLarge is designed
|
||||
[TextStyleFont_TimeHeaderWords] = FONT_KEY_GOTHIC_18_BOLD,
|
||||
//! @note this is the same as Large until ExtraLarge is designed
|
||||
[TextStyleFont_PinSubtitle] = FONT_KEY_GOTHIC_24,
|
||||
[TextStyleFont_ParagraphHeader] = FONT_KEY_GOTHIC_18_BOLD,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
////////////////////
|
||||
// Helpers
|
||||
|
||||
static const char *prv_get_font_for_size(PreferredContentSize content_size, TextStyleFont font) {
|
||||
if (content_size >= NumPreferredContentSizes) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Requested a content size that is out of bounds (%d)", content_size);
|
||||
goto fail;
|
||||
} else if (font >= TextStyleFontCount) {
|
||||
PBL_LOG(LOG_LEVEL_ERROR, "Requested a style font that is out of bounds (%d)", font);
|
||||
goto fail;
|
||||
}
|
||||
return s_text_styles[content_size].fonts[font];
|
||||
fail:
|
||||
PRIVILEGE_WAS_ELEVATED ? syscall_failed() : WTF;
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// Public API
|
||||
|
||||
// *** WARNING WARNING WARNING ***
|
||||
// Be very careful when modifying this syscall. It currently returns a pointer
|
||||
// to constant data in flash, which unprivileged apps are allowed to read. But
|
||||
// if the data pointed to is ever moved to RAM, the syscall will need to be
|
||||
// changed to copy the data into a caller-provided buffer. Unprivileged apps
|
||||
// are not allowed to read kernel RAM, so they will fault if they attempt to
|
||||
// dereference a pointer into kernel RAM.
|
||||
DEFINE_SYSCALL(const char *, system_theme_get_font_key, TextStyleFont font) {
|
||||
return prv_get_font_for_size(system_theme_get_content_size(), font);
|
||||
}
|
||||
|
||||
// *** WARNING WARNING WARNING ***
|
||||
// Be very careful when modifying this syscall. It currently returns a pointer
|
||||
// to constant data in flash, which unprivileged apps are allowed to read.
|
||||
DEFINE_SYSCALL(const char *, system_theme_get_font_key_for_size, PreferredContentSize content_size,
|
||||
TextStyleFont font) {
|
||||
const PreferredContentSize size_on_runtime_platform =
|
||||
system_theme_convert_host_content_size_to_runtime_platform(content_size);
|
||||
return prv_get_font_for_size(size_on_runtime_platform, font);
|
||||
}
|
||||
|
||||
GFont system_theme_get_font(TextStyleFont font) {
|
||||
return fonts_get_system_font(system_theme_get_font_key(font));
|
||||
}
|
||||
|
||||
GFont system_theme_get_font_for_size(PreferredContentSize size, TextStyleFont font) {
|
||||
return fonts_get_system_font(system_theme_get_font_key_for_size(size, font));
|
||||
}
|
||||
|
||||
GFont system_theme_get_font_for_default_size(TextStyleFont font) {
|
||||
return fonts_get_system_font(system_theme_get_font_key_for_size(PreferredContentSizeDefault,
|
||||
font));
|
||||
}
|
||||
|
||||
static const PreferredContentSize s_platform_default_content_sizes[] = {
|
||||
[PlatformTypeAplite] = PreferredContentSizeMedium,
|
||||
[PlatformTypeBasalt] = PreferredContentSizeMedium,
|
||||
[PlatformTypeChalk] = PreferredContentSizeMedium,
|
||||
[PlatformTypeDiorite] = PreferredContentSizeMedium,
|
||||
[PlatformTypeEmery] = PreferredContentSizeLarge,
|
||||
};
|
||||
|
||||
T_STATIC PreferredContentSize prv_convert_content_size_between_platforms(PreferredContentSize size,
|
||||
PlatformType from_platform,
|
||||
PlatformType to_platform) {
|
||||
const size_t num_platform_default_content_sizes = ARRAY_LENGTH(s_platform_default_content_sizes);
|
||||
PBL_ASSERTN(from_platform < num_platform_default_content_sizes);
|
||||
PBL_ASSERTN(to_platform < num_platform_default_content_sizes);
|
||||
|
||||
const PreferredContentSize from_platform_default_size =
|
||||
s_platform_default_content_sizes[from_platform];
|
||||
const PreferredContentSize to_platform_default_size =
|
||||
s_platform_default_content_sizes[to_platform];
|
||||
const int resulting_size = size + (to_platform_default_size - from_platform_default_size);
|
||||
return (PreferredContentSize)CLIP(resulting_size, 0, (NumPreferredContentSizes - 1));
|
||||
}
|
||||
|
||||
PreferredContentSize system_theme_get_default_content_size_for_runtime_platform(void) {
|
||||
const PlatformType runtime_platform = process_manager_current_platform();
|
||||
return prv_convert_content_size_between_platforms(PreferredContentSizeDefault,
|
||||
PBL_PLATFORM_TYPE_CURRENT,
|
||||
runtime_platform);
|
||||
}
|
||||
|
||||
PreferredContentSize system_theme_convert_host_content_size_to_runtime_platform(
|
||||
PreferredContentSize size) {
|
||||
const PlatformType runtime_platform = process_manager_current_platform();
|
||||
return prv_convert_content_size_between_platforms(size, PBL_PLATFORM_TYPE_CURRENT,
|
||||
runtime_platform);
|
||||
}
|
||||
|
||||
void analytics_external_collect_system_theme_settings(void) {
|
||||
const SettingsContentSize content_size =
|
||||
settings_content_size_from_preferred_size(system_theme_get_content_size());
|
||||
analytics_set(ANALYTICS_DEVICE_METRIC_SYSTEM_THEME_TEXT_STYLE, content_size,
|
||||
AnalyticsClient_System);
|
||||
}
|
111
src/fw/shell/system_theme.h
Normal file
111
src/fw/shell/system_theme.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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 "applib/fonts/fonts.h"
|
||||
#include "applib/platform.h"
|
||||
#include "applib/preferred_content_size.h"
|
||||
|
||||
//! System Theme Text Style is a font collection used to unify text styles across the system.
|
||||
//! It contains a variety of different font sizes for use in an application, each meant for a
|
||||
//! distinct class of use cases. Each font type will resize based on the user's preferences.
|
||||
//! Consumers should attempt to have a complete mapping of their font types to the system style.
|
||||
|
||||
typedef enum TextStyleFont {
|
||||
//! Header is for metadata text that gives extra context for the user, such as which application
|
||||
//! a notification belongs to, or who sent a message. It is smaller than the body copy when
|
||||
//! readable, but always bold.
|
||||
TextStyleFont_Header,
|
||||
#if !RECOVERY_FW
|
||||
//! Title is for prominent text that is usually the title or name of the content, giving context
|
||||
//! for the user such as the subject line of an email. It is comparable to the body, but
|
||||
//! always bold.
|
||||
TextStyleFont_Title,
|
||||
//! Body is for body text that can be long stretches of text, such as the body of an email.
|
||||
TextStyleFont_Body,
|
||||
#endif
|
||||
//! Subtitle is for subtitle text that should be prominent, such as the title of a section in a
|
||||
//! larger content matter. Subtitle can also be used when the title should be large, but not
|
||||
//! prominent. It is comparable to the title, but not bold for the Larger theme.
|
||||
TextStyleFont_Subtitle,
|
||||
//! Caption is for a contextual description text of a subject in a larger content matter.
|
||||
//! It is usually smaller than the footer.
|
||||
TextStyleFont_Caption,
|
||||
//! Footer is for metadata text that the user may be interested in after consuming the main
|
||||
//! content, such as age of a notification. It is smaller than the body copy.
|
||||
TextStyleFont_Footer,
|
||||
//! For titles of menu cells that identify an item of a list.
|
||||
TextStyleFont_MenuCellTitle,
|
||||
//! For subtitles of menu cells that provide auxiliary information about an item of a list.
|
||||
TextStyleFont_MenuCellSubtitle,
|
||||
#if !RECOVERY_FW
|
||||
//! Time Header Numbers is used specially by Timeline to display content time in extra bold e.g.
|
||||
//! 4:06 AM. It is comparable to the title.
|
||||
TextStyleFont_TimeHeaderNumbers,
|
||||
#endif
|
||||
//! Time Header Words is used in conjunction with its numbers counterpart for AM PM. The size is
|
||||
//! significantly smaller than the numbers counterpart to display AM PM in capitals.
|
||||
TextStyleFont_TimeHeaderWords,
|
||||
//! Pin Subtitle is for subtitle text where the text box is small used specially by Timeline.
|
||||
//! It is smaller than the title and is not bold.
|
||||
TextStyleFont_PinSubtitle,
|
||||
//! Paragraph Header is for text that describes the content of a body paragraph. The size is
|
||||
//! smaller than both Body and Header.
|
||||
TextStyleFont_ParagraphHeader,
|
||||
|
||||
TextStyleFontCount
|
||||
} TextStyleFont;
|
||||
|
||||
|
||||
//! @param font The desired font class to obtain a font key of.
|
||||
//! @return The font key of the font class using the user's preferred content size.
|
||||
const char *system_theme_get_font_key(TextStyleFont font);
|
||||
|
||||
//! @param content_size The desired content size.
|
||||
//! @param font The desired font class to obtain a font key of.
|
||||
//! @return The font key of the given content size and font class.
|
||||
const char *system_theme_get_font_key_for_size(PreferredContentSize size, TextStyleFont font);
|
||||
|
||||
//! @param font The desired font class to obtain a font of.
|
||||
//! @return The font of the font class using the user's preferred content size.
|
||||
GFont system_theme_get_font(TextStyleFont font);
|
||||
|
||||
//! @param content_size The desired content size.
|
||||
//! @param font The desired font class to obtain a font of.
|
||||
//! @return The font of the given content size and font class.
|
||||
GFont system_theme_get_font_for_size(PreferredContentSize size, TextStyleFont font);
|
||||
|
||||
//! @param font The desired font class for which to obtain a font for the runtime platform's default
|
||||
//! size.
|
||||
//! @return The font of the given font class for the runtime platform's default size.
|
||||
GFont system_theme_get_font_for_default_size(TextStyleFont font);
|
||||
|
||||
//! @param content_size The user's desired content size.
|
||||
void system_theme_set_content_size(PreferredContentSize content_size);
|
||||
|
||||
//! @return The user's preferred content size.
|
||||
PreferredContentSize system_theme_get_content_size(void);
|
||||
|
||||
//! @return The default content size for the current runtime platform
|
||||
PreferredContentSize system_theme_get_default_content_size_for_runtime_platform(void);
|
||||
|
||||
//! @param size The \ref PreferredContentSize to convert from the host to the current runtime
|
||||
//! platform
|
||||
//! @return The input \ref PreferredContentSize converted from the host to the current runtime
|
||||
//! platform
|
||||
PreferredContentSize system_theme_convert_host_content_size_to_runtime_platform(
|
||||
PreferredContentSize size);
|
209
src/fw/shell/wscript
Normal file
209
src/fw/shell/wscript
Normal file
|
@ -0,0 +1,209 @@
|
|||
import json
|
||||
|
||||
def configure(conf):
|
||||
pass
|
||||
|
||||
def generate_apps_table(task):
|
||||
in_node = task.inputs[0]
|
||||
registry = task.outputs[0]
|
||||
app_enum = task.outputs[1]
|
||||
# apps that are assumed to exist by source and must always have a define
|
||||
default_app_enums = [
|
||||
'ALARMS',
|
||||
'GOLF',
|
||||
'MUSIC',
|
||||
'SETTINGS',
|
||||
'SPORTS',
|
||||
]
|
||||
system_apps = []
|
||||
resource_apps = []
|
||||
definition = {}
|
||||
with open(in_node.abspath(), 'r') as f_in:
|
||||
definition.update(json.load(f_in))
|
||||
|
||||
system_apps.extend(definition['system_apps'])
|
||||
resource_apps.extend(definition['resource_apps'])
|
||||
|
||||
def uuid_to_byte_hex_str(uuid):
|
||||
uuid = uuid.replace("-", "")
|
||||
|
||||
# split string into groups of twos
|
||||
n = 2
|
||||
pieces = [uuid[i:i+n] for i in range(0, len(uuid), n)]
|
||||
# prefix every group of two with 0x and add commas
|
||||
hex_str = "0x" + ", 0x".join(pieces)
|
||||
return hex_str
|
||||
|
||||
def generate_registry(f_out):
|
||||
# write generated header
|
||||
f_out.write('// @'+'generated -- DO NOT EDIT\n\n')
|
||||
|
||||
# write out includes
|
||||
f_out.write('#include "system_app_ids.auto.h"\n')
|
||||
f_out.write('#include "process_management/pebble_process_md.h"\n')
|
||||
f_out.write('#include "resource/resource_ids.auto.h"\n\n')
|
||||
|
||||
# zeroed app id's indicate disabled apps
|
||||
enabled_system_apps = filter(lambda e: e['id'] != 0, system_apps)
|
||||
|
||||
# write out function declarations for system apps
|
||||
for entry in enabled_system_apps:
|
||||
f_out.write('extern const PebbleProcessMd *'
|
||||
"{cb_str}(void);\n".format(
|
||||
cb_str=entry['md_fn']))
|
||||
|
||||
# write variable name
|
||||
f_out.write('\n\nstatic const AppRegistryEntry '
|
||||
'APP_RECORDS[] = {\n')
|
||||
|
||||
# comment for system apps
|
||||
f_out.write('\n // System Apps\n')
|
||||
|
||||
# write all entries for system apps
|
||||
for entry in enabled_system_apps:
|
||||
f_out.write(
|
||||
' {{\n'
|
||||
' .id = APP_ID_{enum},\n'
|
||||
' .type = AppInstallStorageFw,\n'
|
||||
' .md_fn = &{cb_str},\n'
|
||||
' .color.argb = {color_argb8},\n'
|
||||
' }},\n'.format(
|
||||
enum=entry['enum'],
|
||||
cb_str=entry['md_fn'],
|
||||
color_argb8=entry.get('color_argb8', 'GColorClearARGB8')))
|
||||
|
||||
# comment for resource apps
|
||||
f_out.write('\n // Resource (stored) Apps\n')
|
||||
|
||||
# write all entries for resource apps
|
||||
for entry in resource_apps:
|
||||
f_out.write(
|
||||
' {{\n'
|
||||
' .id = APP_ID_{enum},\n'
|
||||
' .type = AppInstallStorageResources,\n'
|
||||
' .name = "{name}",\n'
|
||||
' .uuid = {{ {uuid} }},\n'
|
||||
' .bin_resource_id = {bin_id},\n'
|
||||
' .icon_resource_id = {icon_id},\n'
|
||||
' .color.argb = {color_argb8},\n'
|
||||
' }},\n'.format(
|
||||
enum=entry['enum'],
|
||||
name=entry['name'],
|
||||
uuid=uuid_to_byte_hex_str(entry['uuid']),
|
||||
bin_id=entry['bin_resource_id'],
|
||||
icon_id=entry['icon_resource_id'],
|
||||
color_argb8=entry.get('color_argb8', 'GColorClearARGB8')))
|
||||
|
||||
f_out.write('};\n')
|
||||
|
||||
def generate_app_enum(f_out):
|
||||
# write generated header
|
||||
f_out.write('// @'+'generated -- DO NOT EDIT\n\n')
|
||||
|
||||
# write out includes
|
||||
f_out.write('#include "process_management/app_install_types.h"\n\n')
|
||||
|
||||
# write all entries for resource apps
|
||||
built_app_enums = []
|
||||
|
||||
def write_app_enum(enum, id):
|
||||
f_out.write('#define APP_ID_{enum} ((AppInstallId) {id})\n'.format(enum=enum, id=id))
|
||||
|
||||
for entry in (system_apps + resource_apps):
|
||||
built_app_enums.append(entry['enum'])
|
||||
write_app_enum(enum=entry['enum'], id=entry['id'])
|
||||
|
||||
# write remaining default apps that are not being built
|
||||
for enum in [x for x in default_app_enums if x not in built_app_enums]:
|
||||
write_app_enum(enum=enum, id=0)
|
||||
|
||||
|
||||
def _entry_in_test_apps_list(entry):
|
||||
return str(entry['id']) in task.env.test_apps_list
|
||||
|
||||
# If the current build config allows the app with the given list of defines to be added.
|
||||
# Disabled entries have no entry in the app registry.
|
||||
def entry_enabled(entry):
|
||||
|
||||
defines = entry.get("ifdefs") or []
|
||||
platforms = entry.get("target_platforms") or []
|
||||
|
||||
# Disabled overrides any define
|
||||
if "DISABLED" in defines:
|
||||
return False
|
||||
|
||||
if (platforms) and (task.generator.bld.get_platform_name() not in platforms):
|
||||
# If we have a platform list and the current platform isn't listed, return false
|
||||
return False
|
||||
|
||||
# keep around for legacy purposes
|
||||
if "DEFAULT" in defines:
|
||||
return True
|
||||
|
||||
if defines:
|
||||
# If we have a defines list, run through all of them and see if we should be including
|
||||
# this application
|
||||
|
||||
# if test apps are enabled and we have a list
|
||||
test_apps_enabled = "ENABLE_TEST_APPS" in task.env.DEFINES
|
||||
entry_is_test_app = "ENABLE_TEST_APPS" in defines
|
||||
has_test_app_list = len(task.env.test_apps_list) > 0
|
||||
if test_apps_enabled and has_test_app_list:
|
||||
# check if app is allowed. If not, return False
|
||||
desired_test_app = _entry_in_test_apps_list(entry)
|
||||
if entry_is_test_app and not desired_test_app:
|
||||
return False
|
||||
else:
|
||||
if entry_is_test_app and not test_apps_enabled:
|
||||
return False
|
||||
|
||||
if all(define in task.env.DEFINES for define in defines):
|
||||
return True
|
||||
else:
|
||||
# if there are no defines, it means we should include it by default
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _set_disabled_app_ids_to_zero():
|
||||
for entry in system_apps:
|
||||
if not entry_enabled(entry):
|
||||
entry['id'] = 0
|
||||
|
||||
def _move_desired_test_apps_to_top():
|
||||
for app_list in [system_apps, resource_apps]:
|
||||
temp_list = []
|
||||
for entry in app_list:
|
||||
if _entry_in_test_apps_list(entry):
|
||||
temp_list.insert(0, entry)
|
||||
else:
|
||||
temp_list.append(entry)
|
||||
app_list[:] = temp_list
|
||||
|
||||
_set_disabled_app_ids_to_zero()
|
||||
_move_desired_test_apps_to_top()
|
||||
|
||||
with open(registry.abspath(), 'w') as f_registry:
|
||||
generate_registry(f_registry)
|
||||
|
||||
with open(app_enum.abspath(), 'w') as f_app_enum:
|
||||
generate_app_enum(f_app_enum)
|
||||
|
||||
|
||||
def build(bld):
|
||||
if bld.variant == 'prf':
|
||||
shell_name = 'prf'
|
||||
else:
|
||||
shell_name = bld.env.NORMAL_SHELL
|
||||
|
||||
in_node = bld.path.find_node('%s/system_app_registry_list.json' % shell_name)
|
||||
|
||||
registry = bld.path.get_bld().make_node('system_app_registry_list.auto.h')
|
||||
app_enum = bld.path.get_bld().make_node('system_app_ids.auto.h')
|
||||
|
||||
bld(rule=generate_apps_table,
|
||||
source=[in_node],
|
||||
target=[registry, app_enum],
|
||||
vars=['DEFINES', 'test_apps_list'])
|
||||
|
||||
# vim:filetype=python
|
Loading…
Add table
Add a link
Reference in a new issue