Skip to content
Draft
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
93 changes: 80 additions & 13 deletions src/fw/apps/system/settings/quick_launch_app_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "quick_launch_app_menu.h"
#include "quick_launch_setup_menu.h"
#include "quick_launch_two_clicks.h"
#include "quick_launch.h"
#include "menu.h"
#include "option_menu.h"
Expand All @@ -16,10 +17,12 @@
#include "applib/ui/app_window_stack.h"
#include "applib/ui/option_menu_window.h"
#include "applib/ui/window_stack.h"
#include "apps/system_app_ids.h"
#include "kernel/pbl_malloc.h"
#include "services/common/i18n/i18n.h"
#include "process_management/app_install_manager.h"
#include "apps/system/timeline/timeline.h"
#include "apps/system/two_clicks_launcher.h"
#include "process_management/app_menu_data_source.h"
#include "resource/resource_ids.auto.h"
#include "shell/prefs.h"
Expand All @@ -30,6 +33,8 @@ typedef struct {
bool is_tap;
int16_t selected;
OptionMenu *option_menu;
bool is_two_clicks;
ButtonId second_button;
} QuickLaunchAppMenuData;

#define NUM_CUSTOM_CELLS 1
Expand All @@ -42,6 +47,7 @@ static bool prv_app_filter_callback(struct AppMenuDataSource *source, AppInstall
const Uuid timeline_uuid = TIMELINE_UUID_INIT;
const Uuid timeline_past_uuid = TIMELINE_PAST_UUID_INIT;
const Uuid health_uuid = UUID_HEALTH_DATA_SOURCE;
const Uuid two_clicks_uuid = TWO_CLICKS_LAUNCHER_UUID_INIT;

if (app_install_entry_is_watchface(entry)) {
return false; // Skip watchfaces
Expand All @@ -50,21 +56,27 @@ static bool prv_app_filter_callback(struct AppMenuDataSource *source, AppInstall
!app_install_entry_is_quick_launch_visible_only(entry)) {
return false; // Skip hidden apps unless they are quick launch visible
}

if (data->is_two_clicks && uuid_equal(&entry->uuid, &two_clicks_uuid)) {
return false;
}

ButtonId buttonIdCheck = data->is_two_clicks ? data->second_button : data->button;

// For tap buttons, filter Timeline apps based on button
if (data->is_tap) {
if (data->button == BUTTON_ID_UP) {
if (data->is_tap || data->is_two_clicks) {
if (buttonIdCheck == BUTTON_ID_UP) {
// Tap Up: Only show Timeline Past, hide Timeline Future
if (uuid_equal(&entry->uuid, &timeline_uuid)) {
return false;
}
} else if (data->button == BUTTON_ID_DOWN) {
} else if (buttonIdCheck == BUTTON_ID_DOWN) {
// Tap Down: Only show Timeline Future, hide Timeline Past
if (uuid_equal(&entry->uuid, &timeline_past_uuid)) {
return false;
}
// We also only want the Health app for Tap Up
if (uuid_equal(&entry->uuid, &health_uuid)) {
// We also only want the Health app for Tap Up when not in Two Clicks Quick Launch
if (uuid_equal(&entry->uuid, &health_uuid) && !data->is_two_clicks) {
return false;
}
} else {
Expand Down Expand Up @@ -97,11 +109,9 @@ static void prv_menu_draw_row(OptionMenu *option_menu, GContext* ctx, const Laye
option_menu_system_draw_row(option_menu, ctx, cell_layer, text_frame, text, selected, context);
}

static void prv_menu_select(OptionMenu *option_menu, int selection, void *context) {
window_set_click_config_provider(&option_menu->window, NULL);

QuickLaunchAppMenuData *data = context;
static void prv_menu_select_ql(OptionMenu *option_menu, int selection, QuickLaunchAppMenuData *data) {
if (selection == 0) {
window_set_click_config_provider(&option_menu->window, NULL);
if (data->is_tap) {
quick_launch_single_click_set_app(data->button, INSTALL_ID_INVALID);
quick_launch_single_click_set_enabled(data->button, false);
Expand All @@ -118,7 +128,51 @@ static void prv_menu_select(OptionMenu *option_menu, int selection, void *contex
} else {
quick_launch_set_app(data->button, app_menu_node->install_id);
}

if (app_menu_node->install_id == APP_ID_TWO_CLICKS) {
// We need to display a window similar to 'settings_quick_launch' but only with Tap Up, Tap Center, Tap Down
// Which, themselves will display options similar to this file.
Window *window = settings_quick_launch_two_clicks_init(data->button, data->is_tap);
app_window_stack_push(window, true /* animated */);
} else {
window_set_click_config_provider(&option_menu->window, NULL);
app_window_stack_pop(true);
}
}
}

static void prv_menu_select_ql_2c(OptionMenu *option_menu, int selection, QuickLaunchAppMenuData *data) {
window_set_click_config_provider(&option_menu->window, NULL);

if (selection == 0) {
if (data->is_tap) {
quick_launch_two_clicks_tap_set_app(data->button, data->second_button, INSTALL_ID_INVALID);
quick_launch_two_clicks_tap_set_enabled(data->button, data->second_button, false);
} else {
quick_launch_two_clicks_set_app(data->button, data->second_button, INSTALL_ID_INVALID);
quick_launch_two_clicks_set_enabled(data->button, data->second_button, false);
}

app_window_stack_pop(true);
} else {
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_two_clicks_tap_set_app(data->button, data->second_button, app_menu_node->install_id);
} else {
quick_launch_two_clicks_set_app(data->button, data->second_button, app_menu_node->install_id);
}

app_window_stack_pop(true);
}
}

static void prv_menu_select(OptionMenu *option_menu, int selection, void *context) {
QuickLaunchAppMenuData *data = context;
if (data->is_two_clicks) {
prv_menu_select_ql_2c(option_menu, selection, data);
} else {
prv_menu_select_ql(option_menu, selection, data);
}
}

Expand All @@ -136,10 +190,12 @@ 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 prv_quick_launch_app_menu_window_push(ButtonId button, bool is_tap, bool is_two_clicks, ButtonId second_button) {
QuickLaunchAppMenuData *data = app_zalloc_check(sizeof(*data));
data->button = button;
data->is_tap = is_tap;
data->is_two_clicks = is_two_clicks;
data->second_button = second_button;

OptionMenu *option_menu = option_menu_create();
data->option_menu = option_menu;
Expand All @@ -149,14 +205,15 @@ void quick_launch_app_menu_window_push(ButtonId button, bool is_tap) {
.filter = prv_app_filter_callback,
}, data);

const AppInstallId install_id = is_tap ? quick_launch_single_click_get_app(button)
: quick_launch_get_app(button);
const AppInstallId install_id = is_two_clicks ?
(is_tap ? quick_launch_two_clicks_tap_get_app(button, second_button) : quick_launch_two_clicks_get_app(button, second_button)) :
(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);

GColor highlight_bg = shell_prefs_get_theme_highlight_color();
const OptionMenuConfig config = {
.title = i18n_get(i18n_noop("Quick Launch"), data),
.title = i18n_get(i18n_noop(is_two_clicks ? "Quick Launch 2C" : "Quick Launch"), data),
.choice = (install_id == INSTALL_ID_INVALID) ? 0 : (app_index + NUM_CUSTOM_CELLS),
.status_colors = { GColorWhite, GColorBlack, },
.highlight_colors = { highlight_bg, gcolor_legible_over(highlight_bg) },
Expand All @@ -173,3 +230,13 @@ void quick_launch_app_menu_window_push(ButtonId button, bool is_tap) {
const bool animated = true;
app_window_stack_push(&option_menu->window, animated);
}

void quick_launch_app_menu_window_push(ButtonId button, bool is_tap) {
const bool is_two_clicks = false;
prv_quick_launch_app_menu_window_push(button, is_tap, is_two_clicks, (ButtonId)0);
}

void quick_launch_two_clicks_app_menu_window_push(ButtonId first_button, bool first_button_is_tap, ButtonId second_button) {
const bool is_two_clicks = true;
prv_quick_launch_app_menu_window_push(first_button, first_button_is_tap, is_two_clicks, second_button);
}
1 change: 1 addition & 0 deletions src/fw/apps/system/settings/quick_launch_app_menu.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
#include <stdbool.h>

void quick_launch_app_menu_window_push(ButtonId button, bool is_tap);
void quick_launch_two_clicks_app_menu_window_push(ButtonId first_button, bool first_button_is_tap, ButtonId second_button);
169 changes: 169 additions & 0 deletions src/fw/apps/system/settings/quick_launch_two_clicks.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/* SPDX-FileCopyrightText: 2024 Google LLC */
/* 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
//! 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 "quick_launch.h"
#include "quick_launch_app_menu.h"
#include "quick_launch_setup_menu.h"
#include "window.h"

#include "applib/app.h"
#include "applib/app_launch_button.h"
#include "applib/app_launch_reason.h"
#include "applib/ui/window_stack.h"
#include "apps/system_app_ids.h"
#include "kernel/pbl_malloc.h"
#include "process_management/app_menu_data_source.h"
#include "resource/resource_ids.auto.h"
#include "services/common/i18n/i18n.h"
#include "shell/normal/quick_launch.h"
#include "system/passert.h"
#include "system/status_codes.h"

#define NUM_ROWS (NUM_BUTTONS - 1) // 4 buttons - back button

typedef enum {
ROW_UP = 0,
ROW_SELECT,
ROW_DOWN,
} QuickLaunchTwoClicksRow;

typedef struct QuickLaunchTwoClicksData {
SettingsCallbacks callbacks;
char app_names[NUM_ROWS][APP_NAME_SIZE_BYTES];
ButtonId first_button;
bool first_button_was_tap;
} QuickLaunchTwoClicksData;

static const char *s_row_titles[NUM_ROWS] = {
/// Shown in Quick Launch Settings as the title of the tap up button option.
[ROW_UP] = i18n_noop("Tap Up"),
/// Shown in Quick Launch Settings as the title of the tap down button option.
[ROW_SELECT] = i18n_noop("Tap Center"),
/// Shown in Quick Launch Settings as the title of the hold up button quick launch option.
[ROW_DOWN] = i18n_noop("Tap Down"),
};

static void prv_get_subtitle_string(AppInstallId app_id, QuickLaunchTwoClicksData *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);
return;
} else {
AppInstallEntry entry;
if (app_install_get_entry_for_install_id(app_id, &entry)) {
strncpy(buffer, entry.name, buf_len);
buffer[buf_len - 1] = '\0';
return;
}
}
// if failed both, set as empty string
buffer[0] = '\0';
}

// Filter List Callbacks
////////////////////////
static void prv_deinit_cb(SettingsCallbacks *context) {
QuickLaunchTwoClicksData *data = (QuickLaunchTwoClicksData *) context;
i18n_free_all(data);
app_free(data);
}

static void prv_update_app_names(QuickLaunchTwoClicksData *data) {
if (data->first_button_was_tap) {
prv_get_subtitle_string(quick_launch_two_clicks_tap_get_app(data->first_button, BUTTON_ID_UP), data,
data->app_names[ROW_UP], APP_NAME_SIZE_BYTES);
prv_get_subtitle_string(quick_launch_two_clicks_tap_get_app(data->first_button, BUTTON_ID_SELECT), data,
data->app_names[ROW_SELECT], APP_NAME_SIZE_BYTES);
prv_get_subtitle_string(quick_launch_two_clicks_tap_get_app(data->first_button, BUTTON_ID_DOWN), data,
data->app_names[ROW_DOWN], APP_NAME_SIZE_BYTES);
} else {
prv_get_subtitle_string(quick_launch_two_clicks_get_app(data->first_button, BUTTON_ID_UP), data,
data->app_names[ROW_UP], APP_NAME_SIZE_BYTES);
prv_get_subtitle_string(quick_launch_two_clicks_get_app(data->first_button, BUTTON_ID_SELECT), data,
data->app_names[ROW_SELECT], APP_NAME_SIZE_BYTES);
prv_get_subtitle_string(quick_launch_two_clicks_get_app(data->first_button, BUTTON_ID_DOWN), data,
data->app_names[ROW_DOWN], APP_NAME_SIZE_BYTES);
}
}

static void prv_draw_row_cb(SettingsCallbacks *context, GContext *ctx,
const Layer *cell_layer, uint16_t row, bool selected) {
QuickLaunchTwoClicksData *data = (QuickLaunchTwoClicksData *)context;
PBL_ASSERTN(row < NUM_ROWS);
const char *title = i18n_get(s_row_titles[row], data);
char *subtitle_buf = data->app_names[row];
menu_cell_basic_draw(ctx, cell_layer, title, subtitle_buf, NULL);
}

static uint16_t prv_get_initial_selection_cb(SettingsCallbacks *context) {
// If launched by quick launch, select the row of the button pressed, otherwise default to 0
if (app_launch_reason() == APP_LAUNCH_QUICK_LAUNCH) {
ButtonId button = app_launch_button();
// Map button to hold row (quick launch is always hold/long press)
switch (button) {
case BUTTON_ID_UP: return ROW_UP;
case BUTTON_ID_SELECT: return ROW_SELECT;
case BUTTON_ID_DOWN: return ROW_DOWN;
default: break;
}
}
return 0;
}

static void prv_select_click_cb(SettingsCallbacks *context, uint16_t row) {
PBL_ASSERTN(row < NUM_ROWS);
QuickLaunchTwoClicksData *data = (QuickLaunchTwoClicksData *)context;
ButtonId button;

switch (row) {
case ROW_UP:
button = BUTTON_ID_UP;
break;
case ROW_SELECT:
button = BUTTON_ID_SELECT;
break;
case ROW_DOWN:
button = BUTTON_ID_DOWN;
break;
default:
return;
}

// Here, we need to display a window similar to 'settings_quick_launch_app_menu', dismissing the 2-Clicks app
quick_launch_two_clicks_app_menu_window_push(data->first_button, data->first_button_was_tap, button);
}

static uint16_t prv_num_rows_cb(SettingsCallbacks *context) {
return NUM_ROWS;
}

static void prv_appear(SettingsCallbacks *context) {
QuickLaunchTwoClicksData *data = (QuickLaunchTwoClicksData *)context;
prv_update_app_names(data);
}

Window *settings_quick_launch_two_clicks_init(ButtonId first_button, bool first_button_was_tap) {
QuickLaunchTwoClicksData *data = app_malloc_check(sizeof(*data));
*data = (QuickLaunchTwoClicksData){};

data->callbacks = (SettingsCallbacks) {
.deinit = prv_deinit_cb,
.draw_row = prv_draw_row_cb,
.get_initial_selection = prv_get_initial_selection_cb,
.select_click = prv_select_click_cb,
.num_rows = prv_num_rows_cb,
.appear = prv_appear,
};
data->first_button = first_button;
data->first_button_was_tap = first_button_was_tap;

return settings_window_create(SettingsMenuItemQuickLaunch, &data->callbacks);
}
8 changes: 8 additions & 0 deletions src/fw/apps/system/settings/quick_launch_two_clicks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* SPDX-FileCopyrightText: 2024 Google LLC */
/* SPDX-License-Identifier: Apache-2.0 */

#pragma once

#include "menu.h"

Window *settings_quick_launch_two_clicks_init(ButtonId first_button, bool first_button_was_tap);
Loading
Loading