From af362600ef664366bbb8dfb47a4a087b38f5e339 Mon Sep 17 00:00:00 2001 From: sommermorgentraum <24917424+zxkmm@users.noreply.github.com> Date: Fri, 27 Dec 2024 19:01:33 +0800 Subject: [PATCH] app manager (#2442) * fix unset autostart * clean up - add comments to prevent misleading * move the app to external and with necessary changes * replace autostart app --- .../apps/ui_external_module_view.cpp | 3 + firmware/application/apps/ui_settings.cpp | 72 +---- firmware/application/apps/ui_settings.hpp | 42 +-- .../application/external/app_manager/main.cpp | 84 ++++++ .../external/app_manager/ui_app_manager.cpp | 275 ++++++++++++++++++ .../external/app_manager/ui_app_manager.hpp | 87 ++++++ firmware/application/external/external.cmake | 7 +- firmware/application/external/external.ld | 7 + firmware/application/ui_navigation.cpp | 2 +- firmware/application/ui_navigation.hpp | 4 + firmware/common/standalone_app.hpp | 3 +- 11 files changed, 479 insertions(+), 107 deletions(-) create mode 100644 firmware/application/external/app_manager/main.cpp create mode 100644 firmware/application/external/app_manager/ui_app_manager.cpp create mode 100644 firmware/application/external/app_manager/ui_app_manager.hpp diff --git a/firmware/application/apps/ui_external_module_view.cpp b/firmware/application/apps/ui_external_module_view.cpp index 5e376981..abd6d30d 100644 --- a/firmware/application/apps/ui_external_module_view.cpp +++ b/firmware/application/apps/ui_external_module_view.cpp @@ -92,6 +92,9 @@ void ExternalModuleView::on_tick_second() { case app_location_t::TX: btnText += " (TX)"; break; + case app_location_t::SETTINGS: + btnText += " (Settings)"; + break; case app_location_t::DEBUG: btnText += " (Debug)"; break; diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index 04fbdd50..50b45a5d 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -53,6 +53,8 @@ namespace fs = std::filesystem; #include "i2cdevmanager.hpp" #include "i2cdev_max17055.hpp" +#include "file_reader.hpp" + extern ui::SystemView* system_view_ptr; namespace pmem = portapack::persistent_memory; @@ -975,67 +977,6 @@ void SetMenuColorView::focus() { button_save.focus(); } -/* SetAutoStartView ************************************/ - -SetAutostartView::SetAutostartView(NavigationView& nav) { - add_children({&labels, - &button_save, - &button_cancel, - &button_reset, - &options}); - - button_save.on_select = [&nav, this](Button&) { - autostart_app = ""; - if (selected != 0) { - auto it = full_app_list.find(selected); - if (it != full_app_list.end()) - autostart_app = it->second; - } - nav.pop(); - }; - - button_cancel.on_select = [&nav, this](Button&) { - nav.pop(); - }; - - button_reset.on_select = [this](Button&) { - selected = 0; - options.set_selected_index(0); - autostart_app = ""; - }; - - // options - i = 0; - OptionsField::option_t o{"-none-", i}; - opts.emplace_back(o); - for (auto& app : NavigationView::appList) { - if (app.id == nullptr) continue; - i++; - o = {app.displayName, i}; - opts.emplace_back(o); - full_app_list.emplace(i, app.id); - if (autostart_app == app.id) selected = i; - } - ExternalItemsMenuLoader::load_all_external_items_callback([this](ui::AppInfoConsole& app) { - if (app.appCallName == nullptr) return; - i++; - OptionsField::option_t o = {app.appFriendlyName, i}; - opts.emplace_back(o); - full_app_list.emplace(i, app.appCallName); - if (autostart_app == app.appCallName) selected = i; - }); - - options.set_options(opts); - options.on_change = [this](size_t, OptionsField::value_t v) { - selected = v; - }; - options.set_selected_index(selected); -} - -void SetAutostartView::focus() { - options.focus(); -} - /* SetThemeView ************************************/ SetThemeView::SetThemeView(NavigationView& nav) { @@ -1117,9 +1058,12 @@ SettingsMenuView::SettingsMenuView(NavigationView& nav) } void SettingsMenuView::on_populate() { - if (pmem::show_gui_return_icon()) { + const bool return_icon = pmem::show_gui_return_icon(); + + if (return_icon) { add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [this]() { nav_.pop(); }}}); } + add_items({ {"App Settings", ui::Color::dark_cyan(), &bitmap_icon_notepad, [this]() { nav_.push(); }}, {"Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [this]() { nav_.push(); }}, @@ -1138,9 +1082,11 @@ 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(); }}, - {"Autostart", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push(); }}, }); + if (battery::BatteryManagement::isDetected()) add_item({"Battery", ui::Color::dark_cyan(), &bitmap_icon_batt_icon, [this]() { nav_.push(); }}); + + add_external_items(nav_, app_location_t::SETTINGS, *this, return_icon ? 1 : 0); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index 9a587946..d9a244bf 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -888,47 +888,6 @@ class SetMenuColorView : public View { }; }; -class SetAutostartView : public View { - public: - SetAutostartView(NavigationView& nav); - - void focus() override; - - std::string title() const override { return "Autostart"; }; - - private: - int32_t i = 0; - std::string autostart_app{""}; - OptionsField::options_t opts{}; - std::map full_app_list{}; // looking table - int32_t selected = 0; - SettingsStore nav_setting{ - "nav"sv, - {{"autostart_app"sv, &autostart_app}}}; - Labels labels{ - {{1 * 8, 1 * 16}, "Select app to start on boot", Theme::getInstance()->fg_light->foreground}, - {{2 * 8, 2 * 16}, "(an SD Card is required)", Theme::getInstance()->fg_light->foreground}}; - - Button button_save{ - {2 * 8, 16 * 16, 12 * 8, 32}, - "Save"}; - - OptionsField options{ - {0 * 8, 4 * 16}, - screen_width / 8, - {}, - true}; - - Button button_cancel{ - {16 * 8, 16 * 16, 12 * 8, 32}, - "Cancel", - }; - - Button button_reset{ - {2 * 8, 6 * 16, screen_width - 4 * 8, 32}, - "Reset"}; -}; - class SetThemeView : public View { public: SetThemeView(NavigationView& nav); @@ -1012,6 +971,7 @@ class SettingsMenuView : public BtnGridView { private: NavigationView& nav_; + void on_populate() override; }; diff --git a/firmware/application/external/app_manager/main.cpp b/firmware/application/external/app_manager/main.cpp new file mode 100644 index 00000000..ccb9b38b --- /dev/null +++ b/firmware/application/external/app_manager/main.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2024 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui.hpp" +#include "ui_app_manager.hpp" +#include "ui_navigation.hpp" +#include "external_app.hpp" + +namespace ui::external_app::app_manager { +void initialize_app(ui::NavigationView& nav) { + nav.push(); +} +} // namespace ui::external_app::app_manager + +extern "C" { + +__attribute__((section(".external_app.app_app_manager.application_information"), used)) application_information_t _application_information_app_manager = { + /*.memory_location = */ (uint8_t*)0x00000000, + /*.externalAppEntry = */ ui::external_app::app_manager::initialize_app, + /*.header_version = */ CURRENT_HEADER_VERSION, + /*.app_version = */ VERSION_MD5, + + /*.app_name = */ "AppManager", + /*.bitmap_data = */ { + 0xE0, + 0x00, + 0x10, + 0x01, + 0x10, + 0x01, + 0x1F, + 0x1F, + 0x01, + 0x10, + 0x01, + 0x10, + 0x01, + 0x10, + 0x01, + 0x70, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x80, + 0x01, + 0x70, + 0xE1, + 0x10, + 0x11, + 0x11, + 0x11, + 0x11, + 0x1F, + 0x1F, + + }, + /*.icon_color = */ ui::Color::cyan().v, + /*.menu_location = */ app_location_t::SETTINGS, + /*.desired_menu_position = */ -1, + + /*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0}, + /*.m4_app_offset = */ 0x00000000, // will be filled at compile time +}; +} \ No newline at end of file diff --git a/firmware/application/external/app_manager/ui_app_manager.cpp b/firmware/application/external/app_manager/ui_app_manager.cpp new file mode 100644 index 00000000..d7ebbfc1 --- /dev/null +++ b/firmware/application/external/app_manager/ui_app_manager.cpp @@ -0,0 +1,275 @@ +/* + * copyleft spammingdramaqueen + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui_app_manager.hpp" +#include "ui_navigation.hpp" +#include "ui_external_items_menu_loader.hpp" + +#include "file.hpp" +namespace fs = std::filesystem; + +#include "string_format.hpp" + +#include "file_reader.hpp" + +using namespace portapack; + +namespace ui::external_app::app_manager { + +/* AppManagerView **************************************/ +/* | inner apps | external apps | + * ------------------------------ + * | id | appCallName | + * | displayName|appFriendlyName| + */ +AppManagerView::AppManagerView(NavigationView& nav) + : nav_{nav} { + add_children({&labels, + &menu_view, + &text_app_info, + &button_hide_unhide, + &button_clean_hide, + &button_set_cancel_autostart, + &button_clean_autostart}); + + menu_view.set_parent_rect({0, 2 * 8, screen_width, 24 * 8}); + + menu_view.on_highlight = [this]() { + if (menu_view.highlighted_index() >= app_list_index) { + text_app_info.set(""); + return; + } + + std::string info; + auto app_name = get_app_info(menu_view.highlighted_index(), true); + auto app_id = get_app_info(menu_view.highlighted_index(), false); + + info += "Hidden:"; + + if (is_blacklisted(app_name)) { + info += "Yes "; + } else { + info += "No "; + } + + info += "Autostart:"; + if (is_autostart_app(app_id)) { + info += "Yes"; + } else { + info += "No"; + } + + if (info.empty()) { + info = "Highlight an app"; + } + + text_app_info.set(info); + }; + + button_hide_unhide.on_select = [this](Button&) { + hide_unhide_app(); + refresh_list(); + }; + + button_clean_hide.on_select = [this](Button&) { + clean_blacklist(); + refresh_list(); + }; + + button_set_cancel_autostart.on_select = [this](Button&) { + set_unset_autostart_app(); + refresh_list(); + }; + + button_clean_autostart.on_select = [this](Button&) { + unset_auto_start(); + refresh_list(); + }; + + refresh_list(); +} + +void AppManagerView::refresh_list() { + auto previous_cursor_index = menu_view.highlighted_index(); + app_list_index = 0; + menu_view.clear(); + + size_t index = 0; + + auto padding = [](const std::string& str) { + return str.length() < 25 ? std::string(25 - str.length(), ' ') + '' : ""; + // that weird char is a icon in portapack's font base that looks like a switch, which i use to indicate the auto start + }; + + for (auto& app : NavigationView::appList) { + if (app.id == nullptr) continue; + + app_list_index++; + + menu_view.add_item({app.displayName + std::string(is_autostart_app(app.id) ? padding(app.displayName) : ""), + app.iconColor, + app.icon, + [this, app_id = std::string(app.id)](KeyEvent) { + button_hide_unhide.focus(); + }}); + } + + ExternalItemsMenuLoader::load_all_external_items_callback([this, &index, &padding](ui::AppInfoConsole& app) { + if (app.appCallName == nullptr) return; + + app_list_index++; + + menu_view.add_item({app.appFriendlyName + std::string(is_autostart_app(app.appCallName) ? padding(app.appFriendlyName) : ""), + ui::Color::light_grey(), + &bitmap_icon_sdcard, + [this, app_id = std::string(app.appCallName)](KeyEvent) { + button_hide_unhide.focus(); + }}); + }); + + menu_view.set_highlighted(previous_cursor_index); +} + +void AppManagerView::focus() { + menu_view.focus(); +} + +void AppManagerView::hide_app() { + std::vector blacklist; + get_blacklist(blacklist); + + blacklist.push_back(get_app_info(menu_view.highlighted_index(), true)); + write_blacklist(blacklist); +} + +void AppManagerView::unhide_app() { + std::vector blacklist; + get_blacklist(blacklist); + blacklist.erase(std::remove(blacklist.begin(), blacklist.end(), get_app_info(menu_view.highlighted_index(), true)), blacklist.end()); + write_blacklist(blacklist); +} + +void AppManagerView::hide_unhide_app() { + if (is_blacklisted(get_app_info(menu_view.highlighted_index(), true))) + unhide_app(); + else + hide_app(); +} + +void AppManagerView::get_blacklist(std::vector& blacklist) { + File f; + + auto error = f.open(u"SETTINGS/blacklist"); + if (error) + return; + + auto reader = FileLineReader(f); + for (const auto& line : reader) { + if (line.length() == 0 || line[0] == '#') + continue; + + blacklist.push_back(trim(line)); + } +} + +void AppManagerView::write_blacklist(const std::vector& blacklist) { + File f; + auto error = f.create(u"SETTINGS/blacklist"); + if (error) + return; + + for (const auto& app_name : blacklist) { + auto line = app_name + "\n"; + f.write(line.c_str(), line.length()); + } +} + +void AppManagerView::clean_blacklist() { + std::vector blacklist = {}; + write_blacklist(blacklist); +} + +bool AppManagerView::is_blacklisted(const std::string& app_name) { + std::vector blacklist; + get_blacklist(blacklist); + return std::find(blacklist.begin(), blacklist.end(), app_name) != blacklist.end(); +} + +void AppManagerView::set_auto_start() { + auto app_index = menu_view.highlighted_index(); + if (app_index >= app_list_index) return; + + auto id_aka_app_call_name = get_app_info(app_index, false); + + autostart_app = id_aka_app_call_name; + + refresh_list(); +} + +void AppManagerView::unset_auto_start() { + autostart_app = ""; + refresh_list(); +} + +void AppManagerView::set_unset_autostart_app() { + auto app_index = menu_view.highlighted_index(); + if (app_index >= app_list_index) return; + + auto id_aka_friendly_name = get_app_info(app_index, false); + + if (is_autostart_app(id_aka_friendly_name)) + unset_auto_start(); + else + set_auto_start(); +} + +bool AppManagerView::is_autostart_app(const char* id_aka_friendly_name) { + return autostart_app == std::string(id_aka_friendly_name); +} + +bool AppManagerView::is_autostart_app(const std::string& id_aka_friendly_name) { + return autostart_app == id_aka_friendly_name; +} + +std::string AppManagerView::get_app_info(uint16_t index, bool is_display_name) { + size_t current_index = 0; + std::string result; + + for (auto& app : NavigationView::appList) { + if (app.id == nullptr) continue; + if (current_index == index) { + return is_display_name ? std::string(app.displayName) : std::string(app.id); + } + current_index++; + } + + ExternalItemsMenuLoader::load_all_external_items_callback([¤t_index, &index, &result, &is_display_name](ui::AppInfoConsole& app) { + if (app.appCallName == nullptr) return; + if (current_index == index) { + result = is_display_name ? app.appFriendlyName : app.appCallName; + } + current_index++; + }); + + return result; +} + +} // namespace ui::external_app::app_manager \ No newline at end of file diff --git a/firmware/application/external/app_manager/ui_app_manager.hpp b/firmware/application/external/app_manager/ui_app_manager.hpp new file mode 100644 index 00000000..5c2f5597 --- /dev/null +++ b/firmware/application/external/app_manager/ui_app_manager.hpp @@ -0,0 +1,87 @@ +/* + * copyleft spammingdramaqueen + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __UI_APP_MANAGER_H__ +#define __UI_APP_MANAGER_H__ + +#include "ui_navigation.hpp" + +namespace ui::external_app::app_manager { + +class AppManagerView : public View { + public: + AppManagerView(NavigationView& nav); + std::string title() const override { return "AppMan"; }; + + private: + NavigationView& nav_; + std::string autostart_app{""}; + SettingsStore nav_setting{ + "nav"sv, + {{"autostart_app"sv, &autostart_app}}}; + + void focus() override; + void refresh_list(); + uint16_t app_list_index{0}; + + Labels labels{ + {{0 * 8, 0 * 16}, "App list:", Theme::getInstance()->fg_light->foreground}}; + + MenuView menu_view{}; + + Text text_app_info{ + {0, 27 * 8, screen_width, 16}, + "Highlight an app"}; + + Button button_hide_unhide{ + {0, 29 * 8, screen_width / 2 - 1, 32}, + "Hide/Show"}; + + Button button_clean_hide{ + {screen_width / 2 + 2, 29 * 8, screen_width / 2 - 2, 32}, + "Clean Hidden"}; + + Button button_set_cancel_autostart{ + {0, screen_height - 32 - 16, screen_width / 2 - 1, 32}, + "Set Autostart"}; + + Button button_clean_autostart{ + {screen_width / 2 + 2, screen_height - 32 - 16, screen_width / 2 - 2, 32}, + "Del Autostart"}; + + std::string get_app_info(uint16_t index, bool is_display_name); + void get_blacklist(std::vector& blacklist); + void write_blacklist(const std::vector& blacklist); + bool is_blacklisted(const std::string& app_display_name); + void hide_app(); + void unhide_app(); + void hide_unhide_app(); + void clean_blacklist(); + bool is_autostart_app(const std::string& display_name); + void set_auto_start(); + void unset_auto_start(); + void set_unset_autostart_app(); + bool is_autostart_app(const char* id_aka_app_call_name); +}; + +} // namespace ui::external_app::app_manager + +#endif // __UI_APP_MANAGER_H__ \ No newline at end of file diff --git a/firmware/application/external/external.cmake b/firmware/application/external/external.cmake index 03fbffad..87fbaf05 100644 --- a/firmware/application/external/external.cmake +++ b/firmware/application/external/external.cmake @@ -146,6 +146,10 @@ set(EXTCPPSRC #metronome external/metronome/main.cpp external/metronome/ui_metronome.cpp + + #app_manager + external/app_manager/main.cpp + external/app_manager/ui_app_manager.cpp ) set(EXTAPPLIST @@ -184,4 +188,5 @@ set(EXTAPPLIST fmradio tuner metronome -) + app_manager +) \ No newline at end of file diff --git a/firmware/application/external/external.ld b/firmware/application/external/external.ld index 51a24ea5..17acd87e 100644 --- a/firmware/application/external/external.ld +++ b/firmware/application/external/external.ld @@ -58,6 +58,7 @@ MEMORY ram_external_app_fmradio(rwx) : org = 0xADD10000, len = 32k ram_external_app_tuner(rwx) : org = 0xADD20000, len = 32k ram_external_app_metronome(rwx) : org = 0xADD30000, len = 32k + ram_external_app_app_manager(rwx) : org = 0xADD40000, len = 32k } SECTIONS @@ -272,4 +273,10 @@ SECTIONS KEEP(*(.external_app.app_metronome.application_information)); *(*ui*external_app*metronome*); } > ram_external_app_metronome + + .external_app_app_manager : ALIGN(4) SUBALIGN(4) + { + KEEP(*(.external_app.app_app_manager.application_information)); + *(*ui*external_app*app_manager*); + } > ram_external_app_app_manager } diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 80bae630..fb56b15f 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -760,7 +760,7 @@ void NavigationView::handle_autostart() { /* Helpers **************************************************************/ -static void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc) { +void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc) { for (auto& app : NavigationView::appList) { if (app.menuLocation == loc) { grid.add_item({app.displayName, app.iconColor, app.icon, diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index ae49457b..56fad874 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -59,6 +59,10 @@ using namespace sd_card; namespace ui { +void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc); +void add_external_items(NavigationView& nav, app_location_t location, BtnGridView& grid, uint8_t error_tile_pos); +bool verify_sdcard_format(); + enum modal_t { INFO = 0, YESNO, diff --git a/firmware/common/standalone_app.hpp b/firmware/common/standalone_app.hpp index 6d8cfe9f..fa0b61b0 100644 --- a/firmware/common/standalone_app.hpp +++ b/firmware/common/standalone_app.hpp @@ -71,7 +71,8 @@ enum app_location_t : uint32_t { RX, TX, DEBUG, - HOME + HOME, + SETTINGS }; struct standalone_application_information_t {