1818#include "process_management/pebble_process_md.h"
1919#include "services/common/analytics/analytics.h"
2020#include "services/common/compositor/compositor_transitions.h"
21+ #include "applib/app_timer.h"
22+ #include "applib/ui/click_internal.h"
2123#include "services/normal/notifications/do_not_disturb.h"
2224#include "system/logging.h"
2325#include "system/passert.h"
2426
2527#define QUICK_LAUNCH_HOLD_MS (400)
28+ #define BIT_SET (1)
29+ #define BIT_CLEAR (0)
30+ #define COMBO_BACK_UP_BUTTONS ((BIT_SET << BUTTON_ID_BACK) | (BIT_SET << BUTTON_ID_UP))
2631
2732static ClickManager s_click_manager ;
33+ static uint8_t s_buttons_pressed = BIT_CLEAR ;
34+ static AppTimer * s_combo_back_up_timer = NULL ;
2835
2936static bool prv_should_ignore_button_click (void ) {
3037 if (app_manager_get_task_context ()-> closing_state != ProcessRunState_Running ) {
@@ -44,28 +51,82 @@ static void prv_launch_app_via_button(AppLaunchEventConfig *config,
4451 app_manager_put_launch_app_event (config );
4552}
4653
54+ static void prv_combo_back_up_timer_callback (void * data ) {
55+ s_combo_back_up_timer = NULL ;
56+ // Double-check that both buttons are still pressed before executing combo
57+ if ((s_buttons_pressed & COMBO_BACK_UP_BUTTONS ) == COMBO_BACK_UP_BUTTONS ) {
58+ if (quick_launch_combo_back_up_is_enabled ()) {
59+ AppInstallId app_id = quick_launch_combo_back_up_get_app ();
60+ if (app_id != INSTALL_ID_INVALID ) {
61+ // Reset all button states before launching app to prevent state corruption
62+ s_buttons_pressed = BIT_CLEAR ; // Reset our own tracking
63+ app_manager_put_launch_app_event (& (AppLaunchEventConfig ) {
64+ .id = app_id ,
65+ .common .reason = APP_LAUNCH_QUICK_LAUNCH ,
66+ .common .button = BUTTON_ID_BACK ,
67+ });
68+ return ;
69+ }
70+ }
71+ }
72+ }
73+
74+ static void prv_check_combo_back_up (void ) {
75+ bool back_pressed = (s_buttons_pressed & (BIT_SET << BUTTON_ID_BACK )) != BIT_CLEAR ;
76+ bool up_pressed = (s_buttons_pressed & (BIT_SET << BUTTON_ID_UP )) != BIT_CLEAR ;
77+ bool both_pressed = back_pressed && up_pressed ;
78+
79+ if (both_pressed ) {
80+ if (s_combo_back_up_timer == NULL ) {
81+ // Cancel individual button timers to prevent them from firing
82+ // This ensures only the combo executes, not individual hold handlers
83+ click_recognizer_reset (& s_click_manager .recognizers [BUTTON_ID_BACK ]);
84+ click_recognizer_reset (& s_click_manager .recognizers [BUTTON_ID_UP ]);
85+ s_combo_back_up_timer = app_timer_register (QUICK_LAUNCH_HOLD_MS , prv_combo_back_up_timer_callback , NULL );
86+ }
87+ } else {
88+ if (s_combo_back_up_timer != NULL ) {
89+ app_timer_cancel (s_combo_back_up_timer );
90+ s_combo_back_up_timer = NULL ;
91+ }
92+ }
93+ }
94+
4795static void prv_quick_launch_handler (ClickRecognizerRef recognizer , void * data ) {
4896 ButtonId button = click_recognizer_get_button_id (recognizer );
97+
98+ if (s_combo_back_up_timer != NULL ||
99+ (s_buttons_pressed & COMBO_BACK_UP_BUTTONS ) == COMBO_BACK_UP_BUTTONS ) {
100+ return ;
101+ }
102+
49103 if (!quick_launch_is_enabled (button )) {
50104 return ;
51105 }
52106 AppInstallId app_id = quick_launch_get_app (button );
53107 if (app_id == INSTALL_ID_INVALID ) {
54108 app_id = app_install_get_id_for_uuid (& quick_launch_setup_get_app_info ()-> uuid );
55109 }
110+ s_buttons_pressed = BIT_CLEAR ; // Reset our own tracking
56111 prv_launch_app_via_button (& (AppLaunchEventConfig ) {
57112 .id = app_id ,
58113 .common .reason = APP_LAUNCH_QUICK_LAUNCH ,
59114 }, recognizer );
60115}
61116
62117static void prv_launch_up_down (ClickRecognizerRef recognizer , void * data ) {
63- if (!quick_launch_single_click_is_enabled (click_recognizer_get_button_id (recognizer ))) return ;
118+ ButtonId button = click_recognizer_get_button_id (recognizer );
119+
120+ if ((s_buttons_pressed & COMBO_BACK_UP_BUTTONS ) == COMBO_BACK_UP_BUTTONS ) {
121+ return ;
122+ }
123+
124+ if (!quick_launch_single_click_is_enabled (button )) return ;
64125 //check if quick launch app is not timeline
65- if (quick_launch_single_click_get_app (click_recognizer_get_button_id ( recognizer ) ) != APP_ID_TIMELINE ) {
126+ if (quick_launch_single_click_get_app (button ) != APP_ID_TIMELINE ) {
66127 //launch other quick launch apps
67128 prv_launch_app_via_button (& (AppLaunchEventConfig ) {
68- .id = quick_launch_single_click_get_app (click_recognizer_get_button_id ( recognizer ) ),
129+ .id = quick_launch_single_click_get_app (button ),
69130 .common .reason = APP_LAUNCH_QUICK_LAUNCH ,
70131 }, recognizer );
71132 return ;
@@ -120,6 +181,9 @@ static void prv_launch_launcher_app(ClickRecognizerRef recognizer, void *data) {
120181}
121182
122183static void prv_dismiss_timeline_peek (ClickRecognizerRef recognizer , void * data ) {
184+ if ((s_buttons_pressed & COMBO_BACK_UP_BUTTONS ) == COMBO_BACK_UP_BUTTONS ) {
185+ return ;
186+ }
123187 timeline_peek_dismiss ();
124188}
125189
@@ -141,9 +205,13 @@ void watchface_handle_button_event(PebbleEvent *e) {
141205 }
142206 switch (e -> type ) {
143207 case PEBBLE_BUTTON_DOWN_EVENT :
208+ s_buttons_pressed |= (BIT_SET << e -> button .button_id );
144209 click_recognizer_handle_button_down (& s_click_manager .recognizers [e -> button .button_id ]);
210+ prv_check_combo_back_up ();
145211 break ;
146212 case PEBBLE_BUTTON_UP_EVENT :
213+ s_buttons_pressed &= ~(BIT_SET << e -> button .button_id );
214+ prv_check_combo_back_up ();
147215 click_recognizer_handle_button_up (& s_click_manager .recognizers [e -> button .button_id ]);
148216 break ;
149217 default :
0 commit comments