Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/fw/apps/system/quick_launch_nothing.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2026 Core Devices LLC */
/* SPDX-License-Identifier: Apache-2.0 */

//! The "Nothing" Quick Launch action. Binding a button to it makes the button
//! inert: the shell recognizes the app id and never launches anything.

#include "quick_launch_nothing.h"

#include "pbl/services/i18n/i18n.h"

static void prv_main(void) {
// Never runs; the shell drops the launch before it gets here.
}

const PebbleProcessMd *quick_launch_nothing_get_app_info(void) {
static const PebbleProcessMdSystem s_app_info = {
.common = {
.main_func = &prv_main,
.uuid = QUICK_LAUNCH_NOTHING_UUID,
.visibility = ProcessVisibilityQuickLaunch,
},
/// Quick Launch action that makes the button do nothing at all.
.name = i18n_noop("Nothing"),
};
return &s_app_info.common;
}
11 changes: 11 additions & 0 deletions src/fw/apps/system/quick_launch_nothing.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* SPDX-FileCopyrightText: 2026 Core Devices LLC */
/* SPDX-License-Identifier: Apache-2.0 */

#pragma once

#include "process_management/app_manager.h"

#define QUICK_LAUNCH_NOTHING_UUID {0xde, 0x6d, 0xa1, 0x7f, 0x1a, 0x10, 0x47, 0x25, \
0xad, 0xbb, 0x1e, 0xfc, 0x22, 0xe4, 0x3f, 0x04}

const PebbleProcessMd *quick_launch_nothing_get_app_info(void);
66 changes: 58 additions & 8 deletions src/fw/apps/system/settings/quick_launch.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
/* SPDX-License-Identifier: Apache-2.0 */

//! This file displays the main Quick Launch menu that is found in our settings menu
//! It allows the feature to be enabled or for an app to be set
//! The list of apps that the user can choose from is found in settings_quick_launch_app_menu.c
//! It allows the feature to be enabled or for an action or app to be set
//! The list of targets that the user can choose from is found in quick_launch_app_menu.c
//! This file is also responsible for saving / storing the uuid of each quichlaunch app as well as
//! whether or not the quicklaunch app is enabled.

#include "menu.h"
#include "option_menu.h"
#include "quick_launch.h"
#include "quick_launch_app_menu.h"
#include "quick_launch_setup_menu.h"
Expand All @@ -18,6 +19,7 @@
#include "applib/app_launch_reason.h"
#include "applib/ui/window_stack.h"
#include "kernel/pbl_malloc.h"
#include "process_management/app_install_manager.h"
#include "process_management/app_menu_data_source.h"
#include "resource/resource_ids.auto.h"
#include "pbl/services/i18n/i18n.h"
Expand All @@ -36,9 +38,19 @@ typedef enum {
ROW_HOLD_BACK,
} QuickLaunchRow;

typedef enum {
CATEGORY_ROW_ACTIONS = 0,
CATEGORY_ROW_APPS,

CategoryRowCount,
} QuickLaunchCategoryRow;

typedef struct QuickLaunchData {
SettingsCallbacks callbacks;
char app_names[NUM_ROWS][APP_NAME_SIZE_BYTES];
//! The button whose binding the pushed sub-menus are editing.
ButtonId button;
bool is_tap;
} QuickLaunchData;

