#pragma once #include #include #include #include #include #include #include #include class MediaKeys final : public App { // UI elements lv_obj_t* _parent = nullptr; lv_obj_t* _mainWrapper = nullptr; lv_obj_t* _switchWidget = nullptr; lv_obj_t* _buttonMatrix = nullptr; lv_timer_t* _keyHighlightTimer = nullptr; uint32_t _activeKeyBtn = LV_BTNMATRIX_BTN_NONE; bool _keyboardActive = false; // true when matrix has focus group + editing mode // HAL device handles struct Device* _btDevice = nullptr; struct Device* _hidDevice = nullptr; // State - accessed from both LVGL thread and BT callback thread std::atomic _isEnabled {false}; std::atomic _radioEnabling {false}; // true while waiting for radio to come ON std::atomic _radioWasOff {false}; // true if we turned the radio on (restore on exit) std::atomic _deviceWasStarted{false}; // true if we called device_start (restore on exit) // Static event callbacks static void onSwitchToggled(lv_event_t* e); static void onButtonPressed(lv_event_t* e); static void onKeyEvent(lv_event_t* e); static void onKeyHighlightTimer(lv_timer_t* t); static void btEventCallback(struct Device* device, void* context, struct BtEvent event); static void sendKeyTask(void* param); // Instance methods called by static callbacks void teardownBt(); // remove callback + stop HID + restore radio/device state void handleSwitchToggle(bool enabled); void handleButtonPress(uint32_t buttonId); void startHid(); // called once radio is confirmed ON void enterKeyMode(); void exitKeyMode(); public: MediaKeys() = default; MediaKeys(const MediaKeys&) = delete; MediaKeys& operator=(const MediaKeys&) = delete; MediaKeys(MediaKeys&&) = delete; MediaKeys& operator=(MediaKeys&&) = delete; void onShow(AppHandle appHandle, lv_obj_t* parent) override; void onHide(AppHandle appHandle) override; };