diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index e9072b309..c4f2c2562 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -33,6 +33,8 @@ #include "ui_touch_calibration.hpp" #include "ui_text_editor.hpp" #include "ui_external_items_menu_loader.hpp" +#include "ui_ss_viewer.hpp" +#include "ui_fileman.hpp" #include "portapack_persistent_memory.hpp" #include "lpc43xx_cpp.hpp" @@ -348,7 +350,6 @@ SetUIView::SetUIView(NavigationView& nav) { add_children({&checkbox_disable_touchscreen, &checkbox_bloff, &options_bloff, - &checkbox_showsplash, &checkbox_showclock, &options_clockformat, &checkbox_guireturnflag, @@ -375,7 +376,6 @@ SetUIView::SetUIView(NavigationView& nav) { } checkbox_disable_touchscreen.set_value(pmem::disable_touchscreen()); - checkbox_showsplash.set_value(pmem::config_splash()); checkbox_showclock.set_value(!pmem::hide_clock()); checkbox_guireturnflag.set_value(pmem::show_gui_return_icon()); @@ -414,7 +414,6 @@ SetUIView::SetUIView(NavigationView& nav) { pmem::set_clock_with_date(false); } - pmem::set_config_splash(checkbox_showsplash.value()); pmem::set_clock_hidden(!checkbox_showclock.value()); pmem::set_gui_return_icon(checkbox_guireturnflag.value()); pmem::set_disable_touchscreen(checkbox_disable_touchscreen.value()); @@ -1113,6 +1112,46 @@ void SetBatteryView::focus() { button_cancel.focus(); } +/* SetSlpash *********************************************/ + +SetSplash::SetSplash(NavigationView& nav) { + add_children({&checkbox_showsplash, + &checkbox_randomsplash, + &message, + &button_picture_select, + &button_save, + &button_cancel}); + + checkbox_showsplash.set_value(pmem::config_splash()); + splash_bmp_exists = file_exists(splash_dot_bmp); + checkbox_randomsplash.set_value(!splash_bmp_exists); + message.hidden(splash_bmp_exists); + + checkbox_randomsplash.on_select = [this](Checkbox&, bool v) { + random_enabled = v; + }; + + button_picture_select.on_select = [this, &nav](Button&) { + auto ret = nav.push(); + ret->push_dir(splash_dir); + }; + + button_save.on_select = [&nav, this](Button&) { + if (random_enabled == true) delete_file(splash_dot_bmp); + pmem::set_config_splash(checkbox_showsplash.value()); + send_system_refresh(); + nav.pop(); + }; + + button_cancel.on_select = [&nav, this](Button&) { + nav.pop(); + }; +} + +void SetSplash::focus() { + button_save.focus(); +} + /* SettingsMenuView **************************************/ SettingsMenuView::SettingsMenuView(NavigationView& nav) @@ -1146,6 +1185,7 @@ void SettingsMenuView::on_populate() { {"Display", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push(); }}, {"Menu Color", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push(); }}, {"Theme", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push(); }}, + {"Splash settings", ui::Color::dark_cyan(), &bitmap_icon_file_image, [this]() { nav_.push(); }}, }); if (battery::BatteryManagement::isDetected()) add_item({"Battery", ui::Color::dark_cyan(), &bitmap_icon_batt_icon, [this]() { nav_.push(); }}); diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index 72d22c908..30750ce5b 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -343,77 +343,72 @@ class SetUIView : public View { {"1 hour", backlight_timeout_t::Timeout3600Sec}, }}; - Checkbox checkbox_showsplash{ - {3 * 8, 6 * 16}, - 20, - "Show splash"}; - Checkbox checkbox_showclock{ - {3 * 8, 8 * 16}, + {3 * 8, 6 * 16}, 20, "Show clock with:"}; OptionsField options_clockformat{ - {60, 9 * 16 + 8}, + {60, 7 * 16 + 8}, 20, {{"time only", 0}, {"time and date", 1}}}; Checkbox checkbox_guireturnflag{ - {3 * 8, 11 * 16}, + {3 * 8, 9 * 16}, 20, "Back button in menu"}; Labels labels{ - {{3 * 8, 13 * 16}, "Show/Hide Status Icons", Theme::getInstance()->fg_light->foreground}, + {{3 * 8, 11 * 16}, "Show/Hide Status Icons", Theme::getInstance()->fg_light->foreground}, }; ImageToggle toggle_camera{ - {2 * 8, 14 * 16 + 2, 16, 16}, + {2 * 8, 12 * 16 + 2, 16, 16}, &bitmap_icon_camera}; ImageToggle toggle_sleep{ - {4 * 8, 14 * 16 + 2, 16, 16}, + {4 * 8, 12 * 16 + 2, 16, 16}, &bitmap_icon_sleep}; ImageToggle toggle_stealth{ - {6 * 8, 14 * 16 + 2, 16, 16}, + {6 * 8, 12 * 16 + 2, 16, 16}, &bitmap_icon_stealth}; ImageToggle toggle_converter{ - {8 * 8, 14 * 16 + 2, 16, 16}, + {8 * 8, 12 * 16 + 2, 16, 16}, &bitmap_icon_upconvert}; ImageToggle toggle_bias_tee{ - {10 * 8, 14 * 16 + 2, 16, 16}, + {10 * 8, 12 * 16 + 2, 16, 16}, &bitmap_icon_biast_off}; ImageToggle toggle_clock{ - {12 * 8, 14 * 16 + 2, 8, 16}, + {12 * 8, 12 * 16 + 2, 8, 16}, &bitmap_icon_clk_ext}; ImageToggle toggle_mute{ - {13 * 8, 14 * 16 + 2, 16, 16}, + {13 * 8, 12 * 16 + 2, 16, 16}, &bitmap_icon_speaker_and_headphones_mute}; ImageToggle toggle_speaker{ - {15 * 8, 14 * 16 + 2, 16, 16}, + {15 * 8, 12 * 16 + 2, 16, 16}, &bitmap_icon_speaker_mute}; ImageToggle toggle_battery_icon{ - {17 * 8, 14 * 16 + 2, 16, 16}, + {17 * 8, 12 * 16 + 2, 16, 16}, &bitmap_icon_batt_icon}; ImageToggle toggle_battery_text{ - {19 * 8, 14 * 16 + 2, 16, 16}, + {19 * 8, 12 * 16 + 2, 16, 16}, &bitmap_icon_batt_text}; ImageToggle toggle_fake_brightness{ - {21 * 8, 14 * 16 + 2, 16, 16}, + {21 * 8, 12 * 16 + 2, 16, 16}, &bitmap_icon_brightness}; ImageToggle toggle_sd_card{ - {23 * 8, 14 * 16 + 2, 16, 16}, + {23 * 8, 12 * 16 + 2, 16, 16}, &bitmap_sd_card_ok}; Button button_save{ @@ -1069,6 +1064,43 @@ class SettingsMenuView : public BtnGridView { void on_populate() override; }; +class SetSplash : public View { + public: + SetSplash(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "Splash"; }; + + private: + bool splash_bmp_exists = false; + bool random_enabled = false; + + Checkbox checkbox_showsplash{ + {3 * 8, 1 * 16}, + 20, + "Show splash"}; + + Checkbox checkbox_randomsplash{ + {3 * 8, 3 * 16}, + 20, + "Random splash on boot"}; + + Text message{{UI_POS_X(1), UI_POS_Y(6), UI_POS_WIDTH(26), UI_POS_HEIGHT(1)}, "Pick image to stop random."}; + + Button button_picture_select{ + {UI_POS_X_CENTER(8) - UI_POS_WIDTH(4), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(13), UI_POS_HEIGHT(2)}, + "Select pict."}; + + Button button_save{ + {UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, + "Save"}; + + Button button_cancel{ + {UI_POS_X_CENTER(16) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)}, + "Cancel"}; +}; + } /* namespace ui */ #endif /*__UI_SETTINGS_H__*/ diff --git a/firmware/application/apps/ui_ss_viewer.cpp b/firmware/application/apps/ui_ss_viewer.cpp index 67ead2955..bc3690843 100644 --- a/firmware/application/apps/ui_ss_viewer.cpp +++ b/firmware/application/apps/ui_ss_viewer.cpp @@ -27,8 +27,6 @@ namespace fs = std::filesystem; namespace ui { -const std::filesystem::path splash_dot_bmp{u"/splash.bmp"}; - ScreenshotViewer::ScreenshotViewer( NavigationView& nav, const std::filesystem::path& path) @@ -44,7 +42,6 @@ bool ScreenshotViewer::on_key(KeyEvent) { void ScreenshotViewer::paint(Painter& painter) { File file{}; - painter.fill_rectangle({0, 0, screen_width, screen_height}, Color::black()); auto show_invalid = [&]() { diff --git a/firmware/application/apps/ui_ss_viewer.hpp b/firmware/application/apps/ui_ss_viewer.hpp index 852513256..d35ce0708 100644 --- a/firmware/application/apps/ui_ss_viewer.hpp +++ b/firmware/application/apps/ui_ss_viewer.hpp @@ -28,11 +28,10 @@ #include "ui_painter.hpp" #include "ui_widget.hpp" #include "file.hpp" +#include "file_path.hpp" namespace ui { -extern const std::filesystem::path splash_dot_bmp; - class ScreenshotViewer : public View { public: ScreenshotViewer(NavigationView& nav, const std::filesystem::path& path); @@ -49,7 +48,6 @@ class SplashViewer : public View { SplashViewer(NavigationView& nav, const std::filesystem::path& path); bool on_key(KeyEvent key) override; void paint(Painter& painter) override; - void update_ss(void); private: NavigationView& nav_; diff --git a/firmware/application/file_path.cpp b/firmware/application/file_path.cpp index 9deeb9e3f..e50077a7c 100644 --- a/firmware/application/file_path.cpp +++ b/firmware/application/file_path.cpp @@ -53,5 +53,6 @@ const std::filesystem::path hopper_dir = u"HOPPER"; const std::filesystem::path subghz_dir = u"SUBGHZ"; const std::filesystem::path waterfalls_dir = u"WATERFALLS"; const std::filesystem::path macaddress_dir = u"MACADDRESS"; +const std::filesystem::path splash_dot_bmp = u"/splash.bmp"; const std::filesystem::path keeloq_keys_dir = u"KEELOQKEYS"; const std::filesystem::path keeloq_remotes_dir = u"KEELOQREMOTES"; diff --git a/firmware/application/file_path.hpp b/firmware/application/file_path.hpp index edc2df0ec..7dd956abc 100644 --- a/firmware/application/file_path.hpp +++ b/firmware/application/file_path.hpp @@ -58,4 +58,6 @@ extern const std::filesystem::path macaddress_dir; extern const std::filesystem::path keeloq_keys_dir; extern const std::filesystem::path keeloq_remotes_dir; +extern const std::filesystem::path splash_dot_bmp; + #endif /* __FILE_PATH_H__ */ diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 8cc65b5b0..210a8fe3a 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -1054,17 +1054,66 @@ SplashScreenView::SplashScreenView(NavigationView& nav) }; } +uint32_t SplashScreenView::myrand(uint32_t* state) { + uint32_t x = *state; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + *state = x; + return x; +} + +void SplashScreenView::get_random_splash_file(std::filesystem::path& path) { + path = u""; + + uint32_t rng_state = LPC_RTC->CTIME0; + if (rng_state == 0) rng_state = 0xABBACAFE; + + DIR dir; + FILINFO fno; + int32_t valid_count = 0; + + // Open directory + if (f_opendir(&dir, (const TCHAR*)splash_dir.c_str()) == FR_OK) { + while (f_readdir(&dir, &fno) == FR_OK && fno.fname[0] != 0) { + // Skip directories + if (fno.fattrib & AM_DIR) continue; + int len = 0; + while (fno.fname[len]) len++; // Get length + if (len >= 4) { + const TCHAR* ext = &fno.fname[len - 4]; + // Check extension case-insensitively + if (ext[0] == '.' && + (ext[1] == 'B' || ext[1] == 'b') && + (ext[2] == 'M' || ext[2] == 'm') && + (ext[3] == 'P' || ext[3] == 'p')) { + valid_count++; + // Reservoir Sampling: + if ((myrand(&rng_state) % valid_count) == 0) { + path = splash_dir / fno.fname; + } + } + } + } + f_closedir(&dir); + } +} + void SplashScreenView::paint(Painter&) { - constexpr const uint8_t zoom = 3; + set_clean(); // if (!bmp_view.load_bmp(splash_dot_bmp)) { //--too slow drawing, bc of the more bmp format support, and up-> down drawing - if (!portapack::display.draw_bmp_from_sdcard_file({0, 0}, splash_dot_bmp)) - // ^ try draw bmp file from sdcard at (0,0), and the (0,0) already bypassed the status bar, so actual pos is (0, STATUS_BAR_HEIGHT) - portapack::display.draw_bitmap({screen_width / 2 - ((bitmap_titlebar_image.size.width() * zoom) / 2), - screen_height / 2}, - bitmap_titlebar_image.size, - bitmap_titlebar_image.data, - Theme::getInstance()->bg_darkest->foreground, - Theme::getInstance()->bg_darkest->background, zoom); + if (portapack::display.draw_bmp_from_sdcard_file({0, 0}, splash_dot_bmp)) return; + // ^ try draw bmp file from sdcard at (0,0), and the (0,0) already bypassed the status bar, so actual pos is (0, STATUS_BAR_HEIGHT) + + std::filesystem::path path{}; + get_random_splash_file(path); + if (portapack::display.draw_bmp_from_sdcard_file({0, 0}, path)) return; + portapack::display.draw_bitmap({screen_width / 2 - ((bitmap_titlebar_image.size.width() * 3) / 2), + screen_height / 2}, + bitmap_titlebar_image.size, + bitmap_titlebar_image.data, + Theme::getInstance()->bg_darkest->foreground, + Theme::getInstance()->bg_darkest->background, 3); // ^ draw BMP HEX arr in firmware, note that the BMP HEX arr only cover the image part (instead of fill the screen with background, this position is located it in the center) } @@ -1082,7 +1131,6 @@ bool SplashScreenView::on_touch(const TouchEvent event) { case TouchEvent::Type::Start: handle_pop(); return false; - default: break; } diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index 8f608217f..29c834cb5 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -366,7 +366,8 @@ class SplashScreenView : public View { Button button_done{ {screen_width, 0, 1, 1}, ""}; - // BMPViewer bmp_view{ {0, 0, screen_width, screen_height - 16}}; + uint32_t myrand(uint32_t* state); + void get_random_splash_file(std::filesystem::path& path); }; class ReceiversMenuView : public BtnGridView { diff --git a/firmware/standalone/common/ui/file_path.cpp b/firmware/standalone/common/ui/file_path.cpp index a08811fb6..33c4e0057 100644 --- a/firmware/standalone/common/ui/file_path.cpp +++ b/firmware/standalone/common/ui/file_path.cpp @@ -53,3 +53,5 @@ const std::filesystem::path hopper_dir = u"HOPPER"; const std::filesystem::path subghz_dir = u"SUBGHZ"; const std::filesystem::path waterfalls_dir = u"WATERFALLS"; const std::filesystem::path macaddress_dir = u"MACADDRESS"; + +const std::filesystem::path splash_dot_bmp = u"/splash.bmp"; \ No newline at end of file diff --git a/firmware/standalone/common/ui/file_path.hpp b/firmware/standalone/common/ui/file_path.hpp index 0f8548ee0..9acac1f2c 100644 --- a/firmware/standalone/common/ui/file_path.hpp +++ b/firmware/standalone/common/ui/file_path.hpp @@ -56,4 +56,6 @@ extern const std::filesystem::path subghz_dir; extern const std::filesystem::path waterfalls_dir; extern const std::filesystem::path macaddress_dir; +extern const std::filesystem::path splash_dot_bmp; + #endif /* __FILE_PATH_H__ */