static const char *s_row_titles[NUM_ROWS] = {
Expand All @@ -56,11 +68,18 @@ static const char *s_row_titles[NUM_ROWS] = {
[ROW_HOLD_BACK] = i18n_noop("Hold Back"),
};

static const char *s_category_row_titles[CategoryRowCount] = {
/// Shown in Quick Launch Settings as the title of the list of actions.
[CATEGORY_ROW_ACTIONS] = i18n_noop("Actions"),
/// Shown in Quick Launch Settings as the title of the list of apps.
[CATEGORY_ROW_APPS] = i18n_noop("Apps"),
};

static void prv_get_subtitle_string(AppInstallId app_id, QuickLaunchData *data,
char *buffer, uint8_t buf_len) {
if (app_id == INSTALL_ID_INVALID) {
/// Shown in Quick Launch Settings when the button is disabled.
i18n_get_with_buffer("Disabled", buffer, buf_len);
/// Shown in Quick Launch Settings when no action or app is bound to the button.
i18n_get_with_buffer("Unassigned", buffer, buf_len);
return;
} else {
AppInstallEntry entry;
Expand Down Expand Up @@ -125,12 +144,40 @@ static uint16_t prv_get_initial_selection_cb(SettingsCallbacks *context) {
return 0;
}

//! The row to start on, which is the category the button is currently bound to.
static QuickLaunchCategoryRow prv_get_category_row(QuickLaunchData *data) {
const AppInstallId app_id = data->is_tap ? quick_launch_single_click_get_app(data->button)
: quick_launch_get_app(data->button);
AppInstallEntry entry;
if ((app_id == INSTALL_ID_INVALID) || !app_install_get_entry_for_install_id(app_id, &entry)) {
return CATEGORY_ROW_ACTIONS;
}
return app_install_entry_is_quick_launch_visible_only(&entry) ? CATEGORY_ROW_ACTIONS
: CATEGORY_ROW_APPS;
}

static void prv_category_menu_select(OptionMenu *option_menu, int selection, void *context) {
QuickLaunchData *data = settings_option_menu_get_context(context);
const QuickLaunchMenuCategory category = (selection == CATEGORY_ROW_ACTIONS) ?
QuickLaunchMenuCategoryActions : QuickLaunchMenuCategoryApps;
quick_launch_app_menu_window_push(data->button, data->is_tap, category, &option_menu->window);
}

static void prv_category_menu_push(QuickLaunchData *data) {
const OptionMenuCallbacks callbacks = {
.select = prv_category_menu_select,
};
settings_option_menu_push(i18n_noop("Quick Launch"), OptionMenuContentType_SingleLine,
prv_get_category_row(data), &callbacks, CategoryRowCount,
false /* icons_enabled */, s_category_row_titles, data);
}

static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) {
PBL_ASSERTN(row < NUM_ROWS);

ButtonId button;
bool is_tap;

switch (row) {
case ROW_TAP_UP:
button = BUTTON_ID_UP;
Expand Down Expand Up @@ -159,8 +206,11 @@ static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) {
default:
return;
}

quick_launch_app_menu_window_push(button, is_tap);

QuickLaunchData *data = (QuickLaunchData *)context;
data->button = button;
data->is_tap = is_tap;
prv_category_menu_push(data);
}

static uint16_t prv_num_rows_cb(SettingsCallbacks *context) {
Expand Down
70 changes: 50 additions & 20 deletions src/fw/apps/system/settings/quick_launch_app_menu.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/* SPDX-FileCopyrightText: 2024 Google LLC */
/* SPDX-License-Identifier: Apache-2.0 */

//! This file generates a menu that lets the user select a quicklaunch app
//! The menu that is generated is the same as the "main menu" but with a
//! title
//! This file generates the menu that lets the user pick a Quick Launch target
//! within one category, either an action or an app.

#include "quick_launch_app_menu.h"
#include "quick_launch_setup_menu.h"
Expand All @@ -28,12 +27,32 @@ typedef struct {
AppMenuDataSource data_source;
ButtonId button;
bool is_tap;
int16_t selected;
QuickLaunchMenuCategory category;
Window *parent;
OptionMenu *option_menu;
} QuickLaunchAppMenuData;

//! Row 0 of both categories un-assigns the button; the data source starts after it.
#define NUM_CUSTOM_CELLS 1

static const char *s_category_titles[] = {
/// Title of the Quick Launch menu listing the available actions.
[QuickLaunchMenuCategoryActions] = i18n_noop("Actions"),
/// Title of the Quick Launch menu listing the installed apps.
[QuickLaunchMenuCategoryApps] = i18n_noop("Apps"),
};

//! Row 0 when nothing is bound, the matching entry when it is in this category, and no
//! selection at all when the button is bound to something in the other category.
static int prv_choice(AppInstallId install_id, uint16_t app_index) {
if (install_id == INSTALL_ID_INVALID) {
return 0;
}
if (app_index == MENU_INDEX_NOT_FOUND) {
return OPTION_MENU_CHOICE_NONE;
}
return (int)app_index + NUM_CUSTOM_CELLS;
}

/* Callback Functions */

Expand All @@ -42,15 +61,21 @@ static bool prv_app_filter_callback(struct AppMenuDataSource *source, AppInstall
const Uuid timeline_future_uuid = TIMELINE_UUID_INIT;
const Uuid timeline_past_uuid = TIMELINE_PAST_UUID_INIT;
const Uuid timeline_full_uuid = TIMELINE_FULL_UUID_INIT;

if (app_install_entry_is_watchface(entry)) {
return false; // Skip watchfaces
}
if (app_install_entry_is_hidden(entry) &&
!app_install_entry_is_quick_launch_visible_only(entry)) {
return false; // Skip hidden apps unless they are quick launch visible

// Quick launch visible only entries are the actions, everything else is an app.
const bool is_action = app_install_entry_is_quick_launch_visible_only(entry);
if (data->category == QuickLaunchMenuCategoryActions) {
if (!is_action) {
return false;
}
} else if (is_action || app_install_entry_is_hidden(entry)) {
return false;
}

// For tap buttons, filter Timeline apps based on button
if (data->is_tap) {
if (data->button == BUTTON_ID_UP) {
Expand All @@ -71,7 +96,7 @@ static bool prv_app_filter_callback(struct AppMenuDataSource *source, AppInstall
}
}
}

return true;
}

Expand All @@ -84,9 +109,10 @@ static void prv_menu_draw_row(OptionMenu *option_menu, GContext* ctx, const Laye
const GRect *text_frame, uint32_t row, bool selected, void *context) {

QuickLaunchAppMenuData *data = context;
const char *text = NULL;
const char *text;
if (row == 0) {
text = i18n_get("Disable", data);
/// Shown in Quick Launch Settings when no action or app is bound to the button.
text = i18n_get("Unassigned", data);
} else {
AppMenuNode *node = app_menu_data_source_get_node_at_index(&data->data_source,
row - NUM_CUSTOM_CELLS);
Expand All @@ -107,17 +133,18 @@ static void prv_menu_select(OptionMenu *option_menu, int selection, void *contex
quick_launch_set_app(data->button, INSTALL_ID_INVALID);
quick_launch_set_enabled(data->button, false);
}
app_window_stack_pop(true);
} else {
AppMenuNode* app_menu_node =
AppMenuNode *app_menu_node =
app_menu_data_source_get_node_at_index(&data->data_source, selection - NUM_CUSTOM_CELLS);
if (data->is_tap) {
quick_launch_single_click_set_app(data->button, app_menu_node->install_id);
} else {
quick_launch_set_app(data->button, app_menu_node->install_id);
}
app_window_stack_pop(true);
}
// Unwind the category menu too, so that selecting returns to the Quick Launch settings.
app_window_stack_remove(data->parent, false);
app_window_stack_pop(true);
}

static void prv_menu_reload_data(void *context) {
Expand All @@ -134,10 +161,13 @@ static void prv_menu_unload(OptionMenu *option_menu, void *context) {
app_free(data);
}

void quick_launch_app_menu_window_push(ButtonId button, bool is_tap) {
void quick_launch_app_menu_window_push(ButtonId button, bool is_tap,
QuickLaunchMenuCategory category, Window *parent) {
QuickLaunchAppMenuData *data = app_zalloc_check(sizeof(*data));
data->button = button;
data->is_tap = is_tap;
data->category = category;
data->parent = parent;

OptionMenu *option_menu = option_menu_create();
data->option_menu = option_menu;
Expand All @@ -149,13 +179,13 @@ void quick_launch_app_menu_window_push(ButtonId button, bool is_tap) {

const AppInstallId install_id = is_tap ? quick_launch_single_click_get_app(button)
: quick_launch_get_app(button);
const int app_index = app_menu_data_source_get_index_of_app_with_install_id(&data->data_source,
install_id);
const uint16_t app_index =
app_menu_data_source_get_index_of_app_with_install_id(&data->data_source, install_id);

GColor highlight_bg = shell_prefs_get_theme_highlight_color();
const OptionMenuConfig config = {
.title = i18n_get(i18n_noop("Quick Launch"), data),
.choice = (install_id == INSTALL_ID_INVALID) ? 0 : (app_index + NUM_CUSTOM_CELLS),
.title = i18n_get(s_category_titles[category], data),
.choice = prv_choice(install_id, app_index),
.status_colors = { GColorWhite, GColorBlack, },
.highlight_colors = { highlight_bg, gcolor_legible_over(highlight_bg) },
.icons_enabled = true,
Expand Down
14 changes: 13 additions & 1 deletion src/fw/apps/system/settings/quick_launch_app_menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@
#pragma once

#include "shell/normal/quick_launch.h"

#include "applib/ui/window.h"

#include <stdbool.h>

void quick_launch_app_menu_window_push(ButtonId button, bool is_tap);
typedef enum QuickLaunchMenuCategory {
//! Entries that are only visible in Quick Launch, e.g. the system toggles.
QuickLaunchMenuCategoryActions,
//! Regular launcher apps.
QuickLaunchMenuCategoryApps,
} QuickLaunchMenuCategory;

//! @param parent The category menu to unwind along with this one on selection.
void quick_launch_app_menu_window_push(ButtonId button, bool is_tap,
QuickLaunchMenuCategory category, Window *parent);
3 changes: 2 additions & 1 deletion src/fw/apps/system/toggle/airplane_mode.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ const PebbleProcessMd *airplane_mode_toggle_get_app_info(void) {
.uuid = AIRPLANE_MODE_TOGGLE_UUID,
.visibility = ProcessVisibilityQuickLaunch,
},
.name = i18n_noop("Airplane Mode"),
/// The Quick Launch action that toggles Airplane Mode.
.name = i18n_noop("Toggle Airplane Mode"),
};
return &s_app_info.common;
}
3 changes: 2 additions & 1 deletion src/fw/apps/system/toggle/backlight_state.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ const PebbleProcessMd *backlight_state_toggle_get_app_info(void) {
.uuid = BACKLIGHT_STATE_TOGGLE_UUID,
.visibility = ProcessVisibilityQuickLaunch,
},
.name = i18n_noop("Backlight"),
/// The Quick Launch action that toggles the backlight.
.name = i18n_noop("Toggle Backlight"),
};
return &s_app_info.common;
}
3 changes: 2 additions & 1 deletion src/fw/apps/system/toggle/motion_backlight.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ const PebbleProcessMd *motion_backlight_toggle_get_app_info(void) {
.uuid = MOTION_BACKLIGHT_TOGGLE_UUID,
.visibility = ProcessVisibilityQuickLaunch,
},
.name = i18n_noop("Motion Backlight"),
/// The Quick Launch action that toggles the motion backlight.
.name = i18n_noop("Toggle Motion Backlight"),
};
return &s_app_info.common;
}
3 changes: 2 additions & 1 deletion src/fw/apps/system/toggle/quiet_time.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const PebbleProcessMd *quiet_time_toggle_get_app_info(void) {
.uuid = QUIET_TIME_TOGGLE_UUID,
.visibility = ProcessVisibilityQuickLaunch,
},
.name = i18n_noop("Quiet Time"),
/// The Quick Launch action that toggles Quiet Time.
.name = i18n_noop("Toggle Quiet Time"),
};
return &s_app_info.common;
}
5 changes: 5 additions & 0 deletions src/fw/shell/normal/system_app_registry_list.json
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,11 @@
"id": -100,
"enum": "NOTIFICATIONS_CLEAR_HISTORY",
"md_fn": "notifications_clear_history_app_get_info"
},
{
"id": -101,
"enum": "QUICK_LAUNCH_NOTHING",
"md_fn": "quick_launch_nothing_get_app_info"
}
],
"resource_apps": [
Expand Down
Loading
Loading