Appstart and applist from serial (#1736)

* Start app, and list fixed ones.
* Add ext app support
This commit is contained in:
Totoo 2024-01-07 18:56:09 +01:00 committed by GitHub
parent 4b93e78dd9
commit 9d7e06c255
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 304 additions and 10 deletions

View File

@ -6,6 +6,44 @@ namespace ui {
/* static */ std::vector<DynamicBitmap<16, 16>> ExternalItemsMenuLoader::bitmaps; /* static */ std::vector<DynamicBitmap<16, 16>> ExternalItemsMenuLoader::bitmaps;
// iterates over all ppma-s, and if it is runnable on the current system, it'll call the callback, and pass info.
/* static */ void ExternalItemsMenuLoader::load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback) {
if (!callback) return;
if (sd_card::status() != sd_card::Status::Mounted)
return;
for (const auto& entry : std::filesystem::directory_iterator(u"APPS", u"*.ppma")) {
auto filePath = u"/APPS/" + entry.path();
File app;
auto openError = app.open(filePath);
if (openError)
continue;
application_information_t application_information = {};
auto readResult = app.read(&application_information, sizeof(application_information_t));
if (!readResult)
continue;
if (application_information.header_version != CURRENT_HEADER_VERSION)
continue;
bool versionMatches = VERSION_MD5 == application_information.app_version;
if (!versionMatches) continue;
// here the app is startable and good.
std::string appshortname = filePath.filename().string();
if (appshortname.size() >= 5 && appshortname.substr(appshortname.size() - 5) == ".ppma") {
// Remove the ".ppma" suffix
appshortname = appshortname.substr(0, appshortname.size() - 5);
}
AppInfoConsole info{
.appCallName = appshortname.c_str(),
.appFriendlyName = reinterpret_cast<char*>(&application_information.app_name[0]),
.appLocation = application_information.menu_location};
callback(info);
}
}
/* static */ std::vector<GridItem> ExternalItemsMenuLoader::load_external_items(app_location_t app_location, NavigationView& nav) { /* static */ std::vector<GridItem> ExternalItemsMenuLoader::load_external_items(app_location_t app_location, NavigationView& nav) {
bitmaps.clear(); bitmaps.clear();
@ -47,7 +85,9 @@ namespace ui {
bitmaps.push_back(std::move(dyn_bmp)); bitmaps.push_back(std::move(dyn_bmp));
gridItem.on_select = [&nav, app_location, filePath]() { gridItem.on_select = [&nav, app_location, filePath]() {
run_external_app(nav, filePath); if (!run_external_app(nav, filePath)) {
nav.display_modal("Error", "The .ppma file in your APPS\nfolder can't be read. Please\nupdate your SD Card content.");
}
}; };
} else { } else {
gridItem.color = Color::light_grey(); gridItem.color = Color::light_grey();
@ -65,19 +105,19 @@ namespace ui {
return external_apps; return external_apps;
} }
/* static */ void ExternalItemsMenuLoader::run_external_app(ui::NavigationView& nav, std::filesystem::path filePath) { /* static */ bool ExternalItemsMenuLoader::run_external_app(ui::NavigationView& nav, std::filesystem::path filePath) {
File app; File app;
auto openError = app.open(filePath); auto openError = app.open(filePath);
if (openError) if (openError)
chDbgPanic("file gone"); return false;
application_information_t application_information = {}; application_information_t application_information = {};
auto readResult = app.read(&application_information, sizeof(application_information_t)); auto readResult = app.read(&application_information, sizeof(application_information_t));
if (!readResult) if (!readResult)
chDbgPanic("no data"); return false;
app.seek(0); app.seek(0);
@ -93,7 +133,7 @@ namespace ui {
readResult = app.read(&application_information.memory_location[file_read_index], bytes_to_read); readResult = app.read(&application_information.memory_location[file_read_index], bytes_to_read);
if (!readResult) if (!readResult)
chDbgPanic("read error"); return false;
if (readResult.value() < std::filesystem::max_file_block_size) if (readResult.value() < std::filesystem::max_file_block_size)
break; break;
@ -114,7 +154,7 @@ namespace ui {
readResult = app.read(target_memory, bytes_to_read); readResult = app.read(target_memory, bytes_to_read);
if (!readResult) if (!readResult)
chDbgPanic("read error #2"); return false;
if (readResult.value() != bytes_to_read) if (readResult.value() != bytes_to_read)
break; break;
@ -126,7 +166,7 @@ namespace ui {
readResult = app.read(&application_information.memory_location[file_read_index], bytes_to_read); readResult = app.read(&application_information.memory_location[file_read_index], bytes_to_read);
if (!readResult) if (!readResult)
chDbgPanic("read error #3"); return false;
if (readResult.value() < std::filesystem::max_file_block_size) if (readResult.value() < std::filesystem::max_file_block_size)
break; break;
@ -134,6 +174,7 @@ namespace ui {
} }
application_information.externalAppEntry(nav); application_information.externalAppEntry(nav);
return true;
} }
} // namespace ui } // namespace ui

View File

@ -54,11 +54,11 @@ class ExternalItemsMenuLoader {
public: public:
static std::vector<GridItem> load_external_items(app_location_t, NavigationView&); static std::vector<GridItem> load_external_items(app_location_t, NavigationView&);
ExternalItemsMenuLoader() = delete; ExternalItemsMenuLoader() = delete;
static bool run_external_app(ui::NavigationView&, std::filesystem::path);
static void load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback);
private: private:
static std::vector<DynamicBitmap<16, 16>> bitmaps; static std::vector<DynamicBitmap<16, 16>> bitmaps;
static void run_external_app(ui::NavigationView&, std::filesystem::path);
}; };
} // namespace ui } // namespace ui

View File

@ -111,6 +111,169 @@ namespace pmem = portapack::persistent_memory;
namespace ui { namespace ui {
// When adding or removing apps from the Menu, please update it here:
std::vector<AppInfoConsole> NavigationView::fixedAppListFC = {
{"adsbrx", "ADS-B", RX},
{"ais", "AIS Boats", RX},
{"aprsrx", "APRS", RX},
{"audio", "Audio", RX},
{"blerx", "BLE Rx", RX},
{"ert", "ERT Meter", RX},
{"level", "Level", RX},
{"pocsagrx", "POCSAG", RX},
{"radiosnde", "Radiosnde", RX},
{"recon", "Recon", RX},
{"search", "Search", RX},
{"tpms", "TPMS Cars", RX},
{"weather", "Weather", RX},
{"subghzd", "SubGhzD", RX},
{"adsbtx", "ADS-B", TX},
{"aprstx", "APRS TX", TX},
{"bht", "BHT Xy/EP", TX},
{"bletx", "BLE Tx", TX},
{"morsetx", "Morse", TX},
{"ooktx", "OOK", TX},
{"pocsatx", "POCSAG TX", TX},
{"rdstx", "RDS TX", TX},
{"soundbrd", "Soundbrd", TX},
{"sstvtx", "SSTV", TX},
{"touchtune", "TouchTune", TX},
{"capture", "Capture", RX},
{"replay", "Replay", TX},
{"remote", "Remote", TX},
{"scanner", "Scanner", RX},
{"microphone", "Microphone", TX},
{"lookingglass", "Looking Glass", RX}};
bool NavigationView::StartAppByName(const char* name) {
pop();
if (strcmp(name, "adsbrx") == 0) {
push<ADSBRxView>();
return true;
}
if (strcmp(name, "ais") == 0) {
push<AISAppView>();
return true;
}
if (strcmp(name, "aprsrx") == 0) {
push<APRSRXView>();
return true;
}
if (strcmp(name, "audio") == 0) {
push<AnalogAudioView>();
return true;
}
if (strcmp(name, "blerx") == 0) {
push<BLERxView>();
return true;
}
if (strcmp(name, "ert") == 0) {
push<ERTAppView>();
return true;
}
if (strcmp(name, "level") == 0) {
push<LevelView>();
return true;
}
if (strcmp(name, "pocsagrx") == 0) {
push<POCSAGAppView>();
return true;
}
if (strcmp(name, "radiosnode") == 0) {
push<SondeView>();
return true;
}
if (strcmp(name, "recon") == 0) {
push<ReconView>();
return true;
}
if (strcmp(name, "search") == 0) {
push<SearchView>();
return true;
}
if (strcmp(name, "tpms") == 0) {
push<TPMSAppView>();
return true;
}
if (strcmp(name, "weather") == 0) {
push<WeatherView>();
return true;
}
if (strcmp(name, "subghzd") == 0) {
push<SubGhzDView>();
return true;
}
if (strcmp(name, "adsbtx") == 0) {
push<ADSBTxView>();
return true;
}
if (strcmp(name, "aprstx") == 0) {
push<APRSTXView>();
return true;
}
if (strcmp(name, "bht") == 0) {
push<BHTView>();
return true;
}
if (strcmp(name, "bletx") == 0) {
push<BLETxView>();
return true;
}
if (strcmp(name, "morsetx") == 0) {
push<MorseView>();
return true;
}
if (strcmp(name, "ooktx") == 0) {
push<EncodersView>();
return true;
}
if (strcmp(name, "pocsatx") == 0) {
push<POCSAGTXView>();
return true;
}
if (strcmp(name, "rdstx") == 0) {
push<RDSView>();
return true;
}
if (strcmp(name, "soundbrd") == 0) {
push<SoundBoardView>();
return true;
}
if (strcmp(name, "sstvtx") == 0) {
push<SSTVTXView>();
return true;
}
if (strcmp(name, "touchtune") == 0) {
push<TouchTunesView>();
return true;
}
if (strcmp(name, "capture") == 0) {
push<CaptureAppView>();
return true;
}
if (strcmp(name, "replay") == 0) {
push<PlaylistView>();
return true;
}
if (strcmp(name, "remote") == 0) {
push<RemoteView>();
return true;
}
if (strcmp(name, "scanner") == 0) {
push<ScannerView>();
return true;
}
if (strcmp(name, "microphone") == 0) {
push<MicTXView>();
return true;
}
if (strcmp(name, "lookingglass") == 0) {
push<GlassView>();
return true;
}
return false;
}
/* StatusTray ************************************************************/ /* StatusTray ************************************************************/
StatusTray::StatusTray(Point pos) StatusTray::StatusTray(Point pos)
@ -751,6 +914,9 @@ SystemView::SystemView(
Context& SystemView::context() const { Context& SystemView::context() const {
return context_; return context_;
} }
NavigationView* SystemView::get_navigation_view() {
return &navigation_view;
}
void SystemView::toggle_overlay() { void SystemView::toggle_overlay() {
switch (++overlay_active) { switch (++overlay_active) {

View File

@ -40,7 +40,7 @@
#include "diskio.h" #include "diskio.h"
#include "lfsr_random.hpp" #include "lfsr_random.hpp"
#include "sd_card.hpp" #include "sd_card.hpp"
#include "external_app.hpp"
#include <vector> #include <vector>
#include <utility> #include <utility>
@ -57,6 +57,12 @@ enum modal_t {
ABORT ABORT
}; };
struct AppInfoConsole {
const char* appCallName;
const char* appFriendlyName;
const app_location_t appLocation;
};
class NavigationView : public View { class NavigationView : public View {
public: public:
std::function<void(const View&)> on_view_changed{}; std::function<void(const View&)> on_view_changed{};
@ -99,6 +105,8 @@ class NavigationView : public View {
* Returns true if the handler was bound successfully. */ * Returns true if the handler was bound successfully. */
bool set_on_pop(std::function<void()> on_pop); bool set_on_pop(std::function<void()> on_pop);
static std::vector<AppInfoConsole> fixedAppListFC; // fixed app list for console. vector, so can be incomplete and still iterateable
bool StartAppByName(const char* name); // Starts a View (app) by name stored in fixedAppListFC. This is to start apps from console
private: private:
struct ViewState { struct ViewState {
std::unique_ptr<View> view; std::unique_ptr<View> view;
@ -321,6 +329,8 @@ class SystemView : public View {
void toggle_overlay(); void toggle_overlay();
void paint_overlay(); void paint_overlay();
NavigationView* get_navigation_view();
private: private:
uint8_t overlay_active{0}; uint8_t overlay_active{0};

View File

@ -40,9 +40,12 @@
#include "ff.h" #include "ff.h"
#include "chprintf.h" #include "chprintf.h"
#include "chqueues.h" #include "chqueues.h"
#include "ui_external_items_menu_loader.hpp"
#include "untar.hpp" #include "untar.hpp"
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include <string> #include <string>
#include <codecvt> #include <codecvt>
#include <cstring> #include <cstring>
@ -843,6 +846,78 @@ static void cmd_accessibility_readcurr(BaseSequentialStream* chp, int argc, char
chprintf(chp, "\r\nok\r\n"); chprintf(chp, "\r\nok\r\n");
} }
static void cmd_appstart(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argc;
(void)argv;
if (argc != 1) {
chprintf(chp, "Usage: appstart APPCALLNAME");
return;
}
auto evtd = getEventDispatcherInstance();
if (!evtd) return;
auto top_widget = evtd->getTopWidget();
if (!top_widget) return;
auto nav = static_cast<ui::SystemView*>(top_widget)->get_navigation_view();
if (!nav) return;
if (nav->StartAppByName(argv[0])) {
chprintf(chp, "ok\r\n");
return;
}
// since ext app loader changed, we can just pass the string to it, and it"ll return if started or not.
std::string appwithpath = "/APPS/";
appwithpath += argv[0];
appwithpath += ".ppma";
bool ret = ui::ExternalItemsMenuLoader::run_external_app(*nav, path_from_string8((char*)appwithpath.c_str()));
if (!ret) {
chprintf(chp, "error\r\n");
return;
}
chprintf(chp, "ok\r\n");
}
static void printAppInfo(BaseSequentialStream* chp, ui::AppInfoConsole& element) {
if (strlen(element.appCallName) == 0) return;
chprintf(chp, element.appCallName);
chprintf(chp, " ");
chprintf(chp, element.appFriendlyName);
chprintf(chp, " ");
switch (element.appLocation) {
case RX:
chprintf(chp, "[RX]\r\n");
break;
case TX:
chprintf(chp, "[TX]\r\n");
break;
case UTILITIES:
chprintf(chp, "[UTIL]\r\n");
break;
case DEBUG:
chprintf(chp, "[DEBUG]\r\n");
break;
default:
break;
}
}
// returns the installed apps, those can be called by appstart APPNAME
static void cmd_applist(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argc;
(void)argv;
auto evtd = getEventDispatcherInstance();
if (!evtd) return;
auto top_widget = evtd->getTopWidget();
if (!top_widget) return;
auto nav = static_cast<ui::SystemView*>(top_widget)->get_navigation_view();
if (!nav) return;
for (auto element : ui::NavigationView::fixedAppListFC) {
printAppInfo(chp, element);
}
ui::ExternalItemsMenuLoader::load_all_external_items_callback([chp](ui::AppInfoConsole& info) {
printAppInfo(chp, info);
});
chprintf(chp, "ok\r\n");
}
static void cmd_cpld_read(BaseSequentialStream* chp, int argc, char* argv[]) { static void cmd_cpld_read(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = const char* usage =
"usage: cpld_read <device> <target>\r\n" "usage: cpld_read <device> <target>\r\n"
@ -1032,6 +1107,8 @@ static const ShellCommand commands[] = {
{"cpld_read", cmd_cpld_read}, {"cpld_read", cmd_cpld_read},
{"accessibility_readall", cmd_accessibility_readall}, {"accessibility_readall", cmd_accessibility_readall},
{"accessibility_readcurr", cmd_accessibility_readcurr}, {"accessibility_readcurr", cmd_accessibility_readcurr},
{"applist", cmd_applist},
{"appstart", cmd_appstart},
{NULL, NULL}}; {NULL, NULL}};
static const ShellConfig shell_cfg1 = { static const ShellConfig shell_cfg1 = {