diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 644ce3ffb..4bc448f29 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -137,23 +137,33 @@ extern bool hasUnreadMessage; // Displays a temporary centered banner message (e.g., warning, status, etc.) // The banner appears in the center of the screen and disappears after the specified duration +void Screen::showSimpleBanner(const char *message, uint32_t durationMs) +{ + BannerOverlayOptions options; + options.message = message; + options.durationMs = durationMs; + options.notificationType = notificationTypeEnum::text_banner; + showOverlayBanner(options); +} + // Called to trigger a banner with custom message and duration -void Screen::showOverlayBanner(const char *message, uint32_t durationMs, const char **optionsArrayPtr, uint8_t options, - std::function bannerCallback, int8_t InitialSelected) +void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options) { #ifdef USE_EINK EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus #endif // Store the message and set the expiration timestamp - strncpy(NotificationRenderer::alertBannerMessage, message, 255); + strncpy(NotificationRenderer::alertBannerMessage, banner_overlay_options.message, 255); NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination - NotificationRenderer::alertBannerUntil = (durationMs == 0) ? 0 : millis() + durationMs; - NotificationRenderer::optionsArrayPtr = optionsArrayPtr; - NotificationRenderer::alertBannerOptions = options; - NotificationRenderer::alertBannerCallback = bannerCallback; - NotificationRenderer::curSelected = InitialSelected; + NotificationRenderer::alertBannerUntil = + (banner_overlay_options.durationMs == 0) ? 0 : millis() + banner_overlay_options.durationMs; + NotificationRenderer::optionsArrayPtr = banner_overlay_options.optionsArrayPtr; + NotificationRenderer::alertBannerOptions = banner_overlay_options.optionsCount; + NotificationRenderer::alertBannerCallback = banner_overlay_options.bannerCallback; + NotificationRenderer::curSelected = banner_overlay_options.InitialSelected; NotificationRenderer::pauseBanner = false; - static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawAlertBannerOverlay}; + NotificationRenderer::current_notification_type = notificationTypeEnum::selection_picker; + static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback}; ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0])); setFastFramerate(); // Draw ASAP ui->update(); @@ -171,7 +181,9 @@ void Screen::showNodePicker(const char *message, uint32_t durationMs, std::funct NotificationRenderer::alertBannerUntil = (durationMs == 0) ? 0 : millis() + durationMs; NotificationRenderer::alertBannerCallback = bannerCallback; NotificationRenderer::pauseBanner = false; - static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawAlertBannerOverlay}; + NotificationRenderer::current_notification_type = notificationTypeEnum::node_picker; + + static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback}; ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0])); setFastFramerate(); // Draw ASAP ui->update(); @@ -615,7 +627,7 @@ int32_t Screen::runOnce() } #endif if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) { - showOverlayBanner("Rebooting...", 0); + showSimpleBanner("Rebooting...", 0); } // Process incoming commands. @@ -929,7 +941,7 @@ void Screen::setFrames(FrameFocus focus) ui->disableAllIndicators(); // Add overlays: frame icons and alert banner) - static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawAlertBannerOverlay}; + static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback}; ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0])); prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list @@ -1203,7 +1215,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) } } - screen->showOverlayBanner(banner, 3000); + screen->showSimpleBanner(banner, 3000); } } @@ -1243,8 +1255,7 @@ int Screen::handleInputEvent(const InputEvent *event) #endif if (NotificationRenderer::isOverlayBannerShowing()) { NotificationRenderer::inEvent = event->inputEvent; - static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, - NotificationRenderer::drawAlertBannerOverlay}; + static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback}; ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0])); setFastFramerate(); // Draw ASAP ui->update(); diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index f1c55899a..6a01d5b89 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -5,10 +5,25 @@ #include "detect/ScanI2C.h" #include "mesh/generated/meshtastic/config.pb.h" #include +#include #include #include #define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2) +namespace graphics +{ +enum notificationTypeEnum { none, text_banner, selection_picker, node_picker, number_picker }; + +struct BannerOverlayOptions { + const char *message; + uint32_t durationMs = 30000; + const char **optionsArrayPtr = nullptr; + uint8_t optionsCount = 0; + std::function bannerCallback = nullptr; + int8_t InitialSelected = 0; + notificationTypeEnum notificationType = notificationTypeEnum::text_banner; +}; +} // namespace graphics #if !HAS_SCREEN #include "power.h" @@ -40,10 +55,8 @@ class Screen void setFunctionSymbol(std::string) {} void removeFunctionSymbol(std::string) {} void startAlert(const char *) {} - void showOverlayBanner(const char *message, uint32_t durationMs = 3000, const char **optionsArrayPtr = nullptr, - uint8_t options = 0, std::function bannerCallback = NULL, int8_t InitialSelected = 0) - { - } + void showSimpleBanner(const char *message, uint32_t durationMs = 0) {} + void showOverlayBanner(BannerOverlayOptions) {} void setFrames(FrameFocus focus) {} void endAlert() {} }; @@ -293,8 +306,8 @@ class Screen : public concurrency::OSThread enqueueCmd(cmd); } - void showOverlayBanner(const char *message, uint32_t durationMs = 3000, const char **optionsArrayPtr = nullptr, - uint8_t options = 0, std::function bannerCallback = NULL, int8_t InitialSelected = 0); + void showSimpleBanner(const char *message, uint32_t durationMs = 0); + void showOverlayBanner(BannerOverlayOptions); void showNodePicker(const char *message, uint32_t durationMs, std::function bannerCallback); diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 73cd90199..65c259cf5 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -46,47 +46,55 @@ void menuHandler::LoraRegionPicker(uint32_t duration) "PH_868", "PH_915", "ANZ_433"}; - screen->showOverlayBanner( - "Set the LoRa region", duration, optionsArray, 23, - [](int selected) -> void { - if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) { - config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected); - // This is needed as we wait til picking the LoRa region to generate keys for the first time. - if (!owner.is_licensed) { - bool keygenSuccess = false; - if (config.security.private_key.size == 32) { - // public key is derived from private, so this will always have the same result. - if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) { - keygenSuccess = true; - } - } else { - LOG_INFO("Generate new PKI keys"); - crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Set the LoRa region"; + bannerOptions.durationMs = duration; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 23; + bannerOptions.InitialSelected = 0; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) { + config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected); + // This is needed as we wait til picking the LoRa region to generate keys for the first time. + if (!owner.is_licensed) { + bool keygenSuccess = false; + if (config.security.private_key.size == 32) { + // public key is derived from private, so this will always have the same result. + if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) { keygenSuccess = true; } - if (keygenSuccess) { - config.security.public_key.size = 32; - config.security.private_key.size = 32; - owner.public_key.size = 32; - memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); - } + } else { + LOG_INFO("Generate new PKI keys"); + crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); + keygenSuccess = true; } - config.lora.tx_enabled = true; - initRegion(); - if (myRegion->dutyCycle < 100) { - config.lora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit + if (keygenSuccess) { + config.security.public_key.size = 32; + config.security.private_key.size = 32; + owner.public_key.size = 32; + memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32); } - service->reloadConfig(SEGMENT_CONFIG); - rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); } - }, - 0); + config.lora.tx_enabled = true; + initRegion(); + if (myRegion->dutyCycle < 100) { + config.lora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit + } + service->reloadConfig(SEGMENT_CONFIG); + rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); + } + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::TwelveHourPicker() { static const char *optionsArray[] = {"Back", "12-hour", "24-hour"}; - screen->showOverlayBanner("Time Format", 30000, optionsArray, 3, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Time Format"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 3; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 0) { menuHandler::menuQueue = menuHandler::clock_menu; } else if (selected == 1) { @@ -95,13 +103,18 @@ void menuHandler::TwelveHourPicker() config.display.use_12h_clock = false; } service->reloadConfig(SEGMENT_CONFIG); - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::ClockFacePicker() { static const char *optionsArray[] = {"Back", "Digital", "Analog"}; - screen->showOverlayBanner("Which Face?", 30000, optionsArray, 3, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Which Face?"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 3; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 0) { menuHandler::menuQueue = menuHandler::clock_menu; } else if (selected == 1) { @@ -111,7 +124,8 @@ void menuHandler::ClockFacePicker() graphics::ClockRenderer::digitalWatchFace = false; screen->setFrames(Screen::FOCUS_CLOCK); } - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::TZPicker() @@ -135,7 +149,11 @@ void menuHandler::TZPicker() "AU/ACST", "AU/AEST", "Pacific/NZ"}; - screen->showOverlayBanner("Pick Timezone", 30000, optionsArray, 17, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Pick Timezone"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 17; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 0) { menuHandler::menuQueue = menuHandler::clock_menu; } else if (selected == 1) { // Hawaii @@ -177,13 +195,18 @@ void menuHandler::TZPicker() setenv("TZ", config.device.tzdef, 1); service->reloadConfig(SEGMENT_CONFIG); } - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::clockMenu() { static const char *optionsArray[] = {"Back", "Clock Face", "Time Format", "Timezone"}; - screen->showOverlayBanner("Clock Action", 30000, optionsArray, 4, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Clock Action"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 4; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { menuHandler::menuQueue = menuHandler::clock_face_picker; screen->setInterval(0); @@ -197,7 +220,8 @@ void menuHandler::clockMenu() screen->setInterval(0); runASAP = true; } - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::messageResponseMenu() @@ -219,7 +243,11 @@ void menuHandler::messageResponseMenu() optionsArrayPtr = optionsArray; options = 5; #endif - screen->showOverlayBanner("Message Action", 30000, optionsArrayPtr, options, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Message Action"; + bannerOptions.optionsArrayPtr = optionsArrayPtr; + bannerOptions.optionsCount = options; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { screen->dismissCurrentFrame(); } else if (selected == 2) { @@ -243,7 +271,8 @@ void menuHandler::messageResponseMenu() audioThread->readAloud(msg); } #endif - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::homeBaseMenu() @@ -270,7 +299,11 @@ void menuHandler::homeBaseMenu() optionsArrayPtr = optionsArray; options = 5; } - screen->showOverlayBanner("Home Action", 30000, optionsArrayPtr, options, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Home Action"; + bannerOptions.optionsArrayPtr = optionsArrayPtr; + bannerOptions.optionsCount = options; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { #ifdef PIN_EINK_EN if (digitalRead(PIN_EINK_EN) == HIGH) { @@ -282,7 +315,7 @@ void menuHandler::homeBaseMenu() screen->setOn(false); #endif } else if (selected == 2) { - InputEvent event = {.inputEvent = (input_broker_event)175, .kbchar = 175, .touchX = 0, .touchY = 0}; + InputEvent event = {.inputEvent = (input_broker_event)INPUT_BROKER_SEND_PING, .kbchar = 0, .touchX = 0, .touchY = 0}; inputBroker->injectInputEvent(&event); } else if (selected == 3) { cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST); @@ -297,7 +330,8 @@ void menuHandler::homeBaseMenu() InputEvent event = {.inputEvent = (input_broker_event)170, .kbchar = 170, .touchX = 0, .touchY = 0}; inputBroker->injectInputEvent(&event); } - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::systemBaseMenu() @@ -317,7 +351,11 @@ void menuHandler::systemBaseMenu() options = 3; #endif optionsArrayPtr = optionsArray; - screen->showOverlayBanner("System Action", 30000, optionsArrayPtr, options, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "System Action"; + bannerOptions.optionsArrayPtr = optionsArrayPtr; + bannerOptions.optionsCount = options; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { menuHandler::menuQueue = menuHandler::buzzermodemenupicker; screen->setInterval(0); @@ -336,7 +374,8 @@ void menuHandler::systemBaseMenu() screen->setInterval(0); runASAP = true; } - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::favoriteBaseMenu() @@ -353,13 +392,18 @@ void menuHandler::favoriteBaseMenu() optionsArrayPtr = optionsArray; options = 2; } - screen->showOverlayBanner("Favorites Action", 30000, optionsArrayPtr, options, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Favorites Action"; + bannerOptions.optionsArrayPtr = optionsArrayPtr; + bannerOptions.optionsCount = options; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { cannedMessageModule->LaunchWithDestination(graphics::UIRenderer::currentFavoriteNodeNum); } else if (selected == 2) { cannedMessageModule->LaunchFreetextWithDestination(graphics::UIRenderer::currentFavoriteNodeNum); } - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::positionBaseMenu() @@ -376,7 +420,11 @@ void menuHandler::positionBaseMenu() optionsArrayPtr = optionsArray; options = 3; } - screen->showOverlayBanner("Position Action", 30000, optionsArrayPtr, options, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Position Action"; + bannerOptions.optionsArrayPtr = optionsArrayPtr; + bannerOptions.optionsCount = options; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { menuQueue = gps_toggle_menu; } else if (selected == 2) { @@ -384,36 +432,51 @@ void menuHandler::positionBaseMenu() } else if (selected == 3) { accelerometerThread->calibrate(30); } - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::nodeListMenu() { static const char *optionsArray[] = {"Back", "Reset NodeDB"}; - screen->showOverlayBanner("Node Action", 30000, optionsArray, 2, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Node Action"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 2; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { menuQueue = reset_node_db_menu; } - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::resetNodeDBMenu() { static const char *optionsArray[] = {"Back", "Confirm"}; - screen->showOverlayBanner("Confirm Reset NodeDB", 30000, optionsArray, 2, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Confirm Reset NodeDB"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 2; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { disableBluetooth(); LOG_INFO("Initiate node-db reset"); nodeDB->resetNodes(); rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); } - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::compassNorthMenu() { static const char *optionsArray[] = {"Back", "Dynamic", "Fixed Ring", "Freeze Heading"}; - screen->showOverlayBanner("North Directions?", 30000, optionsArray, 4, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "North Directions?"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 4; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { if (config.display.compass_north_top != false) { config.display.compass_north_top = false; @@ -438,62 +501,78 @@ void menuHandler::compassNorthMenu() } else if (selected == 0) { menuQueue = position_base_menu; } - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::GPSToggleMenu() { static const char *optionsArray[] = {"Back", "Enabled", "Disabled"}; - screen->showOverlayBanner( - "Toggle GPS", 30000, optionsArray, 3, - [](int selected) -> void { - if (selected == 1) { - config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; - playGPSEnableBeep(); - gps->enable(); - service->reloadConfig(SEGMENT_CONFIG); - } else if (selected == 2) { - config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED; - playGPSDisableBeep(); - gps->disable(); - service->reloadConfig(SEGMENT_CONFIG); - } else { - menuQueue = position_base_menu; - } - }, - config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED ? 1 : 2); // set inital selection + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Toggle GPS"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 3; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == 1) { + config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; + playGPSEnableBeep(); + gps->enable(); + service->reloadConfig(SEGMENT_CONFIG); + } else if (selected == 2) { + config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED; + playGPSDisableBeep(); + gps->disable(); + service->reloadConfig(SEGMENT_CONFIG); + } else { + menuQueue = position_base_menu; + } + }; + bannerOptions.InitialSelected = config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED ? 1 : 2; + screen->showOverlayBanner(bannerOptions); } void menuHandler::BuzzerModeMenu() { static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only"}; - screen->showOverlayBanner( - "Beep Action", 30000, optionsArray, 4, - [](int selected) -> void { - config.device.buzzer_mode = (meshtastic_Config_DeviceConfig_BuzzerMode)selected; - service->reloadConfig(SEGMENT_CONFIG); - }, - config.device.buzzer_mode); + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Beep Action"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 4; + bannerOptions.bannerCallback = [](int selected) -> void { + config.device.buzzer_mode = (meshtastic_Config_DeviceConfig_BuzzerMode)selected; + service->reloadConfig(SEGMENT_CONFIG); + }; + bannerOptions.InitialSelected = config.device.buzzer_mode; + screen->showOverlayBanner(bannerOptions); } void menuHandler::switchToMUIMenu() { static const char *optionsArray[] = {"Yes", "No"}; - screen->showOverlayBanner("Switch to MUI?", 30000, optionsArray, 2, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Switch to MUI?"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 2; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 0) { config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR; config.bluetooth.enabled = false; service->reloadConfig(SEGMENT_CONFIG); rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); } - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::TFTColorPickerMenu() { static const char *optionsArray[] = {"Back", "Default", "Meshtastic Green", "Yellow", "Red", "Orange", "Purple", "Teal", "Pink", "White"}; - screen->showOverlayBanner("Select Screen Color", 30000, optionsArray, 10, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Select Screen Color"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 10; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 1) { LOG_INFO("Setting color to system default or defined variant"); // Insert unset protobuf code here @@ -529,22 +608,29 @@ void menuHandler::TFTColorPickerMenu() static_cast(screen->getDisplayDevice())->setRGB(TFT_MESH); screen->setFrames(graphics::Screen::FOCUS_SYSTEM); // I think we need a saveToDisk to commit a protobuf change? + // There isn't a protobuf for this setting yet, so no save // nodeDB->saveToDisk(SEGMENT_CONFIG); } #endif - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::rebootMenu() { static const char *optionsArray[] = {"Yes", "No"}; - screen->showOverlayBanner("Reboot Device?", 30000, optionsArray, 2, [](int selected) -> void { + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Reboot Device?"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = 2; + bannerOptions.bannerCallback = [](int selected) -> void { if (selected == 0) { - IF_SCREEN(screen->showOverlayBanner("Rebooting...", 0)); + IF_SCREEN(screen->showSimpleBanner("Rebooting...", 0)); nodeDB->saveToDisk(); rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000; } - }); + }; + screen->showOverlayBanner(bannerOptions); } void menuHandler::handleMenuSwitch() diff --git a/src/graphics/draw/NotificationRenderer.cpp b/src/graphics/draw/NotificationRenderer.cpp index 7b7cd6557..d5acb431b 100644 --- a/src/graphics/draw/NotificationRenderer.cpp +++ b/src/graphics/draw/NotificationRenderer.cpp @@ -34,6 +34,7 @@ uint8_t NotificationRenderer::alertBannerOptions = 0; // last x lines are seelct const char **NotificationRenderer::optionsArrayPtr = nullptr; std::function NotificationRenderer::alertBannerCallback = NULL; bool NotificationRenderer::pauseBanner = false; +notificationTypeEnum NotificationRenderer::current_notification_type = notificationTypeEnum::none; // Used on boot when a certificate is being created void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) @@ -55,6 +56,17 @@ void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiStat } } +void NotificationRenderer::resetBanner() +{ + alertBannerMessage[0] = '\0'; + current_notification_type = notificationTypeEnum::none; +} + +void NotificationRenderer::drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state) +{ + drawAlertBannerOverlay(display, state); +} + void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisplayUiState *state) { if (!isOverlayBannerShowing() || pauseBanner) @@ -108,9 +120,9 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp curSelected++; } else if (inEvent == INPUT_BROKER_SELECT) { alertBannerCallback(curSelected); - alertBannerMessage[0] = '\0'; + resetBanner(); } else if ((inEvent == INPUT_BROKER_CANCEL || inEvent == INPUT_BROKER_ALT_LONG) && alertBannerUntil != 0) { - alertBannerMessage[0] = '\0'; + resetBanner(); } if (curSelected == -1) @@ -119,7 +131,7 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp curSelected = 0; } else { if (inEvent == INPUT_BROKER_SELECT || inEvent == INPUT_BROKER_ALT_LONG || inEvent == INPUT_BROKER_CANCEL) { - alertBannerMessage[0] = '\0'; + resetBanner(); } } diff --git a/src/graphics/draw/NotificationRenderer.h b/src/graphics/draw/NotificationRenderer.h index e1aa0baac..885300a51 100644 --- a/src/graphics/draw/NotificationRenderer.h +++ b/src/graphics/draw/NotificationRenderer.h @@ -2,6 +2,7 @@ #include "OLEDDisplay.h" #include "OLEDDisplayUi.h" +#include "graphics/Screen.h" #define MAX_LINES 5 namespace graphics @@ -20,6 +21,8 @@ class NotificationRenderer static bool pauseBanner; + static void resetBanner(); + static void drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state); static void drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisplayUiState *state); static void drawNotificationBox(OLEDDisplay *display, OLEDDisplayUiState *state, const char *lines[MAX_LINES + 1], uint16_t totalLines, uint8_t firstOptionToShow, uint16_t maxWidth = 0); @@ -28,6 +31,8 @@ class NotificationRenderer static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); static bool isOverlayBannerShowing(); + + static graphics::notificationTypeEnum current_notification_type; }; } // namespace graphics diff --git a/src/main.cpp b/src/main.cpp index 4b64a78ea..ddc1df28c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1357,7 +1357,7 @@ void setup() if (!rIf->reconfigure()) { LOG_WARN("Reconfigure failed, rebooting"); if (screen) { - screen->showOverlayBanner("Rebooting..."); + screen->showSimpleBanner("Rebooting..."); } rebootAtMsec = millis() + 5000; } diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp index aad7f5f06..12a586cd7 100644 --- a/src/modules/AdminModule.cpp +++ b/src/modules/AdminModule.cpp @@ -1190,7 +1190,7 @@ void AdminModule::reboot(int32_t seconds) { LOG_INFO("Reboot in %d seconds", seconds); if (screen) - screen->showOverlayBanner("Rebooting...", 0); // stays on screen + screen->showSimpleBanner("Rebooting...", 0); // stays on screen rebootAtMsec = (seconds < 0) ? 0 : (millis() + seconds * 1000); } diff --git a/src/modules/KeyVerificationModule.cpp b/src/modules/KeyVerificationModule.cpp index c0972c155..408d29126 100644 --- a/src/modules/KeyVerificationModule.cpp +++ b/src/modules/KeyVerificationModule.cpp @@ -59,7 +59,7 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket & r->hash1.size == 0) { memcpy(hash2, r->hash2.bytes, 32); if (screen) - screen->showOverlayBanner("Enter Security Number", 30000); + screen->showSimpleBanner("Enter Security Number", 30000); meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); cn->level = meshtastic_LogRecord_Level_WARNING; @@ -82,12 +82,19 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket & static const char *optionsArray[] = {"ACCEPT", "REJECT"}; LOG_INFO("Hash1 matches!"); if (screen) { - screen->showOverlayBanner(message, 30000, optionsArray, 2, [=](int selected) { + graphics::BannerOverlayOptions options; + options.message = message; + options.durationMs = 30000; + options.optionsArrayPtr = optionsArray; + options.optionsCount = 2; + options.notificationType = graphics::notificationTypeEnum::selection_picker; + options.bannerCallback = [=](int selected) { if (selected == 0) { auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode); remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK; } - }); + }; + screen->showOverlayBanner(options); } meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); cn->level = meshtastic_LogRecord_Level_WARNING; @@ -185,7 +192,7 @@ meshtastic_MeshPacket *KeyVerificationModule::allocReply() responsePacket->pki_encrypted = true; if (screen) { snprintf(message, 25, "Security Number \n%03u %03u", currentSecurityNumber / 1000, currentSecurityNumber % 1000); - screen->showOverlayBanner(message, 30000); + screen->showSimpleBanner(message, 30000); LOG_WARN("%s", message); } meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); @@ -255,7 +262,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber) sprintf(message, "Verification: \n"); generateVerificationCode(message + 15); // send the toPhone packet if (screen) { - screen->showOverlayBanner(message, 30000); + screen->showSimpleBanner(message, 30000); } meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed(); cn->level = meshtastic_LogRecord_Level_WARNING; diff --git a/src/modules/SystemCommandsModule.cpp b/src/modules/SystemCommandsModule.cpp index 08c87ec64..ab9439b39 100644 --- a/src/modules/SystemCommandsModule.cpp +++ b/src/modules/SystemCommandsModule.cpp @@ -47,7 +47,7 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event) bool isMuted = externalNotificationModule->getMute(); externalNotificationModule->setMute(!isMuted); IF_SCREEN(graphics::isMuted = !isMuted; if (!isMuted) externalNotificationModule->stopNow(); - screen->showOverlayBanner(isMuted ? "Notifications\nEnabled" : "Notifications\nDisabled", 3000);) + screen->showSimpleBanner(isMuted ? "Notifications\nEnabled" : "Notifications\nDisabled", 3000);) } return 0; // Bluetooth @@ -58,24 +58,24 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event) #if defined(ARDUINO_ARCH_NRF52) if (!config.bluetooth.enabled) { disableBluetooth(); - IF_SCREEN(screen->showOverlayBanner("Bluetooth OFF\nRebooting", 3000)); + IF_SCREEN(screen->showSimpleBanner("Bluetooth OFF\nRebooting", 3000)); rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 2000; } else { - IF_SCREEN(screen->showOverlayBanner("Bluetooth ON\nRebooting", 3000)); + IF_SCREEN(screen->showSimpleBanner("Bluetooth ON\nRebooting", 3000)); rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000; } #else if (!config.bluetooth.enabled) { disableBluetooth(); - IF_SCREEN(screen->showOverlayBanner("Bluetooth OFF", 3000)); + IF_SCREEN(screen->showSimpleBanner("Bluetooth OFF", 3000)); } else { - IF_SCREEN(screen->showOverlayBanner("Bluetooth ON\nRebooting", 3000)); + IF_SCREEN(screen->showSimpleBanner("Bluetooth ON\nRebooting", 3000)); rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000; } #endif return 0; case INPUT_BROKER_MSG_REBOOT: - IF_SCREEN(screen->showOverlayBanner("Rebooting...", 0)); + IF_SCREEN(screen->showSimpleBanner("Rebooting...", 0)); nodeDB->saveToDisk(); rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000; // runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; @@ -92,7 +92,7 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event) gps->toggleGpsMode(); const char *msg = (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) ? "GPS Enabled" : "GPS Disabled"; - IF_SCREEN(screen->forceDisplay(); screen->showOverlayBanner(msg, 3000);) + IF_SCREEN(screen->forceDisplay(); screen->showSimpleBanner(msg, 3000);) } #endif return true; @@ -100,15 +100,15 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event) case INPUT_BROKER_SEND_PING: service->refreshLocalMeshNode(); if (service->trySendPosition(NODENUM_BROADCAST, true)) { - IF_SCREEN(screen->showOverlayBanner("Position\nSent", 3000)); + IF_SCREEN(screen->showSimpleBanner("Position\nSent", 3000)); } else { - IF_SCREEN(screen->showOverlayBanner("Node Info\nSent", 3000)); + IF_SCREEN(screen->showSimpleBanner("Node Info\nSent", 3000)); } return true; // Power control case INPUT_BROKER_SHUTDOWN: LOG_ERROR("Shutting Down"); - IF_SCREEN(screen->showOverlayBanner("Shutting Down...")); + IF_SCREEN(screen->showSimpleBanner("Shutting Down...")); nodeDB->saveToDisk(); shutdownAtMsec = millis() + DEFAULT_SHUTDOWN_SECONDS * 1000; // runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp index 46a24a816..d1b10fa82 100644 --- a/src/modules/Telemetry/EnvironmentTelemetry.cpp +++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp @@ -450,7 +450,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt if (isOwnTelemetry && bannerMsg && isCooldownOver) { LOG_INFO("drawFrame: IAQ %d (own) — showing banner: %s", m.iaq, bannerMsg); - screen->showOverlayBanner(bannerMsg, 3000); + screen->showSimpleBanner(bannerMsg, 3000); // Only buzz if IAQ is over 200 if (m.iaq > 200 && moduleConfig.external_notification.enabled && !externalNotificationModule->getMute()) { diff --git a/src/shutdown.h b/src/shutdown.h index 998944677..7e2120149 100644 --- a/src/shutdown.h +++ b/src/shutdown.h @@ -42,7 +42,7 @@ void powerCommandsCheck() #if defined(ARCH_ESP32) || defined(ARCH_NRF52) if (shutdownAtMsec && screen) { - screen->showOverlayBanner("Shutting Down...", 0); // stays on screen + screen->showSimpleBanner("Shutting Down...", 0); // stays on screen } #endif